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