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 
27 #include <core/kicad_algo.h>
28 #include <eeschema_id.h>
29 #include <general.h>
30 #include <lib_item.h>
31 #include <sch_bus_entry.h>
32 #include <sch_component.h>
33 #include <sch_edit_frame.h>
34 #include <sch_junction.h>
35 #include <sch_line.h>
36 #include <sch_no_connect.h>
37 #include <sch_screen.h>
38 #include <sch_sheet.h>
39 #include <sch_view.h>
40 #include <tool/tool_manager.h>
41 #include <tools/ee_actions.h>
43 
44 
45 void SCH_EDIT_FRAME::GetSchematicConnections( std::vector< wxPoint >& aConnections )
46 {
47  for( auto item : GetScreen()->Items() )
48  {
49  // Avoid items that are changing
50  if( !( item->GetEditFlags() & ( IS_DRAGGED | IS_MOVED | IS_DELETED ) ) )
51  item->GetConnectionPoints( aConnections );
52  }
53 
54  // We always have some overlapping connection points. Drop duplicates here
55  std::sort( aConnections.begin(), aConnections.end(),
56  []( const wxPoint& a, const wxPoint& b ) -> bool
57  { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
58  aConnections.erase(
59  std::unique( aConnections.begin(), aConnections.end() ), aConnections.end() );
60 }
61 
62 
64 {
65  std::vector<DANGLING_END_ITEM> endPoints;
66  bool hasStateChanged = false;
67 
68  for( auto item : GetScreen()->Items() )
69  item->GetEndPoints( endPoints );
70 
71  for( auto item : GetScreen()->Items() )
72  {
73  if( item->UpdateDanglingState( endPoints ) )
74  {
75  GetCanvas()->GetView()->Update( item, KIGFX::REPAINT );
76  hasStateChanged = true;
77  }
78  item->GetEndPoints( endPoints );
79  }
80 
81  return hasStateChanged;
82 }
83 
84 
85 bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd )
86 {
87  SCH_SCREEN* screen = GetScreen();
88  bool retval = false;
89 
90  if( aStart == aEnd )
91  return retval;
92 
93  for( EDA_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
94  {
95  SCH_LINE* line = static_cast<SCH_LINE*>( item );
96 
97  if( line->GetLayer() != LAYER_WIRE )
98  continue;
99 
100  // Don't remove wires that are already deleted or are currently being dragged
101  if( line->GetEditFlags() & ( STRUCT_DELETED | IS_DRAGGED | IS_MOVED | SKIP_STRUCT ) )
102  continue;
103 
104  if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
105  !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
106  {
107  continue;
108  }
109 
110  // Don't remove entire wires
111  if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
112  || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
113  {
114  continue;
115  }
116 
117  // Step 1: break the segment on one end. return_line remains line if not broken.
118  // Ensure that *line points to the segment containing aEnd
119  SCH_LINE* return_line = line;
120  BreakSegment( line, aStart, &return_line );
121 
122  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
123  line = return_line;
124 
125  // Step 2: break the remaining segment. return_line remains line if not broken.
126  // Ensure that *line _also_ contains aStart. This is our overlapping segment
127  BreakSegment( line, aEnd, &return_line );
128 
129  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
130  line = return_line;
131 
132  SaveCopyInUndoList( screen, line, UR_DELETED, true );
133  RemoveFromScreen( line, screen );
134 
135  retval = true;
136  }
137 
138  return retval;
139 }
140 
141 
143 {
144  PICKED_ITEMS_LIST itemList;
146  std::vector<SCH_ITEM*> deletedItems;
147  std::vector<SCH_LINE*> lines;
148  std::vector<SCH_JUNCTION*> junctions;
149  std::vector<SCH_NO_CONNECT*> ncs;
150 
151  if( aScreen == nullptr )
152  aScreen = GetScreen();
153 
154  auto remove_item = [&itemList, &deletedItems, &aScreen]( SCH_ITEM* aItem ) -> void
155  {
156  aItem->SetFlags( STRUCT_DELETED );
157  itemList.PushItem( ITEM_PICKER( aScreen, aItem, UR_DELETED ) );
158  deletedItems.push_back( aItem );
159  };
160 
161  BreakSegmentsOnJunctions( aScreen );
162 
163  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
164  {
165  if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
166  lines.push_back( static_cast<SCH_LINE*>( item ) );
167  }
168 
169  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
170  {
171  if( !aScreen->IsJunctionNeeded( item->GetPosition() ) )
172  remove_item( item );
173  else
174  junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
175  }
176 
177  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
178  {
179  ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
180  }
181 
183  junctions.begin(), junctions.end(), [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond ) {
184  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
185  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
186  return;
187 
188  if( aFirst->GetPosition() == aSecond->GetPosition() )
189  remove_item( aSecond );
190  } );
191 
193  ncs.begin(), ncs.end(), [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond ) {
194  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
195  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
196  return;
197 
198  if( aFirst->GetPosition() == aSecond->GetPosition() )
199  remove_item( aSecond );
200  } );
201 
202  for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
203  {
204  SCH_LINE* firstLine = *it1;
205 
206  if( firstLine->GetEditFlags() & STRUCT_DELETED )
207  continue;
208 
209  if( firstLine->IsNull() )
210  {
211  remove_item( firstLine );
212  continue;
213  }
214 
215  auto it2 = it1;
216 
217  for( ++it2; it2 != lines.end(); ++it2 )
218  {
219  SCH_LINE* secondLine = *it2;
220  bool needed = false;
221 
222  if( secondLine->GetFlags() & STRUCT_DELETED )
223  continue;
224 
225  if( !secondLine->IsParallel( firstLine )
226  || secondLine->GetLineStyle() != firstLine->GetLineStyle()
227  || secondLine->GetLineColor() != firstLine->GetLineColor()
228  || secondLine->GetLineSize() != firstLine->GetLineSize()
229  || secondLine->GetLayer() != firstLine->GetLayer() )
230  continue;
231 
232  // Remove identical lines
233  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
234  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
235  {
236  remove_item( secondLine );
237  continue;
238  }
239 
240  // If the end points overlap, check if we still need the junction
241  if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
242  needed = aScreen->IsJunctionNeeded( firstLine->GetStartPoint() );
243  else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
244  needed = aScreen->IsJunctionNeeded( firstLine->GetEndPoint() );
245 
246  SCH_LINE* mergedLine = nullptr;
247 
248  if( !needed && ( mergedLine = secondLine->MergeOverlap( firstLine ) ) )
249  {
250  remove_item( firstLine );
251  remove_item( secondLine );
252  itemList.PushItem( ITEM_PICKER( aScreen, mergedLine, UR_NEW ) );
253 
254  AddToScreen( mergedLine, aScreen );
255 
256  if( firstLine->IsSelected() )
257  selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
258 
259  break;
260  }
261  }
262  }
263 
264 
265  for( auto item : deletedItems )
266  {
267  if( item->IsSelected() )
268  selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
269 
270  RemoveFromScreen( item, aScreen );
271  }
272 
273  if( itemList.GetCount() )
274  SaveCopyInUndoList( itemList, UR_DELETED, true );
275 
276  return itemList.GetCount() > 0;
277 }
278 
279 
280 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
281  SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
282 {
283  if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
284  || aSegment->IsEndPoint( aPoint ) )
285  return false;
286 
287  if( aScreen == nullptr )
288  aScreen = GetScreen();
289 
290  SCH_LINE* newSegment = new SCH_LINE( *aSegment );
291 
292  newSegment->SetStartPoint( aPoint );
293  AddToScreen( newSegment, aScreen );
294 
295  SaveCopyInUndoList( aScreen, newSegment, UR_NEW, true );
296  SaveCopyInUndoList( aScreen, aSegment, UR_CHANGED, true );
297 
298  RefreshItem( aSegment );
299  aSegment->SetEndPoint( aPoint );
300 
301  if( aNewSegment )
302  *aNewSegment = newSegment;
303 
304  return true;
305 }
306 
307 
308 bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
309 {
310  static const KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
311 
312  if( aScreen == nullptr )
313  aScreen = GetScreen();
314 
315  bool brokenSegments = false;
316  std::vector<SCH_LINE*> wires;
317  EDA_RECT bbox( aPoint, wxSize( 2, 2 ) );
318 
319  for( auto item : aScreen->Items().Overlapping( SCH_LINE_T, aPoint ) )
320  {
321  if( item->IsType( wiresAndBuses ) )
322  wires.push_back( static_cast<SCH_LINE*>( item ) );
323  }
324 
325  for( auto wire : wires )
326  brokenSegments |= BreakSegment( wire, aPoint, NULL, aScreen );
327 
328  return brokenSegments;
329 }
330 
331 
333 {
334  if( aScreen == nullptr )
335  aScreen = GetScreen();
336 
337  bool brokenSegments = false;
338 
339  std::set<wxPoint> point_set;
340 
341  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
342  point_set.insert( item->GetPosition() );
343 
344  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
345  {
346  SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
347  point_set.insert( entry->GetPosition() );
348  point_set.insert( entry->m_End() );
349  }
350 
351 
352  for( auto pt : point_set )
353  brokenSegments |= BreakSegments( pt, aScreen );
354 
355  return brokenSegments;
356 }
357 
358 
359 void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
360 {
361  SCH_SCREEN* screen = GetScreen();
362  PICKED_ITEMS_LIST undoList;
365 
366  auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
367  {
368  aItem->SetFlags( STRUCT_DELETED );
369  undoList.PushItem( ITEM_PICKER( screen, aItem, UR_DELETED ) );
370  };
371 
372  remove_item( aJunction );
373  RemoveFromScreen( aJunction, screen );
374 
377  std::list<SCH_LINE*> lines;
378 
379  for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
380  {
381  SCH_LINE* line = static_cast<SCH_LINE*>( item );
382 
383  if( line->IsType( wiresAndBuses ) && line->IsEndPoint( aJunction->GetPosition() )
384  && !( line->GetEditFlags() & STRUCT_DELETED ) )
385  lines.push_back( line );
386  }
387 
389  lines.begin(), lines.end(), [&]( SCH_LINE* firstLine, SCH_LINE* secondLine ) {
390  if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
391  || ( secondLine->GetEditFlags() & STRUCT_DELETED )
392  || !secondLine->IsParallel( firstLine ) )
393  return;
394 
395  // Remove identical lines
396  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
397  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
398  {
399  remove_item( firstLine );
400  return;
401  }
402 
403  // Try to merge the remaining lines
404  if( SCH_LINE* line = secondLine->MergeOverlap( firstLine ) )
405  {
406  remove_item( firstLine );
407  remove_item( secondLine );
408  undoList.PushItem( ITEM_PICKER( screen, line, UR_NEW ) );
409  AddToScreen( line, screen );
410 
411  if( line->IsSelected() )
412  selectionTool->AddItemToSel( line, true /*quiet mode*/ );
413 
414  lines.push_back( line );
415  }
416  } );
417 
418  SaveCopyInUndoList( undoList, UR_DELETED, aAppend );
419 
420 
421  for( auto line : lines )
422  {
423  if( line->GetEditFlags() & STRUCT_DELETED )
424  {
425  if( line->IsSelected() )
426  selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
427 
428  RemoveFromScreen( line, screen );
429  }
430  }
431 }
432 
433 
435  bool aUndoAppend, bool aFinal )
436 {
437  SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
438 
439  AddToScreen( junction, aScreen );
440  SaveCopyInUndoList( aScreen, junction, UR_NEW, aUndoAppend );
441  BreakSegments( aPos );
442 
443  if( aFinal )
444  {
446 
448  OnModify();
449 
450  auto view = GetCanvas()->GetView();
451  view->ClearPreview();
452  view->ShowPreview( false );
453  view->ClearHiddenFlags();
454  }
455 
456  return junction;
457 }
458 
459 
SCH_LINE * MergeOverlap(SCH_LINE *aLine)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:370
KIGFX::SCH_VIEW * GetView() const override
Function GetView() Returns a pointer to the VIEW instance used in the panel.
COLOR4D GetLineColor() const
Returns COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
Definition: sch_line.cpp:209
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:94
bool IsSelected() const
Definition: base_struct.h:203
wxPoint GetPosition() const override
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:371
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:128
bool IsParallel(SCH_LINE *aLine)
Definition: sch_line.cpp:357
EE_TYPE OfType(KICAD_T aType)
Definition: sch_rtree.h:219
EE_TYPE Overlapping(const EDA_RECT &aRect)
Definition: sch_rtree.h:224
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)
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.
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
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:207
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:98
SCH_JUNCTION * AddJunction(SCH_SCREEN *aScreen, const wxPoint &aPos, bool aAppendToUndo, bool aFinal=true)
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:119
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:63
Item is being added to the view.
Definition: view_item.h:62
#define NULL
wxPoint GetPosition() const override
Definition: sch_junction.h:97
STATUS_FLAGS GetEditFlags() const
Definition: base_struct.h:237
PLOT_DASH_TYPE GetLineStyle() const
Definition: sch_line.cpp:244
#define IS_DELETED
Definition: base_struct.h:120
bool IsNull() const
Definition: sch_line.h:92
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:95
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void ClearPreview()
Definition: view.cpp:1566
#define STRUCT_DELETED
flag indication structures to be erased
Definition: base_struct.h:126
bool IsEndPoint(const wxPoint &aPoint) const
Definition: sch_line.h:87
wxPoint m_End() const
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:1544
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:272
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:140
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:48
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 RefreshItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, bool aAppend, const wxPoint &aTransformPoint=wxPoint(0, 0))
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()
Definition: sch_screen.h:158
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:159
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,...
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:234
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:268
bool IsType(const KICAD_T aScanTypes[]) const override
Function IsType Checks whether the item is one of the listed types.
Definition: sch_line.h:69
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187
wxPoint GetPosition() const override
#define IS_MOVED
Item being moved.
Definition: base_struct.h:116
wxPoint GetEndPoint() const
Definition: sch_line.h:97