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 auto& 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 = []( const LIBRARY_SHAPE_FACT* a, const LIBRARY_SHAPE_FACT* b )
274 {
275 return a->Compare( *b, ~FLAGS::UUID ) < 0;
276 };
277 std::set<const LIBRARY_SHAPE_FACT*, decltype( lessShape )> lhsShapes( lessShape );
278 std::set<const LIBRARY_SHAPE_FACT*, decltype( lessShape )> rhsShapes( lessShape );
279
280 for( const auto& shape : shapes )
281 lhsShapes.insert( &shape );
282
283 for( const auto& shape : aOther.shapes )
284 rhsShapes.insert( &shape );
285
286 if( lhsShapes.size() != rhsShapes.size() )
287 return false;
288
289 for( auto lhs = lhsShapes.begin(), rhs = rhsShapes.begin(); lhs != lhsShapes.end(); ++lhs, ++rhs )
290 {
291 if( ( *lhs )->Compare( **rhs, aCompareFlags ) != 0 )
292 return false;
293 }
294
295 auto findPin = []( const LIBRARY_SYMBOL_FACT& aSource, const PIN_COMPARISON_DATA& aPin )
296 -> const PIN_COMPARISON_DATA*
297 {
298 const auto& candidates = aSource.inheritedPins ? *aSource.inheritedPins : aSource.pins;
299
300 for( const auto& candidate : candidates )
301 {
302 if( candidate.number == aPin.number
303 && ( !aPin.unit || !candidate.unit || aPin.unit == candidate.unit )
304 && ( !aPin.bodyStyle || !candidate.bodyStyle || aPin.bodyStyle == candidate.bodyStyle ) )
305 {
306 return &candidate;
307 }
308 }
309
310 return nullptr;
311 };
312
313 for( const auto& pin : pins )
314 {
315 const auto* other = findPin( aOther, pin );
316
317 if( !other || pin.Compare( *other, aCompareFlags ) != 0 )
318 return false;
319 }
320
321 for( const auto& pin : aOther.pins )
322 {
323 if( !findPin( *this, pin ) )
324 return false;
325 }
326
327 auto findField = []( const LIBRARY_SYMBOL_FACT& aSource, const LIBRARY_FIELD_FACT& aField )
328 -> const LIBRARY_FIELD_FACT*
329 {
330 for( const auto& candidate : aSource.fields )
331 {
332 if( aField.mandatory ? candidate.id == aField.id : candidate.name == aField.name )
333 return &candidate;
334 }
335
336 return nullptr;
337 };
338
339 for( const auto& field : fields )
340 {
341 const auto* other = findField( aOther, field );
342
343 if( !other )
344 {
345 if( aCompareFlags & FLAGS::EXTRA_FIELDS )
346 return false;
347 }
348 else if( !field.Matches( *other, aCompareFlags ) )
349 {
350 return false;
351 }
352 }
353
354 if( aCompareFlags & FLAGS::MISSING_FIELDS )
355 {
356 for( const auto& field : aOther.fields )
357 {
358 if( !findField( *this, field ) )
359 return false;
360 }
361 }
362
363 return true;
364}
365
366int LIBRARY_SHAPE_FACT::Compare( const LIBRARY_SHAPE_FACT& aOther, int aCompareFlags ) const
367{
368 // Subtraction can overflow at extreme coordinates and flip the order the shape sets rely on
369 const auto sign = []( int aLeft, int aRight )
370 {
371 return ( aLeft > aRight ) - ( aLeft < aRight );
372 };
373
374 if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::UNIT )
375 {
376 if( unit != aOther.unit )
377 return sign( unit, aOther.unit );
378
379 if( bodyStyle != aOther.bodyStyle )
380 return sign( bodyStyle, aOther.bodyStyle );
381 }
382
383 if( isPrivate != aOther.isPrivate )
384 return isPrivate ? 1 : -1;
385
386 if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::POSITION )
387 {
388 if( position.x != aOther.position.x )
389 return sign( position.x, aOther.position.x );
390
391 if( position.y != aOther.position.y )
392 return sign( position.y, aOther.position.y );
393 }
394
395 if( int difference = geometry.Compare( &aOther.geometry ) )
396 return difference;
397
398 if( ( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::UUID ) && id != aOther.id )
399 return id < aOther.id ? -1 : 1;
400
401 return 0;
402}
403
405{
406 // Cache equality must retain edits smaller than the library comparison tolerance
407 return id == aOther.id && unit == aOther.unit && bodyStyle == aOther.bodyStyle
408 && isPrivate == aOther.isPrivate && position == aOther.position
409 && geometry == aOther.geometry && geometry.GetStart() == aOther.geometry.GetStart()
410 && geometry.GetEnd() == aOther.geometry.GetEnd()
411 && ( geometry.GetShape() != SHAPE_T::ARC || geometry.GetArcMid() == aOther.geometry.GetArcMid() )
412 && geometry.GetBezierPoints() == aOther.geometry.GetBezierPoints()
413 && geometry.Compare( &aOther.geometry ) == 0;
414}
415
417{
418 if( id != aOther.id || containedItems != aOther.containedItems || attachedDirectives != aOther.attachedDirectives
419 || polygon.OutlineCount() != aOther.polygon.OutlineCount() )
420 {
421 return false;
422 }
423
424 for( int i = 0; i < polygon.OutlineCount(); ++i )
425 {
426 const auto& first = polygon.CPolygon( i );
427 const auto& second = aOther.polygon.CPolygon( i );
428
429 if( first.size() != second.size() )
430 return false;
431
432 for( size_t j = 0; j < first.size(); ++j )
433 {
434 if( first[j].CPoints() != second[j].CPoints() || first[j].CArcs() != second[j].CArcs()
435 || first[j].CShapes() != second[j].CShapes() || first[j].IsClosed() != second[j].IsClosed() )
436 {
437 return false;
438 }
439 }
440 }
441
442 return true;
443}
444
450
451bool SCREEN_FACTS::operator==( const SCREEN_FACTS& aOther ) const
452{
453 return ( librarySymbols == aOther.librarySymbols || LibrarySymbols() == aOther.LibrarySymbols() )
455 == std::tie( aOther.footprints, aOther.pinMaps, aOther.multiUnits, aOther.items,
456 aOther.ruleAreas, aOther.netclassOwners, aOther.invalidFieldNames );
457}
458
459SCREEN_FACTS ExtractScreenFacts( const SCH_SCREEN& aScreen, std::shared_ptr<const LIBRARY_SYMBOL_FACTS> aLibrarySymbols,
460 const SCREEN_FACTS* aUnchangedNonLines )
461{
462 wxASSERT( wxIsMainThread() );
463 const EE_RTREE& items = aScreen.Items();
465 std::vector<const SCH_RULE_AREA*> areas;
466 std::unordered_set<KIID> identities;
467
468 // Rule-area membership depends on line geometry
469 if( aUnchangedNonLines && ( !aUnchangedNonLines->ruleAreas.empty() || !items.OfType( SCH_RULE_AREA_T ).empty() ) )
470 aUnchangedNonLines = nullptr;
471
472 if( aUnchangedNonLines )
473 {
474 result = *aUnchangedNonLines;
475 std::erase_if( result.items, []( const ITEM_FACT& fact ) { return fact.type == SCH_LINE_T; } );
476 aLibrarySymbols = result.librarySymbols;
477 }
478
479 identities.reserve( items.size() );
480 result.items.reserve( items.size() );
481 auto libraries = aLibrarySymbols ? nullptr : std::make_shared<LIBRARY_SYMBOL_FACTS>();
482 result.librarySymbols = aLibrarySymbols ? std::move( aLibrarySymbols ) : libraries;
483
484 if( libraries )
485 {
486 libraries->reserve( std::count_if( items.begin(), items.end(), []( const SCH_ITEM* item )
487 {
488 return item->Type() == SCH_SYMBOL_T;
489 } ) );
490 }
491
492 const auto checkIdentity = [&]( const KIID& aId )
493 {
494 if( !identities.insert( aId ).second )
495 throw std::runtime_error( "Duplicate connectivity source identity" );
496 };
497
498 for( SCH_ITEM* item : items )
499 {
500 checkIdentity( item->m_Uuid );
501
502 if( aUnchangedNonLines && item->Type() != SCH_LINE_T )
503 {
504 if( item->Type() == SCH_SYMBOL_T )
505 {
506 for( const auto& pin : static_cast<const SCH_SYMBOL*>( item )->GetRawPins() )
507 checkIdentity( pin->m_Uuid );
508 }
509 else if( item->Type() == SCH_SHEET_T )
510 {
511 for( const SCH_SHEET_PIN* pin : static_cast<const SCH_SHEET*>( item )->GetPins() )
512 checkIdentity( pin->m_Uuid );
513 }
514
515 continue;
516 }
517
518 bool hasNetclass = false;
519 item->RunOnChildren(
520 [&]( SCH_ITEM* child )
521 {
522 if( child->Type() != SCH_FIELD_T )
523 return true;
524
525 const auto* field = static_cast<SCH_FIELD*>( child );
526
527 if( field->GetUntranslatedName() == wxS( "Netclass" ) )
528 hasNetclass = true;
529
530 if( item->Type() == SCH_SYMBOL_T || item->Type() == SCH_SHEET_T )
531 {
532 const wxString name = field->GetName();
533 wxString trimmed = name;
534
535 if( trimmed.Trim( false ).Trim( true ) != name )
536 {
537 result.invalidFieldNames.push_back(
538 { item->m_Uuid, field->m_Uuid, field->GetPosition(), name } );
539 }
540 }
541
542 return true;
543 },
545
546 if( hasNetclass )
547 result.netclassOwners.push_back( item->m_Uuid );
548
549 if( item->Type() == SCH_RULE_AREA_T )
550 {
551 areas.push_back( static_cast<const SCH_RULE_AREA*>( item ) );
552 continue;
553 }
554
555 if( item->Type() == SCH_SYMBOL_T )
556 {
557 const auto& symbol = *static_cast<const SCH_SYMBOL*>( item );
558 const auto& library = symbol.GetLibSymbolRef();
559
560 if( libraries )
561 {
562 auto& librarySource = libraries->emplace_back();
563
564 if( library )
565 librarySource = ExtractLibrarySymbolFact( *library );
566
567 librarySource.id = symbol.m_Uuid;
568 librarySource.position = symbol.GetPosition();
569 librarySource.library = symbol.GetLibId();
570 }
571
572 if( library && library->GetUnitCount() > 1 )
573 {
574 result.multiUnits.push_back( { symbol.m_Uuid, symbol.GetPosition(), ExtractUnitFacts( *library ) } );
575 }
576
577 result.footprints.push_back( { symbol.m_Uuid, symbol.GetPosition(),
578 library ? library->GetFPFilters() : wxArrayString() } );
579
580 if( auto maps = ExtractPinMapFacts( symbol ) )
581 result.pinMaps.push_back( std::move( *maps ) );
582
583 std::map<std::tuple<wxString, int, int>, size_t> groups;
584 std::map<wxString, std::vector<size_t>> numbers;
585 const bool hasJumperGroups = library && !library->JumperPinGroups().IsEmpty();
586
587 // Unjumpered duplicates join only when stacked, so ERC can report pins wired to different nets
588 const bool joinApart = library && library->GetDuplicatePinNumbersAreJumpers();
589
590 for( const std::unique_ptr<SCH_PIN>& pin : symbol.GetRawPins() )
591 {
592 checkIdentity( pin->m_Uuid );
593 const bool nc = pin->GetType() == ELECTRICAL_PINTYPE::PT_NC;
594 const VECTOR2I contact = joinApart ? VECTOR2I() : pin->GetPosition();
595 const auto key = std::make_tuple( pin->GetNumber(), contact.x, contact.y );
596 auto found = nc ? groups.end() : groups.find( key );
597 size_t index;
598
599 if( found == groups.end() )
600 {
601 index = result.items.size();
602 ITEM_FACT fact;
603 fact.id = pin->m_Uuid;
604 fact.type = SCH_PIN_T;
605 fact.owner = symbol.m_Uuid;
606 fact.multiUnit = symbol.IsMultiUnit();
607 fact.rawText = symbol.GetField( FIELD_T::VALUE )->GetText();
608 result.items.push_back( std::move( fact ) );
609
610 if( !nc )
611 groups.emplace( key, index );
612 }
613 else
614 {
615 index = found->second;
616 result.items[index].id = std::min( result.items[index].id, pin->m_Uuid );
617 }
618
619 ITEM_FACT& fact = result.items[index];
620 const bool valid = pin->GetLibPin() && library && !symbol.IsMissingLibSymbol();
621 PIN_FACT member;
622 member.id = pin->m_Uuid;
623 member.position = pin->GetPosition();
624 member.unit = pin->GetUnit();
625 member.type = pin->GetType();
626 member.canDrive = valid;
627 member.globalPower = valid && pin->IsGlobalPower();
628 member.localPower = valid && pin->IsLocalPower();
629 member.invisible = !pin->IsVisible();
630 member.globalPowerParent = symbol.IsGlobalPower();
631 member.localPowerParent = symbol.IsLocalPower();
632 member.name = pin->GetName();
633 member.shownName = pin->GetShownName();
634 member.shownNumber = pin->GetShownNumber();
635 member.number = pin->GetNumber();
636
637 if( const SCH_PIN* libPin = pin->GetLibPin() )
638 {
639 member.libraryName = libPin->GetShownName();
640 member.padNumber = libPin->GetSmallestStackedPadNumber();
641 }
642
643 fact.pins.push_back( std::move( member ) );
644
645 if( !nc && hasJumperGroups )
646 {
647 numbers[pin->GetNumber()].push_back( index );
648
649 for( const wxString& number : pin->GetStackedPinNumbers() )
650 numbers[number].push_back( index );
651 }
652 }
653
654 if( hasJumperGroups )
655 {
656 for( const JUMPER_GROUP& group : library->JumperPinGroups().GetAll() )
657 {
658 std::vector<size_t> indices;
659
660 for( const wxString& number : group.GetNames() )
661 {
662 if( auto it = numbers.find( number ); it != numbers.end() )
663 indices.insert( indices.end(), it->second.begin(), it->second.end() );
664 }
665
666 SortUnique( indices );
667
668 for( size_t first : indices )
669 {
670 for( size_t second : indices )
671 {
672 if( first != second )
673 result.items[first].jumperedWith.push_back( result.items[second].id );
674 }
675 }
676 }
677 }
678
679 continue;
680 }
681
682 if( item->Type() == SCH_SHEET_T )
683 {
684 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
685 {
686 checkIdentity( pin->m_Uuid );
687 ITEM_FACT fact;
688 fact.id = pin->m_Uuid;
689 fact.type = pin->Type();
690 fact.owner = item->m_Uuid;
691 fact.ports.push_back( { pin->GetPosition(), PORT_KIND::ANCHOR } );
692 ReadLabel( fact, *pin );
693 result.items.push_back( std::move( fact ) );
694 }
695
696 continue;
697 }
698
699 if( !item->IsConnectable() )
700 continue;
701
702 ITEM_FACT fact;
703 fact.id = item->m_Uuid;
704 fact.type = item->Type();
706
707 if( item->Type() == SCH_LINE_T )
708 {
709 const auto& line = *static_cast<const SCH_LINE*>( item );
711 fact.segment.emplace( line.GetStartPoint(), line.GetEndPoint() );
712 fact.lineWidth = line.GetLineWidth();
713 }
714
715 if( item->Type() == SCH_BUS_WIRE_ENTRY_T || item->Type() == SCH_BUS_BUS_ENTRY_T )
716 {
717 const auto& entry = *static_cast<const SCH_BUS_ENTRY_BASE*>( item );
719 fact.ports = { { entry.GetPosition(), kind }, { entry.GetEnd(), kind } };
720 }
721 else
722 {
723 for( const VECTOR2I& point : item->GetConnectionPoints() )
724 fact.ports.push_back( { point, kind } );
725 }
726
727 if( auto* label = dynamic_cast<const SCH_LABEL_BASE*>( item ) )
728 ReadLabel( fact, *label );
729
730 result.items.push_back( std::move( fact ) );
731 }
732
733 // Spatial-index traversal order changes when unrelated items are reindexed
734 if( libraries )
735 std::ranges::sort( *libraries, ById );
736
737 if( !aUnchangedNonLines )
738 {
739 std::ranges::sort( result.footprints, ById );
740 std::ranges::sort( result.pinMaps, ById );
741 std::ranges::sort( result.multiUnits, ById );
742 }
743
744 SortUnique( result.netclassOwners );
745 std::ranges::sort( result.invalidFieldNames,
746 []( const auto& a, const auto& b )
747 {
748 return std::tie( a.owner, a.field ) < std::tie( b.owner, b.field );
749 } );
750
751 for( ITEM_FACT& fact : result.items )
752 {
753 std::ranges::sort( fact.pins, ById );
754 SortUnique( fact.jumperedWith );
755 }
756
757 std::ranges::sort( result.items, ById );
758
759 for( const SCH_RULE_AREA* area : areas )
760 {
761 RULE_AREA_FACT fact;
762 fact.id = area->m_Uuid;
763 fact.polygon = area->GetPolyShape();
764 const auto& polygon = fact.polygon;
765
766 for( const ITEM_FACT& item : result.items )
767 {
768 if( item.type == SCH_DIRECTIVE_LABEL_T )
769 {
770 if( !item.ports.empty() && polygon.CollideEdge( item.ports.front().position, nullptr, 5 ) )
771 fact.attachedDirectives.push_back( item.id );
772 }
773 else if( item.segment )
774 {
775 const SHAPE_SEGMENT segment( *item.segment, item.lineWidth );
776
777 if( polygon.Collide( &segment ) )
778 fact.containedItems.push_back( item.id );
779 }
780 else
781 {
782 for( const PORT_FACT& port : item.ports )
783 {
784 if( polygon.Collide( port.position ) )
785 fact.containedItems.push_back( item.id );
786 }
787
788 for( const PIN_FACT& pin : item.pins )
789 {
790 if( polygon.Collide( pin.position ) )
791 fact.containedItems.push_back( pin.id );
792 }
793 }
794 }
795
796 SortUnique( fact.containedItems );
797 SortUnique( fact.attachedDirectives );
798 result.ruleAreas.push_back( std::move( fact ) );
799 }
800
801 std::ranges::sort( result.ruleAreas, ById );
802 return result;
803}
804
805std::vector<TEXT_ASSERTION> ExtractTextAssertions( const wxString& aText )
806{
807 wxASSERT( wxThread::IsMain() );
808 std::vector<TEXT_ASSERTION> result;
809
810 if( !aText.Contains( wxS( "${" ) ) )
811 return result;
812
813 static wxRegEx warningExpr( wxS( "(^|[^\\\\])\\$\\{ERC_WARNING\\s*([^}]*)\\}" ) );
814 static wxRegEx errorExpr( wxS( "(^|[^\\\\])\\$\\{ERC_ERROR\\s*([^}]*)\\}" ) );
815
816 for( bool warning : { true, false } )
817 {
818 wxRegEx& expression = warning ? warningExpr : errorExpr;
819 wxString remaining = aText;
820
821 while( expression.Matches( remaining ) )
822 {
823 result.push_back( { warning, expression.GetMatch( remaining, 2 ) } );
824 size_t start = 0;
825 size_t length = 0;
826
827 if( !expression.GetMatch( &start, &length, 0 ) || length == 0 )
828 break;
829
830 remaining = remaining.Mid( start + length );
831 }
832 }
833
834 return result;
835}
836
837std::vector<TEXT_CHECK_FACT> ExtractDrawingSheetTextChecks( const SCH_SCREEN& aScreen, const SCH_SHEET_PATH& aPath )
838{
839 wxASSERT( wxThread::IsMain() );
840 std::vector<TEXT_CHECK_FACT> result;
841
842 if( const SCHEMATIC* schematic = aScreen.Schematic(); schematic && schematic->IsValid() )
843 {
845 drawing.SetPageNumber( aPath.GetPageNumber() );
846 drawing.SetSheetCount( schematic->Hierarchy().size() );
847 drawing.SetFileName( aScreen.GetFileName() );
848 drawing.SetSheetName( aPath.Last()->GetName() );
849 drawing.SetSheetPath( aPath.PathHumanReadable() );
850 drawing.SetIsFirstPage( aPath.GetVirtualPageNumber() == 1 );
851 drawing.SetVariantName( schematic->GetCurrentVariant() );
852 drawing.SetVariantDesc( schematic->GetVariantDescription( schematic->GetCurrentVariant() ) );
853 drawing.SetSheetLayer( wxS( "dummyLayer" ) );
854 drawing.SetProject( &schematic->Project() );
855 drawing.BuildDrawItemsList( aScreen.GetPageSettings(), aScreen.GetTitleBlock() );
856
857 for( DS_DRAW_ITEM_BASE* item = drawing.GetFirst(); item; item = drawing.GetNext() )
858 {
859 const auto* text = dynamic_cast<const DS_DRAW_ITEM_TEXT*>( item );
860
861 if( !text )
862 continue;
863
864 auto assertions = ExtractTextAssertions( text->GetText() );
865 const wxString shown = assertions.empty() ? text->GetShownText( FOR_ERC_DRC ) : wxString();
866
867 if( !assertions.empty() || shown.Contains( wxS( "${" ) ) )
868 {
869 result.push_back( { niluuid, niluuid, text->GetPosition(), text->GetPosition(),
870 std::move( assertions ), shown } );
871 }
872 }
873 }
874
875 return result;
876}
877
878std::vector<TEXT_CHECK_FACT> ExtractTextChecks( const SCH_SCREEN& aScreen, const SCH_SHEET_PATH& aPath )
879{
880 wxASSERT( wxThread::IsMain() );
881 std::vector<TEXT_CHECK_FACT> result;
882 auto append = [&]( const KIID& assertionItem, const KIID& item, const VECTOR2I& assertionPosition,
883 const wxString& raw, bool expandEnvironment, auto&& resolve )
884 {
885 auto assertions = ExtractTextAssertions( raw );
886
887 if( !assertions.empty() )
888 {
889 result.push_back( { assertionItem, item, assertionPosition, assertionPosition,
890 std::move( assertions ), wxString() } );
891 return;
892 }
893
894 auto [shown, position] = resolve();
895 const auto* schematic = aScreen.Schematic();
896
897 if( expandEnvironment && shown.find_first_of( wxS( "$%" ) ) != wxString::npos )
898 shown = ExpandEnvVarSubstitutions( shown, schematic ? &schematic->Project() : nullptr );
899
900 if( shown.Contains( wxS( "${" ) ) )
901 result.push_back( { assertionItem, item, assertionPosition, position, {}, std::move( shown ) } );
902 };
903 auto fields = [&]( const auto& owner )
904 {
905 for( const SCH_FIELD& field : owner.GetFields() )
906 {
907 append( field.m_Uuid, owner.m_Uuid, field.GetPosition(), field.GetText(), true, [&]
908 {
909 return std::make_pair( field.GetShownText( &aPath, FOR_ERC_DRC ), field.GetPosition() );
910 } );
911 }
912 };
913
914 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_LOCATE_ANY_T ) )
915 {
916 if( item->Type() == SCH_SYMBOL_T )
917 {
918 const auto& symbol = *static_cast<SCH_SYMBOL*>( item );
919 fields( symbol );
920
921 if( const auto& library = symbol.GetLibSymbolRef() )
922 {
923 library->RunOnChildren( [&]( SCH_ITEM* child )
924 {
925 auto text = [&]( const auto& source, auto&& shown )
926 {
927 append( symbol.m_Uuid, symbol.m_Uuid, source.GetPosition(), source.GetText(), true, [&]
928 {
929 wxString shownText = shown();
930 const BOX2I box = symbol.GetTransform().TransformCoordinate( source.GetBoundingBox() );
931 return std::make_pair( std::move( shownText ), box.Centre() + symbol.GetPosition() );
932 } );
933 };
934
935 if( child->Type() == SCH_TEXT_T )
936 {
937 const auto& source = *static_cast<SCH_TEXT*>( child );
938 text( source, [&] { return source.GetShownText( &aPath, FOR_ERC_DRC ); } );
939 }
940 else if( child->Type() == SCH_TEXTBOX_T )
941 {
942 const auto& source = *static_cast<SCH_TEXTBOX*>( child );
943 text( source, [&] { return source.GetShownText( nullptr, &aPath, FOR_ERC_DRC ); } );
944 }
946 }
947 }
948 else if( const auto* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
949 {
950 fields( *label );
951 }
952 else if( item->Type() == SCH_SHEET_T )
953 {
954 auto& sheet = *static_cast<SCH_SHEET*>( item );
955 fields( sheet );
956 SCH_SHEET_PATH childPath = aPath;
957 childPath.push_back( &sheet );
958
959 for( const SCH_SHEET_PIN* pin : sheet.GetPins() )
960 {
961 append( niluuid, pin->m_Uuid, pin->GetPosition(), wxString(), false, [&]
962 {
963 return std::make_pair( pin->GetShownText( &childPath, FOR_ERC_DRC ), pin->GetPosition() );
964 } );
965 }
966 }
967 else if( const auto* text = dynamic_cast<SCH_TEXT*>( item ) )
968 {
969 append( text->m_Uuid, text->m_Uuid, text->GetPosition(), text->GetText(), false, [&]
970 {
971 return std::make_pair( text->GetShownText( &aPath, FOR_ERC_DRC ), text->GetPosition() );
972 } );
973 }
974 else if( const auto* textbox = dynamic_cast<SCH_TEXTBOX*>( item ) )
975 {
976 append( textbox->m_Uuid, textbox->m_Uuid, textbox->GetPosition(), textbox->GetText(), false, [&]
977 {
978 return std::make_pair( textbox->GetShownText( nullptr, &aPath, FOR_ERC_DRC ), textbox->GetPosition() );
979 } );
980 }
981 }
982
983 return result;
984}
985
986
987std::optional<PIN_MAP_FACT> ExtractPinMapFacts( const SCH_SYMBOL& aSymbol )
988{
989 const auto& lib = aSymbol.GetLibSymbolRef();
990
991 if( !lib )
992 return std::nullopt;
993
994 const auto& maps = lib->GetEffectivePinMaps();
995 const auto& footprints = lib->GetEffectiveAssociatedFootprints();
996
997 if( maps.IsEmpty() && footprints.empty() )
998 return std::nullopt;
999
1001 result.id = aSymbol.m_Uuid;
1002 result.position = aSymbol.GetPosition();
1003 result.maps = maps;
1004 result.footprints = footprints;
1005 result.jumperGroups = lib->JumperPinGroups();
1006
1007 for( const SCH_PIN* pin : lib->GetPins() )
1008 result.pinNumbers.insert( pin->GetNumber() );
1009
1010 return result;
1011}
1012
1013std::vector<UNIT_FACT> ExtractUnitFacts( const LIB_SYMBOL& aSymbol )
1014{
1015 std::vector<UNIT_FACT> result;
1016
1017 for( int unit = 1; unit <= aSymbol.GetUnitCount(); ++unit )
1018 {
1019 UNIT_FACT fact;
1020 fact.name = aSymbol.GetUnitDisplayName( unit, false );
1021
1022 for( const SCH_PIN* pin : aSymbol.GetGraphicalPins( unit, 0 ) )
1023 {
1024 fact.powerInput |= pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN;
1025 fact.input |= pin->GetType() == ELECTRICAL_PINTYPE::PT_INPUT;
1026 fact.bidirectional |= pin->GetType() == ELECTRICAL_PINTYPE::PT_BIDI;
1027 }
1028
1029 result.push_back( std::move( fact ) );
1030 }
1031
1032 return result;
1033}
1034
1036 const SCH_SHEET_PATH& aPath )
1037{
1038 wxASSERT( wxIsMainThread() );
1039
1040 if( aPath.LastScreen() != &aScreen )
1041 throw std::invalid_argument( "Connectivity instance does not reference its screen" );
1042
1043 std::optional<TEXT_EVAL_VCS::CONTEXT_PATH_SCOPE> vcs;
1044
1045 if( const auto* schematic = aScreen.Schematic(); schematic && schematic->IsValid() )
1046 {
1047 if( const wxString path = schematic->Project().GetProjectPath(); !path.IsEmpty() )
1048 vcs.emplace( path );
1049 }
1050
1051 struct SYMBOL_TEXT
1052 {
1053 const SCH_SYMBOL* symbol = nullptr;
1054 PIN_NAME_REFERENCE reference;
1055 wxString plainReference;
1056 std::map<wxString, std::set<wxString>> numbersByName;
1057 };
1058
1059 INPUT_TEXT_SCOPE inputText;
1061 std::map<KIID, int> units;
1062 std::map<KIID, SYMBOL_TEXT> symbols;
1063 std::set<KIID> inactivePins;
1064 const wxString variant = aScreen.Schematic() ? aScreen.Schematic()->GetCurrentVariant() : wxString();
1065
1066 result.pageOrder = aPath.GetVirtualPageNumber();
1067
1068 for( const FOOTPRINT_FACT& fact : aFacts.footprints )
1069 {
1070 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1071
1072 if( !symbol )
1073 throw std::runtime_error( "Footprint symbol disappeared during extraction" );
1074
1075 result.footprints.emplace_back( fact.id, symbol->GetFootprintFieldText( &aPath, RESOLVED ) );
1076
1077 if( auto source = ExtractVariantSymbolFact( *symbol, aPath ) )
1078 result.variantSymbols.push_back( std::move( *source ) );
1079 }
1080
1081 for( const PIN_MAP_FACT& fact : aFacts.pinMaps )
1082 {
1083 if( fact.footprints.empty() )
1084 continue;
1085
1086 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1087
1088 if( !symbol )
1089 throw std::runtime_error( "Pin-map symbol disappeared during extraction" );
1090
1091 const wxString footprint = symbol->GetFootprintFieldText( &aPath, RESOLVED );
1092 LIB_ID footprintId;
1093
1094 if( footprint.IsEmpty() || footprintId.Parse( footprint, true ) >= 0 )
1095 continue;
1096
1097 for( const SCH_PIN* pin : symbol->GetPins( &aPath ) )
1098 {
1100 pin->GetEffectivePadNumber( aPath, variant, footprintId, nullptr, &state );
1101
1102 if( state == SCH_PIN::PAD_RESOLUTION::MAPPED )
1103 continue;
1104
1105 const auto type = pin->GetType();
1106 result.pinMapCandidates.push_back( { pin->m_Uuid, pin->GetPosition(), pin->GetNumber(), footprint,
1108 }
1109 }
1110
1111 for( const MULTI_UNIT_FACT& fact : aFacts.multiUnits )
1112 {
1113 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1114
1115 if( !symbol )
1116 throw std::runtime_error( "Multiunit symbol disappeared during extraction" );
1117
1118 result.multiUnits.push_back( { fact.id, symbol->GetRef( &aPath ), symbol->GetRef( &aPath, true ),
1119 symbol->GetFootprintFieldText( &aPath, RESOLVED ), symbol->GetUnitSelection( &aPath ) } );
1120 }
1121
1122 // Screen order fixes the main and auxiliary witnesses in saved ERC exclusions
1123 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_SHEET_T ) )
1124 {
1125 const auto* sheet = static_cast<const SCH_SHEET*>( item );
1126 result.childSheets.push_back( { sheet->m_Uuid, sheet->GetPosition(),
1127 sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( &aPath, RESOLVED, variant ) } );
1128 }
1129
1130 for( const KIID& owner : aFacts.netclassOwners )
1131 {
1132 SCH_ITEM* item = aScreen.GetConnectivityItem( owner );
1133
1134 if( !item )
1135 throw std::invalid_argument( "Netclass field owner is missing from its captured screen" );
1136
1137 item->RunOnChildren(
1138 [&]( SCH_ITEM* child )
1139 {
1140 if( child->Type() != SCH_FIELD_T )
1141 return true;
1142
1143 const auto* field = static_cast<SCH_FIELD*>( child );
1144
1145 if( field->GetUntranslatedName() == wxS( "Netclass" ) )
1146 {
1147 wxString name = field->GetShownText( &aPath, FOR_NETNAME, variant );
1148
1149 if( !name.empty() )
1150 result.netclassReferences.push_back(
1151 { aPath.PathRef(), owner, item->GetPosition(), std::move( name ) } );
1152 }
1153
1154 return true;
1155 },
1157 }
1158
1159 for( const ITEM_FACT& fact : aFacts.items )
1160 {
1161 if( fact.type == SCH_PIN_T )
1162 {
1163 auto [entry, inserted] = symbols.try_emplace( fact.owner );
1164 SYMBOL_TEXT& cached = entry->second;
1165
1166 if( inserted )
1167 {
1168 cached.symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.owner ) );
1169
1170 if( !cached.symbol )
1171 throw std::runtime_error( "Connectivity symbol disappeared during extraction" );
1172
1173 units.emplace( fact.owner, cached.symbol->GetUnitSelection( &aPath ) );
1174 SCH_SYMBOL_INSTANCE instance;
1175 const bool hasInstance = cached.symbol->GetInstance( instance, aPath.Path() );
1176 cached.reference.symbolUuid = cached.symbol->m_Uuid.AsString();
1177
1178 if( hasInstance )
1179 {
1180 cached.reference.reference = instance.m_Reference;
1181 cached.reference.referenceWithUnit = cached.symbol->GetRef( &aPath, true );
1182 }
1183
1184 cached.plainReference = cached.symbol->GetRef( &aPath );
1185
1186 for( const SCH_PIN* pin : cached.symbol->GetPins( &aPath ) )
1187 {
1188 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
1189 cached.numbersByName[pin->GetShownName()].insert( pin->GetShownNumber() );
1190 }
1191 }
1192
1193 const SCH_SYMBOL* symbol = cached.symbol;
1194 const int unit = units.at( fact.owner );
1195 const PIN_NAME_REFERENCE& reference = cached.reference;
1196
1197 for( const PIN_FACT& member : fact.pins )
1198 {
1199 if( unit && member.unit && unit != member.unit )
1200 {
1201 inactivePins.insert( member.id );
1202 continue;
1203 }
1204
1205 const auto* pin = dynamic_cast<const SCH_PIN*>( aScreen.GetConnectivityItem( member.id ) );
1206
1207 if( !pin )
1208 throw std::runtime_error( "Connectivity pin disappeared during extraction" );
1209
1211 text.id = member.id;
1212 text.canDrive = member.canDrive;
1213 text.reference = cached.plainReference;
1214
1215 if( member.globalPower || member.localPower )
1216 {
1217 text.name = EscapeString( member.globalPowerParent || member.localPowerParent
1218 ? symbol->GetValue( &aPath, FOR_NETNAME, variant )
1219 : pin->GetLibPin()->GetName(),
1220 CTX_NETNAME );
1221 text.ncName = text.name;
1222 }
1223 else
1224 {
1225 const SCH_PIN* libPin = pin->GetLibPin();
1227 name.name = libPin ? libPin->GetShownName() : wxString( "??" );
1228 name.shownNumber = libPin ? libPin->GetShownNumber() : wxString( "??" );
1229 name.number = libPin ? libPin->GetNumber() : wxString( "??" );
1230 name.padNumber = libPin ? libPin->GetSmallestStackedPadNumber() : name.shownNumber;
1231 name.noConnect = member.type == ELECTRICAL_PINTYPE::PT_NC;
1232
1233 const auto names = cached.numbersByName.find( pin->GetShownName() );
1234
1235 if( names != cached.numbersByName.end() )
1236 {
1237 const auto& numbers = names->second;
1238 name.hasDuplicateName = numbers.size() > 1 || !numbers.contains( pin->GetShownNumber() );
1239 }
1240
1241 text.name = RenderPinNetName( name, reference );
1242 text.ncName = RenderPinNetName( name, reference, true );
1243 text.canDrive &= symbol->IsInNetlist() && !symbol->GetExcludedFromBoard( &aPath, variant )
1244 && !reference.reference.StartsWith( wxS( "#" ) );
1245 }
1246
1247 result.items.push_back( std::move( text ) );
1248 }
1249
1250 continue;
1251 }
1252
1253 const auto* label = dynamic_cast<const SCH_LABEL_BASE*>( aScreen.GetConnectivityItem( fact.id ) );
1254
1255 if( !label )
1256 continue;
1257
1258 SCH_SHEET_PATH path = aPath;
1259
1260 if( fact.type == SCH_SHEET_PIN_T )
1261 path.push_back( static_cast<const SCH_SHEET_PIN*>( label )->GetParent() );
1262
1264 text.id = fact.id;
1265 text.canDrive = fact.type != SCH_DIRECTIVE_LABEL_T;
1266 text.name = EscapeString( label->GetShownText( &path, FOR_NETNAME ), CTX_NETNAME );
1267 text.ncName = text.name;
1268
1269 for( const SCH_FIELD& field : label->GetFields() )
1270 {
1271 if( field.GetUntranslatedName() == wxS( "Netclass" ) )
1272 {
1273 wxString value = field.GetShownText( &path, FOR_NETNAME, variant );
1274
1275 if( !value.empty() )
1276 text.netclasses.push_back( std::move( value ) );
1277 }
1278 }
1279
1280 SortUnique( text.netclasses );
1281 result.items.push_back( std::move( text ) );
1282 }
1283
1284 std::map<KIID, BOX2I> ownerBoxes;
1285
1286 if( !aFacts.ruleAreas.empty() )
1287 {
1288 for( SCH_ITEM* item : aScreen.Items() )
1289 {
1290 if( item->Type() == SCH_SYMBOL_T )
1291 {
1292 const auto& symbol = *static_cast<const SCH_SYMBOL*>( item );
1293 const int unit = symbol.GetUnitSelection( &aPath );
1294 units.emplace( symbol.m_Uuid, unit );
1295 const LIB_SYMBOL* library = symbol.GetEffectiveLibSymbol( &aPath );
1296
1297 if( !library )
1299
1300 BOX2I box = library->GetBodyBoundingBox( unit, symbol.GetBodyStyle(), true, false );
1301 box = symbol.GetTransform().TransformCoordinate( box );
1302 box.Normalize();
1303 box.Offset( symbol.GetPosition() );
1304
1305 for( const SCH_FIELD& field : symbol.GetFields() )
1306 {
1307 if( field.IsVisible() )
1308 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1309 }
1310
1311 ownerBoxes.emplace( symbol.m_Uuid, box );
1312 }
1313 else if( item->Type() == SCH_SHEET_T )
1314 {
1315 const auto& sheet = *static_cast<const SCH_SHEET*>( item );
1316 BOX2I box = sheet.GetBodyBoundingBox();
1317
1318 for( const SCH_FIELD& field : sheet.GetFields() )
1319 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1320
1321 ownerBoxes.emplace( sheet.m_Uuid, box );
1322 }
1323 }
1324 }
1325
1326 for( const RULE_AREA_FACT& area : aFacts.ruleAreas )
1327 {
1328 INSTANCE_RULE_AREA_FACT resolved;
1329 resolved.id = area.id;
1330 resolved.attachedDirectives = area.attachedDirectives;
1331 resolved.containedItems = area.containedItems;
1332 std::erase_if( resolved.containedItems, [&]( const KIID& id ) { return inactivePins.contains( id ); } );
1333
1334 for( const auto& [id, box] : ownerBoxes )
1335 {
1336 const SHAPE_RECT rectangle( box );
1337
1338 if( area.polygon.Collide( &rectangle ) )
1339 resolved.containedItems.push_back( id );
1340 }
1341
1342 for( const ITEM_TEXT_FACT& text : result.items )
1343 {
1344 if( std::binary_search( area.attachedDirectives.begin(), area.attachedDirectives.end(), text.id ) )
1345 resolved.netclasses.insert( resolved.netclasses.end(), text.netclasses.begin(), text.netclasses.end() );
1346 }
1347
1348 SortUnique( resolved.containedItems );
1349 SortUnique( resolved.netclasses );
1350 result.ruleAreas.push_back( std::move( resolved ) );
1351 }
1352
1353 result.units.assign( units.begin(), units.end() );
1354 std::ranges::sort( result.items, ById );
1355 return result;
1356}
1357} // namespace SCH_CONNECTIVITY
int index
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:653
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:256
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:597
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:325
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:275
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:491
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
std::vector< const SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
bool IsDerived() const
Definition lib_symbol.h:236
static LIB_SYMBOL * GetDummy()
Returns 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:832
const PIN_MAP_SET & GetEffectivePinMaps() const
Definition lib_symbol.h:280
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
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the user-defined display name for aUnit for symbols with units.
Holds all the data relating to one schematic.
Definition schematic.h:149
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:289
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:229
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:678
COMPARE_FLAGS
The list of flags used by various compare functions.
Definition sch_item.h:742
bool IsPrivate() const
Definition sch_item.h:258
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:352
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:178
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:674
const wxString & GetShownNumber() const
Definition sch_pin.cpp:685
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:716
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
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:48
wxString GetName() const
Definition sch_sheet.h:142
Schematic symbol object.
Definition sch_symbol.h:74
bool IsInNetlist() const
VECTOR2I GetPosition() const override
Definition sch_symbol.h:896
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:182
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 wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
RESOLUTION_CONTEXT
Definition common.h:87
@ FOR_ERC_DRC
Definition common.h:91
@ FOR_NETNAME
Definition common.h:90
@ RESOLVED
Definition common.h:93
static bool empty(const wxTextEntryBase *aCtrl)
@ NO_RECURSE
Definition eda_item.h:52
KIID niluuid(0)
@ LAYER_BUS
Definition layer_ids.h:475
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)
@ 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.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
static const HTTP_LIB_PART::field_type * findField(const HTTP_LIB_PART &aPart, const std::string &aName)
Issue #23023.
std::string path
KIBIS_PIN * pin
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:141
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683