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  bool retval = false;
88 
89  if( aStart == aEnd )
90  return retval;
91 
92  for( auto item : GetScreen()->Items().OfType( SCH_LINE_T ) )
93  {
94  SCH_LINE* line = static_cast<SCH_LINE*>( item );
95 
96  if( line->GetLayer() != LAYER_WIRE )
97  continue;
98 
99  // Don't remove wires that are already deleted or are currently being dragged
100  if( line->GetEditFlags() & ( STRUCT_DELETED | IS_DRAGGED | IS_MOVED | SKIP_STRUCT ) )
101  continue;
102 
103  if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
104  !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
105  {
106  continue;
107  }
108 
109  // Don't remove entire wires
110  if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
111  || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
112  {
113  continue;
114  }
115 
116  // Step 1: break the segment on one end. return_line remains line if not broken.
117  // Ensure that *line points to the segment containing aEnd
118  SCH_LINE* return_line = line;
119  BreakSegment( line, aStart, &return_line );
120  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
121  line = return_line;
122 
123  // Step 2: break the remaining segment. return_line remains line if not broken.
124  // Ensure that *line _also_ contains aStart. This is our overlapping segment
125  BreakSegment( line, aEnd, &return_line );
126  if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
127  line = return_line;
128 
129  SaveCopyInUndoList( line, UR_DELETED, true );
130  RemoveFromScreen( line );
131 
132  retval = true;
133  }
134 
135  return retval;
136 }
137 
138 
140 {
141  PICKED_ITEMS_LIST itemList;
143  std::vector<SCH_ITEM*> deletedItems;
144  std::vector<SCH_LINE*> lines;
145  std::vector<SCH_JUNCTION*> junctions;
146  std::vector<SCH_NO_CONNECT*> ncs;
147 
148  if( aScreen == nullptr )
149  aScreen = GetScreen();
150 
151  auto remove_item = [&itemList, &deletedItems]( SCH_ITEM* aItem ) -> void {
152  aItem->SetFlags( STRUCT_DELETED );
153  itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
154  deletedItems.push_back( aItem );
155  };
156 
157  BreakSegmentsOnJunctions( aScreen );
158 
159  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
160  {
161  if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
162  lines.push_back( static_cast<SCH_LINE*>( item ) );
163  }
164 
165  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
166  {
167  if( !aScreen->IsJunctionNeeded( item->GetPosition() ) )
168  remove_item( item );
169  else
170  junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
171  }
172 
173  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
174  {
175  ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
176  }
177 
179  junctions.begin(), junctions.end(), [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond ) {
180  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
181  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
182  return;
183 
184  if( aFirst->GetPosition() == aSecond->GetPosition() )
185  remove_item( aSecond );
186  } );
187 
189  ncs.begin(), ncs.end(), [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond ) {
190  if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
191  || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
192  return;
193 
194  if( aFirst->GetPosition() == aSecond->GetPosition() )
195  remove_item( aSecond );
196  } );
197 
198  for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
199  {
200  SCH_LINE* firstLine = *it1;
201 
202  if( firstLine->GetEditFlags() & STRUCT_DELETED )
203  continue;
204 
205  if( firstLine->IsNull() )
206  {
207  remove_item( firstLine );
208  continue;
209  }
210 
211  auto it2 = it1;
212 
213  for( ++it2; it2 != lines.end(); ++it2 )
214  {
215  SCH_LINE* secondLine = *it2;
216  bool needed = false;
217 
218  if( secondLine->GetFlags() & STRUCT_DELETED )
219  continue;
220 
221  if( !secondLine->IsParallel( firstLine )
222  || secondLine->GetLineStyle() != firstLine->GetLineStyle()
223  || secondLine->GetLineColor() != firstLine->GetLineColor()
224  || secondLine->GetLineSize() != firstLine->GetLineSize()
225  || secondLine->GetLayer() != firstLine->GetLayer() )
226  continue;
227 
228  // Remove identical lines
229  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
230  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
231  {
232  remove_item( secondLine );
233  continue;
234  }
235 
236  // If the end points overlap, check if we still need the junction
237  if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
238  needed = aScreen->IsJunctionNeeded( firstLine->GetStartPoint() );
239  else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
240  needed = aScreen->IsJunctionNeeded( firstLine->GetEndPoint() );
241 
242  SCH_LINE* mergedLine = nullptr;
243 
244  if( !needed && ( mergedLine = secondLine->MergeOverlap( firstLine ) ) )
245  {
246  remove_item( firstLine );
247  remove_item( secondLine );
248  itemList.PushItem( ITEM_PICKER( mergedLine, UR_NEW ) );
249 
250  AddToScreen( mergedLine, aScreen );
251 
252  if( firstLine->IsSelected() )
253  selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
254 
255  break;
256  }
257  }
258  }
259 
260 
261  for( auto item : deletedItems )
262  {
263  if( item->IsSelected() )
264  selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
265 
266  RemoveFromScreen( item, aScreen );
267  }
268 
269  if( itemList.GetCount() )
270  SaveCopyInUndoList( itemList, UR_DELETED, true );
271 
272  return itemList.GetCount() > 0;
273 }
274 
275 
276 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
277  SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
278 {
279  if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
280  || aSegment->IsEndPoint( aPoint ) )
281  return false;
282 
283  if( aScreen == nullptr )
284  aScreen = GetScreen();
285 
286  SCH_LINE* newSegment = new SCH_LINE( *aSegment );
287 
288  newSegment->SetStartPoint( aPoint );
289  AddToScreen( newSegment, aScreen );
290 
291  SaveCopyInUndoList( newSegment, UR_NEW, true );
292  SaveCopyInUndoList( aSegment, UR_CHANGED, true );
293 
294  RefreshItem( aSegment );
295  aSegment->SetEndPoint( aPoint );
296 
297  if( aNewSegment )
298  *aNewSegment = newSegment;
299 
300  return true;
301 }
302 
303 
304 bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
305 {
306  static const KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
307 
308  if( aScreen == nullptr )
309  aScreen = GetScreen();
310 
311  bool brokenSegments = false;
312  std::vector<SCH_LINE*> wires;
313  EDA_RECT bbox( aPoint, wxSize( 2, 2 ) );
314 
315  for( auto item : aScreen->Items().Overlapping( SCH_LINE_T, aPoint ) )
316  {
317  if( item->IsType( wiresAndBuses ) )
318  wires.push_back( static_cast<SCH_LINE*>( item ) );
319  }
320 
321  for( auto wire : wires )
322  brokenSegments |= BreakSegment( wire, aPoint, NULL, aScreen );
323 
324  return brokenSegments;
325 }
326 
327 
329 {
330  if( aScreen == nullptr )
331  aScreen = GetScreen();
332 
333  bool brokenSegments = false;
334 
335  std::set<wxPoint> point_set;
336 
337  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
338  point_set.insert( item->GetPosition() );
339 
340  for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
341  {
342  SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
343  point_set.insert( entry->GetPosition() );
344  point_set.insert( entry->m_End() );
345  }
346 
347 
348  for( auto pt : point_set )
349  brokenSegments |= BreakSegments( pt, aScreen );
350 
351  return brokenSegments;
352 }
353 
354 
355 void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
356 {
357  SCH_SCREEN* screen = GetScreen();
358  PICKED_ITEMS_LIST undoList;
361 
362  auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
363  {
364  aItem->SetFlags( STRUCT_DELETED );
365  undoList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
366  };
367 
368  remove_item( aJunction );
369  RemoveFromScreen( aJunction );
370 
373  std::list<SCH_LINE*> lines;
374 
375  for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
376  {
377  SCH_LINE* line = static_cast<SCH_LINE*>( item );
378 
379  if( line->IsType( wiresAndBuses ) && line->IsEndPoint( aJunction->GetPosition() )
380  && !( line->GetEditFlags() & STRUCT_DELETED ) )
381  lines.push_back( line );
382  }
383 
385  lines.begin(), lines.end(), [&]( SCH_LINE* firstLine, SCH_LINE* secondLine ) {
386  if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
387  || ( secondLine->GetEditFlags() & STRUCT_DELETED )
388  || !secondLine->IsParallel( firstLine ) )
389  return;
390 
391  // Remove identical lines
392  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
393  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
394  {
395  remove_item( firstLine );
396  return;
397  }
398 
399  // Try to merge the remaining lines
400  if( SCH_LINE* line = secondLine->MergeOverlap( firstLine ) )
401  {
402  remove_item( firstLine );
403  remove_item( secondLine );
404  undoList.PushItem( ITEM_PICKER( line, UR_NEW ) );
405  AddToScreen( line );
406 
407  if( line->IsSelected() )
408  selectionTool->AddItemToSel( line, true /*quiet mode*/ );
409 
410  lines.push_back( line );
411  }
412  } );
413 
414  SaveCopyInUndoList( undoList, UR_DELETED, aAppend );
415 
416 
417  for( auto line : lines )
418  {
419  if( line->GetEditFlags() & STRUCT_DELETED )
420  {
421  if( line->IsSelected() )
422  selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
423 
424  RemoveFromScreen( line );
425  }
426  }
427 }
428 
429 
430 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPos, bool aUndoAppend, bool aFinal )
431 {
432  SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
433 
434  AddToScreen( junction );
435  SaveCopyInUndoList( junction, UR_NEW, aUndoAppend );
436  BreakSegments( aPos );
437 
438  if( aFinal )
439  {
441 
443  OnModify();
444 
445  auto view = GetCanvas()->GetView();
446  view->ClearPreview();
447  view->ShowPreview( false );
448  view->ClearHiddenFlags();
449  }
450 
451  return junction;
452 }
453 
454 
SCH_LINE * MergeOverlap(SCH_LINE *aLine)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:388
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:249
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:97
bool IsSelected() const
Definition: base_struct.h:207
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:436
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:375
const wxPoint GetPosition() const override
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 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:206
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:101
virtual const wxPoint GetPosition() const
Definition: base_struct.h:344
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:60
#define NULL
STATUS_FLAGS GetEditFlags() const
Definition: base_struct.h:244
PLOT_DASH_TYPE GetLineStyle() const
Definition: sch_line.cpp:279
#define IS_DELETED
Definition: base_struct.h:120
bool IsNull() const
Definition: sch_line.h:95
const wxPoint GetPosition() const override
Definition: sch_junction.h:92
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:98
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:179
#define STRUCT_DELETED
flag indication structures to be erased
Definition: base_struct.h:126
SCH_JUNCTION * AddJunction(const wxPoint &aPos, bool aAppendToUndo=false, bool aFinal=true)
bool IsEndPoint(const wxPoint &aPoint) const
Definition: sch_line.h:90
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:1540
SCH_LAYER_ID GetLayer() const
Function GetLayer returns the layer this item is on.
Definition: sch_item.h:241
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:135
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:48
int AddItemToSel(const TOOL_EVENT &aEvent)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:38
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.
const wxPoint GetPosition() const override
EE_RTREE & Items()
Definition: sch_screen.h:162
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
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:241
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:267
bool IsType(const KICAD_T aScanTypes[]) const override
Function IsType Checks whether the item is one of the listed types.
Definition: sch_line.h:72
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:151
#define IS_MOVED
Item being moved.
Definition: base_struct.h:116
wxPoint GetEndPoint() const
Definition: sch_line.h:100