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_draw_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;
141 
142  if( aScreen == nullptr )
143  aScreen = GetScreen();
144 
145  auto remove_item = [ &itemList ]( SCH_ITEM* aItem ) -> void
146  {
147  aItem->SetFlags( STRUCT_DELETED );
148  itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
149  };
150 
151  BreakSegmentsOnJunctions( aScreen );
152 
153  for( item = aScreen->GetDrawItems(); item; item = item->Next() )
154  {
155  if( ( item->Type() != SCH_LINE_T )
156  && ( item->Type() != SCH_JUNCTION_T )
157  && ( item->Type() != SCH_NO_CONNECT_T ) )
158  continue;
159 
160  if( item->GetEditFlags() & STRUCT_DELETED )
161  continue;
162 
163  // Remove unneeded junctions
164  if( ( item->Type() == SCH_JUNCTION_T )
165  && ( !aScreen->IsJunctionNeeded( item->GetPosition() ) ) )
166  {
167  remove_item( item );
168  continue;
169  }
170 
171  // Remove zero-length lines
172  if( item->Type() == SCH_LINE_T
173  && ( (SCH_LINE*) item )->IsNull() )
174  {
175  remove_item( item );
176  continue;
177  }
178 
179  for( secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
180  {
181  if( item->Type() != secondItem->Type()
182  || ( secondItem->GetEditFlags() & STRUCT_DELETED ) )
183  continue;
184 
185  // Merge overlapping lines
186  if( item->Type() == SCH_LINE_T )
187  {
188  SCH_LINE* firstLine = (SCH_LINE*) item;
189  SCH_LINE* secondLine = (SCH_LINE*) secondItem;
190  SCH_LINE* line = NULL;
191  bool needed = false;
192 
193  if( !secondLine->IsParallel( firstLine )
194  || secondLine->GetLineStyle() != firstLine->GetLineStyle()
195  || secondLine->GetLineColor() != firstLine->GetLineColor()
196  || secondLine->GetLineSize() != firstLine->GetLineSize() )
197  continue;
198 
199  // Remove identical lines
200  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
201  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
202  {
203  remove_item( secondItem );
204  continue;
205  }
206 
207  // If the end points overlap, check if we still need the junction
208  if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
209  needed = aScreen->IsJunctionNeeded( firstLine->GetStartPoint() );
210  else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
211  needed = aScreen->IsJunctionNeeded( firstLine->GetEndPoint() );
212 
213  if( !needed && ( line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) ) )
214  {
215  remove_item( item );
216  remove_item( secondItem );
217  itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
218  AddToScreen( line, aScreen );
219  break;
220  }
221  }
222  // Remove duplicate junctions and no-connects
223  else if( secondItem->GetPosition() == item->GetPosition() )
224  remove_item( secondItem );
225  }
226  }
227 
228  for( item = aScreen->GetDrawItems(); item; item = secondItem )
229  {
230  secondItem = item->Next();
231 
232  if( item->GetEditFlags() & STRUCT_DELETED )
233  RemoveFromScreen( item, aScreen );
234  }
235 
236  if( itemList.GetCount() )
237  SaveCopyInUndoList( itemList, UR_DELETED, true );
238 
239  return itemList.GetCount() > 0;
240 }
241 
242 
244 {
245  bool added = false;
246 
247  auto add_junction = [ & ]( const wxPoint& aPosition ) -> void
248  {
249  auto junction = new SCH_JUNCTION( aPosition );
250  AddToScreen( junction, aScreen );
251  BreakSegments( aPosition );
252  added = true;
253  };
254 
255  for( auto item = aScreen->GetDrawItems(); item; item = item->Next() )
256  {
257  if( item->Type() == SCH_LINE_T )
258  {
259  auto line = static_cast<SCH_LINE*>( item );
260 
261  if( aScreen->IsJunctionNeeded( line->GetStartPoint(), true ) )
262  add_junction( line->GetStartPoint() );
263 
264  if( aScreen->IsJunctionNeeded( line->GetEndPoint(), true ) )
265  add_junction( line->GetEndPoint() );
266  }
267  }
268 
269  return added;
270 }
271 
272 
273 void SCH_EDIT_FRAME::NormalizeSchematicOnFirstLoad( bool recalculateConnections )
274 {
275  if( recalculateConnections )
277  else
279 
280  SCH_SHEET_LIST list( g_RootSheet );
281 
282  for( const auto& sheet : list )
283  AddMissingJunctions( sheet.LastScreen() );
284 }
285 
286 
287 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
288  SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
289 {
290  if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
291  || aSegment->IsEndPoint( aPoint ) )
292  return false;
293 
294  if( aScreen == nullptr )
295  aScreen = GetScreen();
296 
297  SCH_LINE* newSegment = new SCH_LINE( *aSegment );
298 
299  newSegment->SetStartPoint( aPoint );
300  AddToScreen( newSegment, aScreen );
301 
302  SaveCopyInUndoList( newSegment, UR_NEW, true );
303  SaveCopyInUndoList( aSegment, UR_CHANGED, true );
304 
305  RefreshItem( aSegment );
306  aSegment->SetEndPoint( aPoint );
307 
308  if( aNewSegment )
309  *aNewSegment = newSegment;
310 
311  return true;
312 }
313 
314 
315 bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
316 {
317  static KICAD_T wiresAndBusses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
318 
319  if( aScreen == nullptr )
320  aScreen = GetScreen();
321 
322  bool brokenSegments = false;
323 
324  for( SCH_ITEM* segment = aScreen->GetDrawItems(); segment; segment = segment->Next() )
325  {
326  if( segment->IsType( wiresAndBusses ) )
327  brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint, NULL, aScreen );
328  }
329 
330  return brokenSegments;
331 }
332 
333 
335 {
336  if( aScreen == nullptr )
337  aScreen = GetScreen();
338 
339  bool brokenSegments = false;
340 
341  for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
342  {
343  if( item->Type() == SCH_JUNCTION_T )
344  {
345  SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
346 
347  brokenSegments |= BreakSegments( junction->GetPosition(), aScreen );
348  }
349  else if( item->Type() == SCH_BUS_BUS_ENTRY_T || item->Type() == SCH_BUS_WIRE_ENTRY_T )
350  {
351  SCH_BUS_ENTRY_BASE* busEntry = (SCH_BUS_ENTRY_BASE*) item;
352 
353  brokenSegments |= BreakSegments( busEntry->GetPosition(), aScreen );
354  brokenSegments |= BreakSegments( busEntry->m_End(), aScreen );
355  }
356  }
357 
358  return brokenSegments;
359 }
360 
361 
362 void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
363 {
364  SCH_SCREEN* screen = GetScreen();
365  PICKED_ITEMS_LIST itemList;
366 
367  auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
368  {
369  aItem->SetFlags( STRUCT_DELETED );
370  itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
371  RemoveFromScreen( aItem );
372  };
373 
374  remove_item( aJunction );
375 
376  for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() )
377  {
378  SCH_LINE* firstLine = dynamic_cast<SCH_LINE*>( item );
379 
380  if( !firstLine || !firstLine->IsEndPoint( aJunction->GetPosition() )
381  || ( firstLine->GetEditFlags() & STRUCT_DELETED ) )
382  continue;
383 
384  for( SCH_ITEM* secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
385  {
386  SCH_LINE* secondLine = dynamic_cast<SCH_LINE*>( secondItem );
387 
388  if( !secondLine || !secondLine->IsEndPoint( aJunction->GetPosition() )
389  || ( secondItem->GetEditFlags() & STRUCT_DELETED )
390  || !secondLine->IsParallel( firstLine ) )
391  continue;
392 
393 
394  // Remove identical lines
395  if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
396  && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
397  {
398  remove_item( secondItem );
399  continue;
400  }
401 
402  // Try to merge the remaining lines
403  if( SCH_LINE* line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) )
404  {
405  remove_item( item );
406  remove_item( secondItem );
407  itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
408  AddToScreen( line );
409  break;
410  }
411  }
412  }
413 
414  SaveCopyInUndoList( itemList, UR_DELETED, aAppend );
415 
416  SCH_ITEM* nextitem;
417  for( SCH_ITEM* item = screen->GetDrawItems(); item; item = nextitem )
418  {
419  nextitem = item->Next();
420 
421  if( item->GetEditFlags() & STRUCT_DELETED )
422  RemoveFromScreen( item );
423  }
424 }
425 
426 
427 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPos, bool aUndoAppend, bool aFinal )
428 {
429  SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
430 
431  AddToScreen( junction );
432  SaveCopyInUndoList( junction, UR_NEW, aUndoAppend );
433  BreakSegments( aPos );
434 
435  if( aFinal )
436  {
438 
440  OnModify();
441 
442  auto view = GetCanvas()->GetView();
443  view->ClearPreview();
444  view->ShowPreview( false );
445  view->ClearHiddenFlags();
446  }
447 
448  return junction;
449 }
450 
451 
TOOL_MANAGER * m_toolManager
Definition: draw_frame.h:130
Class SCH_SHEET_LIST.
COLOR4D GetLineColor() const
Definition: sch_line.cpp:257
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Function IsPointOnSegment.
Definition: trigo.cpp:39
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:90
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:350
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:125
bool IsParallel(SCH_LINE *aLine)
Definition: sch_line.cpp:391
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:136
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:94
EDA_ITEM * Next() const
Definition: base_struct.h:212
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:116
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:56
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_junction.h:95
STATUS_FLAGS GetEditFlags() const
Definition: base_struct.h:263
#define IS_DELETED
Definition: base_struct.h:117
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:91
SCH_DRAW_PANEL * GetCanvas() const override
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:133
#define STRUCT_DELETED
flag indication structures to be erased
Definition: base_struct.h:123
SCH_JUNCTION * AddJunction(const wxPoint &aPos, bool aAppendToUndo=false, bool aFinal=true)
bool IsEndPoint(const wxPoint &aPoint) const
Definition: sch_line.h:83
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:1539
KIGFX::SCH_VIEW * GetView() const
bool BreakSegmentsOnJunctions(SCH_SCREEN *aScreen=nullptr)
Tests all junctions and bus entries in the schematic for intersections with wires and buses and break...
int GetLineSize() const
Definition: sch_line.h:121
EDA_ITEM * MergeOverlap(SCH_LINE *aLine)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:404
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.
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...
int GetLineStyle() const
Definition: sch_line.cpp:283
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:238
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.
bool AddMissingJunctions(SCH_SCREEN *aScreen)
Checks all wires and adds any junctions that are missing (Intended to be called only on file load)
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:204
wxPoint GetPosition() const override
Function GetPosition.
#define IS_MOVED
Item being moved.
Definition: base_struct.h:113
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:153
wxPoint GetEndPoint() const
Definition: sch_line.h:93