KiCad PCB EDA Suite
bus-wire-junction.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
5  * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <fctsys.h>
26 #include <sch_edit_frame.h>
27 #include <lib_item.h>
28 #include <general.h>
29 #include <sch_bus_entry.h>
30 #include <sch_junction.h>
31 #include <sch_line.h>
32 #include <sch_no_connect.h>
33 #include <sch_component.h>
34 #include <sch_sheet.h>
35 #include <sch_view.h>
36 #include <tools/ee_actions.h>
38 #include <tool/tool_manager.h>
39 #include "eeschema_id.h"
40 
41 void SCH_EDIT_FRAME::GetSchematicConnections( std::vector< wxPoint >& aConnections )
42 {
43  for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
44  {
45  // Avoid items that are changing
46  if( !( item->GetEditFlags() & ( IS_DRAGGED | IS_MOVED | IS_DELETED ) ) )
47  item->GetConnectionPoints( aConnections );
48  }
49 
50  // We always have some overlapping connection points. Drop duplicates here
51  std::sort( aConnections.begin(), aConnections.end(),
52  []( const wxPoint& a, const wxPoint& b ) -> bool
53  { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
54  aConnections.erase( unique( aConnections.begin(), aConnections.end() ), aConnections.end() );
55 }
56 
57 
59 {
60  std::vector<DANGLING_END_ITEM> endPoints;
61  bool hasStateChanged = false;
62 
63  for( SCH_ITEM* item = GetScreen()->GetDrawList().begin(); item; item = item->Next() )
64  item->GetEndPoints( endPoints );
65 
66  for( SCH_ITEM* item = GetScreen()->GetDrawList().begin(); item; item = item->Next() )
67  {
68  if( item->UpdateDanglingState( endPoints ) )
69  {
70  GetCanvas()->GetView()->Update( item, KIGFX::REPAINT );
71  hasStateChanged = true;
72  }
73  }
74 
75  return hasStateChanged;
76 }
77 
78 
79 bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd )
80 {
81  SCH_LINE* line;
82  SCH_ITEM* next_item = NULL;
83  bool retval = false;
84 
85  if( aStart == aEnd )
86  return retval;
87 
88  for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = next_item )
89  {
90  next_item = item->Next();
91 
92  // Don't remove wires that are already deleted or are currently being dragged
93  if( item->GetEditFlags() & ( STRUCT_DELETED | IS_DRAGGED | IS_MOVED | SKIP_STRUCT ) )
94  continue;
95 
96  if( item->Type() != SCH_LINE_T || item->GetLayer() != LAYER_WIRE )
97  continue;
98 
99  line = (SCH_LINE*) item;
100  if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
101  !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
102  {
103  continue;
104  }
105 
106  // Don't remove entire wires
107  if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
108  || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
109  {
110  continue;
111  }
112 
113  // Step 1: break the segment on one end. return_line remains line if not broken.
114  // Ensure that *line points to the segment containing aEnd
115  SCH_LINE* return_line = line;
116  BreakSegment( line, aStart, &return_line );
117  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
118  line = return_line;
119 
120  // Step 2: break the remaining segment. return_line remains line if not broken.
121  // Ensure that *line _also_ contains aStart. This is our overlapping segment
122  BreakSegment( line, aEnd, &return_line );
123  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
124  line = return_line;
125 
126  SaveCopyInUndoList( line, UR_DELETED, true );
127  RemoveFromScreen( line );
128 
129  retval = true;
130  }
131 
132  return retval;
133 }
134 
135 
137 {
138  SCH_ITEM* item = NULL;
139  SCH_ITEM* secondItem = NULL;
140  PICKED_ITEMS_LIST itemList;
142 
143  if( aScreen == nullptr )
144  aScreen = GetScreen();
145 
146  auto remove_item = [ &itemList ]( SCH_ITEM* aItem ) -> void
147  {
148  aItem->SetFlags( STRUCT_DELETED );
149  itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
150  };
151 
152  BreakSegmentsOnJunctions( aScreen );
153 
154  for( item = aScreen->GetDrawItems(); item; item = item->Next() )
155  {
156  if( ( item->Type() != SCH_LINE_T )
157  && ( item->Type() != SCH_JUNCTION_T )
158  && ( item->Type() != SCH_NO_CONNECT_T ) )
159  continue;
160 
161  if( item->GetEditFlags() & STRUCT_DELETED )
162  continue;
163 
164  // Remove unneeded junctions
165  if( ( item->Type() == SCH_JUNCTION_T )
166  && ( !aScreen->IsJunctionNeeded( item->GetPosition() ) ) )
167  {
168  remove_item( item );
169  continue;
170  }
171 
172  // Remove zero-length lines
173  if( item->Type() == SCH_LINE_T
174  && ( (SCH_LINE*) item )->IsNull() )
175  {
176  remove_item( item );
177  continue;
178  }
179 
180  for( secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
181  {
182  if( item->Type() != secondItem->Type()
183  || ( secondItem->GetEditFlags() & STRUCT_DELETED ) )
184  continue;
185 
186  // Merge overlapping lines
187  if( item->Type() == SCH_LINE_T )
188  {
189  SCH_LINE* firstLine = (SCH_LINE*) item;
190  SCH_LINE* secondLine = (SCH_LINE*) secondItem;
191  SCH_LINE* line = NULL;
192  bool needed = false;
193 
194  if( !secondLine->IsParallel( firstLine )
195  || secondLine->GetLineStyle() != firstLine->GetLineStyle()
196  || secondLine->GetLineColor() != firstLine->GetLineColor()
197  || secondLine->GetLineSize() != firstLine->GetLineSize() )
198  continue;
199 
200  // Remove identical lines
201  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
202  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
203  {
204  remove_item( secondItem );
205  continue;
206  }
207 
208  // If the end points overlap, check if we still need the junction
209  if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
210  needed = aScreen->IsJunctionNeeded( firstLine->GetStartPoint() );
211  else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
212  needed = aScreen->IsJunctionNeeded( firstLine->GetEndPoint() );
213 
214  if( !needed && ( line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) ) )
215  {
216  remove_item( item );
217  remove_item( secondItem );
218  itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
219 
220  AddToScreen( line, aScreen );
221 
222  if( line->IsSelected() )
223  selectionTool->AddItemToSel( line, true /*quiet mode*/ );
224 
225  break;
226  }
227  }
228  // Remove duplicate junctions and no-connects
229  else if( secondItem->GetPosition() == item->GetPosition() )
230  remove_item( secondItem );
231  }
232  }
233 
234  for( item = aScreen->GetDrawItems(); item; item = secondItem )
235  {
236  secondItem = item->Next();
237 
238  if( item->GetEditFlags() & STRUCT_DELETED )
239  {
240  if( item->IsSelected() )
241  selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
242 
243  RemoveFromScreen( item, aScreen );
244  }
245  }
246 
247  if( itemList.GetCount() )
248  SaveCopyInUndoList( itemList, UR_DELETED, true );
249 
250  return itemList.GetCount() > 0;
251 }
252 
253 
254 void SCH_EDIT_FRAME::NormalizeSchematicOnFirstLoad( bool recalculateConnections )
255 {
256  SCH_SHEET_LIST list( g_RootSheet );
257 
258  for( const auto& sheet : list )
259  SchematicCleanUp( sheet.LastScreen() );
260 
261  if( recalculateConnections )
262  RecalculateConnections( false );
263 }
264 
265 
266 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
267  SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
268 {
269  if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
270  || aSegment->IsEndPoint( aPoint ) )
271  return false;
272 
273  if( aScreen == nullptr )
274  aScreen = GetScreen();
275 
276  SCH_LINE* newSegment = new SCH_LINE( *aSegment );
277 
278  newSegment->SetStartPoint( aPoint );
279  AddToScreen( newSegment, aScreen );
280 
281  SaveCopyInUndoList( newSegment, UR_NEW, true );
282  SaveCopyInUndoList( aSegment, UR_CHANGED, true );
283 
284  RefreshItem( aSegment );
285  aSegment->SetEndPoint( aPoint );
286 
287  if( aNewSegment )
288  *aNewSegment = newSegment;
289 
290  return true;
291 }
292 
293 
294 bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
295 {
296  static KICAD_T wiresAndBusses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
297 
298  if( aScreen == nullptr )
299  aScreen = GetScreen();
300 
301  bool brokenSegments = false;
302 
303  for( SCH_ITEM* segment = aScreen->GetDrawItems(); segment; segment = segment->Next() )
304  {
305  if( segment->IsType( wiresAndBusses ) )
306  brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint, NULL, aScreen );
307  }
308 
309  return brokenSegments;
310 }
311 
312 
314 {
315  if( aScreen == nullptr )
316  aScreen = GetScreen();
317 
318  bool brokenSegments = false;
319 
320  for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
321  {
322  if( item->Type() == SCH_JUNCTION_T )
323  {
324  SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
325 
326  brokenSegments |= BreakSegments( junction->GetPosition(), aScreen );
327  }
328  else if( item->Type() == SCH_BUS_BUS_ENTRY_T || item->Type() == SCH_BUS_WIRE_ENTRY_T )
329  {
330  SCH_BUS_ENTRY_BASE* busEntry = (SCH_BUS_ENTRY_BASE*) item;
331 
332  brokenSegments |= BreakSegments( busEntry->GetPosition(), aScreen );
333  brokenSegments |= BreakSegments( busEntry->m_End(), aScreen );
334  }
335  }
336 
337  return brokenSegments;
338 }
339 
340 
341 void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
342 {
343  SCH_SCREEN* screen = GetScreen();
344  PICKED_ITEMS_LIST undoList;
346 
347  auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
348  {
349  aItem->SetFlags( STRUCT_DELETED );
350  undoList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
351  };
352 
353  remove_item( aJunction );
354 
355  for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() )
356  {
357  SCH_LINE* firstLine = dynamic_cast<SCH_LINE*>( item );
358 
359  if( !firstLine || !firstLine->IsEndPoint( aJunction->GetPosition() )
360  || ( firstLine->GetEditFlags() & STRUCT_DELETED ) )
361  continue;
362 
363  for( SCH_ITEM* secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
364  {
365  SCH_LINE* secondLine = dynamic_cast<SCH_LINE*>( secondItem );
366 
367  if( !secondLine || !secondLine->IsEndPoint( aJunction->GetPosition() )
368  || ( secondItem->GetEditFlags() & STRUCT_DELETED )
369  || !secondLine->IsParallel( firstLine ) )
370  continue;
371 
372 
373  // Remove identical lines
374  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
375  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
376  {
377  remove_item( secondItem );
378  continue;
379  }
380 
381  // Try to merge the remaining lines
382  if( SCH_LINE* line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) )
383  {
384  remove_item( item );
385  remove_item( secondItem );
386  undoList.PushItem( ITEM_PICKER( line, UR_NEW ) );
387 
388  AddToScreen( line );
389 
390  if( line->IsSelected() )
391  selectionTool->AddItemToSel( line, true /*quiet mode*/ );
392 
393  break;
394  }
395  }
396  }
397 
398  SaveCopyInUndoList( undoList, UR_DELETED, aAppend );
399 
400  for( unsigned ii = 0; ii < undoList.GetCount(); ii++ )
401  {
402  EDA_ITEM* item = undoList.GetPickedItem( ii );
403 
404  if( item->GetEditFlags() & STRUCT_DELETED )
405  {
406  if( item->IsSelected() )
407  selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
408 
409  RemoveFromScreen( item );
410  }
411  }
412 }
413 
414 
415 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPos, bool aUndoAppend, bool aFinal )
416 {
417  SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
418 
419  AddToScreen( junction );
420  SaveCopyInUndoList( junction, UR_NEW, aUndoAppend );
421  BreakSegments( aPos );
422 
423  if( aFinal )
424  {
426 
428  OnModify();
429 
430  auto view = GetCanvas()->GetView();
431  view->ClearPreview();
432  view->ShowPreview( false );
433  view->ClearHiddenFlags();
434  }
435 
436  return junction;
437 }
438 
439 
Class SCH_SHEET_LIST.
KIGFX::SCH_VIEW * GetView() const override
Function GetView() Returns a pointer to the VIEW instance used in the panel.
COLOR4D GetLineColor() const
Definition: sch_line.cpp:263
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Function IsPointOnSegment.
Definition: trigo.cpp:40
bool SchematicCleanUp(SCH_SCREEN *aScreen=nullptr)
Performs routine schematic cleaning including breaking wire and buses and deleting identical objects ...
void GetSchematicConnections(std::vector< wxPoint > &aConnections)
Collects a unique list of all possible connection points in the schematic.
wxPoint GetStartPoint() const
Definition: sch_line.h:95
bool IsSelected() const
Definition: base_struct.h:233
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
bool IsJunctionNeeded(const wxPoint &aPosition, bool aNew=false)
Test if a junction is required for the items at aPosition on the screen.
Definition: sch_screen.cpp:347
SCH_ITEM * Next() const
Definition: sch_item.h:153
bool BreakSegments(const wxPoint &aPoint, SCH_SCREEN *aScreen=nullptr)
Checks every wire and bus for a intersection at aPoint and break into two segments at aPoint if an in...
bool BreakSegment(SCH_LINE *aSegment, const wxPoint &aPoint, SCH_LINE **aNewSegment=NULL, SCH_SCREEN *aScreen=nullptr)
Breaks a single segment into two at the specified point.
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Definition: base_struct.h:131
bool IsParallel(SCH_LINE *aLine)
Definition: sch_line.cpp:395
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
bool TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
unsigned GetCount() const
Function GetCount.
search types array terminator (End Of Types)
Definition: typeinfo.h:82
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
static const TOOL_EVENT SelectedItemsModified
Definition: actions.h:201
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:99
EDA_ITEM * Next() const
Definition: base_struct.h:218
Base class for a bus or wire entry.
Definition: sch_bus_entry.h:41
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
#define IS_DRAGGED
Item being dragged.
Definition: base_struct.h:122
virtual wxPoint GetPosition() const =0
Function GetPosition.
Item is being added to the view.
Definition: view_item.h:60
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:47
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_junction.h:100
STATUS_FLAGS GetEditFlags() const
Definition: base_struct.h:270
#define IS_DELETED
Definition: base_struct.h:123
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:96
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SaveCopyInUndoList(SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, bool aAppend=false, const wxPoint &aTransformPoint=wxPoint(0, 0))
Create a copy of the current schematic item, and put it in the undo list.
void ClearPreview()
Definition: sch_view.cpp:143
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
Function GetPickedItem.
#define STRUCT_DELETED
flag indication structures to be erased
Definition: base_struct.h:129
SCH_JUNCTION * AddJunction(const wxPoint &aPos, bool aAppendToUndo=false, bool aFinal=true)
bool IsEndPoint(const wxPoint &aPoint) const
Definition: sch_line.h:88
wxPoint m_End() const
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags)
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: view.cpp:1540
bool BreakSegmentsOnJunctions(SCH_SCREEN *aScreen=nullptr)
Tests all junctions and bus entries in the schematic for intersections with wires and buses and break...
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
int GetLineSize() const
Definition: sch_line.h:126
EDA_ITEM * MergeOverlap(SCH_LINE *aLine)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:408
int AddItemToSel(const TOOL_EVENT &aEvent)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
void RefreshItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
void DeleteJunction(SCH_ITEM *aItem, bool aAppend=false)
Removes a given junction and heals any wire segments under the junction.
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:163
bool TrimWire(const wxPoint &aStart, const wxPoint &aEnd)
If any single wire passes through both points, remove the portion between the two points,...
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
TOOL_MANAGER * m_toolManager
int GetLineStyle() const
Definition: sch_line.cpp:289
void PostEvent(const TOOL_EVENT &aEvent)
Puts an event to the event queue to be processed at the end of event processing cycle.
Definition: tool_manager.h:228
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
Definition: sch_item.h:114
void RecalculateConnections(bool aDoCleanup=true)
Generates the connection data for the entire schematic hierarchy.
void NormalizeSchematicOnFirstLoad(bool recalculateConnections)
Perform all cleanup and normalization steps so that the whole schematic is in a good state.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:210
wxPoint GetPosition() const override
Function GetPosition.
#define IS_MOVED
Item being moved.
Definition: base_struct.h:119
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:152
wxPoint GetEndPoint() const
Definition: sch_line.h:98