KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_pads.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 (C) 2025 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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
24
26
27#include <lib_symbol.h>
28#include <page_info.h>
29#include <sch_junction.h>
30#include <sch_label.h>
31#include <sch_line.h>
32#include <sch_pin.h>
33#include <sch_screen.h>
34#include <sch_shape.h>
35#include <sch_sheet.h>
36#include <sch_sheet_path.h>
37#include <sch_symbol.h>
38#include <sch_text.h>
39#include <schematic.h>
40#include <schematic_settings.h>
42
43#include <math/util.h>
44#include <stroke_params.h>
45
46#include <advanced_config.h>
47#include <io/pads/pads_common.h>
48#include <locale_io.h>
49#include <progress_reporter.h>
50#include <reporter.h>
51
52#include <fstream>
53#include <map>
54#include <set>
55#include <wx/filename.h>
56#include <wx/log.h>
57
58
64static std::string extractConnectorPinNumber( const std::string& aRef )
65{
66 size_t sepPos = aRef.rfind( '-' );
67
68 if( sepPos == std::string::npos )
69 sepPos = aRef.rfind( '.' );
70
71 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
72 && std::isdigit( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
73 {
74 return aRef.substr( sepPos + 1 );
75 }
76
77 return "";
78}
79
80
86static std::string extractConnectorBaseRef( const std::string& aRef )
87{
88 size_t sepPos = aRef.rfind( '-' );
89
90 if( sepPos == std::string::npos )
91 sepPos = aRef.rfind( '.' );
92
93 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
94 && std::isdigit( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
95 {
96 return aRef.substr( 0, sepPos );
97 }
98
99 return aRef;
100}
101
102
107static std::string stripGateSuffix( const std::string& aRef )
108{
109 size_t sepPos = aRef.rfind( '-' );
110
111 if( sepPos == std::string::npos )
112 sepPos = aRef.rfind( '.' );
113
114 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
115 && std::isalpha( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
116 {
117 return aRef.substr( 0, sepPos );
118 }
119
120 return aRef;
121}
122
123
124static SCH_TEXT* createSchText( const PADS_SCH::TEXT_ITEM& aText, const VECTOR2I& aPos )
125{
126 SCH_TEXT* schText = new SCH_TEXT( aPos, wxString::FromUTF8( aText.content ) );
127
128 if( aText.height > 0 )
129 {
130 int scaledSize = schIUScale.MilsToIU( aText.height );
131 int charHeight = static_cast<int>( scaledSize
133 int charWidth = static_cast<int>( scaledSize
135 schText->SetTextSize( VECTOR2I( charWidth, charHeight ) );
136 }
137
138 if( aText.width_factor > 0 )
139 schText->SetTextThickness( schIUScale.MilsToIU( aText.width_factor ) );
140
143 PADS_COMMON::DecodeJustification( aText.justification, hJustify, vJustify );
144 schText->SetHorizJustify( hJustify );
145 schText->SetVertJustify( vJustify );
146
147 if( aText.rotation != 0 )
148 schText->SetTextAngleDegrees( aText.rotation * 90.0 );
149
150 return schText;
151}
152
153
161static int computePowerOrientation( const std::string& aOpcId,
162 const std::vector<PADS_SCH::SCH_SIGNAL>& aSignals,
163 const VECTOR2I& aOpcPos, bool aPinUp, int aPageHeightIU )
164{
165 // Find the wire endpoint matching this OPC and get the adjacent vertex
166 std::string opcRef = "@@@O" + aOpcId;
167 VECTOR2I adjPos = aOpcPos;
168 bool found = false;
169
170 for( const auto& signal : aSignals )
171 {
172 for( const auto& wire : signal.wires )
173 {
174 if( wire.vertices.size() < 2 )
175 continue;
176
177 if( wire.endpoint_a == opcRef )
178 {
179 adjPos = VECTOR2I(
180 schIUScale.MilsToIU( KiROUND( wire.vertices[1].x ) ),
181 aPageHeightIU
182 - schIUScale.MilsToIU( KiROUND( wire.vertices[1].y ) ) );
183 found = true;
184 break;
185 }
186
187 if( wire.endpoint_b == opcRef )
188 {
189 size_t last = wire.vertices.size() - 1;
190 adjPos = VECTOR2I(
191 schIUScale.MilsToIU( KiROUND( wire.vertices[last - 1].x ) ),
192 aPageHeightIU
193 - schIUScale.MilsToIU(
194 KiROUND( wire.vertices[last - 1].y ) ) );
195 found = true;
196 break;
197 }
198 }
199
200 if( found )
201 break;
202 }
203
204 if( !found )
206
207 // Wire goes from aOpcPos toward adjPos
208 int dx = adjPos.x - aOpcPos.x;
209 int dy = adjPos.y - aOpcPos.y;
210
211 // Determine which direction the wire approaches from (relative to OPC position).
212 // The symbol body should face AWAY from the wire.
213 // In KiCad Y-down coordinates: dy > 0 means wire goes down from OPC.
214
215 if( std::abs( dx ) >= std::abs( dy ) )
216 {
217 // Horizontal wire
218 if( dx > 0 )
219 {
220 // Wire goes right → body should face left
223 }
224 else
225 {
226 // Wire goes left → body should face right
229 }
230 }
231 else
232 {
233 // Vertical wire
234 if( dy > 0 )
235 {
236 // Wire goes down → body should face up
239 }
240 else
241 {
242 // Wire goes up → body should face down
245 }
246 }
247}
248
249
250SCH_IO_PADS::SCH_IO_PADS() : SCH_IO( wxS( "PADS Logic" ) )
251{
252}
253
254
258
259
260bool SCH_IO_PADS::CanReadSchematicFile( const wxString& aFileName ) const
261{
262 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
263 return false;
264
265 return checkFileHeader( aFileName );
266}
267
268
269bool SCH_IO_PADS::CanReadLibrary( const wxString& aFileName ) const
270{
271 if( !SCH_IO::CanReadLibrary( aFileName ) )
272 return false;
273
274 return checkFileHeader( aFileName );
275}
276
277
278SCH_SHEET* SCH_IO_PADS::LoadSchematicFile( const wxString& aFileName,
279 SCHEMATIC* aSchematic,
280 SCH_SHEET* aAppendToMe,
281 const std::map<std::string, UTF8>* aProperties )
282{
283 wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
284
285 LOCALE_IO setlocale;
286
287 SCH_SHEET* rootSheet = nullptr;
288
289 if( aAppendToMe )
290 {
291 wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
292 rootSheet = aAppendToMe;
293 }
294 else
295 {
296 rootSheet = new SCH_SHEET( aSchematic );
297 rootSheet->SetFileName( aFileName );
298 aSchematic->SetTopLevelSheets( { rootSheet } );
299 }
300
301 if( !rootSheet->GetScreen() )
302 {
303 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
304 screen->SetFileName( aFileName );
305 rootSheet->SetScreen( screen );
306
307 const_cast<KIID&>( rootSheet->m_Uuid ) = screen->GetUuid();
308 }
309
310 SCH_SHEET_PATH rootPath;
311 rootPath.push_back( rootSheet );
312
313 SCH_SCREEN* rootScreen = rootSheet->GetScreen();
314 wxCHECK( rootScreen, nullptr );
315
316 SCH_SHEET_INSTANCE sheetInstance;
317 sheetInstance.m_Path = rootPath.Path();
318 sheetInstance.m_PageNumber = wxT( "#" );
319 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
320
322 m_progressReporter->SetNumPhases( 3 );
323
325 std::string filename( aFileName.ToUTF8() );
326
327 if( !parser.Parse( filename ) )
328 THROW_IO_ERRORF( _( "Failed to parse PADS file: %s" ), aFileName );
329
331 m_progressReporter->BeginPhase( 1 );
332
333 const PADS_SCH::PARAMETERS& params = parser.GetParameters();
334 PADS_SCH::PADS_SCH_SYMBOL_BUILDER symbolBuilder( params );
335 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER schBuilder( params, aSchematic );
336
337 // Detect gate suffix separator from multi-gate part references (e.g. U17-A → '-')
338 for( const auto& part : parser.GetPartPlacements() )
339 {
340 const std::string& ref = part.reference;
341 size_t dashPos = ref.rfind( '-' );
342 size_t dotPos = ref.rfind( '.' );
343 size_t sepPos = std::string::npos;
344
345 if( dashPos != std::string::npos )
346 sepPos = dashPos;
347 else if( dotPos != std::string::npos )
348 sepPos = dotPos;
349
350 if( sepPos != std::string::npos && sepPos + 1 < ref.size()
351 && std::isalpha( static_cast<unsigned char>( ref[sepPos + 1] ) ) )
352 {
353 aSchematic->Settings().m_SubpartIdSeparator = static_cast<int>( ref[sepPos] );
354 aSchematic->Settings().m_SubpartFirstId = 'A';
355 break;
356 }
357 }
358
359 // Set KiCad page size to match the PADS drawing sheet
360 PAGE_INFO pageInfo;
361
362 if( !params.sheet_size.name.empty() )
363 pageInfo.SetType( wxString::FromUTF8( params.sheet_size.name ) );
364 else
365 pageInfo.SetType( PAGE_SIZE_TYPE::A );
366
367 // PADS Y-up to KiCad Y-down: Y_kicad = pageHeight - Y_pads
368 const int pageHeightIU = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
369
370 // Build LIB_SYMBOL objects from all CAEDECAL definitions
371 for( const PADS_SCH::SYMBOL_DEF& symDef : parser.GetSymbolDefs() )
372 symbolBuilder.GetOrCreateSymbol( symDef );
373
374 std::set<int> sheetNumbers = parser.GetSheetNumbers();
375
376 if( sheetNumbers.empty() )
377 sheetNumbers.insert( 1 );
378
379 bool isSingleSheet = ( sheetNumbers.size() == 1 );
380
381 // Map sheet number -> (SCH_SHEET*, SCH_SCREEN*, SCH_SHEET_PATH)
382 struct SheetContext
383 {
384 SCH_SHEET* sheet = nullptr;
385 SCH_SCREEN* screen = nullptr;
387 };
388
389 std::map<int, SheetContext> sheetContexts;
390
391 if( isSingleSheet )
392 {
393 int sheetNum = *sheetNumbers.begin();
394 SheetContext ctx;
395 ctx.sheet = rootSheet;
396 ctx.screen = rootScreen;
397 ctx.path = rootPath;
398 ctx.screen->SetPageSettings( pageInfo );
399 sheetContexts[sheetNum] = ctx;
400 }
401 else
402 {
403 // Multi-sheet: root is a container with sub-sheets
404 int totalSheets = static_cast<int>( sheetNumbers.size() );
405
406 for( int sheetNum : sheetNumbers )
407 {
408 SCH_SHEET* subSheet = schBuilder.CreateHierarchicalSheet(
409 sheetNum, totalSheets, rootSheet, aFileName );
410
411 if( !subSheet )
412 continue;
413
414 // Find the sheet name from parser headers
415 for( const PADS_SCH::SHEET_HEADER& hdr : parser.GetSheetHeaders() )
416 {
417 if( hdr.sheet_num == sheetNum && !hdr.sheet_name.empty() )
418 {
420 wxString::FromUTF8( hdr.sheet_name ) );
421
422 break;
423 }
424 }
425
426 SCH_SHEET_PATH subPath;
427 subPath.push_back( rootSheet );
428 subPath.push_back( subSheet );
429
430 wxString pageNo = wxString::Format( wxT( "%d" ), sheetNum );
431 subPath.SetPageNumber( pageNo );
432
433 SCH_SHEET_INSTANCE subInstance;
434 subInstance.m_Path = subPath.Path();
435 subInstance.m_PageNumber = pageNo;
436 subSheet->GetScreen()->m_sheetInstances.emplace_back( subInstance );
437
438 SheetContext ctx;
439 ctx.sheet = subSheet;
440 ctx.screen = subSheet->GetScreen();
441 ctx.path = subPath;
442 ctx.screen->SetPageSettings( pageInfo );
443 sheetContexts[sheetNum] = ctx;
444 }
445 }
446
448 m_progressReporter->BeginPhase( 2 );
449
450 // Track connector base references for wire-endpoint label creation
451 std::set<std::string> connectorBaseRefs;
452
453 // Pre-scan connector placements to group pins by base reference.
454 // Each group becomes one multi-unit connector symbol in KiCad.
455 struct ConnectorGroup
456 {
457 std::vector<std::string> pinNumbers;
458 std::map<std::string, int> pinToUnit;
459 std::string partType;
460 };
461
462 std::map<std::string, ConnectorGroup> connectorGroups;
463
464 for( const auto& [sheetNum, ctx] : sheetContexts )
465 {
466 std::vector<PADS_SCH::PART_PLACEMENT> sheetParts = parser.GetPartsOnSheet( sheetNum );
467
468 for( const PADS_SCH::PART_PLACEMENT& part : sheetParts )
469 {
470 auto ptIt = parser.GetPartTypes().find( part.part_type );
471
472 if( ptIt == parser.GetPartTypes().end() || !ptIt->second.is_connector )
473 continue;
474
475 std::string pinNum = extractConnectorPinNumber( part.reference );
476
477 if( pinNum.empty() )
478 continue;
479
480 std::string baseRef = extractConnectorBaseRef( part.reference );
481 ConnectorGroup& group = connectorGroups[baseRef];
482 group.partType = part.part_type;
483 group.pinNumbers.push_back( pinNum );
484 }
485 }
486
487 for( auto& [baseRef, group] : connectorGroups )
488 {
489 std::sort( group.pinNumbers.begin(), group.pinNumbers.end(),
490 []( const std::string& a, const std::string& b )
491 {
492 return std::stoi( a ) < std::stoi( b );
493 } );
494
495 for( size_t i = 0; i < group.pinNumbers.size(); i++ )
496 group.pinToUnit[group.pinNumbers[i]] = static_cast<int>( i + 1 );
497 }
498
499 // Place symbols on each sheet
500 for( auto& [sheetNum, ctx] : sheetContexts )
501 {
502 std::vector<PADS_SCH::PART_PLACEMENT> parts = parser.GetPartsOnSheet( sheetNum );
503
504 for( const PADS_SCH::PART_PLACEMENT& part : parts )
505 {
506 auto ptIt = parser.GetPartTypes().find( part.part_type );
507
508 LIB_SYMBOL* libSymbol = nullptr;
509 bool isMultiGate = false;
510 bool isConnector = false;
511 bool isPower = false;
512 std::string libItemName;
513 std::string connectorPinNumber;
514
515 if( ptIt != parser.GetPartTypes().end() )
516 {
517 const PADS_SCH::PARTTYPE_DEF& ptDef = ptIt->second;
518
519 if( ptDef.gates.size() > 1 )
520 {
521 // Multi-gate PARTTYPE: composite multi-unit symbol
522 libSymbol = symbolBuilder.GetOrCreateMultiUnitSymbol(
523 ptDef, parser.GetSymbolDefs() );
524 libItemName = ptDef.name;
525 isMultiGate = true;
526 }
527 else if( !ptDef.gates.empty() )
528 {
529 const PADS_SCH::GATE_DEF& gate = ptDef.gates[0];
530 int idx = std::max( 0, part.gate_index );
531 std::string decalName;
532
533 if( idx < static_cast<int>( gate.decal_names.size() ) )
534 decalName = gate.decal_names[idx];
535 else if( !gate.decal_names.empty() )
536 decalName = gate.decal_names[0];
537
538 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( decalName );
539
540 connectorPinNumber = ptDef.is_connector
541 ? extractConnectorPinNumber( part.reference )
542 : std::string();
543
544 if( symDef && !connectorPinNumber.empty() )
545 {
546 // Multi-unit connector placement (e.g. J12-15 → unit of J12).
547 // All pins of the same connector share one multi-unit symbol.
548 std::string baseRef = extractConnectorBaseRef( part.reference );
549 auto groupIt = connectorGroups.find( baseRef );
550
551 if( groupIt != connectorGroups.end() )
552 {
553 std::string cacheKey = ptDef.name + ":conn:" + baseRef;
554
555 libSymbol = symbolBuilder.GetOrCreateMultiUnitConnectorSymbol(
556 ptDef, *symDef, groupIt->second.pinNumbers, cacheKey );
557 libItemName = ptDef.name + "_" + baseRef;
558 isConnector = true;
559 isMultiGate = true;
560
561 connectorBaseRefs.insert( baseRef );
562 }
563 }
564 else if( symDef )
565 {
566 libSymbol = symbolBuilder.GetOrCreatePartTypeSymbol( ptDef, *symDef );
567 libItemName = decalName;
568 }
569 }
570 else if( !ptDef.special_variants.empty() )
571 {
572 // Power/ground symbols
573 int idx = std::max( 0, part.gate_index );
574 idx = std::min( idx, static_cast<int>( ptDef.special_variants.size() ) - 1 );
575 std::string decalName = ptDef.special_variants[idx].decal_name;
576
577 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( decalName );
578
579 if( symDef )
580 {
581 libSymbol = symbolBuilder.GetOrCreateSymbol( *symDef );
582 libItemName = decalName;
583 }
584 }
585
586 if( !ptDef.special_keyword.empty() && ptDef.special_keyword != "OFF" )
587 isPower = true;
588 }
589
590 // Fallback: resolve directly by CAEDECAL name
591 if( !libSymbol )
592 {
593 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( part.symbol_name );
594
595 if( !symDef )
596 {
597 m_errorMessages.emplace(
598 wxString::Format( wxT( "PADS Import: symbol '%s' not found,"
599 " part '%s' skipped" ),
600 wxString::FromUTF8( part.symbol_name ),
601 wxString::FromUTF8( part.reference ) ),
603 continue;
604 }
605
606 libSymbol = symbolBuilder.GetOrCreateSymbol( *symDef );
607 libItemName = symDef->name;
608 }
609
610 if( !libSymbol )
611 continue;
612
613 if( ptIt != parser.GetPartTypes().end() && !ptIt->second.sigpins.empty() )
614 symbolBuilder.AddHiddenPowerPins( libSymbol, ptIt->second.sigpins );
615
616 if( !isPower )
617 isPower = PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( part.part_type );
618
619 // Resolve power symbol style. Prefer the PARTTYPE variant decal style
620 // (e.g. +BUBBLE → +VDC) which preserves the original PADS symbol shape,
621 // falling back to net-name matching (e.g. GND → GND, +5V → +5V).
622 std::string powerStyle;
623
624 if( isPower && ptIt != parser.GetPartTypes().end()
625 && !ptIt->second.special_variants.empty() )
626 {
627 int varIdx = std::max( 0, part.gate_index );
628 varIdx = std::min(
629 varIdx,
630 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
631 const auto& variant = ptIt->second.special_variants[varIdx];
632
634 variant.decal_name, variant.pin_type );
635 }
636
637 if( isPower && powerStyle.empty() )
638 {
639 std::string rawNetName = part.power_net_name.empty()
640 ? part.symbol_name
641 : part.power_net_name;
642
643 auto powerLibId =
645
646 if( powerLibId )
647 powerStyle = std::string( powerLibId->GetLibItemName().c_str() );
648 }
649
650 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
651 SCH_SYMBOL* symbol = symbolPtr.get();
652 LIB_SYMBOL* instanceSymbol = nullptr;
653
654 if( isPower && !powerStyle.empty() )
655 {
656 instanceSymbol = symbolBuilder.BuildKiCadPowerSymbol( powerStyle );
657
658 LIB_ID libId;
659 libId.SetLibNickname( wxT( "power" ) );
660 libId.SetLibItemName( wxString::FromUTF8( powerStyle ) );
661 symbol->SetLibId( libId );
662 }
663 else
664 {
665 LIB_ID libId;
666 libId.SetLibNickname( wxT( "pads_import" ) );
667 libId.SetLibItemName( wxString::FromUTF8( libItemName ) );
668 symbol->SetLibId( libId );
669
670 instanceSymbol = new LIB_SYMBOL( *libSymbol );
671
672 if( isPower )
673 instanceSymbol->SetGlobalPower();
674 }
675
676 symbol->SetLibSymbol( instanceSymbol );
677 symbol->SetPosition( VECTOR2I(
678 schIUScale.MilsToIU( KiROUND( part.position.x ) ),
679 pageHeightIU - schIUScale.MilsToIU( KiROUND( part.position.y ) ) ) );
680
681 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
682
683 if( part.rotation == 90.0 )
685 else if( part.rotation == 180.0 )
687 else if( part.rotation == 270.0 )
689
690 if( part.mirror_flags & 1 )
692
693 if( part.mirror_flags & 2 )
695
696 symbol->SetOrientation( orientation );
697
698 if( isConnector && !connectorPinNumber.empty() )
699 {
700 std::string baseRef = extractConnectorBaseRef( part.reference );
701 auto groupIt = connectorGroups.find( baseRef );
702
703 if( groupIt != connectorGroups.end() )
704 {
705 auto unitIt = groupIt->second.pinToUnit.find( connectorPinNumber );
706
707 if( unitIt != groupIt->second.pinToUnit.end() )
708 symbol->SetUnit( unitIt->second );
709 else
710 symbol->SetUnit( 1 );
711 }
712 else
713 {
714 symbol->SetUnit( 1 );
715 }
716 }
717 else if( isMultiGate )
718 {
719 symbol->SetUnit( part.gate_index + 1 );
720 }
721 else
722 {
723 symbol->SetUnit( 1 );
724 }
725
726 // Assign deterministic UUID so PCB cross-probe can match footprints
727 // to symbols. Only the primary gate (index 0) or the first connector
728 // pin gets the deterministic UUID since one footprint maps to one
729 // symbol instance.
730 bool isPrimaryUnit = isConnector
731 ? ( symbol->GetUnit() == 1 )
732 : ( !isMultiGate || part.gate_index == 0 );
733
734 if( !isPower && isPrimaryUnit )
735 {
736 std::string baseRef = isConnector
737 ? extractConnectorBaseRef( part.reference )
738 : stripGateSuffix( part.reference );
739
740 const_cast<KIID&>( symbol->m_Uuid ) =
742 }
743
744 symbol->SetRef( &ctx.path, wxString::FromUTF8( part.reference ) );
745
746 schBuilder.ApplyPartAttributes( symbol, part );
747 schBuilder.CreateCustomFields( symbol, part );
748
749 // For connectors, override reference to the base (e.g. "J12" not "J12-1").
750 // Must happen after ApplyPartAttributes which only strips alpha suffixes.
751 if( isConnector )
752 {
753 std::string baseRef = extractConnectorBaseRef( part.reference );
754 symbol->SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
755 }
756
757 // For multi-gate parts, strip the alpha gate suffix (e.g. "U1-A" → "U1")
758 // so KiCad recognizes all units as belonging to the same part.
759 if( isMultiGate && !isConnector )
760 {
761 std::string baseRef = stripGateSuffix( part.reference );
762 symbol->SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
763 }
764
765 // For passive components, override Value with VALUE1 parametric value
766 // so that e.g. C10 shows "0.1uF" instead of the generic "CAPMF0805".
767 // Also apply the VALUE1 attribute position.
768 if( ptIt != parser.GetPartTypes().end() )
769 {
770 const std::string& cat = ptIt->second.category;
771
772 if( cat == "CAP" || cat == "RES" || cat == "IND" )
773 {
774 auto valIt = part.attr_overrides.find( "VALUE" );
775
776 if( valIt == part.attr_overrides.end() )
777 valIt = part.attr_overrides.find( "VALUE1" );
778
779 if( valIt != part.attr_overrides.end() && !valIt->second.empty() )
780 {
781 symbol->SetValueFieldText( wxString::FromUTF8( valIt->second ) );
782
783 for( const auto& attr : part.attributes )
784 {
785 if( attr.name == "VALUE" || attr.name == "VALUE1"
786 || attr.name == "Value1" )
787 {
788 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
789 int fx = schIUScale.MilsToIU( KiROUND( attr.position.x ) );
790
791 if( part.mirror_flags & 1 )
792 fx = -fx;
793
794 VECTOR2I fieldPos( fx,
795 -schIUScale.MilsToIU(
796 KiROUND( attr.position.y ) ) );
797 valField->SetPosition( symbol->GetPosition() + fieldPos );
798
799 int fieldTextSize = schIUScale.MilsToIU( 50 );
800 valField->SetTextSize(
801 VECTOR2I( fieldTextSize, fieldTextSize ) );
802
803 // Keep the PADS-authored alignment instead of forcing
804 // center; otherwise this override undoes the justification
805 // applied by ApplyFieldSettings.
808 PADS_COMMON::DecodeJustification( attr.justification,
809 hJustify, vJustify );
810
811 if( part.mirror_flags & 1 )
812 hJustify = GetFlippedAlignment( hJustify );
813
814 valField->SetHorizJustify( hJustify );
815 valField->SetVertJustify( vJustify );
816 break;
817 }
818 }
819 }
820 }
821 }
822
823 if( isPower )
824 {
825 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
826
827 wxString netName = part.power_net_name.empty()
828 ? wxString::FromUTF8( part.symbol_name )
829 : wxString::FromUTF8( part.power_net_name );
830
831 if( netName.StartsWith( wxT( "/" ) ) )
832 netName = wxT( "~{" ) + netName.Mid( 1 ) + wxT( "}" );
833
834 symbol->GetField( FIELD_T::VALUE )->SetText( netName );
835 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
836 }
837
838 {
839 std::string hierRef;
840
841 if( isConnector )
842 hierRef = extractConnectorBaseRef( part.reference );
843 else if( isMultiGate )
844 hierRef = stripGateSuffix( part.reference );
845 else
846 hierRef = part.reference;
847
848 symbol->AddHierarchicalReference( ctx.path.Path(),
849 wxString::FromUTF8( hierRef ),
850 symbol->GetUnit() );
851 }
852
853 symbol->ClearFlags();
854
855 // For connector pins, create a local label at the pin position
856 // before transferring ownership to the screen.
857 // The matching label at the wire endpoint creates the electrical connection.
858 if( isConnector && !connectorPinNumber.empty() )
859 {
860 std::string baseRef = extractConnectorBaseRef( part.reference );
861 wxString labelText = wxString::Format( wxT( "%s.%s" ),
862 wxString::FromUTF8( baseRef ),
863 wxString::FromUTF8( connectorPinNumber ) );
864
865 VECTOR2I pinPos = symbol->GetPosition();
866 std::vector<SCH_PIN*> pins = symbol->GetPins();
867
868 if( !pins.empty() )
869 pinPos = pins[0]->GetPosition();
870
871 SCH_LABEL* label = new SCH_LABEL( pinPos, labelText );
872 int labelSize = schIUScale.MilsToIU( 50 );
873 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
875 label->SetFlags( IS_NEW );
876 ctx.screen->Append( label );
877 }
878
879 ctx.screen->Append( symbolPtr.release() );
880 }
881 }
882
883 // Build set of power signal names so we can suppress duplicate global labels
884 // where a power symbol is placed instead. Non-power signal labels are handled
885 // by CreateNetLabels which places them at dangling wire endpoints.
886 std::set<std::string> powerSignalNames;
887
888 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
889 {
890 if( opc.signal_name.empty() )
891 continue;
892
893 auto ptIt = parser.GetPartTypes().find( opc.symbol_lib );
894
895 if( ptIt != parser.GetPartTypes().end()
896 && !ptIt->second.special_keyword.empty() && ptIt->second.special_keyword != "OFF"
897 && !ptIt->second.special_variants.empty() )
898 {
899 int idx = std::max( 0, opc.flags2 );
900 idx = std::min( idx,
901 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
902 const auto& variant = ptIt->second.special_variants[idx];
903
905 variant.decal_name, variant.pin_type ).empty() )
906 {
907 powerSignalNames.insert( opc.signal_name );
908 }
909 }
910 }
911
912 // Build set of OPC reference IDs for non-power signal OPCs. Each entry
913 // corresponds to a wire endpoint reference like "@@@O48" that should receive
914 // its own global label with orientation derived from the wire direction.
915 std::set<std::string> signalOpcIds;
916
917 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
918 {
919 if( opc.signal_name.empty() || powerSignalNames.count( opc.signal_name ) )
920 continue;
921
922 signalOpcIds.insert( "@@@O" + std::to_string( opc.id ) );
923 }
924
925 // Map each off-page anchor (@@@O..) to its *NETNAMES* entry. PADS authors the label
926 // side there, which is the authoritative orientation when the stub wire is degenerate.
927 std::map<std::string, PADS_SCH::NETNAME_LABEL> netNameLabels;
928
929 for( const PADS_SCH::NETNAME_LABEL& nn : parser.GetNetNameLabels() )
930 {
931 if( !nn.anchor_ref.empty() )
932 netNameLabels[nn.anchor_ref] = nn;
933 }
934
935 // Create wires and connectivity on each sheet
936 for( auto& [sheetNum, ctx] : sheetContexts )
937 {
938 std::vector<PADS_SCH::SCH_SIGNAL> sheetSignals = parser.GetSignalsOnSheet( sheetNum );
939
940 // Create wire segments from vertex data
941 for( const PADS_SCH::SCH_SIGNAL& signal : sheetSignals )
942 {
943 for( const PADS_SCH::WIRE_SEGMENT& wire : signal.wires )
944 {
945 if( wire.vertices.size() < 2 )
946 continue;
947
948 // Each consecutive pair of vertices becomes a wire segment
949 for( size_t v = 0; v + 1 < wire.vertices.size(); v++ )
950 {
951 VECTOR2I start(
952 schIUScale.MilsToIU( KiROUND( wire.vertices[v].x ) ),
953 pageHeightIU
954 - schIUScale.MilsToIU( KiROUND( wire.vertices[v].y ) ) );
956 schIUScale.MilsToIU( KiROUND( wire.vertices[v + 1].x ) ),
957 pageHeightIU
958 - schIUScale.MilsToIU( KiROUND( wire.vertices[v + 1].y ) ) );
959
960 if( start == end )
961 continue;
962
963 SCH_LINE* line = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
964 line->SetEndPoint( end );
965 line->SetConnectivityDirty();
966 ctx.screen->Append( line );
967 }
968 }
969 }
970
971 // Create local labels at wire endpoints that reference connector pins
972 for( const PADS_SCH::SCH_SIGNAL& signal : sheetSignals )
973 {
974 for( const PADS_SCH::WIRE_SEGMENT& wire : signal.wires )
975 {
976 if( wire.vertices.size() < 2 )
977 continue;
978
979 // Check endpoint_a for connector pin reference
980 if( wire.endpoint_a.find( '.' ) != std::string::npos
981 && wire.endpoint_a.find( "@@@" ) == std::string::npos )
982 {
983 size_t dotPos = wire.endpoint_a.find( '.' );
984 std::string ref = wire.endpoint_a.substr( 0, dotPos );
985
986 if( connectorBaseRefs.count( ref ) )
987 {
988 const auto& vtx = wire.vertices.front();
989 VECTOR2I pos(
990 schIUScale.MilsToIU( KiROUND( vtx.x ) ),
991 pageHeightIU - schIUScale.MilsToIU( KiROUND( vtx.y ) ) );
992
993 // Compute label orientation from adjacent vertex
994 const auto& adj = wire.vertices[1];
995 VECTOR2I adjPos(
996 schIUScale.MilsToIU( KiROUND( adj.x ) ),
997 pageHeightIU - schIUScale.MilsToIU( KiROUND( adj.y ) ) );
998
999 int dx = adjPos.x - pos.x;
1000 int dy = adjPos.y - pos.y;
1002
1003 if( std::abs( dx ) >= std::abs( dy ) )
1004 orient = ( dx > 0 ) ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
1005 else
1006 orient = ( dy > 0 ) ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
1007
1008 wxString labelText = wxString::FromUTF8( wire.endpoint_a );
1009 SCH_LABEL* label = new SCH_LABEL( pos, labelText );
1010 int labelSize = schIUScale.MilsToIU( 50 );
1011 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
1012 label->SetSpinStyle( orient );
1013 label->SetFlags( IS_NEW );
1014 ctx.screen->Append( label );
1015 }
1016 }
1017
1018 // Check endpoint_b for connector pin reference
1019 if( wire.endpoint_b.find( '.' ) != std::string::npos
1020 && wire.endpoint_b.find( "@@@" ) == std::string::npos )
1021 {
1022 size_t dotPos = wire.endpoint_b.find( '.' );
1023 std::string ref = wire.endpoint_b.substr( 0, dotPos );
1024
1025 if( connectorBaseRefs.count( ref ) )
1026 {
1027 const auto& vtx = wire.vertices.back();
1028 VECTOR2I pos(
1029 schIUScale.MilsToIU( KiROUND( vtx.x ) ),
1030 pageHeightIU - schIUScale.MilsToIU( KiROUND( vtx.y ) ) );
1031
1032 // Compute label orientation from adjacent vertex
1033 size_t lastIdx = wire.vertices.size() - 1;
1034 const auto& adj = wire.vertices[lastIdx - 1];
1035 VECTOR2I adjPos(
1036 schIUScale.MilsToIU( KiROUND( adj.x ) ),
1037 pageHeightIU - schIUScale.MilsToIU( KiROUND( adj.y ) ) );
1038
1039 int dx = adjPos.x - pos.x;
1040 int dy = adjPos.y - pos.y;
1042
1043 if( std::abs( dx ) >= std::abs( dy ) )
1044 orient = ( dx > 0 ) ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
1045 else
1046 orient = ( dy > 0 ) ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
1047
1048 wxString labelText = wxString::FromUTF8( wire.endpoint_b );
1049 SCH_LABEL* label = new SCH_LABEL( pos, labelText );
1050 int labelSize = schIUScale.MilsToIU( 50 );
1051 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
1052 label->SetSpinStyle( orient );
1053 label->SetFlags( IS_NEW );
1054 ctx.screen->Append( label );
1055 }
1056 }
1057 }
1058 }
1059
1060 // Create junctions from TIEDOTS for this sheet
1061 for( const PADS_SCH::TIED_DOT& dot : parser.GetTiedDots() )
1062 {
1063 if( dot.sheet_number != sheetNum )
1064 continue;
1065
1066 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( dot.position.x ) ),
1067 pageHeightIU - schIUScale.MilsToIU( KiROUND( dot.position.y ) ) );
1068
1069 SCH_JUNCTION* junction = new SCH_JUNCTION( pos );
1070 ctx.screen->Append( junction );
1071 }
1072
1073 // Create net labels, skipping power nets that get dedicated symbols
1074 schBuilder.CreateNetLabels( sheetSignals, ctx.screen, signalOpcIds,
1075 powerSignalNames, netNameLabels );
1076
1077 // Place off-page connectors: power/ground types become SCH_SYMBOL with
1078 // KiCad standard power graphics; signal types become SCH_GLOBALLABEL.
1079 int pwrIndex = 1;
1080
1081 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
1082 {
1083 if( opc.source_sheet != sheetNum )
1084 continue;
1085
1086 if( opc.signal_name.empty() )
1087 continue;
1088
1089 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( opc.position.x ) ),
1090 pageHeightIU
1091 - schIUScale.MilsToIU( KiROUND( opc.position.y ) ) );
1092
1093 // Resolve power style from the PARTTYPE variant definition
1094 std::string powerStyle;
1095 auto opcPtIt = parser.GetPartTypes().find( opc.symbol_lib );
1096
1097 if( opcPtIt != parser.GetPartTypes().end()
1098 && !opcPtIt->second.special_keyword.empty() && opcPtIt->second.special_keyword != "OFF"
1099 && !opcPtIt->second.special_variants.empty() )
1100 {
1101 int idx = std::max( 0, opc.flags2 );
1102 idx = std::min( idx,
1103 static_cast<int>(
1104 opcPtIt->second.special_variants.size() ) - 1 );
1105 const auto& variant = opcPtIt->second.special_variants[idx];
1106
1108 variant.decal_name, variant.pin_type );
1109 }
1110
1111 if( !powerStyle.empty() )
1112 {
1113 LIB_SYMBOL* pwrSym = symbolBuilder.BuildKiCadPowerSymbol( powerStyle );
1114
1115 if( pwrSym )
1116 {
1117 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
1118 SCH_SYMBOL* symbol = symbolPtr.get();
1119
1120 LIB_ID libId;
1121 libId.SetLibNickname( wxT( "power" ) );
1122 libId.SetLibItemName( wxString::FromUTF8( powerStyle ) );
1123 symbol->SetLibId( libId );
1124 symbol->SetLibSymbol( pwrSym );
1125 symbol->SetPosition( pos );
1126 symbol->SetUnit( 1 );
1127
1128 // VCC and PWR_TRIANGLE have pin pointing up (body above pin).
1129 // All others (GND, GNDD, PWR_BAR, VEE, Earth) have pin pointing down.
1130 bool pinUp = ( powerStyle == "VCC" || powerStyle == "PWR_TRIANGLE" );
1131 int orient = computePowerOrientation(
1132 std::to_string( opc.id ), sheetSignals, pos, pinUp,
1133 pageHeightIU );
1134
1135 symbol->SetOrientation( orient );
1136
1137 wxString netName = wxString::FromUTF8( opc.signal_name );
1138
1139 if( netName.StartsWith( wxT( "/" ) ) )
1140 netName = wxT( "~{" ) + netName.Mid( 1 ) + wxT( "}" );
1141
1142 symbol->GetField( FIELD_T::VALUE )->SetText( netName );
1143 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
1144
1145 wxString pwrRef = wxString::Format( wxT( "#PWR%03d" ), pwrIndex++ );
1146 symbol->SetRef( &ctx.path, pwrRef );
1147 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1148
1149 symbol->ClearFlags();
1150 ctx.screen->Append( symbolPtr.release() );
1151 continue;
1152 }
1153 }
1154
1155 // Non-power signal OPCs don't create labels here. CreateNetLabels
1156 // handles all signal net labels, placing them at dangling wire endpoints
1157 // rather than at OPC positions (which may not land on a wire).
1158 }
1159 }
1160
1161 // Resolve a parsed item's sheet number to its screen, falling back to the
1162 // first sheet when the number is unknown. Each *SHT* section in PADS Logic
1163 // is followed by its own *TEXT* and *LINES* blocks, so items are tagged with
1164 // the sheet number current at parse time.
1165 auto screenForSheet =
1166 [&]( int aSheetNumber ) -> SCH_SCREEN*
1167 {
1168 auto ctxIt = sheetContexts.find( aSheetNumber );
1169
1170 return ctxIt != sheetContexts.end() ? ctxIt->second.screen
1171 : sheetContexts.begin()->second.screen;
1172 };
1173
1174 // Place free text items from *TEXT* section on the correct sheet.
1175 if( !sheetContexts.empty() )
1176 {
1177 for( const PADS_SCH::TEXT_ITEM& textItem : parser.GetTextItems() )
1178 {
1179 if( textItem.content.empty() )
1180 continue;
1181
1182 SCH_SCREEN* textScreen = screenForSheet( textItem.sheet_number );
1183
1184 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( textItem.position.x ) ),
1185 pageHeightIU
1186 - schIUScale.MilsToIU( KiROUND( textItem.position.y ) ) );
1187
1188 textScreen->Append( createSchText( textItem, pos ) );
1189 }
1190 }
1191
1192 // Place graphic lines from *LINES* section (skip the border template) on
1193 // the sheet they belong to.
1194 if( !sheetContexts.empty() )
1195 {
1196 for( const PADS_SCH::LINES_ITEM& linesItem : parser.GetLinesItems() )
1197 {
1198 if( linesItem.name == params.border_template )
1199 continue;
1200
1201 SCH_SCREEN* linesScreen = screenForSheet( linesItem.sheet_number );
1202
1203 double ox = linesItem.origin.x;
1204 double oy = linesItem.origin.y;
1205
1206 for( const PADS_SCH::SYMBOL_GRAPHIC& prim : linesItem.primitives )
1207 {
1208 int strokeWidth = prim.line_width > 0.0
1209 ? schIUScale.MilsToIU( KiROUND( prim.line_width ) )
1210 : 0;
1211
1213
1215 {
1217 schIUScale.MilsToIU( KiROUND( ox + prim.center.x ) ),
1218 pageHeightIU
1219 - schIUScale.MilsToIU( KiROUND( oy + prim.center.y ) ) );
1220 int radius = schIUScale.MilsToIU( KiROUND( prim.radius ) );
1221
1223 circle->SetStart( center );
1224 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1225 circle->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1226
1227 if( prim.filled )
1228 circle->SetFillMode( FILL_T::FILLED_SHAPE );
1229
1230 linesScreen->Append( circle );
1231 }
1233 && prim.points.size() == 2 )
1234 {
1235 VECTOR2I pos(
1236 schIUScale.MilsToIU( KiROUND( ox + prim.points[0].coord.x ) ),
1237 pageHeightIU
1238 - schIUScale.MilsToIU(
1239 KiROUND( oy + prim.points[0].coord.y ) ) );
1240 VECTOR2I end(
1241 schIUScale.MilsToIU( KiROUND( ox + prim.points[1].coord.x ) ),
1242 pageHeightIU
1243 - schIUScale.MilsToIU(
1244 KiROUND( oy + prim.points[1].coord.y ) ) );
1245
1246 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
1247 rect->SetPosition( pos );
1248 rect->SetEnd( end );
1249 rect->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1250
1251 if( prim.filled )
1253
1254 linesScreen->Append( rect );
1255 }
1256 else if( prim.points.size() >= 2 )
1257 {
1258 for( size_t p = 0; p + 1 < prim.points.size(); p++ )
1259 {
1260 VECTOR2I start(
1261 schIUScale.MilsToIU(
1262 KiROUND( ox + prim.points[p].coord.x ) ),
1263 pageHeightIU
1264 - schIUScale.MilsToIU(
1265 KiROUND( oy + prim.points[p].coord.y ) ) );
1266 VECTOR2I end(
1267 schIUScale.MilsToIU(
1268 KiROUND( ox + prim.points[p + 1].coord.x ) ),
1269 pageHeightIU
1270 - schIUScale.MilsToIU(
1271 KiROUND( oy + prim.points[p + 1].coord.y ) ) );
1272
1273 if( start == end )
1274 continue;
1275
1276 if( prim.points[p].arc.has_value() )
1277 {
1278 const PADS_SCH::ARC_DATA& ad = *prim.points[p].arc;
1279 double cx = ( ad.bbox_x1 + ad.bbox_x2 ) / 2.0;
1280 double cy = ( ad.bbox_y1 + ad.bbox_y2 ) / 2.0;
1282 schIUScale.MilsToIU( KiROUND( ox + cx ) ),
1283 pageHeightIU
1284 - schIUScale.MilsToIU( KiROUND( oy + cy ) ) );
1285
1286 double sx = start.x - center.x;
1287 double sy = start.y - center.y;
1288 double ex = end.x - center.x;
1289 double ey = end.y - center.y;
1290 double radius = std::sqrt( sx * sx + sy * sy );
1291
1292 double mx = sx + ex;
1293 double my = sy + ey;
1294 double mlen = std::sqrt( mx * mx + my * my );
1295
1296 VECTOR2I midPt;
1297
1298 if( mlen > 0.001 )
1299 {
1300 midPt.x = center.x
1301 + static_cast<int>( radius * mx / mlen );
1302 midPt.y = center.y
1303 + static_cast<int>( radius * my / mlen );
1304 }
1305 else
1306 {
1307 midPt.x = center.x
1308 + static_cast<int>( -sy * radius
1309 / std::max( radius, 1.0 ) );
1310 midPt.y = center.y
1311 + static_cast<int>( sx * radius
1312 / std::max( radius, 1.0 ) );
1313 }
1314
1315 if( ad.angle < 0 )
1316 {
1317 midPt.x = 2 * center.x - midPt.x;
1318 midPt.y = 2 * center.y - midPt.y;
1319 }
1320
1321 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
1322 arc->SetArcGeometry( start, midPt, end );
1323 arc->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1324
1325 if( prim.filled )
1327
1328 linesScreen->Append( arc );
1329 }
1330 else
1331 {
1332 SCH_LINE* line = new SCH_LINE(
1334 line->SetEndPoint( end );
1335 line->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1336 linesScreen->Append( line );
1337 }
1338 }
1339 }
1340 }
1341
1342 // Render text items within this LINES group
1343 for( const PADS_SCH::TEXT_ITEM& textItem : linesItem.texts )
1344 {
1345 if( textItem.content.empty() )
1346 continue;
1347
1348 VECTOR2I pos(
1349 schIUScale.MilsToIU( KiROUND( ox + textItem.position.x ) ),
1350 pageHeightIU
1351 - schIUScale.MilsToIU( KiROUND( oy + textItem.position.y ) ) );
1352
1353 linesScreen->Append( createSchText( textItem, pos ) );
1354 }
1355 }
1356 }
1357
1358 // Set title block from parsed parameters
1359 schBuilder.CreateTitleBlock( rootScreen );
1360
1361 // Finalize all sheets
1362 SCH_SCREENS allSheets( rootSheet );
1363 allSheets.UpdateSymbolLinks();
1364 allSheets.ClearEditFlags();
1365
1366 if( m_reporter )
1367 {
1368 for( const auto& [msg, severity] : m_errorMessages )
1369 m_reporter->Report( msg, severity );
1370 }
1371
1372 m_errorMessages.clear();
1373
1374 return rootSheet;
1375}
1376
1377
1378void SCH_IO_PADS::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
1379 const wxString& aLibraryPath,
1380 const std::map<std::string, UTF8>* aProperties )
1381{
1382 ensureLoadedLibrary( aLibraryPath );
1383
1384 bool powerSymbolsOnly = aProperties
1385 && aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly );
1386
1387 for( const auto& [name, symbol] : m_librarySymbols )
1388 {
1389 if( powerSymbolsOnly && !symbol->IsPower() )
1390 continue;
1391
1392 aSymbolNameList.Add( name );
1393 }
1394}
1395
1396
1397void SCH_IO_PADS::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
1398 const wxString& aLibraryPath,
1399 const std::map<std::string, UTF8>* aProperties )
1400{
1401 ensureLoadedLibrary( aLibraryPath );
1402
1403 bool powerSymbolsOnly = aProperties
1404 && aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly );
1405
1406 for( const auto& [name, symbol] : m_librarySymbols )
1407 {
1408 if( powerSymbolsOnly && !symbol->IsPower() )
1409 continue;
1410
1411 aSymbolList.push_back( symbol.get() );
1412 }
1413}
1414
1415
1416LIB_SYMBOL* SCH_IO_PADS::LoadSymbol( const wxString& aLibraryPath, const wxString& aPartName,
1417 const std::map<std::string, UTF8>* aProperties )
1418{
1419 ensureLoadedLibrary( aLibraryPath );
1420
1421 auto it = m_librarySymbols.find( aPartName );
1422
1423 if( it != m_librarySymbols.end() )
1424 return it->second.get();
1425
1426 return nullptr;
1427}
1428
1429
1430long long SCH_IO_PADS::getLibraryTimestamp( const wxString& aLibraryPath ) const
1431{
1432 wxFileName fn( aLibraryPath );
1433
1434 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
1435 return fn.GetModificationTime().GetValue().GetValue();
1436
1437 return 0;
1438}
1439
1440
1441void SCH_IO_PADS::ensureLoadedLibrary( const wxString& aLibraryPath )
1442{
1443 long long timestamp = getLibraryTimestamp( aLibraryPath );
1444
1445 if( m_libraryCacheValid && aLibraryPath == m_cachedLibraryPath
1446 && timestamp == m_cachedLibraryTimestamp )
1447 {
1448 return;
1449 }
1450
1451 m_librarySymbols.clear();
1452 m_libraryCacheValid = false;
1453 m_cachedLibraryPath = aLibraryPath;
1454 m_cachedLibraryTimestamp = timestamp;
1455
1456 if( !checkFileHeader( aLibraryPath ) )
1457 THROW_IO_ERRORF( _( "'%s' is not a PADS Logic ASCII file." ), aLibraryPath );
1458
1459 LOCALE_IO setlocale;
1460
1462 std::string filename( aLibraryPath.ToUTF8() );
1463
1464 if( !parser.Parse( filename ) )
1465 THROW_IO_ERRORF( _( "Failed to parse PADS Logic file '%s'." ), aLibraryPath );
1466
1467 const PADS_SCH::PARAMETERS& params = parser.GetParameters();
1468 PADS_SCH::PADS_SCH_SYMBOL_BUILDER symbolBuilder( params );
1469
1470 std::set<std::string> referencedDecals;
1471
1472 // Build a LIB_SYMBOL per PARTTYPE: multi-gate parts become multi-unit symbols,
1473 // single-gate parts apply PARTTYPE pin overrides to the CAEDECAL graphics,
1474 // and power/ground special variants are skipped (they map to KiCad power lib).
1475 for( const auto& [ptName, ptDef] : parser.GetPartTypes() )
1476 {
1477 if( !ptDef.special_keyword.empty() && ptDef.special_keyword != "OFF" )
1478 continue;
1479
1480 LIB_SYMBOL* built = nullptr;
1481 wxString libName = wxString::FromUTF8( ptDef.name );
1482
1483 if( ptDef.gates.size() > 1 )
1484 {
1485 built = symbolBuilder.BuildMultiUnitSymbol( ptDef, parser.GetSymbolDefs() );
1486
1487 for( const PADS_SCH::GATE_DEF& gate : ptDef.gates )
1488 {
1489 for( const std::string& decalName : gate.decal_names )
1490 referencedDecals.insert( decalName );
1491 }
1492 }
1493 else if( !ptDef.gates.empty() )
1494 {
1495 const PADS_SCH::GATE_DEF& gate = ptDef.gates[0];
1496 std::string decalName;
1497
1498 if( !gate.decal_names.empty() )
1499 decalName = gate.decal_names[0];
1500
1501 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( decalName );
1502
1503 if( symDef && ptDef.is_connector && !gate.pins.empty() )
1504 {
1505 // Connectors declare one CAEDECAL shared by every pin. Build a
1506 // multi-unit library symbol so each PARTTYPE pin is representable
1507 // without assuming a particular schematic placement grouping.
1508 std::vector<std::string> pinNumbers;
1509 pinNumbers.reserve( gate.pins.size() );
1510
1511 for( const PADS_SCH::PARTTYPE_PIN& pin : gate.pins )
1512 pinNumbers.push_back( pin.pin_id );
1513
1514 built = symbolBuilder.BuildMultiUnitConnectorSymbol( ptDef, *symDef, pinNumbers );
1515 referencedDecals.insert( decalName );
1516 }
1517 else if( symDef )
1518 {
1519 // GetOrCreatePartTypeSymbol caches inside the builder and returns a
1520 // non-owning pointer; clone it so the library owns its own copy.
1521 LIB_SYMBOL* cached = symbolBuilder.GetOrCreatePartTypeSymbol( ptDef, *symDef );
1522
1523 if( cached )
1524 built = new LIB_SYMBOL( *cached );
1525
1526 referencedDecals.insert( decalName );
1527 }
1528 }
1529
1530 if( !built )
1531 continue;
1532
1533 built->SetName( libName );
1534
1535 if( !ptDef.sigpins.empty() )
1536 symbolBuilder.AddHiddenPowerPins( built, ptDef.sigpins );
1537
1538 m_librarySymbols[libName] = std::unique_ptr<LIB_SYMBOL>( built );
1539 }
1540
1541 // Also expose any CAEDECAL entries that no PARTTYPE referenced, so the user
1542 // still sees orphan decal graphics that ship with the PADS library.
1543 for( const PADS_SCH::SYMBOL_DEF& symDef : parser.GetSymbolDefs() )
1544 {
1545 if( referencedDecals.count( symDef.name ) )
1546 continue;
1547
1548 wxString libName = wxString::FromUTF8( symDef.name );
1549
1550 if( libName.IsEmpty() || m_librarySymbols.count( libName ) )
1551 continue;
1552
1553 LIB_SYMBOL* built = symbolBuilder.BuildSymbol( symDef );
1554
1555 if( !built )
1556 continue;
1557
1558 m_librarySymbols[libName] = std::unique_ptr<LIB_SYMBOL>( built );
1559 }
1560
1561 m_libraryCacheValid = true;
1562}
1563
1564
1565bool SCH_IO_PADS::checkFileHeader( const wxString& aFileName ) const
1566{
1567 try
1568 {
1569 std::ifstream file( aFileName.fn_str() );
1570
1571 if( !file.is_open() )
1572 return false;
1573
1574 std::string line;
1575
1576 if( std::getline( file, line ) )
1577 {
1578 if( line.find( "*PADS-POWERLOGIC" ) != std::string::npos )
1579 return true;
1580
1581 if( line.find( "*PADS-LOGIC" ) != std::string::npos )
1582 return true;
1583 }
1584 }
1585 catch( ... )
1586 {
1587 }
1588
1589 return false;
1590}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
const KIID m_Uuid
Definition eda_item.h:531
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:244
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
void SetFillMode(FILL_T aFill)
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:412
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:279
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:171
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:238
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:241
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:124
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
Define a library symbol object.
Definition lib_symbol.h:114
void SetGlobalPower()
virtual void SetName(const wxString &aName)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
Parser for PADS Logic schematic design export files.
const std::vector< TEXT_ITEM > & GetTextItems() const
const std::vector< TIED_DOT > & GetTiedDots() const
const std::vector< OFF_PAGE_CONNECTOR > & GetOffPageConnectors() const
bool Parse(const std::string &aFileName)
const std::vector< SHEET_HEADER > & GetSheetHeaders() const
const std::vector< NETNAME_LABEL > & GetNetNameLabels() const
const std::vector< LINES_ITEM > & GetLinesItems() const
std::vector< SCH_SIGNAL > GetSignalsOnSheet(int aSheetNumber) const
std::set< int > GetSheetNumbers() const
const std::vector< SYMBOL_DEF > & GetSymbolDefs() const
const std::vector< PART_PLACEMENT > & GetPartPlacements() const
const std::map< std::string, PARTTYPE_DEF > & GetPartTypes() const
std::vector< PART_PLACEMENT > GetPartsOnSheet(int aSheetNumber) const
const SYMBOL_DEF * GetSymbolDef(const std::string &aName) const
const PARAMETERS & GetParameters() const
Builder class to create KiCad schematic elements from parsed PADS data.
void ApplyPartAttributes(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Apply part attributes to a symbol instance.
void CreateTitleBlock(SCH_SCREEN *aScreen)
Create title block from parsed PADS parameters.
int CreateCustomFields(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Create custom fields from non-standard PADS attributes.
SCH_SHEET * CreateHierarchicalSheet(int aSheetNumber, int aTotalSheets, SCH_SHEET *aParentSheet, const wxString &aBaseFilename)
Create hierarchical sheet for a sub-schematic page.
int CreateNetLabels(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen, const std::set< std::string > &aSignalOpcIds, const std::set< std::string > &aSkipSignals={}, const std::map< std::string, NETNAME_LABEL > &aNetNameLabels={})
Create net labels for named signals.
Builder class to convert PADS symbol definitions to KiCad LIB_SYMBOL objects.
LIB_SYMBOL * BuildMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Build a composite multi-unit LIB_SYMBOL from a multi-gate PARTTYPE.
void AddHiddenPowerPins(LIB_SYMBOL *aSymbol, const std::vector< PARTTYPE_DEF::SIGPIN > &aSigpins)
Add hidden power pins from PARTTYPE SIGPIN entries to an existing symbol.
LIB_SYMBOL * GetOrCreateMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Get or create a multi-unit symbol for the given PARTTYPE.
static std::optional< LIB_ID > GetKiCadPowerSymbolId(const std::string &aPadsName)
Get KiCad power library symbol ID for a PADS power symbol.
LIB_SYMBOL * BuildKiCadPowerSymbol(const std::string &aKiCadName)
Build a power symbol using hard-coded KiCad-standard graphics.
static bool IsPowerSymbol(const std::string &aName)
Check if a symbol name indicates a power symbol.
static std::string GetPowerStyleFromVariant(const std::string &aDecalName, const std::string &aPinType)
Map a PADS special_variant to a power symbol style name.
LIB_SYMBOL * GetOrCreateSymbol(const SYMBOL_DEF &aSymbolDef)
Get or create a symbol for the given definition.
LIB_SYMBOL * GetOrCreateMultiUnitConnectorSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef, const std::vector< std::string > &aPinNumbers, const std::string &aCacheKey)
Get or create a multi-unit connector symbol, cached by base reference.
LIB_SYMBOL * BuildMultiUnitConnectorSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef, const std::vector< std::string > &aPinNumbers)
Build a multi-unit connector symbol where each unit represents one pin.
LIB_SYMBOL * BuildSymbol(const SYMBOL_DEF &aSymbolDef)
Build a KiCad LIB_SYMBOL from a PADS symbol definition.
LIB_SYMBOL * GetOrCreatePartTypeSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef)
Get or create a single-gate symbol with PARTTYPE-specific pin remapping.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition page_info.h:164
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Holds all the data relating to one schematic.
Definition schematic.h:90
SCHEMATIC_SETTINGS & Settings() const
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:174
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_librarySymbols
Definition sch_io_pads.h:99
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aPartName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
long long getLibraryTimestamp(const wxString &aLibraryPath) const
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
bool checkFileHeader(const wxString &aFileName) const
Check if the file header indicates a PADS Logic schematic file.
void ensureLoadedLibrary(const wxString &aLibraryPath)
Parse the PADS Logic ASCII file and populate the library symbol cache.
wxString m_cachedLibraryPath
Definition sch_io_pads.h:96
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
long long m_cachedLibraryTimestamp
Definition sch_io_pads.h:97
std::unordered_map< wxString, SEVERITY > m_errorMessages
Definition sch_io_pads.h:94
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
bool m_libraryCacheValid
Definition sch_io_pads.h:98
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
Definition sch_io.cpp:45
SCH_IO(const wxString &aName)
Definition sch_io.h:384
int GetUnit() const
Definition sch_item.h:233
void SetConnectivityDirty(bool aDirty=true)
Definition sch_item.h:587
virtual void SetUnit(int aUnit)
Definition sch_item.h:232
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:198
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:746
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
void ClearEditFlags()
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:724
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
const KIID & GetUuid() const
Definition sch_screen.h:529
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:85
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
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.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:376
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:69
void SetLibId(const LIB_ID &aName)
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_symbol.h:915
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
void AddHierarchicalReference(const KIID_PATH &aPath, const wxString &aRef, int aUnit)
Add a full hierarchical reference to this symbol.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:914
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Simple container to manage line stroke parameters.
static const char * PropPowerSymsOnly
#define _(s)
#define IS_NEW
New item, just created.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
double m_PadsSchTextWidthScale
PADS text width scale factor for schematic imports.
double m_PadsSchTextHeightScale
PADS text height scale factor for schematic imports.
#define THROW_IO_ERRORF(msg,...)
@ LAYER_WIRE
Definition layer_ids.h:458
@ LAYER_NOTES
Definition layer_ids.h:473
void DecodeJustification(int aJustification, GR_TEXT_H_ALIGN_T &aHJustify, GR_TEXT_V_ALIGN_T &aVJustify)
Decode a PADS text justification code into KiCad horizontal and vertical alignment.
LINE_STYLE PadsLineStyleToKiCad(int aPadsStyle)
Convert a PADS line style integer to a KiCad LINE_STYLE enum value.
KIID GenerateDeterministicUuid(const std::string &aIdentifier)
Generate a deterministic KIID from a PADS component identifier.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Common utilities and types for parsing PADS file formats.
@ RPT_SEVERITY_WARNING
static int computePowerOrientation(const std::string &aOpcId, const std::vector< PADS_SCH::SCH_SIGNAL > &aSignals, const VECTOR2I &aOpcPos, bool aPinUp, int aPageHeightIU)
Determine the orientation for a power symbol at an OPC position based on the wire direction at that p...
static std::string extractConnectorBaseRef(const std::string &aRef)
Extract the base reference from a connector reference designator.
static SCH_TEXT * createSchText(const PADS_SCH::TEXT_ITEM &aText, const VECTOR2I &aPos)
static std::string extractConnectorPinNumber(const std::string &aRef)
Extract the numeric connector pin suffix from a reference designator.
static std::string stripGateSuffix(const std::string &aRef)
Strip any alphabetic gate suffix (e.g.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
Gate definition within a PARTTYPE.
std::vector< std::string > decal_names
std::vector< PARTTYPE_PIN > pins
Graphical line/shape item from LINES section.
Net name label from NETNAMES section.
Off-page reference from OFFPAGE REFS section.
General schematic parameters from SCH and FIELDS sections.
std::string border_template
Part type definition from PARTTYPE section.
std::vector< GATE_DEF > gates
std::vector< SPECIAL_VARIANT > special_variants
Pin definition within a PARTTYPE GATE.
Part instance from PART section.
Signal (net) definition from CONNECTION and SIGNAL sections.
Sheet header from SHT section.
Symbol definition from CAEDECAL section.
Graphic primitive from CAEDECAL or LINES sections (OPEN, CLOSED, CIRCLE, COPCLS).
std::vector< GRAPHIC_POINT > points
Free text item from TEXT section.
Junction dot from TIEDOTS section.
Wire segment connecting two endpoints through coordinate vertices.
std::vector< POINT > vertices
A simple container for sheet instance information.
@ SYM_ORIENT_270
Definition symbol.h:38
@ SYM_MIRROR_Y
Definition symbol.h:40
@ SYM_ORIENT_180
Definition symbol.h:37
@ SYM_MIRROR_X
Definition symbol.h:39
@ SYM_ORIENT_90
Definition symbol.h:36
@ SYM_ORIENT_0
Definition symbol.h:35
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_PIN * pin
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
constexpr GR_TEXT_H_ALIGN_T GetFlippedAlignment(GR_TEXT_H_ALIGN_T aAlign)
Get the reverse alignment: left-right are swapped, others are unchanged.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.