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
28#include <unordered_set>
29
30using namespace JUNCTION_HELPERS;
31
33 bool aBreakCrossings )
34{
35 enum layers
36 {
37 WIRES = 0,
38 BUSES
39 };
40
42 info.hasBusEntry = false;
43 info.hasExplicitJunctionDot = false;
44 info.isJunction = false;
45 info.hasBusEntryToMultipleWires = false;
46 info.hasBusAtPoint = false;
47
48 bool breakLines[2] = { false };
49 std::unordered_set<int> exitAngles[2];
50 std::vector<const SCH_LINE*> midPointLines[2];
51
52 EE_RTREE filtered;
53 std::list<std::unique_ptr<SCH_LINE>> mergedLines;
54
55 // Ignore items that are currently being moved or flagged to skip
56 // and temporarily merge collinear wires before analyzing the point.
57 for( SCH_ITEM* item : aItems.Overlapping( aPosition ) )
58 {
59 if( item->GetEditFlags() & ( SKIP_STRUCT | STRUCT_DELETED ) )
60 continue;
61
62 switch( item->Type() )
63 {
64 case SCH_LINE_T:
65 {
66 SCH_LINE* line = static_cast<SCH_LINE*>( item );
67
68 if( line->IsConnectable() )
69 mergedLines.emplace_back( new SCH_LINE( *line ) );
70
71 break;
72 }
73
74 case SCH_JUNCTION_T:
75 if( item->HitTest( aPosition, -1 ) )
76 info.hasExplicitJunctionDot = true;
77
78 filtered.insert( item );
79 break;
80
82 info.hasBusEntry = true;
83 filtered.insert( item );
84 break;
85
86 case SCH_SHEET_T:
87 case SCH_SYMBOL_T:
88 case SCH_LABEL_T:
91 filtered.insert( item );
92 break;
93
94 default:
95 break;
96 }
97 }
98
99 if( mergedLines.size() + filtered.size() < 2 )
100 return info;
101
102 // Merge collinear wire segments
103 bool merged = false;
104
105 do
106 {
107 if( info.hasExplicitJunctionDot || aBreakCrossings )
108 break;
109
110 merged = false;
111
112 for( auto it_i = mergedLines.begin(); it_i != mergedLines.end() && !merged; ++it_i )
113 {
114 for( auto it_j = std::next( it_i ); it_j != mergedLines.end(); ++it_j )
115 {
116 if( auto* line = ( *it_i )->MergeOverlap( nullptr, it_j->get(), false ) )
117 {
118 it_i->reset( line );
119 mergedLines.erase( it_j );
120 merged = true;
121 break;
122 }
123 }
124 }
125 } while( merged );
126
127 for( const auto& line : mergedLines )
128 filtered.insert( line.get() );
129
130
131 // A pin at 90° still shouldn't match a line at 90° so just give pins unique numbers
132 int uniqueAngle = 10000;
133
134 for( const SCH_ITEM* item : filtered )
135 {
136 if( item->GetEditFlags() & STRUCT_DELETED )
137 continue;
138
139 switch( item->Type() )
140 {
141 case SCH_JUNCTION_T:
142 if( item->HitTest( aPosition, -1 ) )
143 info.hasExplicitJunctionDot = true;
144
145 break;
146
147 case SCH_LINE_T:
148 {
149 const SCH_LINE* line = static_cast<const SCH_LINE*>( item );
150 int layer;
151
152 if( line->GetStartPoint() == line->GetEndPoint() )
153 break;
154 else if( line->GetLayer() == LAYER_WIRE )
155 layer = WIRES;
156 else if( line->GetLayer() == LAYER_BUS )
157 layer = BUSES;
158 else
159 break;
160
161 if( line->IsConnected( aPosition ) )
162 {
163 breakLines[layer] = true;
164 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
165 }
166 else if( line->HitTest( aPosition, -1 ) )
167 {
168 if( aBreakCrossings )
169 breakLines[layer] = true;
170
171 // Defer any line midpoints until we know whether or not we're breaking them
172 midPointLines[layer].push_back( line );
173 }
174
175 if( layer == BUSES && line->HitTest( aPosition, -1 ) )
176 info.hasBusAtPoint = true;
177 }
178 break;
179
181 if( item->IsConnected( aPosition ) )
182 {
183 breakLines[BUSES] = true;
184 exitAngles[BUSES].insert( uniqueAngle++ );
185 breakLines[WIRES] = true;
186 exitAngles[WIRES].insert( uniqueAngle++ );
187 info.hasBusEntry = true;
188 }
189
190 break;
191
192 case SCH_SYMBOL_T:
193 case SCH_SHEET_T:
194 if( item->IsConnected( aPosition ) )
195 {
196 breakLines[WIRES] = true;
197 exitAngles[WIRES].insert( uniqueAngle++ );
198 }
199
200 break;
201
202 case SCH_LABEL_T:
203 if( item->IsConnected( aPosition ) )
204 {
205 if( SCH_CONNECTION::IsBusLabel( static_cast<const SCH_LABEL*>( item )->GetText() ) )
206 breakLines[BUSES] = true;
207 else
208 breakLines[WIRES] = true;
209 }
210
211 break;
212
213 case SCH_HIER_LABEL_T:
215 if( item->IsConnected( aPosition ) )
216 breakLines[WIRES] = true;
217
218 break;
219
220 default:
221 break;
222 }
223 }
224
225 for( int layer : { WIRES, BUSES } )
226 {
227 if( breakLines[layer] )
228 {
229 for( const SCH_LINE* line : midPointLines[layer] )
230 {
231 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
232 exitAngles[layer].insert( line->GetReverseAngleFrom( aPosition ) );
233 }
234 }
235 }
236
237 if( info.hasBusEntry )
238 {
239 // The bus entry and one wire is 2 wires, and the one entry is exactly one bus
240 // Any more wires must be multiple wires, but any more buses means a wire
241 // crossing at the bus entry root.
242 info.hasBusEntryToMultipleWires = exitAngles[WIRES].size() > 2 && exitAngles[BUSES].size() == 1;
243 }
244
245 // Any three things of the same type is a junction of some sort
246 info.isJunction = exitAngles[WIRES].size() >= 3 || exitAngles[BUSES].size() >= 3;
247
248 return info;
249}
250
251
252std::vector<SCH_JUNCTION*> JUNCTION_HELPERS::PreviewJunctions( const SCH_SCREEN* aScreen,
253 const std::vector<SCH_ITEM*>& aItems )
254{
256 std::unordered_set<const SCH_ITEM*> previewSet( aItems.begin(), aItems.end() );
257
258 // Existing items, skipping any that are also in aItems to avoid double-counting
259 for( const SCH_ITEM* item : aScreen->Items() )
260 {
261 if( !item->IsConnectable() )
262 continue;
263
264 if( previewSet.count( item ) )
265 continue;
266
267 combined.insert( const_cast<SCH_ITEM*>( item ) );
268 }
269
270 // Temporary/preview items
271 for( SCH_ITEM* item : aItems )
272 {
273 if( !item || !item->IsConnectable() )
274 continue;
275
276 combined.insert( item );
277 }
278
279 std::vector<VECTOR2I> connections = aScreen->GetConnections();
280 std::vector<VECTOR2I> pts;
281
282 for( SCH_ITEM* item : aItems )
283 {
284 if( !item || !item->IsConnectable() )
285 continue;
286
287 std::vector<VECTOR2I> new_pts = item->GetConnectionPoints();
288 pts.insert( pts.end(), new_pts.begin(), new_pts.end() );
289
290 if( item->Type() == SCH_LINE_T )
291 {
292 SCH_LINE* line = static_cast<SCH_LINE*>( item );
293
294 for( const VECTOR2I& pt : connections )
295 {
296 if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), pt ) )
297 pts.push_back( pt );
298 }
299 }
300 }
301
302 std::sort( pts.begin(), pts.end(),
303 []( const VECTOR2I& a, const VECTOR2I& b )
304 {
305 return a.x < b.x || ( a.x == b.x && a.y < b.y );
306 } );
307
308 pts.erase( std::unique( pts.begin(), pts.end() ), pts.end() );
309
310 std::vector<SCH_JUNCTION*> jcts;
311
312 for( const VECTOR2I& pt : pts )
313 {
314 POINT_INFO info = AnalyzePoint( combined, pt, false );
315
316 if( info.isJunction && ( !info.hasBusEntry || info.hasBusEntryToMultipleWires ) )
317 {
318 SCH_JUNCTION* junction = new SCH_JUNCTION( pt );
319
320 if( info.hasBusAtPoint )
321 junction->SetLayer( LAYER_BUS_JUNCTION );
322
323 jcts.push_back( junction );
324 }
325 }
326
327 return jcts;
328}
Implement an R-tree for fast spatial and type indexing of schematic items.
Definition sch_rtree.h:38
size_t size() const
Return the number of items in the tree.
Definition sch_rtree.h:154
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:230
void insert(SCH_ITEM *aItem)
Insert an item into the tree.
Definition sch_rtree.h:49
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:168
void SetLayer(SCH_LAYER_ID aLayer)
Definition sch_item.h:345
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:344
bool IsConnected(const VECTOR2I &aPoint) const
Test the item to see if it is connected to aPoint.
Definition sch_item.cpp:479
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:817
int GetAngleFrom(const VECTOR2I &aPoint) const
Definition sch_line.cpp:418
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
bool IsConnectable() const override
Definition sch_line.cpp:665
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
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:454
@ LAYER_BUS
Definition layer_ids.h:455
@ LAYER_BUS_JUNCTION
Definition layer_ids.h:500
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:164
@ SCH_SYMBOL_T
Definition typeinfo.h:173
@ SCH_LABEL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:176
@ SCH_HIER_LABEL_T
Definition typeinfo.h:170
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:162
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:169
@ SCH_JUNCTION_T
Definition typeinfo.h:160
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687