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