KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_islands.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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21#include <core/union_find.h>
23
24#include <algorithm>
25#include <array>
26#include <cassert>
27#include <limits>
28#include <stdexcept>
29#include <unordered_map>
30
31namespace SCH_CONNECTIVITY
32{
33namespace
34{
35 enum CONTACT_KIND
36 {
37 ANCHOR,
38 WIRE,
39 BUS,
40 ENTRY,
42 NC,
43 BUS_SIDE,
44 KIND_COUNT
45 };
46
47 // clang-format off
48 constexpr bool PROPAGATES[KIND_COUNT][KIND_COUNT] = {
49 { true, true, true, true, true, false, false },
50 { true, true, false, true, false, false, false },
51 { true, false, true, false, true, false, false },
52 { true, true, false, false, false, false, false },
53 { true, false, true, false, true, false, false },
54 { false, false, false, false, false, false, false },
55 { false, false, false, false, false, false, false }
56 };
57 // clang-format on
58
59 static_assert(
60 []
61 {
62 for( size_t i = 0; i < KIND_COUNT; ++i )
63 {
64 for( size_t j = 0; j < KIND_COUNT; ++j )
65 {
66 if( PROPAGATES[i][j] != PROPAGATES[j][i] )
67 return false;
68 }
69 }
70
71 return true;
72 }(),
73 "Connectivity propagation must be symmetric" );
74
75 struct PORT
76 {
77 size_t vertex;
78 KIID item;
79 CONTACT_KIND kind;
80 uint8_t bit;
81 };
82
83 struct POINT
84 {
85 VECTOR2I position;
86 std::vector<PORT> ports;
87 };
88
89 int Bound( int aCoordinate, int aMargin )
90 {
91 return int( std::clamp<int64_t>( int64_t( aCoordinate ) + aMargin, std::numeric_limits<int>::min(),
92 std::numeric_limits<int>::max() ) );
93 }
94
95 uint64_t PointKey( VECTOR2I aPoint )
96 {
97 return ( uint64_t( uint32_t( aPoint.x ) ) << 32 ) | uint32_t( aPoint.y );
98 }
99
100 template <typename T>
101 void SortUnique( std::vector<T>& aValues )
102 {
103 std::sort( aValues.begin(), aValues.end() );
104 aValues.erase( std::unique( aValues.begin(), aValues.end() ), aValues.end() );
105 }
106
107 bool IsLabel( KICAD_T aType )
108 {
109 return aType == SCH_LABEL_T || aType == SCH_GLOBAL_LABEL_T || aType == SCH_HIER_LABEL_T
110 || aType == SCH_DIRECTIVE_LABEL_T || aType == SCH_SHEET_PIN_T;
111 }
112
113 bool ClosesDangling( const ITEM_GEOMETRY& aItem, const ITEM_GEOMETRY& aOther )
114 {
115 if( aItem.type == SCH_PIN_T )
116 {
117 if( aOther.type == SCH_PIN_T && aItem.owner == aOther.owner )
118 return false;
119
120 return aOther.type == SCH_PIN_T || IsLabel( aOther.type ) || aOther.type == SCH_NO_CONNECT_T
121 || aOther.type == SCH_JUNCTION_T
122 || ( aOther.segment && aOther.ports.front().kind == PORT_KIND::WIRE );
123 }
124
125 if( aItem.segment )
126 {
127 if( aItem.ports.front().kind == PORT_KIND::WIRE )
128 {
129 return aOther.type != SCH_BUS_BUS_ENTRY_T
130 && !( aOther.segment && aOther.ports.front().kind == PORT_KIND::BUS );
131 }
132
133 return aOther.type != SCH_PIN_T && !( aOther.segment && aOther.ports.front().kind == PORT_KIND::WIRE );
134 }
135
136 if( IsLabel( aItem.type ) )
137 {
138 return aOther.type == SCH_PIN_T || aOther.type == SCH_NO_CONNECT_T || IsLabel( aOther.type )
139 || aOther.segment.has_value();
140 }
141
142 return false;
143 }
144
145 CONTACT_KIND Kind( PORT_KIND aKind )
146 {
147 switch( aKind )
148 {
149 case PORT_KIND::ANCHOR: return ANCHOR;
150 case PORT_KIND::WIRE: return WIRE;
151 case PORT_KIND::BUS: return BUS;
152 case PORT_KIND::ENTRY: return ENTRY;
153 case PORT_KIND::ENTRY_BUS: return ENTRY_BUS;
154 }
155
156 return ANCHOR;
157 }
158} // namespace
159
161{
163 result.items.reserve( aFacts.items.size() );
164
165 for( const ITEM_FACT& fact : aFacts.items )
166 {
167 ITEM_GEOMETRY item;
168 item.id = fact.id;
169 item.type = fact.type;
170 item.owner = fact.owner;
171 item.ports = fact.ports;
172 item.segment = fact.segment;
173 item.jumperedWith = fact.jumperedWith;
174 item.pins.reserve( fact.pins.size() );
175
176 for( const PIN_FACT& pin : fact.pins )
177 item.pins.push_back( { pin.id, pin.position, pin.unit, pin.type } );
178
179 result.items.push_back( std::move( item ) );
180 }
181
182 for( const RULE_AREA_FACT& area : aFacts.ruleAreas )
183 {
184 result.attachedDirectives.insert( result.attachedDirectives.end(), area.attachedDirectives.begin(),
185 area.attachedDirectives.end() );
186 }
187
188 SortUnique( result.attachedDirectives );
189 return result;
190}
191
192SCREEN_ISLANDS BuildScreenIslands( const SCREEN_GEOMETRY& aFacts, const std::vector<std::pair<KIID, int>>& aUnits )
193{
194 std::map<KIID, int> units( aUnits.begin(), aUnits.end() );
195 std::vector<const ITEM_GEOMETRY*> vertices;
196 std::vector<std::vector<KIID>> members;
197 std::unordered_map<KIID, size_t> byId;
198 std::unordered_map<uint64_t, POINT> points;
199 std::unordered_map<KIID, uint8_t> dangling;
201
202 auto addPort = [&]( size_t aVertex, const KIID& aId, VECTOR2I aPosition, CONTACT_KIND aKind, uint8_t aBit )
203 {
204 POINT& point = points[PointKey( aPosition )];
205 point.position = aPosition;
206 point.ports.push_back( { aVertex, aId, aKind, aBit } );
207 dangling[aId] |= aBit;
208 };
209
210 for( const ITEM_GEOMETRY& fact : aFacts.items )
211 {
212 assert( fact.ports.size() <= 2 && ( !fact.segment || fact.ports.size() == 2 ) );
213 size_t index = vertices.size();
214 std::vector<KIID> active;
215
216 if( fact.type == SCH_PIN_T )
217 {
218 auto unit = units.find( fact.owner );
219
220 if( unit == units.end() )
221 throw std::invalid_argument( "Missing symbol unit in connectivity geometry" );
222
223 for( const PIN_GEOMETRY& pin : fact.pins )
224 {
225 if( pin.unit != 0 && unit->second != 0 && pin.unit != unit->second )
226 continue;
227
228 active.push_back( pin.id );
229 addPort( index, pin.id, pin.position, pin.type == ELECTRICAL_PINTYPE::PT_NC ? NC : ANCHOR, 1 );
230
232 dangling[pin.id] = 0;
233 }
234 }
235 else
236 {
237 active.push_back( fact.id );
238
239 for( size_t port = 0; port < fact.ports.size(); ++port )
240 addPort( index, fact.id, fact.ports[port].position, Kind( fact.ports[port].kind ),
241 uint8_t( 1 << port ) );
242
243 if( fact.type == SCH_JUNCTION_T || fact.type == SCH_NO_CONNECT_T )
244 dangling[fact.id] = 0;
245 }
246
247 if( active.empty() )
248 continue;
249
250 vertices.push_back( &fact );
251 SortUnique( active );
252 members.push_back( std::move( active ) );
253 byId.emplace( fact.id, index );
254
255 if( fact.segment )
256 {
257 const SEG& segment = *fact.segment;
258 // SEG::Contains allows a small rounding tolerance; the broadphase must contain it
259 int min[2] = { Bound( std::min( segment.A.x, segment.B.x ), -2 ),
260 Bound( std::min( segment.A.y, segment.B.y ), -2 ) };
261 int max[2] = { Bound( std::max( segment.A.x, segment.B.x ), 2 ),
262 Bound( std::max( segment.A.y, segment.B.y ), 2 ) };
263 lines.Add( min, max, index );
264 }
265 }
266
267 auto tree = lines.Build();
268 KI_UNION_FIND sets( vertices.size() );
269 std::vector<std::array<bool, 2>> entryWire( vertices.size() );
270 std::vector<std::array<bool, 2>> entryBus( vertices.size() );
271 std::vector<std::pair<KIID, KIID>> adjacency;
272 std::vector<std::pair<KIID, KIID>> ncContacts;
273 std::vector<std::pair<KIID, KIID>> busLinks;
274
275 auto entryContact = [&]( const PORT& aEntry, const PORT& aOther )
276 {
277 if( aEntry.kind != ENTRY && aEntry.kind != BUS_SIDE && aEntry.kind != ENTRY_BUS )
278 return;
279
280 size_t end = aEntry.bit == 2 ? 1 : 0;
281
282 if( aOther.kind == WIRE )
283 entryWire[aEntry.vertex][end] = true;
284
285 if( aOther.kind == BUS || aOther.kind == ENTRY_BUS )
286 {
287 entryBus[aEntry.vertex][end] = true;
288
289 if( aEntry.kind != ENTRY_BUS )
290 busLinks.emplace_back( aEntry.item, aOther.item );
291 }
292 };
293
294 for( auto& [key, point] : points )
295 {
296 int position[2] = { point.position.x, point.position.y };
297 auto visitor = [&]( size_t aIndex )
298 {
299 const ITEM_GEOMETRY& fact = *vertices[aIndex];
300
301 if( fact.segment->Contains( point.position ) && point.position != fact.segment->A
302 && point.position != fact.segment->B )
303 {
304 point.ports.push_back( { aIndex, fact.id, Kind( fact.ports.front().kind ), 0 } );
305 }
306
307 return true;
308 };
309 tree.Search( position, position, visitor );
310 bool hasBus = std::any_of( point.ports.begin(), point.ports.end(),
311 []( const PORT& aPort )
312 {
313 return aPort.kind == BUS || aPort.kind == ENTRY_BUS;
314 } );
315
316 for( PORT& port : point.ports )
317 {
318 if( port.kind == ENTRY && hasBus )
319 port.kind = BUS_SIDE;
320 else if( vertices[port.vertex]->type == SCH_JUNCTION_T && hasBus )
321 port.kind = BUS;
322 }
323
324 for( size_t first = 0; first < point.ports.size(); ++first )
325 {
326 const PORT& a = point.ports[first];
327
328 for( size_t second = first + 1; second < point.ports.size(); ++second )
329 {
330 const PORT& b = point.ports[second];
331
332 if( a.vertex == b.vertex )
333 continue;
334
335 auto pair = std::minmax( a.item, b.item );
336
337 if( ClosesDangling( *vertices[a.vertex], *vertices[b.vertex] ) )
338 dangling[a.item] &= ~a.bit;
339
340 if( ClosesDangling( *vertices[b.vertex], *vertices[a.vertex] ) )
341 dangling[b.item] &= ~b.bit;
342
343 if( a.kind == NC || b.kind == NC )
344 {
345 ncContacts.emplace_back( pair.first, pair.second );
346 continue;
347 }
348
349 entryContact( a, b );
350 entryContact( b, a );
351
352 if( PROPAGATES[a.kind][b.kind] )
353 {
354 sets.Unite( a.vertex, b.vertex );
355 adjacency.emplace_back( pair.first, pair.second );
356 }
357 }
358 }
359 }
360
361 for( size_t index = 0; index < vertices.size(); ++index )
362 {
363 const ITEM_GEOMETRY& fact = *vertices[index];
364 const std::vector<KIID>& merged = members[index];
365
366 // Pins merged by number are stacked or jumpered, so each touches the others wherever they sit
367 for( size_t first = 0; first < merged.size(); ++first )
368 {
369 for( size_t second = first + 1; second < merged.size(); ++second )
370 adjacency.emplace_back( merged[first], merged[second] );
371 }
372
373 for( const KIID& other : fact.jumperedWith )
374 {
375 auto found = byId.find( other );
376
377 if( found != byId.end() && vertices[found->second]->owner == fact.owner )
378 {
379 sets.Unite( index, found->second );
380
381 for( const KIID& first : merged )
382 {
383 for( const KIID& second : members[found->second] )
384 {
385 auto pair = std::minmax( first, second );
386 adjacency.emplace_back( pair.first, pair.second );
387 }
388 }
389 }
390 }
391
392 if( fact.type == SCH_BUS_WIRE_ENTRY_T )
393 {
394 const auto& wire = entryWire[index];
395 const auto& bus = entryBus[index];
396 uint8_t state = 3;
397
398 if( ( wire[0] && bus[1] ) || ( wire[1] && bus[0] ) )
399 state = 0;
400 else if( wire[0] || bus[0] )
401 state = 2;
402 else if( wire[1] || bus[1] )
403 state = 1;
404
405 dangling[fact.id] = state;
406 }
407 else if( fact.type == SCH_BUS_BUS_ENTRY_T )
408 {
409 const auto& bus = entryBus[index];
410 dangling[fact.id] = ( bus[0] ? 0 : 1 ) | ( bus[1] ? 0 : 2 );
411 }
412 }
413
414 for( const KIID& directive : aFacts.attachedDirectives )
415 {
416 if( auto found = dangling.find( directive ); found != dangling.end() )
417 found->second = 0;
418 }
419
421 std::map<size_t, size_t> roots;
422 std::unordered_map<KIID, size_t> islandOf;
423
424 for( size_t index = 0; index < vertices.size(); ++index )
425 {
426 auto [root, inserted] = roots.emplace( sets.FindCompress( index ), result.islands.size() );
427
428 if( inserted )
429 result.islands.emplace_back();
430
431 ISLAND& island = result.islands[root->second];
432 const ITEM_GEOMETRY& fact = *vertices[index];
433 island.vertices.push_back( fact.id );
434 island.items.insert( island.items.end(), members[index].begin(), members[index].end() );
435 island.hasWire |= fact.segment && fact.ports.front().kind == PORT_KIND::WIRE;
436 island.hasBusLine |=
437 fact.type == SCH_BUS_BUS_ENTRY_T || ( fact.segment && fact.ports.front().kind == PORT_KIND::BUS );
438
439 for( const KIID& item : members[index] )
440 {
441 islandOf.emplace( item, root->second );
442 island.dangling[item] = dangling[item];
443 }
444 }
445
446 auto distribute = [&]( auto& aPairs, auto aMember )
447 {
448 SortUnique( aPairs );
449
450 for( const auto& pair : aPairs )
451 {
452 size_t first = islandOf.at( pair.first );
453 size_t second = islandOf.at( pair.second );
454 ( result.islands[first].*aMember ).push_back( pair );
455
456 if( first != second )
457 ( result.islands[second].*aMember ).push_back( pair );
458 }
459 };
460 distribute( adjacency, &ISLAND::adjacency );
461 distribute( ncContacts, &ISLAND::ncContacts );
462 distribute( busLinks, &ISLAND::busEntryLinks );
463
464 for( ISLAND& island : result.islands )
465 {
466 SortUnique( island.items );
467 SortUnique( island.vertices );
468 island.anchor = island.items.front();
469 }
470
471 std::ranges::sort( result.islands, std::less<KIID>(), &ISLAND::anchor );
472 return result;
473}
474} // namespace SCH_CONNECTIVITY
int index
Definition kiid.h:46
Builder for constructing a PACKED_RTREE from a set of items.
void Add(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
Lock-free disjoint-set over a dense range of indices.
Definition union_find.h:48
size_t FindCompress(size_t aX)
Shorten the path from aX to its root so that later queries walk less of it.
Definition union_find.h:150
bool Unite(size_t aA, size_t aB)
Merge the components that hold aA and aB.
Definition union_find.h:82
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Value keys and the key session of the schematic connectivity engine.
SCREEN_GEOMETRY GeometryOf(const SCREEN_FACTS &aFacts)
SCREEN_ISLANDS BuildScreenIslands(const SCREEN_GEOMETRY &aFacts, const std::vector< std::pair< KIID, int > > &aUnits)
Kindless geometry for one unit signature.
@ PT_NC
not connected (must be left open)
Definition pin_type.h:46
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:40
The smallest set of items on one screen that the drawing connects.
std::vector< std::pair< KIID, KIID > > adjacency
std::vector< std::pair< KIID, KIID > > busEntryLinks
std::vector< std::pair< KIID, KIID > > ncContacts
std::vector< KIID > items
std::map< KIID, uint8_t > dangling
std::vector< KIID > vertices
KIID anchor
The smallest KIID in items, which names the island.
A value copy of one connectable item, or of one group of pins that share a number and a position.
Definition conn_facts.h:101
KIID id
The item KIID, or the smallest member KIID of a pin group.
Definition conn_facts.h:102
std::vector< PIN_FACT > pins
Definition conn_facts.h:108
KIID owner
The parent symbol or sheet, or niluuid.
Definition conn_facts.h:104
std::vector< KIID > jumperedWith
Definition conn_facts.h:109
std::optional< SEG > segment
Definition conn_facts.h:106
std::vector< PORT_FACT > ports
Definition conn_facts.h:105
std::optional< SEG > segment
std::vector< PIN_GEOMETRY > pins
std::vector< PORT_FACT > ports
std::vector< KIID > jumperedWith
std::vector< KIID > attachedDirectives
Definition conn_facts.h:122
The value copy of one screen.
Definition conn_facts.h:251
std::vector< ITEM_FACT > items
Definition conn_facts.h:256
std::vector< RULE_AREA_FACT > ruleAreas
Definition conn_facts.h:257
std::vector< KIID > attachedDirectives
std::vector< ITEM_GEOMETRY > items
The islands of one screen and unit signature, sorted by anchor.
KIBIS_PIN * pin
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683