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