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-2021 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 <core/kicad_algo.h>
26 #include <eeschema_id.h>
27 #include <general.h>
28 #include <lib_item.h>
29 #include <sch_bus_entry.h>
30 #include <sch_symbol.h>
31 #include <sch_edit_frame.h>
32 #include <sch_junction.h>
33 #include <sch_line.h>
34 #include <sch_no_connect.h>
35 #include <sch_screen.h>
36 #include <sch_sheet.h>
37 #include <sch_view.h>
38 #include <tool/tool_manager.h>
39 #include <tools/ee_actions.h>
41 #include <trigo.h>
42 
43 
45 {
46  std::vector<wxPoint> retval;
47 
48  for( SCH_ITEM* item : GetScreen()->Items() )
49  {
50  // Avoid items that are changing
51  if( !( item->GetEditFlags() & ( IS_DRAGGING | IS_MOVING | IS_DELETED ) ) )
52  {
53  std::vector<wxPoint> pts = item->GetConnectionPoints();
54  retval.insert( retval.end(), pts.begin(), pts.end() );
55  }
56  }
57 
58  // We always have some overlapping connection points. Drop duplicates here
59  std::sort( retval.begin(), retval.end(),
60  []( const wxPoint& a, const wxPoint& b ) -> bool
61  { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
62  retval.erase(
63  std::unique( retval.begin(), retval.end() ), retval.end() );
64 
65  return retval;
66 }
67 
68 
70 {
71  std::function<void( SCH_ITEM* )> changeHandler =
72  [&]( SCH_ITEM* aChangedItem ) -> void
73  {
74  GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT );
75  };
76 
77  GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
78 }
79 
80 
81 bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd )
82 {
83  SCH_SCREEN* screen = GetScreen();
84  bool retval = false;
85 
86  std::vector<SCH_LINE*> wires;
87  EDA_RECT bb( aStart, wxSize( 1, 1 ) );
88 
89  bb.Merge( aEnd );
90 
91  if( aStart == aEnd )
92  return retval;
93 
94  // We cannot modify the RTree while iterating, so push the possible
95  // wires into a separate structure.
96  for( EDA_ITEM* item : screen->Items().Overlapping( bb ) )
97  {
98  SCH_LINE* line = static_cast<SCH_LINE*>( item );
99 
100  if( item->Type() == SCH_LINE_T && line->GetLayer() == LAYER_WIRE )
101  wires.push_back( line );
102  }
103 
104  for( SCH_LINE* line : wires )
105  {
106  // Don't remove wires that are already deleted or are currently being dragged
107  if( line->GetEditFlags() & ( STRUCT_DELETED | IS_DRAGGING | IS_MOVING | SKIP_STRUCT ) )
108  continue;
109 
110  if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
111  !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
112  {
113  continue;
114  }
115 
116  // Don't remove entire wires
117  if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
118  || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
119  {
120  continue;
121  }
122 
123  // Step 1: break the segment on one end. return_line remains line if not broken.
124  // Ensure that *line points to the segment containing aEnd
125  SCH_LINE* return_line = line;
126  BreakSegment( line, aStart, &return_line );
127 
128  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
129  line = return_line;
130 
131  // Step 2: break the remaining segment. return_line remains line if not broken.
132  // Ensure that *line _also_ contains aStart. This is our overlapping segment
133  BreakSegment( line, aEnd, &return_line );
134 
135  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
136  line = return_line;
137 
138  SaveCopyInUndoList( screen, line, UNDO_REDO::DELETED, true );
139  RemoveFromScreen( line, screen );
140 
141  retval = true;
142  }
143 
144  return retval;
145 }
146 
147 
149 {
150  PICKED_ITEMS_LIST itemList;
152  std::vector<SCH_ITEM*> deletedItems;
153  std::vector<SCH_LINE*> lines;
154  std::vector<SCH_JUNCTION*> junctions;
155  std::vector<SCH_NO_CONNECT*> ncs;
156  bool changed = true;
157 
158  if( aScreen == nullptr )
159  aScreen = GetScreen();
160 
161  auto remove_item = [&]( SCH_ITEM* aItem ) -> void
162  {
163  changed = true;
164  aItem->SetFlags( STRUCT_DELETED );
165  itemList.PushItem( ITEM_PICKER( aScreen, aItem, UNDO_REDO::DELETED ) );
166  deletedItems.push_back( aItem );
167  };
168 
169  BreakSegmentsOnJunctions( aScreen );
170 
171  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
172  {
173  if( !aScreen->IsJunctionNeeded( item->GetPosition() ) )
174  remove_item( item );
175  else
176  junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
177  }
178 
179  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
180  {
181  ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
182  }
183 
184  alg::for_all_pairs( junctions.begin(), junctions.end(),
185  [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
186  {
187  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
188  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
189  {
190  return;
191  }
192 
193  if( aFirst->GetPosition() == aSecond->GetPosition() )
194  remove_item( aSecond );
195  } );
196 
197  alg::for_all_pairs( ncs.begin(), ncs.end(),
198  [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
199  {
200  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
201  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
202  {
203  return;
204  }
205 
206  if( aFirst->GetPosition() == aSecond->GetPosition() )
207  remove_item( aSecond );
208  } );
209 
210 
211  while( changed )
212  {
213  changed = false;
214  lines.clear();
215 
216  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
217  {
218  if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
219  lines.push_back( static_cast<SCH_LINE*>( item ) );
220  }
221 
222  for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
223  {
224  SCH_LINE* firstLine = *it1;
225 
226  if( firstLine->GetEditFlags() & STRUCT_DELETED )
227  continue;
228 
229  if( firstLine->IsNull() )
230  {
231  remove_item( firstLine );
232  continue;
233  }
234 
235  auto it2 = it1;
236 
237  for( ++it2; it2 != lines.end(); ++it2 )
238  {
239  SCH_LINE* secondLine = *it2;
240 
241  if( secondLine->GetFlags() & STRUCT_DELETED )
242  continue;
243 
244  if( !secondLine->IsParallel( firstLine )
245  || !secondLine->IsStrokeEquivalent( firstLine )
246  || secondLine->GetLayer() != firstLine->GetLayer() )
247  {
248  continue;
249  }
250 
251  // Remove identical lines
252  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
253  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
254  {
255  remove_item( secondLine );
256  continue;
257  }
258 
259  // See if we can merge an overlap (or two colinear touching segments with
260  // no junction where they meet).
261  SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
262 
263  if( mergedLine != nullptr )
264  {
265  remove_item( firstLine );
266  remove_item( secondLine );
267  itemList.PushItem( ITEM_PICKER( aScreen, mergedLine, UNDO_REDO::NEWITEM ) );
268 
269  AddToScreen( mergedLine, aScreen );
270 
271  if( firstLine->IsSelected() )
272  selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
273 
274  break;
275  }
276  }
277  }
278  }
279 
280  for( SCH_ITEM* item : deletedItems )
281  {
282  if( item->IsSelected() )
283  selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
284 
285  RemoveFromScreen( item, aScreen );
286  }
287 
288  if( itemList.GetCount() )
289  SaveCopyInUndoList( itemList, UNDO_REDO::DELETED, true );
290 
291  return itemList.GetCount() > 0;
292 }
293 
294 
295 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
296  SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
297 {
298  if( aScreen == nullptr )
299  aScreen = GetScreen();
300 
301  SCH_LINE* newSegment = new SCH_LINE( *aSegment );
302 
303  newSegment->SetStartPoint( aPoint );
304  AddToScreen( newSegment, aScreen );
305 
306  SaveCopyInUndoList( aScreen, newSegment, UNDO_REDO::NEWITEM, true );
307  SaveCopyInUndoList( aScreen, aSegment, UNDO_REDO::CHANGED, true );
308 
309  UpdateItem( aSegment, false, true );
310  aSegment->SetEndPoint( aPoint );
311 
312  if( aNewSegment )
313  *aNewSegment = newSegment;
314 
315  return true;
316 }
317 
318 
319 bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
320 {
321  static const KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
322 
323  if( aScreen == nullptr )
324  aScreen = GetScreen();
325 
326  bool brokenSegments = false;
327  std::vector<SCH_LINE*> wires;
328 
329  for( SCH_ITEM* item : aScreen->Items().Overlapping( SCH_LINE_T, aPoint ) )
330  {
331  if( item->IsType( wiresAndBuses ) )
332  {
333  SCH_LINE* wire = static_cast<SCH_LINE*>( item );
334 
335  if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), aPoint )
336  && !wire->IsEndPoint( aPoint ) )
337  {
338  wires.push_back( wire );
339  }
340  }
341  }
342 
343  for( SCH_LINE* wire : wires )
344  brokenSegments |= BreakSegment( wire, aPoint, nullptr, aScreen );
345 
346  return brokenSegments;
347 }
348 
349 
351 {
352  if( aScreen == nullptr )
353  aScreen = GetScreen();
354 
355  bool brokenSegments = false;
356 
357  std::set<wxPoint> point_set;
358 
359  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
360  point_set.insert( item->GetPosition() );
361 
362  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
363  {
364  SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
365  point_set.insert( entry->GetPosition() );
366  point_set.insert( entry->GetEnd() );
367  }
368 
369 
370  for( const wxPoint& pt : point_set )
371  brokenSegments |= BreakSegments( pt, aScreen );
372 
373  return brokenSegments;
374 }
375 
376 
377 void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
378 {
379  SCH_SCREEN* screen = GetScreen();
380  PICKED_ITEMS_LIST undoList;
383 
384  auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
385  {
386  aItem->SetFlags( STRUCT_DELETED );
387  undoList.PushItem( ITEM_PICKER( screen, aItem, UNDO_REDO::DELETED ) );
388  };
389 
390  remove_item( aJunction );
391  RemoveFromScreen( aJunction, screen );
392 
395  std::list<SCH_LINE*> lines;
396 
397  for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
398  {
399  SCH_LINE* line = static_cast<SCH_LINE*>( item );
400 
401  if( line->IsType( wiresAndBuses ) && line->IsEndPoint( aJunction->GetPosition() )
402  && !( line->GetEditFlags() & STRUCT_DELETED ) )
403  lines.push_back( line );
404  }
405 
406  alg::for_all_pairs( lines.begin(), lines.end(),
407  [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
408  {
409  if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
410  || ( secondLine->GetEditFlags() & STRUCT_DELETED )
411  || !secondLine->IsParallel( firstLine ) )
412  {
413  return;
414  }
415 
416  // Remove identical lines
417  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
418  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
419  {
420  remove_item( firstLine );
421  return;
422  }
423 
424  // Try to merge the remaining lines
425  if( SCH_LINE* line = secondLine->MergeOverlap( screen, firstLine, false ) )
426  {
427  remove_item( firstLine );
428  remove_item( secondLine );
429  undoList.PushItem( ITEM_PICKER( screen, line, UNDO_REDO::NEWITEM ) );
430  AddToScreen( line, screen );
431 
432  if( line->IsSelected() )
433  selectionTool->AddItemToSel( line, true /*quiet mode*/ );
434 
435  lines.push_back( line );
436  }
437  } );
438 
439  SaveCopyInUndoList( undoList, UNDO_REDO::DELETED, aAppend );
440 
441  for( SCH_LINE* line : lines )
442  {
443  if( line->GetEditFlags() & STRUCT_DELETED )
444  {
445  if( line->IsSelected() )
446  selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
447 
448  RemoveFromScreen( line, screen );
449  }
450  }
451 }
452 
453 
454 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( SCH_SCREEN* aScreen, const wxPoint& aPos,
455  bool aUndoAppend, bool aFinal )
456 {
457  SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
458 
459  AddToScreen( junction, aScreen );
460  SaveCopyInUndoList( aScreen, junction, UNDO_REDO::NEWITEM, aUndoAppend );
461  BreakSegments( aPos );
462 
463  if( aFinal )
464  {
466 
468  OnModify();
469 
470  KIGFX::SCH_VIEW* view = GetCanvas()->GetView();
471  view->ClearPreview();
472  view->ShowPreview( false );
473  view->ClearHiddenFlags();
474  }
475 
476  return junction;
477 }
478 
479 
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:216
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
SCH_LINE * MergeOverlap(SCH_SCREEN *aScreen, SCH_LINE *aLine, bool aCheckJunctions)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:466
bool SchematicCleanUp(SCH_SCREEN *aScreen=nullptr)
Perform routine schematic cleaning including breaking wire and buses and deleting identical objects s...
void ClearHiddenFlags()
Clear the hide flag of all items in the view.
Definition: sch_view.cpp:171
void Merge(const EDA_RECT &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: eda_rect.cpp:432
bool IsStrokeEquivalent(const SCH_LINE *aLine)
Definition: sch_line.h:127
wxPoint GetStartPoint() const
Definition: sch_line.h:90
bool IsSelected() const
Definition: eda_item.h:123
wxPoint GetPosition() const override
bool BreakSegments(const wxPoint &aPoint, SCH_SCREEN *aScreen=nullptr)
Check every wire and bus for a intersection at aPoint and break into two segments at aPoint if an int...
#define IS_DRAGGING
Item being dragged.
void TestDanglingEnds(const SCH_SHEET_PATH *aPath=nullptr, std::function< void(SCH_ITEM *)> *aChangedHandler=nullptr) const
Test all of the connectable objects in the schematic for unused connection points.
Definition: sch_screen.cpp:980
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Test if aTestPoint is on line defined by aSegStart and aSegEnd.
Definition: trigo.cpp:42
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
void PushItem(const ITEM_PICKER &aItem)
Push aItem to the top of the list.
virtual wxPoint GetPosition() const
Definition: eda_item.h:252
unsigned GetCount() const
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:204
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:94
SCH_JUNCTION * AddJunction(SCH_SCREEN *aScreen, const wxPoint &aPos, bool aAppendToUndo, bool aFinal=true)
void ShowPreview(bool aShow=true)
Definition: view.cpp:1570
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition: kicad_algo.h:83
Item needs to be redrawn.
Definition: view_item.h:52
#define IS_DELETED
#define IS_MOVING
Item being moved.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
wxPoint GetPosition() const override
Definition: sch_junction.h:92
bool IsNull() const
Definition: sch_line.h:88
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:91
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void ClearPreview()
Definition: view.cpp:1534
std::vector< wxPoint > GetSchematicConnections()
Collect a unique list of all possible connection points in the schematic.
#define STRUCT_DELETED
flag indication structures to be erased
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false)
Mark an item for refresh.
bool IsEndPoint(const wxPoint &aPoint) const
Definition: sch_line.h:80
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:158
A holder to handle information on schematic or board items.
EE_TYPE Overlapping(const EDA_RECT &aRect) const
Definition: sch_rtree.h:221
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:155
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:257
bool BreakSegmentsOnJunctions(SCH_SCREEN *aScreen=nullptr)
Test all junctions and bus entries in the schematic for intersections with wires and buses and breaks...
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool BreakSegment(SCH_LINE *aSegment, const wxPoint &aPoint, SCH_LINE **aNewSegment=nullptr, SCH_SCREEN *aScreen=nullptr)
Break a single segment into two at the specified point.
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:158
#define SKIP_STRUCT
flag indicating that the structure should be ignored
int AddItemToSel(const TOOL_EVENT &aEvent)
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend)
Create a copy of the current schematic item, and put it in the undo list.
void DeleteJunction(SCH_ITEM *aItem, bool aAppend=false)
Removes a given junction and heals any wire segments under the junction.
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
Handle the component boundary box.
Definition: eda_rect.h:42
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
bool IsJunctionNeeded(const wxPoint &aPosition, bool aNew=false) const
Test if a junction is required for the items at aPosition on the screen.
Definition: sch_screen.cpp:403
Class for a wire to bus entry.
bool TrimWire(const wxPoint &aStart, const wxPoint &aEnd)
If any single wire passes through both points, remove the portion between the two points,...
bool IsParallel(const SCH_LINE *aLine) const
Definition: sch_line.cpp:453
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool IsType(const KICAD_T aScanTypes[]) const override
Check whether the item is one of the listed types.
Definition: sch_line.h:62
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1512
wxPoint GetEnd() const
wxPoint GetPosition() const override
wxPoint GetEndPoint() const
Definition: sch_line.h:93