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