KiCad PCB EDA Suite
Loading...
Searching...
No Matches
junction_helpers.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "junction_helpers.h"
21
22#include <sch_line.h>
23#include <sch_screen.h>
24#include <sch_junction.h>
25#include <sch_item.h>
26#include <trigo.h>
27
28using namespace JUNCTION_HELPERS;
29
31 bool aBreakCrossings )
32{
33 enum layers
34 {
35 WIRES = 0,
36 BUSES
37 };
38
40 info.hasBusEntry = false;
41 info.hasExplicitJunctionDot = false;
42 info.isJunction = false;
43 info.hasBusEntryToMultipleWires = false;
44
45 bool breakLines[2] = { false };
46 std::unordered_set<int> exitAngles[2];
47 std::vector<const SCH_LINE*> midPointLines[2];
48
49 EE_RTREE filtered;
50 std::list<std::unique_ptr<SCH_LINE>> mergedLines;
51
52 // Ignore items that are currently being moved or flagged to skip
53 // and temporarily merge collinear wires before analyzing the point.
54 for( SCH_ITEM* item : aItems.Overlapping( aPosition ) )
55 {
56 if( item->GetEditFlags() & ( SKIP_STRUCT | STRUCT_DELETED ) )
57 continue;
58
59 switch( item->Type() )
60 {
61 case SCH_LINE_T:
62 {
63 SCH_LINE* line = static_cast<SCH_LINE*>( item );
64
65 if( line->IsConnectable() )
66 mergedLines.emplace_back( new SCH_LINE( *line ) );
67
68 break;
69 }
70
71 case SCH_JUNCTION_T:
72 if( item->HitTest( aPosition, -1 ) )
73 info.hasExplicitJunctionDot = true;
74
75 filtered.insert( item );
76 break;
77
79 info.hasBusEntry = true;
80 filtered.insert( item );
81 break;
82
83 case SCH_SHEET_T:
84 case SCH_SYMBOL_T:
85 case SCH_LABEL_T:
88 filtered.insert( item );
89 break;
90
91 default:
92 break;
93 }
94 }
95
96 if( mergedLines.size() + filtered.size() < 2 )
97 return info;
98
99 // Merge collinear wire segments
100 bool merged = false;
101
102 do
103 {
104 if( info.hasExplicitJunctionDot || aBreakCrossings )
105 break;
106
107 merged = false;
108
109 for( auto it_i = mergedLines.begin(); it_i != mergedLines.end() && !merged; ++it_i )
110 {
111 for( auto it_j = std::next( it_i ); it_j != mergedLines.end(); ++it_j )
112 {
113 if( auto* line = ( *it_i )->MergeOverlap( nullptr, it_j->get(), false ) )
114 {
115 it_i->reset( line );
116 mergedLines.erase( it_j );
117 merged = true;
118 break;
119 }
120 }
121 }
122 } while( merged );
123
124 for( const auto& line : mergedLines )
125 filtered.insert( line.get() );
126
127
128 // A pin at 90° still shouldn't match a line at 90° so just give pins unique numbers
129 int uniqueAngle = 10000;
130
131 for( const SCH_ITEM* item : filtered )
132 {
133 if( item->GetEditFlags() & STRUCT_DELETED )
134 continue;
135
136 switch( item->Type() )
137 {
138 case SCH_JUNCTION_T:
139 if( item->HitTest( aPosition, -1 ) )
140 info.hasExplicitJunctionDot = true;
141
142 break;
143
144 case SCH_LINE_T:
145 {
146 const SCH_LINE* line = static_cast<const SCH_LINE*>( item );
147 int layer;
148
149 if( line->GetStartPoint() == line->GetEndPoint() )
150 break;
151 else if( line->GetLayer() == LAYER_WIRE )
152 layer = WIRES;
153 else if( line->GetLayer() == LAYER_BUS )
154 layer = BUSES;
155 else
156 break;
157
158 if( line->IsConnected( aPosition ) )
159 {
160 breakLines[layer] = true;
161 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
162 }
163 else if( line->HitTest( aPosition, -1 ) )
164 {
165 if( aBreakCrossings )
166 breakLines[layer] = true;
167
168 // Defer any line midpoints until we know whether or not we're breaking them
169 midPointLines[layer].push_back( line );
170 }
171 }
172 break;
173
175 if( item->IsConnected( aPosition ) )
176 {
177 breakLines[BUSES] = true;
178 exitAngles[BUSES].insert( uniqueAngle++ );
179 breakLines[WIRES] = true;
180 exitAngles[WIRES].insert( uniqueAngle++ );
181 info.hasBusEntry = true;
182 }
183
184 break;
185
186 case SCH_SYMBOL_T:
187 case SCH_SHEET_T:
188 if( item->IsConnected( aPosition ) )
189 {
190 breakLines[WIRES] = true;
191 exitAngles[WIRES].insert( uniqueAngle++ );
192 }
193
194 break;
195
196 case SCH_LABEL_T:
197 if( item->IsConnected( aPosition ) )
198 {
199 if( SCH_CONNECTION::IsBusLabel( static_cast<const SCH_LABEL*>( item )->GetText() ) )
200 breakLines[BUSES] = true;
201 else
202 breakLines[WIRES] = true;
203 }
204
205 break;
206
207 case SCH_HIER_LABEL_T:
209 if( item->IsConnected( aPosition ) )
210 breakLines[WIRES] = true;
211
212 break;
213
214 default:
215 break;
216 }
217 }
218
219 for( int layer : { WIRES, BUSES } )
220 {
221 if( breakLines[layer] )
222 {
223 for( const SCH_LINE* line : midPointLines[layer] )
224 {
225 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
226 exitAngles[layer].insert( line->GetReverseAngleFrom( aPosition ) );
227 }
228 }
229 }
230
231 if( info.hasBusEntry )
232 {
233 // The bus entry and one wire is 2 wires, and the one entry is exactly one bus
234 // Any more wires must be multiple wires, but any more buses means a wire
235 // crossing at the bus entry root.
236 info.hasBusEntryToMultipleWires = exitAngles[WIRES].size() > 2 && exitAngles[BUSES].size() == 1;
237 }
238
239 // Any three things of the same type is a junction of some sort
240 info.isJunction = exitAngles[WIRES].size() >= 3 || exitAngles[BUSES].size() >= 3;
241
242 return info;
243}
244
245
246std::vector<SCH_JUNCTION*> JUNCTION_HELPERS::PreviewJunctions( const SCH_SCREEN* aScreen,
247 const std::vector<SCH_ITEM*>& aItems )
248{
250
251 // Existing items
252 for( const SCH_ITEM* item : aScreen->Items() )
253 {
254 if( !item->IsConnectable() )
255 continue;
256
257 combined.insert( const_cast<SCH_ITEM*>( item ) );
258 }
259
260 // Temporary items
261 for( SCH_ITEM* item : aItems )
262 {
263 if( !item || !item->IsConnectable() )
264 continue;
265 combined.insert( item );
266 }
267
268 std::vector<VECTOR2I> connections = aScreen->GetConnections();
269 std::vector<VECTOR2I> pts;
270
271 for( SCH_ITEM* item : aItems )
272 {
273 if( !item || !item->IsConnectable() )
274 continue;
275
276 std::vector<VECTOR2I> new_pts = item->GetConnectionPoints();
277 pts.insert( pts.end(), new_pts.begin(), new_pts.end() );
278
279 if( item->Type() == SCH_LINE_T )
280 {
281 SCH_LINE* line = static_cast<SCH_LINE*>( item );
282
283 for( const VECTOR2I& pt : connections )
284 {
285 if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), pt ) )
286 pts.push_back( pt );
287 }
288 }
289 }
290
291 std::sort( pts.begin(), pts.end(),
292 []( const VECTOR2I& a, const VECTOR2I& b )
293 {
294 return a.x < b.x || ( a.x == b.x && a.y < b.y );
295 } );
296
297 pts.erase( std::unique( pts.begin(), pts.end() ), pts.end() );
298
299 std::vector<SCH_JUNCTION*> jcts;
300
301 for( const VECTOR2I& pt : pts )
302 {
303 POINT_INFO info = AnalyzePoint( combined, pt, false );
304
305 if( info.isJunction && ( !info.hasBusEntry || info.hasBusEntryToMultipleWires ) )
306 {
307 jcts.push_back( new SCH_JUNCTION( pt ) );
308 }
309 }
310
311 return jcts;
312}
Implement an R-tree for fast spatial and type indexing of schematic items.
Definition sch_rtree.h:40
size_t size() const
Return the number of items in the tree.
Definition sch_rtree.h:174
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:246
void insert(SCH_ITEM *aItem)
Insert an item into the tree.
Definition sch_rtree.h:59
static bool IsBusLabel(const wxString &aLabel)
Test if aLabel has a bus notation.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:321
bool IsConnected(const VECTOR2I &aPoint) const
Test the item to see if it is connected to aPoint.
Definition sch_item.cpp:343
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition sch_line.cpp:816
int GetAngleFrom(const VECTOR2I &aPoint) const
Definition sch_line.cpp:417
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
bool IsConnectable() const override
Definition sch_line.cpp:664
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:117
std::vector< VECTOR2I > GetConnections() const
Collect a unique list of all possible connection points in the schematic.
#define STRUCT_DELETED
flag indication structures to be erased
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_BUS
Definition layer_ids.h:453
std::vector< SCH_JUNCTION * > PreviewJunctions(const class SCH_SCREEN *aScreen, const std::vector< class SCH_ITEM * > &aItems)
Determine the points where explicit junctions would be required if the given temporary items were com...
POINT_INFO AnalyzePoint(const EE_RTREE &aItem, const VECTOR2I &aPosition, bool aBreakCrossings)
Check a tree of items for a confluence at a given point and work out what kind of junction it is,...
A selection of information about a point in the schematic that might be eligible for turning into a j...
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:167
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:165
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_JUNCTION_T
Definition typeinfo.h:163
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695