KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_facts.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
20#include "conn_facts.h"
21#include "conn_pin_name.h"
22#include "conn_text.h"
23
24#include <algorithm>
25#include <common.h>
27#include <map>
28#include <geometry/shape_rect.h>
30#include <stdexcept>
31#include <tuple>
32#include <unordered_set>
33#include <string_utils.h>
34#include <wx/thread.h>
35#include <wx/regex.h>
36#include <lib_symbol.h>
37#include <sch_bus_entry.h>
38#include <sch_field.h>
39#include <sch_label.h>
40#include <sch_line.h>
41#include <sch_pin.h>
42#include <sch_rule_area.h>
43#include <sch_screen.h>
44#include <sch_shape.h>
45#include <sch_sheet.h>
46#include <sch_sheet_pin.h>
47#include <sch_sheet_path.h>
48#include <sch_symbol.h>
49#include <sch_text.h>
50#include <sch_textbox.h>
51#include <schematic.h>
52#include <sim/sim_lib_mgr.h>
53#include <project.h>
55
56namespace SCH_CONNECTIVITY
57{
58namespace
59{
60 class INSTANCE_FIELD : public SCH_FIELD
61 {
62 public:
63 INSTANCE_FIELD( const SCH_FIELD& aField, const SCH_SHEET_PATH& aPath ) :
64 SCH_FIELD( aField ),
65 m_path( aPath )
66 {
67 ClearBoundingBoxCache();
68 }
69
70 wxString GetShownText( RESOLUTION_CONTEXT aContext, int aDepth = 0 ) const override
71 {
72 const wxString variant = Schematic() ? Schematic()->GetCurrentVariant() : wxString();
73 return SCH_FIELD::GetShownText( &m_path, aContext, variant, aDepth );
74 }
75
76 private:
77 const SCH_SHEET_PATH& m_path;
78 };
79
80 template <typename T>
81 void SortUnique( std::vector<T>& aValues )
82 {
83 std::sort( aValues.begin(), aValues.end() );
84 aValues.erase( std::unique( aValues.begin(), aValues.end() ), aValues.end() );
85 }
86
87 constexpr auto ById = []( const auto& a, const auto& b )
88 {
89 return a.id < b.id;
90 };
91
92 void ReadLabel( ITEM_FACT& aFact, const SCH_LABEL_BASE& aLabel )
93 {
94 aFact.rawText = aLabel.GetText();
95 aFact.outputShape = aLabel.GetShape() == L_OUTPUT;
96
97 for( const SCH_FIELD& field : aLabel.GetFields() )
98 {
99 if( field.GetUntranslatedName() == wxS( "Netclass" ) )
100 aFact.netclassFields.push_back( field.GetText() );
101 }
102 }
103} // namespace
104
106{
107 LIBRARY_FIELD_FACT result{ aField.GetId(), aField.IsMandatory(), aField.GetName(), aField.GetText(),
108 aField.GetPosition(), aField.IsVisible(), aField.IsNameShown(), aField.IsPrivate(),
109 reinterpret_cast<uintptr_t>( aField.GetFont() ), aField.GetAttributes() };
110
111 // Fonts are process-interned resources; comparison needs identity, never a deferred dereference
112 result.style.m_Font = nullptr;
113 return result;
114}
115
116bool LIBRARY_FIELD_FACT::Matches( const LIBRARY_FIELD_FACT& aOther, int aCompareFlags ) const
117{
118 using FLAGS = SCH_ITEM::COMPARE_FLAGS;
119
120 if( ( aCompareFlags & FLAGS::FIELD_TEXT ) && id != FIELD_T::REFERENCE && text != aOther.text )
121 return false;
122
123 if( ( aCompareFlags & FLAGS::FIELD_SIZE_AND_STYLE )
124 && ( fontIdentity != aOther.fontIdentity || style != aOther.style ) )
125 {
126 return false;
127 }
128
129 if( ( aCompareFlags & FLAGS::FIELD_VISIBILITY )
130 && ( visible != aOther.visible || nameShown != aOther.nameShown ) )
131 {
132 return false;
133 }
134
135 return isPrivate == aOther.isPrivate
136 && ( !( aCompareFlags & FLAGS::FIELD_POSITIONS ) || position == aOther.position );
137}
138
139std::vector<SIMULATION_MODEL_FACT> ExtractSimulationModelFacts( const SCH_SHEET_PATH& aPath,
140 const wxString& aVariantName )
141{
142 wxASSERT( wxThread::IsMain() );
143
145 throw std::logic_error( "Simulation sources require published connectivity text" );
146
147 std::vector<SIMULATION_MODEL_FACT> result;
148 SCH_SCREEN* screen = aPath.LastScreen();
149
150 if( !screen || aPath.GetExcludedFromSim( aVariantName ) )
151 return result;
152
153 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
154 {
155 const auto* symbol = static_cast<const SCH_SYMBOL*>( item );
156
157 if( symbol->GetRef( &aPath ).StartsWith( '#' ) || symbol->ResolveExcludedFromSim( &aPath, aVariantName ) )
158 continue;
159
160 result.push_back( { symbol->m_Uuid, symbol->GetPosition(),
161 SIM_LIB_MGR::CaptureModelInput( &aPath, *symbol, 0, aVariantName ) } );
162 }
163
164 std::ranges::sort( result, std::less<KIID>{}, &SIMULATION_MODEL_FACT::id );
165 return result;
166}
167
168std::optional<VARIANT_SYMBOL_FACT> ExtractVariantSymbolFact( const SCH_SYMBOL& aSymbol,
169 const SCH_SHEET_PATH& aPath )
170{
171 wxASSERT( wxIsMainThread() );
172 SCH_SYMBOL_INSTANCE instance;
173
174 if( !aSymbol.GetInstance( instance, aPath.Path() ) )
175 return std::nullopt;
176
178 result.id = aSymbol.m_Uuid;
179
180 for( const auto& [name, variant] : instance.m_Variants )
181 {
182 if( variant.m_SymbolOverride )
183 result.overrides.emplace( name, *variant.m_SymbolOverride );
184 }
185
186 if( result.overrides.empty() )
187 return std::nullopt;
188
189 return result;
190}
191
193{
194 wxASSERT( wxIsMainThread() );
196 result.hasEmbeddedSymbol = true;
197 result.unitCount = aSymbol.GetUnitCount();
198 result.bodyStyleCount = aSymbol.GetBodyStyleCount();
199 result.attributes = aSymbol.ComparisonAttributes();
200
201 for( const SCH_ITEM& field : aSymbol.GetDrawItems()[SCH_FIELD_T] )
202 result.fields.push_back( ExtractLibraryFieldFact( static_cast<const SCH_FIELD&>( field ) ) );
203
204 for( const SCH_ITEM& pin : aSymbol.GetDrawItems()[SCH_PIN_T] )
205 result.pins.push_back( static_cast<const SCH_PIN&>( pin ).ComparisonData() );
206
207 for( const SCH_ITEM& drawing : aSymbol.GetDrawItems()[SCH_SHAPE_T] )
208 {
209 const SCH_SHAPE& shape = static_cast<const SCH_SHAPE&>( drawing );
210 result.shapes.push_back( { shape.m_Uuid, shape.GetUnit(), shape.GetBodyStyle(), shape.IsPrivate(),
211 shape.GetPosition(), shape } );
212 }
213
214 if( aSymbol.IsDerived() )
215 {
216 result.inheritedPins.emplace();
217
218 for( const SCH_PIN* pin : aSymbol.GetGraphicalPins( 0, 0 ) )
219 result.inheritedPins->push_back( pin->ComparisonData() );
220 }
221
222 return result;
223}
224
226{
227 const auto& source = inheritedPins ? *inheritedPins : pins;
228 std::vector<std::pair<wxString, size_t>> logical;
229
230 for( size_t i = 0; i < source.size(); ++i )
231 {
232 bool valid = false;
233 auto numbers = ExpandStackedPinNotation( source[i].number, &valid );
234
235 if( !valid || numbers.empty() )
236 numbers = { source[i].number };
237
238 for( const auto& number : numbers )
239 logical.emplace_back( number, i );
240 }
241
242 std::sort( logical.begin(), logical.end(), [&]( const auto& a, const auto& b )
243 {
244 return std::tie( a.first, source[a.second].bodyStyle, source[a.second].unit, a.second )
245 < std::tie( b.first, source[b.second].bodyStyle, source[b.second].unit, b.second );
246 } );
247
248 const auto clash = [&]( const auto& a, const auto& b )
249 {
250 const int first = source[a.second].bodyStyle;
251 const int second = source[b.second].bodyStyle;
252 return a.first == b.first && a.second != b.second && ( !first || !second || first == second );
253 };
254
255 return std::ranges::adjacent_find( logical, clash ) != logical.end();
256}
257
258bool LIBRARY_SYMBOL_FACT::Matches( const LIBRARY_SYMBOL_FACT& aOther, int aCompareFlags ) const
259{
260 using FLAGS = SCH_ITEM::COMPARE_FLAGS;
261 wxCHECK_MSG( !( aCompareFlags & FLAGS::IDENTITY ), false,
262 "Captured library comparison requires content-only flags" );
263
265 return false;
266
267 if( !hasEmbeddedSymbol )
268 return true;
269
270 if( !attributes.Matches( aOther.attributes, aCompareFlags ) )
271 return false;
272
273 auto lessShape =
274 []( const LIBRARY_SHAPE_FACT* a, const LIBRARY_SHAPE_FACT* b )
275 {
276 return a->Compare( *b, ~FLAGS::UUID ) < 0;
277 };
278
279 std::set<const LIBRARY_SHAPE_FACT*, decltype( lessShape )> lhsShapes( lessShape );
280 std::set<const LIBRARY_SHAPE_FACT*, decltype( lessShape )> rhsShapes( lessShape );
281
282 for( const LIBRARY_SHAPE_FACT& shape : shapes )
283 lhsShapes.insert( &shape );
284
285 for( const LIBRARY_SHAPE_FACT& shape : aOther.shapes )
286 rhsShapes.insert( &shape );
287
288 if( lhsShapes.size() != rhsShapes.size() )
289 return false;
290
291 for( auto lhs = lhsShapes.begin(), rhs = rhsShapes.begin(); lhs != lhsShapes.end(); ++lhs, ++rhs )
292 {
293 if( ( *lhs )->Compare( **rhs, aCompareFlags ) != 0 )
294 return false;
295 }
296
297 auto findPin =
298 []( const LIBRARY_SYMBOL_FACT& aSource, const PIN_COMPARISON_DATA& aPin ) -> const PIN_COMPARISON_DATA*
299 {
300 const auto& candidates = aSource.inheritedPins ? *aSource.inheritedPins : aSource.pins;
301
302 for( const PIN_COMPARISON_DATA& candidate : candidates )
303 {
304 if( candidate.number == aPin.number
305 && ( !aPin.unit || !candidate.unit || aPin.unit == candidate.unit )
306 && ( !aPin.bodyStyle || !candidate.bodyStyle || aPin.bodyStyle == candidate.bodyStyle ) )
307 {
308 return &candidate;
309 }
310 }
311
312 return nullptr;
313 };
314
315 for( const PIN_COMPARISON_DATA& pin : pins )
316 {
317 const PIN_COMPARISON_DATA* other = findPin( aOther, pin );
318
319 if( !other || pin.Compare( *other, aCompareFlags ) != 0 )
320 return false;
321 }
322
323 for( const PIN_COMPARISON_DATA& pin : aOther.pins )
324 {
325 if( !findPin( *this, pin ) )
326 return false;
327 }
328
329 auto findField =
330 []( const LIBRARY_SYMBOL_FACT& aSource, const LIBRARY_FIELD_FACT& aField ) -> const LIBRARY_FIELD_FACT*
331 {
332 for( const LIBRARY_FIELD_FACT& candidate : aSource.fields )
333 {
334 if( aField.mandatory ? candidate.id == aField.id : candidate.name == aField.name )
335 return &candidate;
336 }
337
338 return nullptr;
339 };
340
341 for( const LIBRARY_FIELD_FACT& field : fields )
342 {
343 const LIBRARY_FIELD_FACT* other = findField( aOther, field );
344
345 if( !other )
346 {
347 if( aCompareFlags & FLAGS::EXTRA_FIELDS )
348 return false;
349 }
350 else if( !field.Matches( *other, aCompareFlags ) )
351 {
352 return false;
353 }
354 }
355
356 if( aCompareFlags & FLAGS::MISSING_FIELDS )
357 {
358 for( const LIBRARY_FIELD_FACT& field : aOther.fields )
359 {
360 if( !findField( *this, field ) )
361 return false;
362 }
363 }
364
365 return true;
366}
367
368int LIBRARY_SHAPE_FACT::Compare( const LIBRARY_SHAPE_FACT& aOther, int aCompareFlags ) const
369{
370 // Subtraction can overflow at extreme coordinates and flip the order the shape sets rely on
371 const auto sign =
372 []( int aLeft, int aRight )
373 {
374 return ( aLeft > aRight ) - ( aLeft < aRight );
375 };
376
377 if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::UNIT )
378 {
379 if( unit != aOther.unit )
380 return sign( unit, aOther.unit );
381
382 if( bodyStyle != aOther.bodyStyle )
383 return sign( bodyStyle, aOther.bodyStyle );
384 }
385
386 if( isPrivate != aOther.isPrivate )
387 return isPrivate ? 1 : -1;
388
389 if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::POSITION )
390 {
391 if( position.x != aOther.position.x )
392 return sign( position.x, aOther.position.x );
393
394 if( position.y != aOther.position.y )
395 return sign( position.y, aOther.position.y );
396 }
397
398 if( int difference = geometry.Compare( &aOther.geometry ) )
399 return difference;
400
401 if( ( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::UUID ) && id != aOther.id )
402 return id < aOther.id ? -1 : 1;
403
404 return 0;
405}
406
408{
409 // Cache equality must retain edits smaller than the library comparison tolerance
410 return id == aOther.id && unit == aOther.unit && bodyStyle == aOther.bodyStyle
411 && isPrivate == aOther.isPrivate && position == aOther.position
412 && geometry == aOther.geometry && geometry.GetStart() == aOther.geometry.GetStart()
413 && geometry.GetEnd() == aOther.geometry.GetEnd()
414 && ( geometry.GetShape() != SHAPE_T::ARC || geometry.GetArcMid() == aOther.geometry.GetArcMid() )
415 && geometry.GetBezierPoints() == aOther.geometry.GetBezierPoints()
416 && geometry.Compare( &aOther.geometry ) == 0;
417}
418
420{
421 if( id != aOther.id || containedItems != aOther.containedItems || attachedDirectives != aOther.attachedDirectives
422 || polygon.OutlineCount() != aOther.polygon.OutlineCount() )
423 {
424 return false;
425 }
426
427 for( int i = 0; i < polygon.OutlineCount(); ++i )
428 {
429 const auto& first = polygon.CPolygon( i );
430 const auto& second = aOther.polygon.CPolygon( i );
431
432 if( first.size() != second.size() )
433 return false;
434
435 for( size_t j = 0; j < first.size(); ++j )
436 {
437 if( first[j].CPoints() != second[j].CPoints() || first[j].CArcs() != second[j].CArcs()
438 || first[j].CShapes() != second[j].CShapes() || first[j].IsClosed() != second[j].IsClosed() )
439 {
440 return false;
441 }
442 }
443 }
444
445 return true;
446}
447
453
454bool SCREEN_FACTS::operator==( const SCREEN_FACTS& aOther ) const
455{
456 return ( librarySymbols == aOther.librarySymbols || LibrarySymbols() == aOther.LibrarySymbols() )
458 == std::tie( aOther.footprints, aOther.pinMaps, aOther.multiUnits, aOther.items,
459 aOther.ruleAreas, aOther.netclassOwners, aOther.invalidFieldNames );
460}
461
462SCREEN_FACTS ExtractScreenFacts( const SCH_SCREEN& aScreen, std::shared_ptr<const LIBRARY_SYMBOL_FACTS> aLibrarySymbols,
463 const SCREEN_FACTS* aUnchangedNonLines )
464{
465 wxASSERT( wxIsMainThread() );
466 const EE_RTREE& items = aScreen.Items();
468 std::vector<const SCH_RULE_AREA*> areas;
469 std::unordered_set<KIID> identities;
470
471 // Rule-area membership depends on line geometry
472 if( aUnchangedNonLines && ( !aUnchangedNonLines->ruleAreas.empty() || !items.OfType( SCH_RULE_AREA_T ).empty() ) )
473 aUnchangedNonLines = nullptr;
474
475 if( aUnchangedNonLines )
476 {
477 result = *aUnchangedNonLines;
478 std::erase_if( result.items, []( const ITEM_FACT& fact ) { return fact.type == SCH_LINE_T; } );
479 aLibrarySymbols = result.librarySymbols;
480 }
481
482 identities.reserve( items.size() );
483 result.items.reserve( items.size() );
484 auto libraries = aLibrarySymbols ? nullptr : std::make_shared<LIBRARY_SYMBOL_FACTS>();
485 result.librarySymbols = aLibrarySymbols ? std::move( aLibrarySymbols ) : libraries;
486
487 if( libraries )
488 {
489 libraries->reserve( std::count_if( items.begin(), items.end(), []( const SCH_ITEM* item )
490 {
491 return item->Type() == SCH_SYMBOL_T;
492 } ) );
493 }
494
495 const auto checkIdentity = [&]( const KIID& aId )
496 {
497 if( !identities.insert( aId ).second )
498 throw std::runtime_error( "Duplicate connectivity source identity" );
499 };
500
501 for( SCH_ITEM* item : items )
502 {
503 checkIdentity( item->m_Uuid );
504
505 if( aUnchangedNonLines && item->Type() != SCH_LINE_T )
506 {
507 if( item->Type() == SCH_SYMBOL_T )
508 {
509 for( const auto& pin : static_cast<const SCH_SYMBOL*>( item )->GetRawPins() )
510 checkIdentity( pin->m_Uuid );
511 }
512 else if( item->Type() == SCH_SHEET_T )
513 {
514 for( const SCH_SHEET_PIN* pin : static_cast<const SCH_SHEET*>( item )->GetPins() )
515 checkIdentity( pin->m_Uuid );
516 }
517
518 continue;
519 }
520
521 bool hasNetclass = false;
522 item->RunOnChildren(
523 [&]( SCH_ITEM* child )
524 {
525 if( child->Type() != SCH_FIELD_T )
526 return true;
527
528 const auto* field = static_cast<SCH_FIELD*>( child );
529
530 if( field->GetUntranslatedName() == wxS( "Netclass" ) )
531 hasNetclass = true;
532
533 if( item->Type() == SCH_SYMBOL_T || item->Type() == SCH_SHEET_T )
534 {
535 const wxString name = field->GetName();
536 wxString trimmed = name;
537
538 if( trimmed.Trim( false ).Trim( true ) != name )
539 {
540 result.invalidFieldNames.push_back(
541 { item->m_Uuid, field->m_Uuid, field->GetPosition(), name } );
542 }
543 }
544
545 return true;
546 },
548
549 if( hasNetclass )
550 result.netclassOwners.push_back( item->m_Uuid );
551
552 if( item->Type() == SCH_RULE_AREA_T )
553 {
554 areas.push_back( static_cast<const SCH_RULE_AREA*>( item ) );
555 continue;
556 }
557
558 if( item->Type() == SCH_SYMBOL_T )
559 {
560 const auto& symbol = *static_cast<const SCH_SYMBOL*>( item );
561 const auto& library = symbol.GetLibSymbolRef();
562
563 if( libraries )
564 {
565 auto& librarySource = libraries->emplace_back();
566
567 if( library )
568 librarySource = ExtractLibrarySymbolFact( *library );
569
570 librarySource.id = symbol.m_Uuid;
571 librarySource.position = symbol.GetPosition();
572 librarySource.library = symbol.GetLibId();
573 }
574
575 if( library && library->GetUnitCount() > 1 )
576 {
577 result.multiUnits.push_back( { symbol.m_Uuid, symbol.GetPosition(), ExtractUnitFacts( *library ) } );
578 }
579
580 result.footprints.push_back( { symbol.m_Uuid, symbol.GetPosition(),
581 library ? library->GetFPFilters() : wxArrayString() } );
582
583 if( auto maps = ExtractPinMapFacts( symbol ) )
584 result.pinMaps.push_back( std::move( *maps ) );
585
586 std::map<std::tuple<wxString, int, int>, size_t> groups;
587 std::map<wxString, std::vector<size_t>> numbers;
588 const bool hasJumperGroups = library && !library->JumperPinGroups().IsEmpty();
589
590 // Unjumpered duplicates join only when stacked, so ERC can report pins wired to different nets
591 const bool joinApart = library && library->GetDuplicatePinNumbersAreJumpers();
592
593 for( const std::unique_ptr<SCH_PIN>& pin : symbol.GetRawPins() )
594 {
595 checkIdentity( pin->m_Uuid );
596 const bool nc = pin->GetType() == ELECTRICAL_PINTYPE::PT_NC;
597 const VECTOR2I contact = joinApart ? VECTOR2I() : pin->GetPosition();
598 const auto key = std::make_tuple( pin->GetNumber(), contact.x, contact.y );
599 auto found = nc ? groups.end() : groups.find( key );
600 size_t index;
601
602 if( found == groups.end() )
603 {
604 index = result.items.size();
605 ITEM_FACT fact;
606 fact.id = pin->m_Uuid;
607 fact.type = SCH_PIN_T;
608 fact.owner = symbol.m_Uuid;
609 fact.multiUnit = symbol.IsMultiUnit();
610 fact.rawText = symbol.GetField( FIELD_T::VALUE )->GetText();
611 result.items.push_back( std::move( fact ) );
612
613 if( !nc )
614 groups.emplace( key, index );
615 }
616 else
617 {
618 index = found->second;
619 result.items[index].id = std::min( result.items[index].id, pin->m_Uuid );
620 }
621
622 ITEM_FACT& fact = result.items[index];
623 const bool valid = pin->GetLibPin() && library && !symbol.IsMissingLibSymbol();
624 PIN_FACT member;
625 member.id = pin->m_Uuid;
626 member.position = pin->GetPosition();
627 member.unit = pin->GetUnit();
628 member.type = pin->GetType();
629 member.canDrive = valid;
630 member.globalPower = valid && pin->IsGlobalPower();
631 member.localPower = valid && pin->IsLocalPower();
632 member.invisible = !pin->IsVisible();
633 member.globalPowerParent = symbol.IsGlobalPower();
634 member.localPowerParent = symbol.IsLocalPower();
635 member.name = pin->GetName();
636 member.shownName = pin->GetShownName();
637 member.shownNumber = pin->GetShownNumber();
638 member.number = pin->GetNumber();
639
640 if( const SCH_PIN* libPin = pin->GetLibPin() )
641 {
642 member.libraryName = libPin->GetShownName();
643 member.padNumber = libPin->GetSmallestStackedPadNumber();
644 }
645
646 fact.pins.push_back( std::move( member ) );
647
648 if( !nc && hasJumperGroups )
649 {
650 numbers[pin->GetNumber()].push_back( index );
651
652 for( const wxString& number : pin->GetStackedPinNumbers() )
653 numbers[number].push_back( index );
654 }
655 }
656
657 if( hasJumperGroups )
658 {
659 for( const JUMPER_GROUP& group : library->JumperPinGroups().GetAll() )
660 {
661 std::vector<size_t> indices;
662
663 for( const wxString& number : group.GetNames() )
664 {
665 if( auto it = numbers.find( number ); it != numbers.end() )
666 indices.insert( indices.end(), it->second.begin(), it->second.end() );
667 }
668
669 SortUnique( indices );
670
671 for( size_t first : indices )
672 {
673 for( size_t second : indices )
674 {
675 if( first != second )
676 result.items[first].jumperedWith.push_back( result.items[second].id );
677 }
678 }
679 }
680 }
681
682 continue;
683 }
684
685 if( item->Type() == SCH_SHEET_T )
686 {
687 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
688 {
689 checkIdentity( pin->m_Uuid );
690 ITEM_FACT fact;
691 fact.id = pin->m_Uuid;
692 fact.type = pin->Type();
693 fact.owner = item->m_Uuid;
694 fact.ports.push_back( { pin->GetPosition(), PORT_KIND::ANCHOR } );
695 ReadLabel( fact, *pin );
696 result.items.push_back( std::move( fact ) );
697 }
698
699 continue;
700 }
701
702 if( !item->IsConnectable() )
703 continue;
704
705 ITEM_FACT fact;
706 fact.id = item->m_Uuid;
707 fact.type = item->Type();
709
710 if( item->Type() == SCH_LINE_T )
711 {
712 const auto& line = *static_cast<const SCH_LINE*>( item );
714 fact.segment.emplace( line.GetStartPoint(), line.GetEndPoint() );
715 fact.lineWidth = line.GetLineWidth();
716 }
717
718 if( item->Type() == SCH_BUS_WIRE_ENTRY_T || item->Type() == SCH_BUS_BUS_ENTRY_T )
719 {
720 const auto& entry = *static_cast<const SCH_BUS_ENTRY_BASE*>( item );
722 fact.ports = { { entry.GetPosition(), kind }, { entry.GetEnd(), kind } };
723 }
724 else
725 {
726 for( const VECTOR2I& point : item->GetConnectionPoints() )
727 fact.ports.push_back( { point, kind } );
728 }
729
730 if( auto* label = dynamic_cast<const SCH_LABEL_BASE*>( item ) )
731 ReadLabel( fact, *label );
732
733 result.items.push_back( std::move( fact ) );
734 }
735
736 // Spatial-index traversal order changes when unrelated items are reindexed
737 if( libraries )
738 std::ranges::sort( *libraries, ById );
739
740 if( !aUnchangedNonLines )
741 {
742 std::ranges::sort( result.footprints, ById );
743 std::ranges::sort( result.pinMaps, ById );
744 std::ranges::sort( result.multiUnits, ById );
745 }
746
747 SortUnique( result.netclassOwners );
748 std::ranges::sort( result.invalidFieldNames,
749 []( const auto& a, const auto& b )
750 {
751 return std::tie( a.owner, a.field ) < std::tie( b.owner, b.field );
752 } );
753
754 for( ITEM_FACT& fact : result.items )
755 {
756 std::ranges::sort( fact.pins, ById );
757 SortUnique( fact.jumperedWith );
758 }
759
760 std::ranges::sort( result.items, ById );
761
762 for( const SCH_RULE_AREA* area : areas )
763 {
764 RULE_AREA_FACT fact;
765 fact.id = area->m_Uuid;
766 fact.polygon = area->GetPolyShape();
767 const auto& polygon = fact.polygon;
768
769 for( const ITEM_FACT& item : result.items )
770 {
771 if( item.type == SCH_DIRECTIVE_LABEL_T )
772 {
773 if( !item.ports.empty() && polygon.CollideEdge( item.ports.front().position, nullptr, 5 ) )
774 fact.attachedDirectives.push_back( item.id );
775 }
776 else if( item.segment )
777 {
778 const SHAPE_SEGMENT segment( *item.segment, item.lineWidth );
779
780 if( polygon.Collide( &segment ) )
781 fact.containedItems.push_back( item.id );
782 }
783 else
784 {
785 for( const PORT_FACT& port : item.ports )
786 {
787 if( polygon.Collide( port.position ) )
788 fact.containedItems.push_back( item.id );
789 }
790
791 for( const PIN_FACT& pin : item.pins )
792 {
793 if( polygon.Collide( pin.position ) )
794 fact.containedItems.push_back( pin.id );
795 }
796 }
797 }
798
799 SortUnique( fact.containedItems );
800 SortUnique( fact.attachedDirectives );
801 result.ruleAreas.push_back( std::move( fact ) );
802 }
803
804 std::ranges::sort( result.ruleAreas, ById );
805 return result;
806}
807
808std::vector<TEXT_ASSERTION> ExtractTextAssertions( const wxString& aText )
809{
810 wxASSERT( wxThread::IsMain() );
811 std::vector<TEXT_ASSERTION> result;
812
813 if( !aText.Contains( wxS( "${" ) ) )
814 return result;
815
816 static wxRegEx warningExpr( wxS( "(^|[^\\\\])\\$\\{ERC_WARNING\\s*([^}]*)\\}" ) );
817 static wxRegEx errorExpr( wxS( "(^|[^\\\\])\\$\\{ERC_ERROR\\s*([^}]*)\\}" ) );
818
819 for( bool warning : { true, false } )
820 {
821 wxRegEx& expression = warning ? warningExpr : errorExpr;
822 wxString remaining = aText;
823
824 while( expression.Matches( remaining ) )
825 {
826 result.push_back( { warning, expression.GetMatch( remaining, 2 ) } );
827 size_t start = 0;
828 size_t length = 0;
829
830 if( !expression.GetMatch( &start, &length, 0 ) || length == 0 )
831 break;
832
833 remaining = remaining.Mid( start + length );
834 }
835 }
836
837 return result;
838}
839
840std::vector<TEXT_CHECK_FACT> ExtractDrawingSheetTextChecks( const SCH_SCREEN& aScreen, const SCH_SHEET_PATH& aPath )
841{
842 wxASSERT( wxThread::IsMain() );
843 std::vector<TEXT_CHECK_FACT> result;
844
845 if( const SCHEMATIC* schematic = aScreen.Schematic(); schematic && schematic->IsValid() )
846 {
848 drawing.SetPageNumber( aPath.GetPageNumber() );
849 drawing.SetSheetCount( schematic->Hierarchy().size() );
850 drawing.SetFileName( aScreen.GetFileName() );
851 drawing.SetSheetName( aPath.Last()->GetName() );
852 drawing.SetSheetPath( aPath.PathHumanReadable() );
853 drawing.SetIsFirstPage( aPath.GetVirtualPageNumber() == 1 );
854 drawing.SetVariantName( schematic->GetCurrentVariant() );
855 drawing.SetVariantDesc( schematic->GetVariantDescription( schematic->GetCurrentVariant() ) );
856 drawing.SetSheetLayer( wxS( "dummyLayer" ) );
857 drawing.SetProject( &schematic->Project() );
858 drawing.BuildDrawItemsList( aScreen.GetPageSettings(), aScreen.GetTitleBlock() );
859
860 for( DS_DRAW_ITEM_BASE* item = drawing.GetFirst(); item; item = drawing.GetNext() )
861 {
862 const auto* text = dynamic_cast<const DS_DRAW_ITEM_TEXT*>( item );
863
864 if( !text )
865 continue;
866
867 auto assertions = ExtractTextAssertions( text->GetText() );
868 const wxString shown = assertions.empty() ? text->GetShownText( FOR_ERC_DRC ) : wxString();
869
870 if( !assertions.empty() || shown.Contains( wxS( "${" ) ) )
871 {
872 result.push_back( { niluuid, niluuid, text->GetPosition(), text->GetPosition(),
873 std::move( assertions ), shown } );
874 }
875 }
876 }
877
878 return result;
879}
880
881std::vector<TEXT_CHECK_FACT> ExtractTextChecks( const SCH_SCREEN& aScreen, const SCH_SHEET_PATH& aPath )
882{
883 wxASSERT( wxThread::IsMain() );
884 std::vector<TEXT_CHECK_FACT> result;
885 auto append = [&]( const KIID& assertionItem, const KIID& item, const VECTOR2I& assertionPosition,
886 const wxString& raw, bool expandEnvironment, auto&& resolve )
887 {
888 auto assertions = ExtractTextAssertions( raw );
889
890 if( !assertions.empty() )
891 {
892 result.push_back( { assertionItem, item, assertionPosition, assertionPosition,
893 std::move( assertions ), wxString() } );
894 return;
895 }
896
897 auto [shown, position] = resolve();
898 const auto* schematic = aScreen.Schematic();
899
900 if( expandEnvironment && shown.find_first_of( wxS( "$%" ) ) != wxString::npos )
901 shown = ExpandEnvVarSubstitutions( shown, schematic ? &schematic->Project() : nullptr );
902
903 if( shown.Contains( wxS( "${" ) ) )
904 result.push_back( { assertionItem, item, assertionPosition, position, {}, std::move( shown ) } );
905 };
906 auto fields = [&]( const auto& owner )
907 {
908 for( const SCH_FIELD& field : owner.GetFields() )
909 {
910 append( field.m_Uuid, owner.m_Uuid, field.GetPosition(), field.GetText(), true, [&]
911 {
912 return std::make_pair( field.GetShownText( &aPath, FOR_ERC_DRC ), field.GetPosition() );
913 } );
914 }
915 };
916
917 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_LOCATE_ANY_T ) )
918 {
919 if( item->Type() == SCH_SYMBOL_T )
920 {
921 const auto& symbol = *static_cast<SCH_SYMBOL*>( item );
922 fields( symbol );
923
924 if( const auto& library = symbol.GetLibSymbolRef() )
925 {
926 library->RunOnChildren(
927 [&]( SCH_ITEM* child )
928 {
929 auto text =
930 [&]( const auto& source, auto&& shown )
931 {
932 append( symbol.m_Uuid, symbol.m_Uuid, source.GetPosition(), source.GetText(),
933 true,
934 [&]()
935 {
936 wxString shownText = shown();
937 const BOX2I bbox = source.GetBoundingBox();
938 const BOX2I box = symbol.GetTransform().TransformCoordinate( bbox );
939 return std::make_pair( std::move( shownText ),
940 box.Centre() + symbol.GetPosition() );
941 } );
942 };
943
944 if( child->Type() == SCH_TEXT_T )
945 {
946 const auto& source = *static_cast<SCH_TEXT*>( child );
947 text( source,
948 [&]()
949 {
950 return source.GetShownText( &aPath, FOR_ERC_DRC );
951 } );
952 }
953 else if( child->Type() == SCH_TEXTBOX_T )
954 {
955 const auto& source = *static_cast<SCH_TEXTBOX*>( child );
956 text( source,
957 [&]()
958 {
959 return source.GetShownText( nullptr, &aPath, FOR_ERC_DRC );
960 } );
961 }
963 }
964 }
965 else if( const auto* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
966 {
967 fields( *label );
968 }
969 else if( item->Type() == SCH_SHEET_T )
970 {
971 SCH_SHEET& sheet = *static_cast<SCH_SHEET*>( item );
972 fields( sheet );
973 SCH_SHEET_PATH childPath = aPath;
974 childPath.push_back( &sheet );
975
976 for( const SCH_SHEET_PIN* pin : sheet.GetPins() )
977 {
978 append( niluuid, pin->m_Uuid, pin->GetPosition(), wxString(), false,
979 [&]()
980 {
981 return std::make_pair( pin->GetShownText( &childPath, FOR_ERC_DRC ), pin->GetPosition() );
982 } );
983 }
984 }
985 else if( const auto* text = dynamic_cast<SCH_TEXT*>( item ) )
986 {
987 append( text->m_Uuid, text->m_Uuid, text->GetPosition(), text->GetText(), false,
988 [&]()
989 {
990 return std::make_pair( text->GetShownText( &aPath, FOR_ERC_DRC ), text->GetPosition() );
991 } );
992 }
993 else if( const auto* textbox = dynamic_cast<SCH_TEXTBOX*>( item ) )
994 {
995 append( textbox->m_Uuid, textbox->m_Uuid, textbox->GetPosition(), textbox->GetText(), false,
996 [&]()
997 {
998 return std::make_pair( textbox->GetShownText( nullptr, &aPath, FOR_ERC_DRC ),
999 textbox->GetPosition() );
1000 } );
1001 }
1002 }
1003
1004 return result;
1005}
1006
1007
1008std::optional<PIN_MAP_FACT> ExtractPinMapFacts( const SCH_SYMBOL& aSymbol )
1009{
1010 const std::unique_ptr<LIB_SYMBOL>& lib = aSymbol.GetLibSymbolRef();
1011
1012 if( !lib )
1013 return std::nullopt;
1014
1015 const PIN_MAP_SET& maps = lib->GetEffectivePinMaps();
1016 const std::vector<ASSOCIATED_FOOTPRINT>& footprints = lib->GetEffectiveAssociatedFootprints();
1017
1018 if( maps.IsEmpty() && footprints.empty() )
1019 return std::nullopt;
1020
1022 result.id = aSymbol.m_Uuid;
1023 result.position = aSymbol.GetPosition();
1024 result.maps = maps;
1025 result.footprints = footprints;
1026 result.jumperGroups = lib->JumperPinGroups();
1027
1028 for( const SCH_PIN* pin : lib->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
1029 result.pinNumbers.insert( pin->GetNumber() );
1030
1031 return result;
1032}
1033
1034std::vector<UNIT_FACT> ExtractUnitFacts( const LIB_SYMBOL& aSymbol )
1035{
1036 std::vector<UNIT_FACT> result;
1037
1038 for( int unit = 1; unit <= aSymbol.GetUnitCount(); ++unit )
1039 {
1040 UNIT_FACT fact;
1041 fact.name = aSymbol.GetUnitDisplayName( unit, false );
1042
1043 for( const SCH_PIN* pin : aSymbol.GetGraphicalPins( unit, 0 ) )
1044 {
1045 fact.powerInput |= pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN;
1046 fact.input |= pin->GetType() == ELECTRICAL_PINTYPE::PT_INPUT;
1047 fact.bidirectional |= pin->GetType() == ELECTRICAL_PINTYPE::PT_BIDI;
1048 }
1049
1050 result.push_back( std::move( fact ) );
1051 }
1052
1053 return result;
1054}
1055
1057 const SCH_SHEET_PATH& aPath )
1058{
1059 wxASSERT( wxIsMainThread() );
1060
1061 if( aPath.LastScreen() != &aScreen )
1062 throw std::invalid_argument( "Connectivity instance does not reference its screen" );
1063
1064 std::optional<TEXT_EVAL_VCS::CONTEXT_PATH_SCOPE> vcs;
1065
1066 if( const auto* schematic = aScreen.Schematic(); schematic && schematic->IsValid() )
1067 {
1068 if( const wxString path = schematic->Project().GetProjectPath(); !path.IsEmpty() )
1069 vcs.emplace( path );
1070 }
1071
1072 struct SYMBOL_TEXT
1073 {
1074 const SCH_SYMBOL* symbol = nullptr;
1075 PIN_NAME_REFERENCE reference;
1076 wxString plainReference;
1077 std::map<wxString, std::set<wxString>> numbersByName;
1078 };
1079
1080 INPUT_TEXT_SCOPE inputText;
1082 std::map<KIID, int> units;
1083 std::map<KIID, SYMBOL_TEXT> symbols;
1084 std::set<KIID> inactivePins;
1085 const wxString variant = aScreen.Schematic() ? aScreen.Schematic()->GetCurrentVariant() : wxString();
1086
1087 result.pageOrder = aPath.GetVirtualPageNumber();
1088
1089 for( const FOOTPRINT_FACT& fact : aFacts.footprints )
1090 {
1091 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1092
1093 if( !symbol )
1094 throw std::runtime_error( "Footprint symbol disappeared during extraction" );
1095
1096 result.footprints.emplace_back( fact.id, symbol->GetFootprintFieldText( &aPath, RESOLVED ) );
1097
1098 if( auto source = ExtractVariantSymbolFact( *symbol, aPath ) )
1099 result.variantSymbols.push_back( std::move( *source ) );
1100 }
1101
1102 for( const PIN_MAP_FACT& fact : aFacts.pinMaps )
1103 {
1104 if( fact.footprints.empty() )
1105 continue;
1106
1107 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1108
1109 if( !symbol )
1110 throw std::runtime_error( "Pin-map symbol disappeared during extraction" );
1111
1112 const wxString footprint = symbol->GetFootprintFieldText( &aPath, RESOLVED );
1113 LIB_ID footprintId;
1114
1115 if( footprint.IsEmpty() || footprintId.Parse( footprint, true ) >= 0 )
1116 continue;
1117
1118 for( const SCH_PIN* pin : symbol->GetPins( &aPath ) )
1119 {
1121 pin->GetEffectivePadNumber( aPath, variant, footprintId, nullptr, &state );
1122
1123 if( state == SCH_PIN::PAD_RESOLUTION::MAPPED )
1124 continue;
1125
1126 const ELECTRICAL_PINTYPE type = pin->GetType();
1127 result.pinMapCandidates.push_back( { pin->m_Uuid, pin->GetPosition(), pin->GetNumber(), footprint,
1129 || type == ELECTRICAL_PINTYPE::PT_NIC } );
1130 }
1131 }
1132
1133 for( const MULTI_UNIT_FACT& fact : aFacts.multiUnits )
1134 {
1135 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1136
1137 if( !symbol )
1138 throw std::runtime_error( "Multiunit symbol disappeared during extraction" );
1139
1140 result.multiUnits.push_back( { fact.id, symbol->GetRef( &aPath ), symbol->GetRef( &aPath, true ),
1141 symbol->GetFootprintFieldText( &aPath, RESOLVED ),
1142 symbol->GetUnitSelection( &aPath ) } );
1143 }
1144
1145 // Screen order fixes the main and auxiliary witnesses in saved ERC exclusions
1146 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_SHEET_T ) )
1147 {
1148 const auto* sheet = static_cast<const SCH_SHEET*>( item );
1149 result.childSheets.push_back( { sheet->m_Uuid, sheet->GetPosition(),
1150 sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( &aPath, RESOLVED,
1151 variant ) } );
1152 }
1153
1154 for( const KIID& owner : aFacts.netclassOwners )
1155 {
1156 SCH_ITEM* item = aScreen.GetConnectivityItem( owner );
1157
1158 if( !item )
1159 throw std::invalid_argument( "Netclass field owner is missing from its captured screen" );
1160
1161 item->RunOnChildren(
1162 [&]( SCH_ITEM* child )
1163 {
1164 if( child->Type() != SCH_FIELD_T )
1165 return true;
1166
1167 const auto* field = static_cast<SCH_FIELD*>( child );
1168
1169 if( field->GetUntranslatedName() == wxS( "Netclass" ) )
1170 {
1171 wxString name = field->GetShownText( &aPath, FOR_NETNAME, variant );
1172
1173 if( !name.empty() )
1174 {
1175 result.netclassReferences.push_back( { aPath.PathRef(), owner, item->GetPosition(),
1176 std::move( name ) } );
1177 }
1178 }
1179
1180 return true;
1181 },
1183 }
1184
1185 for( const ITEM_FACT& fact : aFacts.items )
1186 {
1187 if( fact.type == SCH_PIN_T )
1188 {
1189 auto [entry, inserted] = symbols.try_emplace( fact.owner );
1190 SYMBOL_TEXT& cached = entry->second;
1191
1192 if( inserted )
1193 {
1194 cached.symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.owner ) );
1195
1196 if( !cached.symbol )
1197 throw std::runtime_error( "Connectivity symbol disappeared during extraction" );
1198
1199 units.emplace( fact.owner, cached.symbol->GetUnitSelection( &aPath ) );
1200 SCH_SYMBOL_INSTANCE instance;
1201 const bool hasInstance = cached.symbol->GetInstance( instance, aPath.Path() );
1202 cached.reference.symbolUuid = cached.symbol->m_Uuid.AsString();
1203
1204 if( hasInstance )
1205 {
1206 // Channels of a reused sheet share a base, so only the composed reference keeps their nets apart
1207 cached.reference.reference = cached.symbol->GetRef( &aPath );
1208 cached.reference.referenceWithUnit = cached.symbol->GetRef( &aPath, true );
1209 }
1210
1211 cached.plainReference = cached.symbol->GetRef( &aPath );
1212
1213 for( const SCH_PIN* pin : cached.symbol->GetPins( &aPath ) )
1214 {
1215 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
1216 cached.numbersByName[pin->GetShownName()].insert( pin->GetShownNumber() );
1217 }
1218 }
1219
1220 const SCH_SYMBOL* symbol = cached.symbol;
1221 const int unit = units.at( fact.owner );
1222 const PIN_NAME_REFERENCE& reference = cached.reference;
1223
1224 for( const PIN_FACT& member : fact.pins )
1225 {
1226 if( unit && member.unit && unit != member.unit )
1227 {
1228 inactivePins.insert( member.id );
1229 continue;
1230 }
1231
1232 const auto* pin = dynamic_cast<const SCH_PIN*>( aScreen.GetConnectivityItem( member.id ) );
1233
1234 if( !pin )
1235 throw std::runtime_error( "Connectivity pin disappeared during extraction" );
1236
1238 text.id = member.id;
1239 text.canDrive = member.canDrive;
1240 text.reference = cached.plainReference;
1241
1242 if( member.globalPower || member.localPower )
1243 {
1244 text.name = EscapeString( member.globalPowerParent || member.localPowerParent
1245 ? symbol->GetValue( &aPath, FOR_NETNAME, variant )
1246 : pin->GetLibPin()->GetName(),
1247 CTX_NETNAME );
1248 text.ncName = text.name;
1249 }
1250 else
1251 {
1252 const SCH_PIN* libPin = pin->GetLibPin();
1254 name.name = libPin ? libPin->GetShownName() : wxString( "??" );
1255 name.shownNumber = libPin ? libPin->GetShownNumber() : wxString( "??" );
1256 name.number = libPin ? libPin->GetNumber() : wxString( "??" );
1257 name.padNumber = libPin ? libPin->GetSmallestStackedPadNumber() : name.shownNumber;
1258 name.noConnect = member.type == ELECTRICAL_PINTYPE::PT_NC;
1259
1260 const auto names = cached.numbersByName.find( pin->GetShownName() );
1261
1262 if( names != cached.numbersByName.end() )
1263 {
1264 const auto& numbers = names->second;
1265 name.hasDuplicateName = numbers.size() > 1 || !numbers.contains( pin->GetShownNumber() );
1266 }
1267
1268 text.name = RenderPinNetName( name, reference );
1269 text.ncName = RenderPinNetName( name, reference, true );
1270 text.canDrive &= symbol->DoNetList() && !symbol->GetExcludedFromBoard( &aPath, variant )
1271 && !reference.reference.StartsWith( wxS( "#" ) );
1272 }
1273
1274 result.items.push_back( std::move( text ) );
1275 }
1276
1277 continue;
1278 }
1279
1280 const SCH_LABEL_BASE* label = dynamic_cast<const SCH_LABEL_BASE*>( aScreen.GetConnectivityItem( fact.id ) );
1281
1282 if( !label )
1283 continue;
1284
1285 SCH_SHEET_PATH path = aPath;
1286
1287 if( fact.type == SCH_SHEET_PIN_T )
1288 path.push_back( static_cast<const SCH_SHEET_PIN*>( label )->GetParent() );
1289
1291 text.id = fact.id;
1292 text.canDrive = fact.type != SCH_DIRECTIVE_LABEL_T;
1294 text.ncName = text.name;
1295
1296 for( const SCH_FIELD& field : label->GetFields() )
1297 {
1298 if( field.GetUntranslatedName() == wxS( "Netclass" ) )
1299 {
1300 wxString value = field.GetShownText( &path, FOR_NETNAME, variant );
1301
1302 if( !value.empty() )
1303 text.netclasses.push_back( std::move( value ) );
1304 }
1305 }
1306
1307 SortUnique( text.netclasses );
1308 result.items.push_back( std::move( text ) );
1309 }
1310
1311 std::map<KIID, BOX2I> ownerBoxes;
1312
1313 if( !aFacts.ruleAreas.empty() )
1314 {
1315 for( SCH_ITEM* item : aScreen.Items() )
1316 {
1317 if( item->Type() == SCH_SYMBOL_T )
1318 {
1319 const SCH_SYMBOL& symbol = *static_cast<const SCH_SYMBOL*>( item );
1320 const int unit = symbol.GetUnitSelection( &aPath );
1321 units.emplace( symbol.m_Uuid, unit );
1322 const LIB_SYMBOL* library = symbol.GetEffectiveLibSymbol( &aPath );
1323
1324 if( !library )
1326
1327 BOX2I box = library->GetBodyBoundingBox( unit, symbol.GetBodyStyle(), true, false );
1328 box = symbol.GetTransform().TransformCoordinate( box );
1329 box.Normalize();
1330 box.Offset( symbol.GetPosition() );
1331
1332 for( const SCH_FIELD& field : symbol.GetFields() )
1333 {
1334 if( field.IsVisible() )
1335 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1336 }
1337
1338 ownerBoxes.emplace( symbol.m_Uuid, box );
1339 }
1340 else if( item->Type() == SCH_SHEET_T )
1341 {
1342 const SCH_SHEET& sheet = *static_cast<const SCH_SHEET*>( item );
1343 BOX2I box = sheet.GetBodyBoundingBox();
1344
1345 for( const SCH_FIELD& field : sheet.GetFields() )
1346 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1347
1348 ownerBoxes.emplace( sheet.m_Uuid, box );
1349 }
1350 }
1351 }
1352
1353 for( const RULE_AREA_FACT& area : aFacts.ruleAreas )
1354 {
1355 INSTANCE_RULE_AREA_FACT resolved;
1356 resolved.id = area.id;
1357 resolved.attachedDirectives = area.attachedDirectives;
1358 resolved.containedItems = area.containedItems;
1359 std::erase_if( resolved.containedItems, [&]( const KIID& id ) { return inactivePins.contains( id ); } );
1360
1361 for( const auto& [id, box] : ownerBoxes )
1362 {
1363 const SHAPE_RECT rectangle( box );
1364
1365 if( area.polygon.Collide( &rectangle ) )
1366 resolved.containedItems.push_back( id );
1367 }
1368
1369 for( const ITEM_TEXT_FACT& text : result.items )
1370 {
1371 if( std::binary_search( area.attachedDirectives.begin(), area.attachedDirectives.end(), text.id ) )
1372 resolved.netclasses.insert( resolved.netclasses.end(), text.netclasses.begin(), text.netclasses.end() );
1373 }
1374
1375 SortUnique( resolved.containedItems );
1376 SortUnique( resolved.netclasses );
1377 result.ruleAreas.push_back( std::move( resolved ) );
1378 }
1379
1380 result.units.assign( units.begin(), units.end() );
1381 std::ranges::sort( result.items, ById );
1382 return result;
1383}
1384} // namespace SCH_CONNECTIVITY
int index
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:917
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:149
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:597
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:262
Base class to handle basic graphic items.
Store the list of graphic items: rect, lines, polygons and texts to draw/plot the title block and fra...
DS_DRAW_ITEM_BASE * GetFirst()
void SetVariantName(const wxString &aVariant)
Set the current variant name and description to draw/plot.
void BuildDrawItemsList(const PAGE_INFO &aPageInfo, const TITLE_BLOCK &aTitleBlock)
Drawing or plot the drawing sheet.
void SetSheetPath(const wxString &aSheetPath)
Set the sheet path to draw/plot.
void SetFileName(const wxString &aFileName)
Set the filename to draw/plot.
void SetVariantDesc(const wxString &aDesc)
void SetSheetName(const wxString &aSheetName)
Set the sheet name to draw/plot.
void SetIsFirstPage(bool aIsFirstPage)
Set if the page is the first page.
void SetSheetLayer(const wxString &aSheetLayer)
Set the sheet layer to draw/plot.
void SetSheetCount(int aSheetCount)
Set the value of the count of sheets, for basic inscriptions.
void SetPageNumber(const wxString &aPageNumber)
Set the value of the sheet number.
DS_DRAW_ITEM_BASE * GetNext()
void SetProject(const PROJECT *aProject)
A graphic text.
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:505
VECTOR2I GetArcMid() const
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:270
Implement an R-tree for fast spatial and type indexing of schematic items.
Definition sch_rtree.h:37
size_t size() const
Return the number of items in the tree.
Definition sch_rtree.h:177
ee_rtree::Iterator begin() const
Return a read/write iterator that points to the first.
Definition sch_rtree.h:288
ee_rtree::Iterator end() const
Return a read/write iterator that points to one past the last element in the EE_RTREE.
Definition sch_rtree.h:296
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
Represents a group of jumper pins or pads, keyed by name.
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
Define a library symbol object.
Definition lib_symbol.h:114
bool IsDerived() const
Definition lib_symbol.h:241
static LIB_SYMBOL * GetDummy()
Return a dummy LIB_SYMBOL, used when one is missing in the schematic.
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
int GetBodyStyleCount() const override
The body styles are a property of the drawings, which a derived symbol inherits from its root symbol ...
LIB_SYMBOL_ATTRIBUTES ComparisonAttributes() const
int GetUnitCount() const override
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the user-defined display name for aUnit for symbols with units.
A symbol-owned ordered set of named pin maps.
Definition pin_map.h:124
bool IsEmpty() const
Definition pin_map.h:140
Holds all the data relating to one schematic.
Definition schematic.h:153
wxString GetCurrentVariant() const
Return the current variant being edited.
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:369
Base class for a bus or wire entry.
Excludes derived graph text and shares dynamic source values through nested resolvers.
Definition conn_text.h:34
bool IsMandatory() const
VECTOR2I GetPosition() const override
bool IsNameShown() const
Definition sch_field.h:225
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
FIELD_T GetId() const
Definition sch_field.h:143
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, int aDepth=0) const
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:683
int GetBodyStyle() const
Definition sch_item.h:253
COMPARE_FLAGS
The list of flags used by various compare functions.
Definition sch_item.h:749
int GetUnit() const
Definition sch_item.h:243
bool IsPrivate() const
Definition sch_item.h:259
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:353
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:178
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, int aDepth=0) const override
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:210
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
PAD_RESOLUTION
Outcome of pin-to-pad resolution (issue #2282).
Definition sch_pin.h:175
const wxString & GetShownName() const
Definition sch_pin.cpp:697
const wxString & GetShownNumber() const
Definition sch_pin.cpp:708
const wxString & GetNumber() const
Definition sch_pin.h:144
wxString GetSmallestStackedPadNumber() const
Return the pin number to be used for deterministic operations such as auto‑generated net names.
Definition sch_pin.cpp:739
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:144
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
SCHEMATIC * Schematic() const
SCH_ITEM * GetConnectivityItem(const KIID &aId) const
Resolve a drawing item or a connectable child on this screen; ambiguous IDs return null.
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:168
VECTOR2I GetPosition() const override
Definition sch_shape.h:88
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
wxString GetPageNumber() const
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
bool GetExcludedFromSim() const
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
const KIID_PATH & PathRef() const
Borrow the cached path until this sheet path is modified or destroyed.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
int GetVirtualPageNumber() const
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:50
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:93
wxString GetName() const
Definition sch_sheet.h:150
const BOX2I GetBodyBoundingBox() const
Return a bounding box for the sheet body but not the fields.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:285
Schematic symbol object.
Definition sch_symbol.h:76
bool DoNetList() const
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:958
const LIB_SYMBOL * GetEffectiveLibSymbol(const SCH_SHEET_PATH *aPath=nullptr) const
Return the library symbol to use for rendering and bounding box calculations.
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
const wxString GetValue(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString) const override
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:185
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int OutlineCount() const
Return the number of outlines in the set.
const POLYGON & CPolygon(int aIndex) const
static SIM_MODEL_INPUT CaptureModelInput(const SCH_SHEET_PATH *aSheetPath, const SCH_SYMBOL &aSymbol, int aDepth, const wxString &aVariantName, const wxString &aMergedSimPins=wxEmptyString)
Resolve instance fields once; editors use the field-based overloads for unresolved input.
const TRANSFORM & GetTransform() const
Definition symbol.h:247
VECTOR2I TransformCoordinate(const VECTOR2I &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition transform.cpp:40
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject, bool aAllowEscapes)
Replace any environment variable & text variable references with their values.
Definition common.cpp:777
RESOLUTION_CONTEXT
Definition common.h:91
@ FOR_ERC_DRC
Definition common.h:95
@ FOR_NETNAME
Definition common.h:94
@ RESOLVED
Definition common.h:97
static DATABASE_FIELD_MAPPING * findField(DATABASE_LIB_TABLE &aLib, const char *aName)
static bool empty(const wxTextEntryBase *aCtrl)
@ NO_RECURSE
Definition eda_item.h:52
std::string source
KIID niluuid(0)
@ LAYER_BUS
Definition layer_ids.h:484
Value keys and the key session of the schematic connectivity engine.
LIBRARY_FIELD_FACT ExtractLibraryFieldFact(const SCH_FIELD &aField)
std::optional< PIN_MAP_FACT > ExtractPinMapFacts(const SCH_SYMBOL &aSymbol)
INSTANCE_FACTS ExtractInstanceFacts(const SCREEN_FACTS &aFacts, const SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath)
std::vector< SIMULATION_MODEL_FACT > ExtractSimulationModelFacts(const SCH_SHEET_PATH &aPath, const wxString &aVariantName)
std::vector< TEXT_CHECK_FACT > ExtractTextChecks(const SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath)
wxString RenderPinNetName(const PIN_NAME_FACT &aPin, const PIN_NAME_REFERENCE &aReference, bool aForceNoConnect)
Render a non-power pin name from unescaped values; an absent reference uses the symbol UUID.
LIBRARY_SYMBOL_FACT ExtractLibrarySymbolFact(const LIB_SYMBOL &aSymbol)
std::vector< TEXT_ASSERTION > ExtractTextAssertions(const wxString &aText)
std::vector< TEXT_CHECK_FACT > ExtractDrawingSheetTextChecks(const SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath)
SCREEN_FACTS ExtractScreenFacts(const SCH_SCREEN &aScreen, std::shared_ptr< const LIBRARY_SYMBOL_FACTS > aLibrarySymbols, const SCREEN_FACTS *aUnchangedNonLines)
Main-thread snapshots; no derived connectivity or model pointers survive extraction.
std::vector< LIBRARY_SYMBOL_FACT > LIBRARY_SYMBOL_FACTS
Definition conn_facts.h:243
std::optional< VARIANT_SYMBOL_FACT > ExtractVariantSymbolFact(const SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aPath)
std::vector< UNIT_FACT > ExtractUnitFacts(const LIB_SYMBOL &aSymbol)
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ 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
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ L_OUTPUT
Definition sch_label.h:99
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
Owned library-pin inputs; no parent, layout cache or schematic pointers.
The text and unit data that one sheet instance resolves for the items of its screen.
Definition conn_facts.h:348
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
bool Matches(const LIBRARY_FIELD_FACT &aOther, int aCompareFlags) const
int Compare(const LIBRARY_SHAPE_FACT &aOther, int aCompareFlags) const
bool operator==(const LIBRARY_SHAPE_FACT &aOther) const
std::vector< LIBRARY_FIELD_FACT > fields
Definition conn_facts.h:233
std::vector< PIN_COMPARISON_DATA > pins
Definition conn_facts.h:230
std::optional< std::vector< PIN_COMPARISON_DATA > > inheritedPins
Definition conn_facts.h:234
bool Matches(const LIBRARY_SYMBOL_FACT &aOther, int aCompareFlags) const
std::vector< LIBRARY_SHAPE_FACT > shapes
Definition conn_facts.h:231
ELECTRICAL_PINTYPE type
Definition conn_facts.h:79
One connection point of an item.
Definition conn_facts.h:68
std::vector< KIID > attachedDirectives
Definition conn_facts.h:122
std::vector< KIID > containedItems
Definition conn_facts.h:121
bool operator==(const RULE_AREA_FACT &aOther) const
The value copy of one screen.
Definition conn_facts.h:251
bool operator==(const SCREEN_FACTS &aOther) const
std::vector< KIID > netclassOwners
Definition conn_facts.h:258
std::vector< MULTI_UNIT_FACT > multiUnits
Definition conn_facts.h:255
std::vector< FIELD_NAME_FACT > invalidFieldNames
Definition conn_facts.h:259
std::vector< ITEM_FACT > items
Definition conn_facts.h:256
const LIBRARY_SYMBOL_FACTS & LibrarySymbols() const
std::vector< RULE_AREA_FACT > ruleAreas
Definition conn_facts.h:257
std::vector< PIN_MAP_FACT > pinMaps
Definition conn_facts.h:254
std::vector< FOOTPRINT_FACT > footprints
Definition conn_facts.h:253
std::shared_ptr< const LIBRARY_SYMBOL_FACTS > librarySymbols
Definition conn_facts.h:252
A simple container for schematic symbol instance information.
std::map< wxString, SCH_SYMBOL_VARIANT > m_Variants
A list of symbol variants.
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_PIN * pin
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
wxString result
Test unit parsing edge cases and error handling.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LOCATE_ANY_T
Definition typeinfo.h:195
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_RULE_AREA_T
Definition typeinfo.h:166
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_TEXT_T
Definition typeinfo.h:147
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_TEXTBOX_T
Definition typeinfo.h:148
@ SCH_PIN_T
Definition typeinfo.h:149
constexpr int sign(T val)
Definition util.h:167
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708