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(
924 [&]( SCH_ITEM* child )
925 {
926 auto text =
927 [&]( const auto& source, auto&& shown )
928 {
929 append( symbol.m_Uuid, symbol.m_Uuid, source.GetPosition(), source.GetText(),
930 true,
931 [&]()
932 {
933 wxString shownText = shown();
934 const BOX2I bbox = source.GetBoundingBox();
935 const BOX2I box = symbol.GetTransform().TransformCoordinate( bbox );
936 return std::make_pair( std::move( shownText ),
937 box.Centre() + symbol.GetPosition() );
938 } );
939 };
940
941 if( child->Type() == SCH_TEXT_T )
942 {
943 const auto& source = *static_cast<SCH_TEXT*>( child );
944 text( source,
945 [&]()
946 {
947 return source.GetShownText( &aPath, FOR_ERC_DRC );
948 } );
949 }
950 else if( child->Type() == SCH_TEXTBOX_T )
951 {
952 const auto& source = *static_cast<SCH_TEXTBOX*>( child );
953 text( source,
954 [&]()
955 {
956 return source.GetShownText( nullptr, &aPath, FOR_ERC_DRC );
957 } );
958 }
960 }
961 }
962 else if( const auto* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
963 {
964 fields( *label );
965 }
966 else if( item->Type() == SCH_SHEET_T )
967 {
968 SCH_SHEET& sheet = *static_cast<SCH_SHEET*>( item );
969 fields( sheet );
970 SCH_SHEET_PATH childPath = aPath;
971 childPath.push_back( &sheet );
972
973 for( const SCH_SHEET_PIN* pin : sheet.GetPins() )
974 {
975 append( niluuid, pin->m_Uuid, pin->GetPosition(), wxString(), false,
976 [&]()
977 {
978 return std::make_pair( pin->GetShownText( &childPath, FOR_ERC_DRC ), pin->GetPosition() );
979 } );
980 }
981 }
982 else if( const auto* text = dynamic_cast<SCH_TEXT*>( item ) )
983 {
984 append( text->m_Uuid, text->m_Uuid, text->GetPosition(), text->GetText(), false,
985 [&]()
986 {
987 return std::make_pair( text->GetShownText( &aPath, FOR_ERC_DRC ), text->GetPosition() );
988 } );
989 }
990 else if( const auto* textbox = dynamic_cast<SCH_TEXTBOX*>( item ) )
991 {
992 append( textbox->m_Uuid, textbox->m_Uuid, textbox->GetPosition(), textbox->GetText(), false,
993 [&]()
994 {
995 return std::make_pair( textbox->GetShownText( nullptr, &aPath, FOR_ERC_DRC ),
996 textbox->GetPosition() );
997 } );
998 }
999 }
1000
1001 return result;
1002}
1003
1004
1005std::optional<PIN_MAP_FACT> ExtractPinMapFacts( const SCH_SYMBOL& aSymbol )
1006{
1007 const std::unique_ptr<LIB_SYMBOL>& lib = aSymbol.GetLibSymbolRef();
1008
1009 if( !lib )
1010 return std::nullopt;
1011
1012 const PIN_MAP_SET& maps = lib->GetEffectivePinMaps();
1013 const std::vector<ASSOCIATED_FOOTPRINT>& footprints = lib->GetEffectiveAssociatedFootprints();
1014
1015 if( maps.IsEmpty() && footprints.empty() )
1016 return std::nullopt;
1017
1019 result.id = aSymbol.m_Uuid;
1020 result.position = aSymbol.GetPosition();
1021 result.maps = maps;
1022 result.footprints = footprints;
1023 result.jumperGroups = lib->JumperPinGroups();
1024
1025 for( const SCH_PIN* pin : lib->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
1026 result.pinNumbers.insert( pin->GetNumber() );
1027
1028 return result;
1029}
1030
1031std::vector<UNIT_FACT> ExtractUnitFacts( const LIB_SYMBOL& aSymbol )
1032{
1033 std::vector<UNIT_FACT> result;
1034
1035 for( int unit = 1; unit <= aSymbol.GetUnitCount(); ++unit )
1036 {
1037 UNIT_FACT fact;
1038 fact.name = aSymbol.GetUnitDisplayName( unit, false );
1039
1040 for( const SCH_PIN* pin : aSymbol.GetGraphicalPins( unit, 0 ) )
1041 {
1042 fact.powerInput |= pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN;
1043 fact.input |= pin->GetType() == ELECTRICAL_PINTYPE::PT_INPUT;
1044 fact.bidirectional |= pin->GetType() == ELECTRICAL_PINTYPE::PT_BIDI;
1045 }
1046
1047 result.push_back( std::move( fact ) );
1048 }
1049
1050 return result;
1051}
1052
1054 const SCH_SHEET_PATH& aPath )
1055{
1056 wxASSERT( wxIsMainThread() );
1057
1058 if( aPath.LastScreen() != &aScreen )
1059 throw std::invalid_argument( "Connectivity instance does not reference its screen" );
1060
1061 std::optional<TEXT_EVAL_VCS::CONTEXT_PATH_SCOPE> vcs;
1062
1063 if( const auto* schematic = aScreen.Schematic(); schematic && schematic->IsValid() )
1064 {
1065 if( const wxString path = schematic->Project().GetProjectPath(); !path.IsEmpty() )
1066 vcs.emplace( path );
1067 }
1068
1069 struct SYMBOL_TEXT
1070 {
1071 const SCH_SYMBOL* symbol = nullptr;
1072 PIN_NAME_REFERENCE reference;
1073 wxString plainReference;
1074 std::map<wxString, std::set<wxString>> numbersByName;
1075 };
1076
1077 INPUT_TEXT_SCOPE inputText;
1079 std::map<KIID, int> units;
1080 std::map<KIID, SYMBOL_TEXT> symbols;
1081 std::set<KIID> inactivePins;
1082 const wxString variant = aScreen.Schematic() ? aScreen.Schematic()->GetCurrentVariant() : wxString();
1083
1084 result.pageOrder = aPath.GetVirtualPageNumber();
1085
1086 for( const FOOTPRINT_FACT& fact : aFacts.footprints )
1087 {
1088 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1089
1090 if( !symbol )
1091 throw std::runtime_error( "Footprint symbol disappeared during extraction" );
1092
1093 result.footprints.emplace_back( fact.id, symbol->GetFootprintFieldText( &aPath, RESOLVED ) );
1094
1095 if( auto source = ExtractVariantSymbolFact( *symbol, aPath ) )
1096 result.variantSymbols.push_back( std::move( *source ) );
1097 }
1098
1099 for( const PIN_MAP_FACT& fact : aFacts.pinMaps )
1100 {
1101 if( fact.footprints.empty() )
1102 continue;
1103
1104 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1105
1106 if( !symbol )
1107 throw std::runtime_error( "Pin-map symbol disappeared during extraction" );
1108
1109 const wxString footprint = symbol->GetFootprintFieldText( &aPath, RESOLVED );
1110 LIB_ID footprintId;
1111
1112 if( footprint.IsEmpty() || footprintId.Parse( footprint, true ) >= 0 )
1113 continue;
1114
1115 for( const SCH_PIN* pin : symbol->GetPins( &aPath ) )
1116 {
1118 pin->GetEffectivePadNumber( aPath, variant, footprintId, nullptr, &state );
1119
1120 if( state == SCH_PIN::PAD_RESOLUTION::MAPPED )
1121 continue;
1122
1123 const ELECTRICAL_PINTYPE type = pin->GetType();
1124 result.pinMapCandidates.push_back( { pin->m_Uuid, pin->GetPosition(), pin->GetNumber(), footprint,
1126 || type == ELECTRICAL_PINTYPE::PT_NIC } );
1127 }
1128 }
1129
1130 for( const MULTI_UNIT_FACT& fact : aFacts.multiUnits )
1131 {
1132 const auto* symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.id ) );
1133
1134 if( !symbol )
1135 throw std::runtime_error( "Multiunit symbol disappeared during extraction" );
1136
1137 result.multiUnits.push_back( { fact.id, symbol->GetRef( &aPath ), symbol->GetRef( &aPath, true ),
1138 symbol->GetFootprintFieldText( &aPath, RESOLVED ),
1139 symbol->GetUnitSelection( &aPath ) } );
1140 }
1141
1142 // Screen order fixes the main and auxiliary witnesses in saved ERC exclusions
1143 for( SCH_ITEM* item : aScreen.Items().OfType( SCH_SHEET_T ) )
1144 {
1145 const auto* sheet = static_cast<const SCH_SHEET*>( item );
1146 result.childSheets.push_back( { sheet->m_Uuid, sheet->GetPosition(),
1147 sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( &aPath, RESOLVED,
1148 variant ) } );
1149 }
1150
1151 for( const KIID& owner : aFacts.netclassOwners )
1152 {
1153 SCH_ITEM* item = aScreen.GetConnectivityItem( owner );
1154
1155 if( !item )
1156 throw std::invalid_argument( "Netclass field owner is missing from its captured screen" );
1157
1158 item->RunOnChildren(
1159 [&]( SCH_ITEM* child )
1160 {
1161 if( child->Type() != SCH_FIELD_T )
1162 return true;
1163
1164 const auto* field = static_cast<SCH_FIELD*>( child );
1165
1166 if( field->GetUntranslatedName() == wxS( "Netclass" ) )
1167 {
1168 wxString name = field->GetShownText( &aPath, FOR_NETNAME, variant );
1169
1170 if( !name.empty() )
1171 {
1172 result.netclassReferences.push_back( { aPath.PathRef(), owner, item->GetPosition(),
1173 std::move( name ) } );
1174 }
1175 }
1176
1177 return true;
1178 },
1180 }
1181
1182 for( const ITEM_FACT& fact : aFacts.items )
1183 {
1184 if( fact.type == SCH_PIN_T )
1185 {
1186 auto [entry, inserted] = symbols.try_emplace( fact.owner );
1187 SYMBOL_TEXT& cached = entry->second;
1188
1189 if( inserted )
1190 {
1191 cached.symbol = dynamic_cast<const SCH_SYMBOL*>( aScreen.GetConnectivityItem( fact.owner ) );
1192
1193 if( !cached.symbol )
1194 throw std::runtime_error( "Connectivity symbol disappeared during extraction" );
1195
1196 units.emplace( fact.owner, cached.symbol->GetUnitSelection( &aPath ) );
1197 SCH_SYMBOL_INSTANCE instance;
1198 const bool hasInstance = cached.symbol->GetInstance( instance, aPath.Path() );
1199 cached.reference.symbolUuid = cached.symbol->m_Uuid.AsString();
1200
1201 if( hasInstance )
1202 {
1203 cached.reference.reference = instance.m_Reference;
1204 cached.reference.referenceWithUnit = cached.symbol->GetRef( &aPath, true );
1205 }
1206
1207 cached.plainReference = cached.symbol->GetRef( &aPath );
1208
1209 for( const SCH_PIN* pin : cached.symbol->GetPins( &aPath ) )
1210 {
1211 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
1212 cached.numbersByName[pin->GetShownName()].insert( pin->GetShownNumber() );
1213 }
1214 }
1215
1216 const SCH_SYMBOL* symbol = cached.symbol;
1217 const int unit = units.at( fact.owner );
1218 const PIN_NAME_REFERENCE& reference = cached.reference;
1219
1220 for( const PIN_FACT& member : fact.pins )
1221 {
1222 if( unit && member.unit && unit != member.unit )
1223 {
1224 inactivePins.insert( member.id );
1225 continue;
1226 }
1227
1228 const auto* pin = dynamic_cast<const SCH_PIN*>( aScreen.GetConnectivityItem( member.id ) );
1229
1230 if( !pin )
1231 throw std::runtime_error( "Connectivity pin disappeared during extraction" );
1232
1234 text.id = member.id;
1235 text.canDrive = member.canDrive;
1236 text.reference = cached.plainReference;
1237
1238 if( member.globalPower || member.localPower )
1239 {
1240 text.name = EscapeString( member.globalPowerParent || member.localPowerParent
1241 ? symbol->GetValue( &aPath, FOR_NETNAME, variant )
1242 : pin->GetLibPin()->GetName(),
1243 CTX_NETNAME );
1244 text.ncName = text.name;
1245 }
1246 else
1247 {
1248 const SCH_PIN* libPin = pin->GetLibPin();
1250 name.name = libPin ? libPin->GetShownName() : wxString( "??" );
1251 name.shownNumber = libPin ? libPin->GetShownNumber() : wxString( "??" );
1252 name.number = libPin ? libPin->GetNumber() : wxString( "??" );
1253 name.padNumber = libPin ? libPin->GetSmallestStackedPadNumber() : name.shownNumber;
1254 name.noConnect = member.type == ELECTRICAL_PINTYPE::PT_NC;
1255
1256 const auto names = cached.numbersByName.find( pin->GetShownName() );
1257
1258 if( names != cached.numbersByName.end() )
1259 {
1260 const auto& numbers = names->second;
1261 name.hasDuplicateName = numbers.size() > 1 || !numbers.contains( pin->GetShownNumber() );
1262 }
1263
1264 text.name = RenderPinNetName( name, reference );
1265 text.ncName = RenderPinNetName( name, reference, true );
1266 text.canDrive &= symbol->DoNetList() && !symbol->GetExcludedFromBoard( &aPath, variant )
1267 && !reference.reference.StartsWith( wxS( "#" ) );
1268 }
1269
1270 result.items.push_back( std::move( text ) );
1271 }
1272
1273 continue;
1274 }
1275
1276 const SCH_LABEL_BASE* label = dynamic_cast<const SCH_LABEL_BASE*>( aScreen.GetConnectivityItem( fact.id ) );
1277
1278 if( !label )
1279 continue;
1280
1281 SCH_SHEET_PATH path = aPath;
1282
1283 if( fact.type == SCH_SHEET_PIN_T )
1284 path.push_back( static_cast<const SCH_SHEET_PIN*>( label )->GetParent() );
1285
1287 text.id = fact.id;
1288 text.canDrive = fact.type != SCH_DIRECTIVE_LABEL_T;
1290 text.ncName = text.name;
1291
1292 for( const SCH_FIELD& field : label->GetFields() )
1293 {
1294 if( field.GetUntranslatedName() == wxS( "Netclass" ) )
1295 {
1296 wxString value = field.GetShownText( &path, FOR_NETNAME, variant );
1297
1298 if( !value.empty() )
1299 text.netclasses.push_back( std::move( value ) );
1300 }
1301 }
1302
1303 SortUnique( text.netclasses );
1304 result.items.push_back( std::move( text ) );
1305 }
1306
1307 std::map<KIID, BOX2I> ownerBoxes;
1308
1309 if( !aFacts.ruleAreas.empty() )
1310 {
1311 for( SCH_ITEM* item : aScreen.Items() )
1312 {
1313 if( item->Type() == SCH_SYMBOL_T )
1314 {
1315 const SCH_SYMBOL& symbol = *static_cast<const SCH_SYMBOL*>( item );
1316 const int unit = symbol.GetUnitSelection( &aPath );
1317 units.emplace( symbol.m_Uuid, unit );
1318 const LIB_SYMBOL* library = symbol.GetEffectiveLibSymbol( &aPath );
1319
1320 if( !library )
1322
1323 BOX2I box = library->GetBodyBoundingBox( unit, symbol.GetBodyStyle(), true, false );
1324 box = symbol.GetTransform().TransformCoordinate( box );
1325 box.Normalize();
1326 box.Offset( symbol.GetPosition() );
1327
1328 for( const SCH_FIELD& field : symbol.GetFields() )
1329 {
1330 if( field.IsVisible() )
1331 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1332 }
1333
1334 ownerBoxes.emplace( symbol.m_Uuid, box );
1335 }
1336 else if( item->Type() == SCH_SHEET_T )
1337 {
1338 const SCH_SHEET& sheet = *static_cast<const SCH_SHEET*>( item );
1339 BOX2I box = sheet.GetBodyBoundingBox();
1340
1341 for( const SCH_FIELD& field : sheet.GetFields() )
1342 box.Merge( INSTANCE_FIELD( field, aPath ).GetBoundingBox() );
1343
1344 ownerBoxes.emplace( sheet.m_Uuid, box );
1345 }
1346 }
1347 }
1348
1349 for( const RULE_AREA_FACT& area : aFacts.ruleAreas )
1350 {
1351 INSTANCE_RULE_AREA_FACT resolved;
1352 resolved.id = area.id;
1353 resolved.attachedDirectives = area.attachedDirectives;
1354 resolved.containedItems = area.containedItems;
1355 std::erase_if( resolved.containedItems, [&]( const KIID& id ) { return inactivePins.contains( id ); } );
1356
1357 for( const auto& [id, box] : ownerBoxes )
1358 {
1359 const SHAPE_RECT rectangle( box );
1360
1361 if( area.polygon.Collide( &rectangle ) )
1362 resolved.containedItems.push_back( id );
1363 }
1364
1365 for( const ITEM_TEXT_FACT& text : result.items )
1366 {
1367 if( std::binary_search( area.attachedDirectives.begin(), area.attachedDirectives.end(), text.id ) )
1368 resolved.netclasses.insert( resolved.netclasses.end(), text.netclasses.begin(), text.netclasses.end() );
1369 }
1370
1371 SortUnique( resolved.containedItems );
1372 SortUnique( resolved.netclasses );
1373 result.ruleAreas.push_back( std::move( resolved ) );
1374 }
1375
1376 result.units.assign( units.begin(), units.end() );
1377 std::ranges::sort( result.items, ById );
1378 return result;
1379}
1380} // namespace SCH_CONNECTIVITY
int index
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
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:594
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: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: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
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: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
int GetBodyStyle() const
Definition sch_item.h:253
COMPARE_FLAGS
The list of flags used by various compare functions.
Definition sch_item.h:744
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
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
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:91
wxString GetName() const
Definition sch_sheet.h:142
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:243
Schematic symbol object.
Definition sch_symbol.h:73
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:900
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: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 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)
Replace any environment variable & text variable references with their values.
Definition common.cpp:776
RESOLUTION_CONTEXT
Definition common.h:89
@ FOR_ERC_DRC
Definition common.h:93
@ FOR_NETNAME
Definition common.h:92
@ RESOLVED
Definition common.h:95
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: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)
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".
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
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:166
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708