KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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 <general.h>
27#include <sch_bus_entry.h>
28#include <sch_edit_frame.h>
29#include <sch_junction.h>
30#include <sch_line.h>
31#include <sch_no_connect.h>
32#include <sch_screen.h>
33#include <sch_view.h>
34#include <sch_commit.h>
35#include <tool/tool_manager.h>
36#include <tools/ee_actions.h>
38#include <trigo.h>
39
40
42{
43 std::function<void( SCH_ITEM* )> changeHandler =
44 [&]( SCH_ITEM* aChangedItem ) -> void
45 {
46 GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT );
47 };
48
49 GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
50}
51
52
53bool SCH_EDIT_FRAME::TrimWire( SCH_COMMIT* aCommit, const VECTOR2I& aStart, const VECTOR2I& aEnd )
54{
55 if( aStart == aEnd )
56 return false;
57
58 SCH_SCREEN* screen = GetScreen();
59 std::vector<SCH_LINE*> wires;
60 BOX2I bb( aStart );
61
62 bb.Merge( aEnd );
63
64 // We cannot modify the RTree while iterating, so push the possible
65 // wires into a separate structure.
66 for( EDA_ITEM* item : screen->Items().Overlapping( bb ) )
67 {
68 SCH_LINE* line = static_cast<SCH_LINE*>( item );
69
70 if( item->Type() == SCH_LINE_T && line->GetLayer() == LAYER_WIRE )
71 wires.push_back( line );
72 }
73
74 for( SCH_LINE* line : wires )
75 {
76 // Don't remove wires that are already deleted or are currently being dragged
77 if( line->GetEditFlags() & ( STRUCT_DELETED | IS_MOVING | SKIP_STRUCT ) )
78 continue;
79
80 if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
81 !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
82 {
83 continue;
84 }
85
86 // Don't remove entire wires
87 if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
88 || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
89 {
90 continue;
91 }
92
93 // Step 1: break the segment on one end.
94 // Ensure that *line points to the segment containing aEnd
95 SCH_LINE* new_line;
96 BreakSegment( aCommit, line, aStart, &new_line, screen );
97
98 if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aEnd ) )
99 line = new_line;
100
101 // Step 2: break the remaining segment.
102 // Ensure that *line _also_ contains aStart. This is our overlapping segment
103 BreakSegment( aCommit, line, aEnd, &new_line, screen );
104
105 if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aStart ) )
106 line = new_line;
107
108 RemoveFromScreen( line, screen );
109 aCommit->Removed( line, screen );
110
111 return true;
112 }
113
114 return false;
115}
116
117
119{
121 std::vector<SCH_LINE*> lines;
122 std::vector<SCH_JUNCTION*> junctions;
123 std::vector<SCH_NO_CONNECT*> ncs;
124 std::vector<SCH_ITEM*> items_to_remove;
125 bool changed = true;
126
127 if( aScreen == nullptr )
128 aScreen = GetScreen();
129
130 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
131 {
132 changed = true;
133
134 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
135 {
136 aItem->SetFlags( STRUCT_DELETED );
137
138 if( aItem->IsSelected() )
139 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
140
141 RemoveFromScreen( aItem, aScreen );
142 aCommit->Removed( aItem, aScreen );
143 }
144 };
145
146 BreakSegmentsOnJunctions( aCommit, aScreen );
147
148 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
149 {
150 if( !aScreen->IsExplicitJunction( item->GetPosition() ) )
151 items_to_remove.push_back( item );
152 else
153 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
154 }
155
156 for( SCH_ITEM* item : items_to_remove )
157 remove_item( item );
158
159 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
160 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
161
162 alg::for_all_pairs( junctions.begin(), junctions.end(),
163 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
164 {
165 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
166 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
167 {
168 return;
169 }
170
171 if( aFirst->GetPosition() == aSecond->GetPosition() )
172 remove_item( aSecond );
173 } );
174
175 alg::for_all_pairs( ncs.begin(), ncs.end(),
176 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
177 {
178 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
179 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
180 {
181 return;
182 }
183
184 if( aFirst->GetPosition() == aSecond->GetPosition() )
185 remove_item( aSecond );
186 } );
187
188
189 auto minX = []( const SCH_LINE* l )
190 {
191 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
192 };
193
194 auto maxX = []( const SCH_LINE* l )
195 {
196 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
197 };
198
199 auto minY = []( const SCH_LINE* l )
200 {
201 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
202 };
203
204 auto maxY = []( const SCH_LINE* l )
205 {
206 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
207 };
208
209 // Would be nice to put lines in a canonical form here by swapping
210 // start <-> end as needed but I don't know what swapping breaks.
211 while( changed )
212 {
213 changed = false;
214 lines.clear();
215
216 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
217 {
218 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
219 lines.push_back( static_cast<SCH_LINE*>( item ) );
220 }
221
222 // Sort by minimum X position
223 std::sort( lines.begin(), lines.end(),
224 [&]( const SCH_LINE* a, const SCH_LINE* b )
225 {
226 return minX( a ) < minX( b );
227 } );
228
229 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
230 {
231 SCH_LINE* firstLine = *it1;
232
233 if( firstLine->GetEditFlags() & STRUCT_DELETED )
234 continue;
235
236 if( firstLine->IsNull() )
237 {
238 remove_item( firstLine );
239 continue;
240 }
241
242 int firstRightXEdge = maxX( firstLine );
243 auto it2 = it1;
244
245 for( ++it2; it2 != lines.end(); ++it2 )
246 {
247 SCH_LINE* secondLine = *it2;
248 int secondLeftXEdge = minX( secondLine );
249
250 // impossible to overlap remaining lines
251 if( secondLeftXEdge > firstRightXEdge )
252 break;
253
254 // No Y axis overlap
255 if( !( std::max( minY( firstLine ), minY( secondLine ) )
256 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
257 {
258 continue;
259 }
260
261 if( secondLine->GetFlags() & STRUCT_DELETED )
262 continue;
263
264 if( !secondLine->IsParallel( firstLine )
265 || !secondLine->IsStrokeEquivalent( firstLine )
266 || secondLine->GetLayer() != firstLine->GetLayer() )
267 {
268 continue;
269 }
270
271 // Remove identical lines
272 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
273 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
274 {
275 remove_item( secondLine );
276 continue;
277 }
278
279 // See if we can merge an overlap (or two colinear touching segments with
280 // no junction where they meet).
281 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
282
283 if( mergedLine != nullptr )
284 {
285 remove_item( firstLine );
286 remove_item( secondLine );
287
288 AddToScreen( mergedLine, aScreen );
289 aCommit->Added( mergedLine, aScreen );
290
291 if( firstLine->IsSelected() || secondLine->IsSelected() )
292 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
293
294 break;
295 }
296 }
297 }
298 }
299}
300
301
302void SCH_EDIT_FRAME::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
303 SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
304{
305 // Save the copy of aSegment before breaking it
306 aCommit->Modify( aSegment, aScreen );
307
308 SCH_LINE* newSegment = aSegment->BreakAt( aPoint );
309
310 aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
311 newSegment->SetFlags( IS_NEW | IS_BROKEN );
312
313 AddToScreen( newSegment, aScreen );
314 aCommit->Added( newSegment, aScreen );
315
316 *aNewSegment = newSegment;
317}
318
319
320bool SCH_EDIT_FRAME::BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPos, SCH_SCREEN* aScreen )
321{
322 bool brokenSegments = false;
323 SCH_LINE* new_line;
324
325 for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
326 {
327 BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
328 brokenSegments = true;
329 }
330
331 return brokenSegments;
332}
333
334
336{
337 bool brokenSegments = false;
338
339 std::set<VECTOR2I> 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->GetEnd() );
349 }
350
351 for( const VECTOR2I& pt : point_set )
352 {
353 BreakSegments( aCommit, pt, aScreen );
354 brokenSegments = true;
355 }
356
357 return brokenSegments;
358}
359
360
362{
363 SCH_SCREEN* screen = GetScreen();
364 PICKED_ITEMS_LIST undoList;
366
367 aJunction->SetFlags( STRUCT_DELETED );
368 RemoveFromScreen( aJunction, screen );
369 aCommit->Removed( aJunction, screen );
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->IsWire() || line->IsBus() )
380 && line->IsEndPoint( aJunction->GetPosition() )
381 && !( line->GetEditFlags() & STRUCT_DELETED ) )
382 {
383 lines.push_back( line );
384 }
385 }
386
387 alg::for_all_pairs( lines.begin(), lines.end(),
388 [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
389 {
390 if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
391 || ( secondLine->GetEditFlags() & STRUCT_DELETED )
392 || !secondLine->IsParallel( firstLine ) )
393 {
394 return;
395 }
396
397 // Remove identical lines
398 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
399 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
400 {
401 firstLine->SetFlags( STRUCT_DELETED );
402 return;
403 }
404
405 // Try to merge the remaining lines
406 if( SCH_LINE* new_line = secondLine->MergeOverlap( screen, firstLine, false ) )
407 {
408 firstLine->SetFlags( STRUCT_DELETED );
409 secondLine->SetFlags( STRUCT_DELETED );
410 AddToScreen( new_line, screen );
411 aCommit->Added( new_line, screen );
412
413 if( new_line->IsSelected() )
414 selectionTool->AddItemToSel( new_line, true /*quiet mode*/ );
415
416 lines.push_back( new_line );
417 }
418 } );
419
420 for( SCH_LINE* line : lines )
421 {
422 if( line->GetEditFlags() & STRUCT_DELETED )
423 {
424 if( line->IsSelected() )
425 selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
426
427 RemoveFromScreen( line, screen );
428 aCommit->Removed( line, screen );
429 }
430 }
431}
432
433
435 const VECTOR2I& aPos )
436{
437 SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
438
439 AddToScreen( junction, aScreen );
440 aCommit->Added( junction, aScreen );
441
442 BreakSegments( aCommit, aPos, aScreen );
443
444 return junction;
445}
446
447
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:86
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:98
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:243
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:133
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
bool IsSelected() const
Definition: eda_item.h:110
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition: sch_rtree.h:243
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
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:1687
A holder to handle information on schematic or board items.
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,...
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,...
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.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
bool BreakSegmentsOnJunctions(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen)
Test all junctions and bus entries in the schematic for intersections with wires and buses and breaks...
void SchematicCleanUp(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen=nullptr)
Perform routine schematic cleaning including breaking wire and buses and deleting identical objects s...
bool BreakSegments(SCH_COMMIT *aCommit, const VECTOR2I &aPoint, SCH_SCREEN *aScreen)
Check every wire and bus for a intersection at aPoint and break into two segments at aPoint if an int...
void BreakSegment(SCH_COMMIT *aCommit, SCH_LINE *aSegment, const VECTOR2I &aPoint, SCH_LINE **aNewSegment, SCH_SCREEN *aScreen)
Break a single segment into two at the specified point.
bool TrimWire(SCH_COMMIT *aCommit, const VECTOR2I &aStart, const VECTOR2I &aEnd)
If any single wire passes through both points, remove the portion between the two points,...
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
void DeleteJunction(SCH_COMMIT *aCommit, SCH_ITEM *aItem)
Removes a given junction and heals any wire segments under the junction.
SCH_JUNCTION * AddJunction(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen, const VECTOR2I &aPos)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:166
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:281
VECTOR2I GetPosition() const override
Definition: sch_junction.h:107
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:41
bool IsWire() const
Return true if the line is a wire.
Definition: sch_line.cpp:977
bool IsParallel(const SCH_LINE *aLine) const
Definition: sch_line.cpp:449
VECTOR2I GetEndPoint() const
Definition: sch_line.h:141
VECTOR2I GetStartPoint() const
Definition: sch_line.h:136
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:462
bool IsBus() const
Return true if the line is a bus.
Definition: sch_line.cpp:983
bool IsNull() const
Definition: sch_line.h:134
bool IsEndPoint(const VECTOR2I &aPoint) const
Definition: sch_line.h:90
bool IsStrokeEquivalent(const SCH_LINE *aLine)
Definition: sch_line.h:187
SCH_LINE * BreakAt(const VECTOR2I &aPoint)
Break this segment into two at the specified point.
Definition: sch_line.cpp:583
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:487
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:167
#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:357
@ LAYER_BUS
Definition: layer_ids.h:358
@ REPAINT
Item needs to be redrawn.
Definition: view_item.h:57
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:84
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:89
@ SCH_LINE_T
Definition: typeinfo.h:163
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:160
@ SCH_BUS_WIRE_ENTRY_T
Definition: typeinfo.h:161
@ SCH_JUNCTION_T
Definition: typeinfo.h:159