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