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::function<void( SCH_ITEM* )> changeHandler =
47 [&]( SCH_ITEM* aChangedItem ) -> void
48 {
49 GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT );
50 };
51
52 GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
53}
54
55
56bool SCH_EDIT_FRAME::TrimWire( const VECTOR2I& aStart, const VECTOR2I& aEnd )
57{
58 if( aStart == aEnd )
59 return false;
60
61 SCH_SCREEN* screen = GetScreen();
62 std::vector<SCH_LINE*> wires;
63 BOX2I bb( aStart );
64
65 bb.Merge( aEnd );
66
67 // We cannot modify the RTree while iterating, so push the possible
68 // wires into a separate structure.
69 for( EDA_ITEM* item : screen->Items().Overlapping( bb ) )
70 {
71 SCH_LINE* line = static_cast<SCH_LINE*>( item );
72
73 if( item->Type() == SCH_LINE_T && line->GetLayer() == LAYER_WIRE )
74 wires.push_back( line );
75 }
76
77 for( SCH_LINE* line : wires )
78 {
79 // Don't remove wires that are already deleted or are currently being dragged
80 if( line->GetEditFlags() & ( STRUCT_DELETED | IS_MOVING | SKIP_STRUCT ) )
81 continue;
82
83 if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
84 !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
85 {
86 continue;
87 }
88
89 // Don't remove entire wires
90 if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
91 || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
92 {
93 continue;
94 }
95
96 // Step 1: break the segment on one end.
97 // Ensure that *line points to the segment containing aEnd
98 SCH_LINE* new_line;
99 BreakSegment( line, aStart, &new_line );
100
101 if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aEnd ) )
102 line = new_line;
103
104 // Step 2: break the remaining segment.
105 // Ensure that *line _also_ contains aStart. This is our overlapping segment
106 BreakSegment( line, aEnd, &new_line );
107
108 if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aStart ) )
109 line = new_line;
110
111 SaveCopyInUndoList( screen, line, UNDO_REDO::DELETED, true );
112 RemoveFromScreen( line, screen );
113
114 return true;
115 }
116
117 return false;
118}
119
120
122{
123 PICKED_ITEMS_LIST itemList;
125 std::vector<SCH_ITEM*> deletedItems;
126 std::vector<SCH_LINE*> lines;
127 std::vector<SCH_JUNCTION*> junctions;
128 std::vector<SCH_NO_CONNECT*> ncs;
129 bool changed = true;
130
131 if( aScreen == nullptr )
132 aScreen = GetScreen();
133
134 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
135 {
136 changed = true;
137
138 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
139 {
140 aItem->SetFlags( STRUCT_DELETED );
141 itemList.PushItem( ITEM_PICKER( aScreen, aItem, UNDO_REDO::DELETED ) );
142 deletedItems.push_back( aItem );
143 }
144 };
145
146 BreakSegmentsOnJunctions( aScreen );
147
148 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
149 {
150 if( !aScreen->IsExplicitJunction( item->GetPosition() ) )
151 remove_item( item );
152 else
153 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
154 }
155
156 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
157 {
158 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
159 }
160
161 alg::for_all_pairs( junctions.begin(), junctions.end(),
162 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
163 {
164 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
165 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
166 {
167 return;
168 }
169
170 if( aFirst->GetPosition() == aSecond->GetPosition() )
171 remove_item( aSecond );
172 } );
173
174 alg::for_all_pairs( ncs.begin(), ncs.end(),
175 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
176 {
177 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
178 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
179 {
180 return;
181 }
182
183 if( aFirst->GetPosition() == aSecond->GetPosition() )
184 remove_item( aSecond );
185 } );
186
187
188 while( changed )
189 {
190 changed = false;
191 lines.clear();
192
193 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
194 {
195 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
196 lines.push_back( static_cast<SCH_LINE*>( item ) );
197 }
198
199 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
200 {
201 SCH_LINE* firstLine = *it1;
202
203 if( firstLine->GetEditFlags() & STRUCT_DELETED )
204 continue;
205
206 if( firstLine->IsNull() )
207 {
208 remove_item( firstLine );
209 continue;
210 }
211
212 auto it2 = it1;
213
214 for( ++it2; it2 != lines.end(); ++it2 )
215 {
216 SCH_LINE* secondLine = *it2;
217
218 if( secondLine->GetFlags() & STRUCT_DELETED )
219 continue;
220
221 if( !secondLine->IsParallel( firstLine )
222 || !secondLine->IsStrokeEquivalent( firstLine )
223 || secondLine->GetLayer() != firstLine->GetLayer() )
224 {
225 continue;
226 }
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 // See if we can merge an overlap (or two colinear touching segments with
237 // no junction where they meet).
238 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
239
240 if( mergedLine != nullptr )
241 {
242 remove_item( firstLine );
243 remove_item( secondLine );
244 itemList.PushItem( ITEM_PICKER( aScreen, mergedLine, UNDO_REDO::NEWITEM ) );
245
246 AddToScreen( mergedLine, aScreen );
247
248 if( firstLine->IsSelected() || secondLine->IsSelected() )
249 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
250
251 break;
252 }
253 }
254 }
255 }
256
257 for( SCH_ITEM* item : deletedItems )
258 {
259 if( item->IsSelected() )
260 selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
261
262 RemoveFromScreen( item, aScreen );
263 }
264
265 if( itemList.GetCount() )
266 SaveCopyInUndoList( itemList, UNDO_REDO::DELETED, true );
267
268 return itemList.GetCount() > 0;
269}
270
271
272void SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const VECTOR2I& aPoint,
273 SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
274{
275 if( aScreen == nullptr )
276 aScreen = GetScreen();
277
278 // Save the copy of aSegment before breaking it
279 SaveCopyInUndoList( aScreen, aSegment, UNDO_REDO::CHANGED, true );
280
281 SCH_LINE* newSegment = aSegment->BreakAt( aPoint );
282 aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
283 newSegment->SetFlags( IS_NEW | IS_BROKEN );
284 AddToScreen( newSegment, aScreen );
285
286 SaveCopyInUndoList( aScreen, newSegment, UNDO_REDO::NEWITEM, true );
287
288 UpdateItem( aSegment, false, true );
289
290 if( aNewSegment )
291 *aNewSegment = newSegment;
292}
293
294
295bool SCH_EDIT_FRAME::BreakSegments( const VECTOR2I& aPoint, SCH_SCREEN* aScreen )
296{
297 if( aScreen == nullptr )
298 aScreen = GetScreen();
299
300 bool brokenSegments = false;
301
302 for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPoint, true ) )
303 {
304 BreakSegment( wire, aPoint, nullptr, aScreen );
305 brokenSegments = true;
306 }
307
308 return brokenSegments;
309}
310
311
313{
314 if( aScreen == nullptr )
315 aScreen = GetScreen();
316
317 bool brokenSegments = false;
318
319 std::set<VECTOR2I> point_set;
320
321 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
322 point_set.insert( item->GetPosition() );
323
324 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
325 {
326 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
327 point_set.insert( entry->GetPosition() );
328 point_set.insert( entry->GetEnd() );
329 }
330
331 for( const VECTOR2I& pt : point_set )
332 {
333 BreakSegments( pt, aScreen );
334 brokenSegments = true;
335 }
336
337 return brokenSegments;
338}
339
340
341void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
342{
343 SCH_SCREEN* screen = GetScreen();
344 PICKED_ITEMS_LIST undoList;
346
347 auto remove_item =
348 [&]( SCH_ITEM* aItem ) -> void
349 {
350 aItem->SetFlags( STRUCT_DELETED );
351 undoList.PushItem( ITEM_PICKER( screen, aItem, UNDO_REDO::DELETED ) );
352 };
353
354 remove_item( aJunction );
355 RemoveFromScreen( aJunction, screen );
356
359 std::list<SCH_LINE*> lines;
360
361 for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
362 {
363 SCH_LINE* line = static_cast<SCH_LINE*>( item );
364
365 if( line->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } )
366 && line->IsEndPoint( aJunction->GetPosition() )
367 && !( line->GetEditFlags() & STRUCT_DELETED ) )
368 {
369 lines.push_back( line );
370 }
371 }
372
373 alg::for_all_pairs( lines.begin(), lines.end(),
374 [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
375 {
376 if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
377 || ( secondLine->GetEditFlags() & STRUCT_DELETED )
378 || !secondLine->IsParallel( firstLine ) )
379 {
380 return;
381 }
382
383 // Remove identical lines
384 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
385 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
386 {
387 remove_item( firstLine );
388 return;
389 }
390
391 // Try to merge the remaining lines
392 if( SCH_LINE* line = secondLine->MergeOverlap( screen, firstLine, false ) )
393 {
394 remove_item( firstLine );
395 remove_item( secondLine );
396 undoList.PushItem( ITEM_PICKER( screen, line, UNDO_REDO::NEWITEM ) );
397 AddToScreen( line, screen );
398
399 if( line->IsSelected() )
400 selectionTool->AddItemToSel( line, true /*quiet mode*/ );
401
402 lines.push_back( line );
403 }
404 } );
405
406 SaveCopyInUndoList( undoList, UNDO_REDO::DELETED, aAppend );
407
408 for( SCH_LINE* line : lines )
409 {
410 if( line->GetEditFlags() & STRUCT_DELETED )
411 {
412 if( line->IsSelected() )
413 selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
414
415 RemoveFromScreen( line, screen );
416 }
417 }
418}
419
420
422 bool aUndoAppend, bool aFinal )
423{
424 SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
425
426 AddToScreen( junction, aScreen );
427 SaveCopyInUndoList( aScreen, junction, UNDO_REDO::NEWITEM, aUndoAppend );
428 BreakSegments( aPos );
429
430 if( aFinal )
431 {
433
435 OnModify();
436
437 KIGFX::SCH_VIEW* view = GetCanvas()->GetView();
438 view->ClearPreview();
439 view->ShowPreview( false );
440 view->ClearHiddenFlags();
441 }
442
443 return junction;
444}
445
446
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:249
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:145
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:139
bool IsSelected() const
Definition: eda_item.h:106
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:142
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:214
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:1649
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:1591
void ClearPreview()
Definition: view.cpp:1613
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,...
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.
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 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 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
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
bool IsParallel(const SCH_LINE *aLine) const
Definition: sch_line.cpp:458
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:471
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:180
SCH_LINE * BreakAt(const VECTOR2I &aPoint)
Break this segment into two at the specified point.
Definition: sch_line.cpp:592
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.
std::vector< SCH_LINE * > GetBusesAndWires(const VECTOR2I &aPosition, bool aIgnoreEndpoints=false) const
Return buses and wires passing through aPosition.
bool IsExplicitJunction(const VECTOR2I &aPosition) const
Indicates that a junction dot is necessary at the given location.
Definition: sch_screen.cpp:468
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_CHANGED
Item was edited, and modified.
#define IS_NEW
New item, just created.
#define IS_BROKEN
Is a segment just broken by BreakSegment.
#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