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 if( item->Type() == SCH_LINE_T )
60 {
61 SCH_LINE* line = static_cast<SCH_LINE*>( item );
62
63 if( line->IsConnectable() )
64 {
65 mergedLines.emplace_back( new SCH_LINE( *line ) );
66 continue;
67 }
68 }
69 else if( item->Type() == SCH_JUNCTION_T )
70 {
71 if( item->HitTest( aPosition, -1 ) )
72 info.hasExplicitJunctionDot = true;
73
74 filtered.insert( item );
75 }
76 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
77 {
78 info.hasBusEntry = true;
79 filtered.insert( item );
80 }
81 else if( item->Type() == SCH_SHEET_T || item->Type() == SCH_SYMBOL_T )
82 {
83 filtered.insert( item );
84 }
85 }
86
87 if( mergedLines.size() + filtered.size() < 2 )
88 return info;
89
90 // Merge collinear wire segments
91 bool merged = false;
92
93 do
94 {
95 if( info.hasExplicitJunctionDot || aBreakCrossings )
96 break;
97
98 merged = false;
99
100 for( auto it_i = mergedLines.begin(); it_i != mergedLines.end() && !merged; ++it_i )
101 {
102 for( auto it_j = std::next( it_i ); it_j != mergedLines.end(); ++it_j )
103 {
104 if( auto* line = ( *it_i )->MergeOverlap( nullptr, it_j->get(), false ) )
105 {
106 it_i->reset( line );
107 mergedLines.erase( it_j );
108 merged = true;
109 break;
110 }
111 }
112 }
113 } while( merged );
114
115 for( const auto& line : mergedLines )
116 filtered.insert( line.get() );
117
118
119 // A pin at 90° still shouldn't match a line at 90° so just give pins unique numbers
120 int uniqueAngle = 10000;
121
122 for( const SCH_ITEM* item : filtered )
123 {
124 if( item->GetEditFlags() & STRUCT_DELETED )
125 continue;
126
127 switch( item->Type() )
128 {
129 case SCH_JUNCTION_T:
130 if( item->HitTest( aPosition, -1 ) )
131 info.hasExplicitJunctionDot = true;
132
133 break;
134
135 case SCH_LINE_T:
136 {
137 const SCH_LINE* line = static_cast<const SCH_LINE*>( item );
138 int layer;
139
140 if( line->GetStartPoint() == line->GetEndPoint() )
141 break;
142 else if( line->GetLayer() == LAYER_WIRE )
143 layer = WIRES;
144 else if( line->GetLayer() == LAYER_BUS )
145 layer = BUSES;
146 else
147 break;
148
149 if( line->IsConnected( aPosition ) )
150 {
151 breakLines[layer] = true;
152 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
153 }
154 else if( line->HitTest( aPosition, -1 ) )
155 {
156 if( aBreakCrossings )
157 breakLines[layer] = true;
158
159 // Defer any line midpoints until we know whether or not we're breaking them
160 midPointLines[layer].push_back( line );
161 }
162 }
163 break;
164
166 if( item->IsConnected( aPosition ) )
167 {
168 breakLines[BUSES] = true;
169 exitAngles[BUSES].insert( uniqueAngle++ );
170 breakLines[WIRES] = true;
171 exitAngles[WIRES].insert( uniqueAngle++ );
172 info.hasBusEntry = true;
173 }
174
175 break;
176
177 case SCH_SYMBOL_T:
178 case SCH_SHEET_T:
179 if( item->IsConnected( aPosition ) )
180 {
181 breakLines[WIRES] = true;
182 exitAngles[WIRES].insert( uniqueAngle++ );
183 }
184
185 break;
186
187 default: break;
188 }
189 }
190
191 for( int layer : { WIRES, BUSES } )
192 {
193 if( breakLines[layer] )
194 {
195 for( const SCH_LINE* line : midPointLines[layer] )
196 {
197 exitAngles[layer].insert( line->GetAngleFrom( aPosition ) );
198 exitAngles[layer].insert( line->GetReverseAngleFrom( aPosition ) );
199 }
200 }
201 }
202
203 if( info.hasBusEntry )
204 {
205 // The bus entry and one wire is 2 wires, and the one entry is exactly one bus
206 // Any more wires must be multiple wires, but any more buses means a wire
207 // crossing at the bus entry root.
208 info.hasBusEntryToMultipleWires =
209 exitAngles[WIRES].size() > 2 && exitAngles[BUSES].size() == 1;
210 }
211
212 // Any three things of the same type is a junction of some sort
213 info.isJunction = exitAngles[WIRES].size() >= 3 || exitAngles[BUSES].size() >= 3;
214
215 return info;
216}
217
218
219std::vector<SCH_JUNCTION*> JUNCTION_HELPERS::PreviewJunctions( const SCH_SCREEN* aScreen,
220 const std::vector<SCH_ITEM*>& aItems )
221{
223
224 // Existing items
225 for( const SCH_ITEM* item : aScreen->Items() )
226 {
227 if( !item->IsConnectable() )
228 continue;
229
230 combined.insert( const_cast<SCH_ITEM*>( item ) );
231 }
232
233 // Temporary items
234 for( SCH_ITEM* item : aItems )
235 {
236 if( !item || !item->IsConnectable() )
237 continue;
238 combined.insert( item );
239 }
240
241 std::vector<VECTOR2I> connections = aScreen->GetConnections();
242 std::vector<VECTOR2I> pts;
243
244 for( SCH_ITEM* item : aItems )
245 {
246 if( !item || !item->IsConnectable() )
247 continue;
248
249 std::vector<VECTOR2I> new_pts = item->GetConnectionPoints();
250 pts.insert( pts.end(), new_pts.begin(), new_pts.end() );
251
252 if( item->Type() == SCH_LINE_T )
253 {
254 SCH_LINE* line = static_cast<SCH_LINE*>( item );
255
256 for( const VECTOR2I& pt : connections )
257 {
258 if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), pt ) )
259 pts.push_back( pt );
260 }
261 }
262 }
263
264 std::sort( pts.begin(), pts.end(),
265 []( const VECTOR2I& a, const VECTOR2I& b )
266 {
267 return a.x < b.x || ( a.x == b.x && a.y < b.y );
268 } );
269
270 pts.erase( std::unique( pts.begin(), pts.end() ), pts.end() );
271
272 std::vector<SCH_JUNCTION*> jcts;
273
274 for( const VECTOR2I& pt : pts )
275 {
276 POINT_INFO info = AnalyzePoint( combined, pt, false );
277
278 if( info.isJunction && ( !info.hasBusEntry || info.hasBusEntryToMultipleWires ) )
279 {
280 jcts.push_back( new SCH_JUNCTION( pt ) );
281 }
282 }
283
284 return jcts;
285}
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
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:309
bool IsConnected(const VECTOR2I &aPoint) const
Test the item to see if it is connected to aPoint.
Definition sch_item.cpp:314
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:789
int GetAngleFrom(const VECTOR2I &aPoint) const
Definition sch_line.cpp:390
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
bool IsConnectable() const override
Definition sch_line.cpp:637
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:451
@ LAYER_BUS
Definition layer_ids.h:452
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:165
@ SCH_SYMBOL_T
Definition typeinfo.h:174
@ SCH_SHEET_T
Definition typeinfo.h:177
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:163
@ SCH_JUNCTION_T
Definition typeinfo.h:161
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695