311 const std::map<std::string, UTF8>* aProperties )
313 wxCHECK( !aFileName.IsEmpty() && aSchematic,
nullptr );
321 wxCHECK_MSG( aSchematic->
IsValid(),
nullptr,
"Can't append to a schematic with no root!" );
322 rootSheet = aAppendToMe;
344 wxCHECK( rootScreen,
nullptr );
355 std::string filename( aFileName.ToUTF8() );
357 if( !parser.
Parse( filename ) )
359 THROW_IO_ERROR( wxString::Format( wxT(
"Failed to parse PADS file: %s" ), aFileName ) );
372 const std::string& ref = part.reference;
373 size_t dashPos = ref.rfind(
'-' );
374 size_t dotPos = ref.rfind(
'.' );
375 size_t sepPos = std::string::npos;
377 if( dashPos != std::string::npos )
379 else if( dotPos != std::string::npos )
382 if( sepPos != std::string::npos && sepPos + 1 < ref.size()
383 && std::isalpha(
static_cast<unsigned char>( ref[sepPos + 1] ) ) )
408 if( sheetNumbers.empty() )
409 sheetNumbers.insert( 1 );
411 bool isSingleSheet = ( sheetNumbers.size() == 1 );
421 std::map<int, SheetContext> sheetContexts;
425 int sheetNum = *sheetNumbers.begin();
427 ctx.sheet = rootSheet;
428 ctx.screen = rootScreen;
430 ctx.screen->SetPageSettings( pageInfo );
431 sheetContexts[sheetNum] = ctx;
436 int totalSheets =
static_cast<int>( sheetNumbers.size() );
438 for(
int sheetNum : sheetNumbers )
441 sheetNum, totalSheets, rootSheet, aFileName );
449 if( hdr.sheet_num == sheetNum && !hdr.sheet_name.empty() )
452 wxString::FromUTF8( hdr.sheet_name ) );
462 wxString pageNo = wxString::Format( wxT(
"%d" ), sheetNum );
471 ctx.sheet = subSheet;
474 ctx.screen->SetPageSettings( pageInfo );
475 sheetContexts[sheetNum] = ctx;
483 std::set<std::string> connectorBaseRefs;
487 struct ConnectorGroup
489 std::vector<std::string> pinNumbers;
490 std::map<std::string, int> pinToUnit;
491 std::string partType;
494 std::map<std::string, ConnectorGroup> connectorGroups;
496 for(
const auto& [sheetNum, ctx] : sheetContexts )
498 std::vector<PADS_SCH::PART_PLACEMENT> sheetParts = parser.
GetPartsOnSheet( sheetNum );
502 auto ptIt = parser.
GetPartTypes().find( part.part_type );
504 if( ptIt == parser.
GetPartTypes().end() || !ptIt->second.is_connector )
513 ConnectorGroup&
group = connectorGroups[baseRef];
514 group.partType = part.part_type;
515 group.pinNumbers.push_back( pinNum );
519 for(
auto& [baseRef,
group] : connectorGroups )
521 std::sort(
group.pinNumbers.begin(),
group.pinNumbers.end(),
522 [](
const std::string& a,
const std::string& b )
524 return std::stoi( a ) < std::stoi( b );
527 for(
size_t i = 0; i <
group.pinNumbers.size(); i++ )
528 group.pinToUnit[
group.pinNumbers[i]] =
static_cast<int>( i + 1 );
532 for(
auto& [sheetNum, ctx] : sheetContexts )
534 std::vector<PADS_SCH::PART_PLACEMENT> parts = parser.
GetPartsOnSheet( sheetNum );
538 auto ptIt = parser.
GetPartTypes().find( part.part_type );
541 bool isMultiGate =
false;
542 bool isConnector =
false;
543 bool isPower =
false;
544 std::string libItemName;
545 std::string connectorPinNumber;
551 if( ptDef.
gates.size() > 1 )
556 libItemName = ptDef.
name;
559 else if( !ptDef.
gates.empty() )
562 int idx = std::max( 0, part.gate_index );
563 std::string decalName;
565 if( idx <
static_cast<int>( gate.
decal_names.size() ) )
576 if( symDef && !connectorPinNumber.empty() )
581 auto groupIt = connectorGroups.find( baseRef );
583 if( groupIt != connectorGroups.end() )
585 std::string cacheKey = ptDef.
name +
":conn:" + baseRef;
588 ptDef, *symDef, groupIt->second.pinNumbers, cacheKey );
589 libItemName = ptDef.
name +
"_" + baseRef;
593 connectorBaseRefs.insert( baseRef );
599 libItemName = decalName;
605 int idx = std::max( 0, part.gate_index );
606 idx = std::min( idx,
static_cast<int>( ptDef.
special_variants.size() ) - 1 );
614 libItemName = decalName;
630 wxString::Format( wxT(
"PADS Import: symbol '%s' not found,"
631 " part '%s' skipped" ),
632 wxString::FromUTF8( part.symbol_name ),
633 wxString::FromUTF8( part.reference ) ),
639 libItemName = symDef->
name;
645 if( ptIt != parser.
GetPartTypes().end() && !ptIt->second.sigpins.empty() )
654 std::string powerStyle;
657 && !ptIt->second.special_variants.empty() )
659 int varIdx = std::max( 0, part.gate_index );
662 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
663 const auto& variant = ptIt->second.special_variants[varIdx];
666 variant.decal_name, variant.pin_type );
669 if( isPower && powerStyle.empty() )
671 std::string rawNetName = part.power_net_name.empty()
673 : part.power_net_name;
679 powerStyle = std::string( powerLibId->GetLibItemName().c_str() );
682 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
686 if( isPower && !powerStyle.empty() )
702 instanceSymbol =
new LIB_SYMBOL( *libSymbol );
715 if( part.rotation == 90.0 )
717 else if( part.rotation == 180.0 )
719 else if( part.rotation == 270.0 )
722 if( part.mirror_flags & 1 )
725 if( part.mirror_flags & 2 )
730 if( isConnector && !connectorPinNumber.empty() )
733 auto groupIt = connectorGroups.find( baseRef );
735 if( groupIt != connectorGroups.end() )
737 auto unitIt = groupIt->second.pinToUnit.find( connectorPinNumber );
739 if( unitIt != groupIt->second.pinToUnit.end() )
740 symbol->
SetUnit( unitIt->second );
749 else if( isMultiGate )
751 symbol->
SetUnit( part.gate_index + 1 );
762 bool isPrimaryUnit = isConnector
764 : ( !isMultiGate || part.gate_index == 0 );
766 if( !isPower && isPrimaryUnit )
768 std::string baseRef = isConnector
776 symbol->
SetRef( &ctx.path, wxString::FromUTF8( part.reference ) );
786 symbol->
SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
791 if( isMultiGate && !isConnector )
794 symbol->
SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
802 const std::string& cat = ptIt->second.category;
804 if( cat ==
"CAP" || cat ==
"RES" || cat ==
"IND" )
806 auto valIt = part.attr_overrides.find(
"VALUE" );
808 if( valIt == part.attr_overrides.end() )
809 valIt = part.attr_overrides.find(
"VALUE1" );
811 if( valIt != part.attr_overrides.end() && !valIt->second.empty() )
815 for(
const auto& attr : part.attributes )
817 if( attr.name ==
"VALUE" || attr.name ==
"VALUE1"
818 || attr.name ==
"Value1" )
823 if( part.mirror_flags & 1 )
828 KiROUND( attr.position.y ) ) );
831 int fieldTextSize =
schIUScale.MilsToIU( 50 );
833 VECTOR2I( fieldTextSize, fieldTextSize ) );
847 wxString netName = part.power_net_name.empty()
848 ? wxString::FromUTF8( part.symbol_name )
849 : wxString::FromUTF8( part.power_net_name );
851 if( netName.StartsWith( wxT(
"/" ) ) )
852 netName = wxT(
"~{" ) + netName.Mid( 1 ) + wxT(
"}" );
863 else if( isMultiGate )
866 hierRef = part.reference;
869 wxString::FromUTF8( hierRef ),
878 if( isConnector && !connectorPinNumber.empty() )
881 wxString labelText = wxString::Format( wxT(
"%s.%s" ),
882 wxString::FromUTF8( baseRef ),
883 wxString::FromUTF8( connectorPinNumber ) );
886 std::vector<SCH_PIN*> pins = symbol->
GetPins();
889 pinPos = pins[0]->GetPosition();
896 ctx.screen->Append( label );
899 ctx.screen->Append( symbolPtr.release() );
906 std::set<std::string> powerSignalNames;
910 if( opc.signal_name.empty() )
913 auto ptIt = parser.
GetPartTypes().find( opc.symbol_lib );
916 && !ptIt->second.special_keyword.empty() && ptIt->second.special_keyword !=
"OFF"
917 && !ptIt->second.special_variants.empty() )
919 int idx = std::max( 0, opc.flags2 );
921 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
922 const auto& variant = ptIt->second.special_variants[idx];
925 variant.decal_name, variant.pin_type ).empty() )
927 powerSignalNames.insert( opc.signal_name );
935 std::set<std::string> signalOpcIds;
939 if( opc.signal_name.empty() || powerSignalNames.count( opc.signal_name ) )
942 signalOpcIds.insert(
"@@@O" + std::to_string( opc.id ) );
946 for(
auto& [sheetNum, ctx] : sheetContexts )
948 std::vector<PADS_SCH::SCH_SIGNAL> sheetSignals = parser.
GetSignalsOnSheet( sheetNum );
959 for(
size_t v = 0; v + 1 < wire.
vertices.size(); v++ )
976 ctx.screen->Append( line );
990 if( wire.
endpoint_a.find(
'.' ) != std::string::npos
991 && wire.
endpoint_a.find(
"@@@" ) == std::string::npos )
994 std::string ref = wire.
endpoint_a.substr( 0, dotPos );
996 if( connectorBaseRefs.count( ref ) )
998 const auto& vtx = wire.
vertices.front();
1004 const auto& adj = wire.
vertices[1];
1009 int dx = adjPos.
x - pos.
x;
1010 int dy = adjPos.
y - pos.
y;
1018 wxString labelText = wxString::FromUTF8( wire.
endpoint_a );
1024 ctx.screen->Append( label );
1029 if( wire.
endpoint_b.find(
'.' ) != std::string::npos
1030 && wire.
endpoint_b.find(
"@@@" ) == std::string::npos )
1033 std::string ref = wire.
endpoint_b.substr( 0, dotPos );
1035 if( connectorBaseRefs.count( ref ) )
1037 const auto& vtx = wire.
vertices.back();
1043 size_t lastIdx = wire.
vertices.size() - 1;
1044 const auto& adj = wire.
vertices[lastIdx - 1];
1049 int dx = adjPos.
x - pos.
x;
1050 int dy = adjPos.
y - pos.
y;
1058 wxString labelText = wxString::FromUTF8( wire.
endpoint_b );
1064 ctx.screen->Append( label );
1073 if( dot.sheet_number != sheetNum )
1080 ctx.screen->Append( junction );
1093 if( opc.source_sheet != sheetNum )
1096 if( opc.signal_name.empty() )
1104 std::string powerStyle;
1105 auto opcPtIt = parser.
GetPartTypes().find( opc.symbol_lib );
1108 && !opcPtIt->second.special_keyword.empty() && opcPtIt->second.special_keyword !=
"OFF"
1109 && !opcPtIt->second.special_variants.empty() )
1111 int idx = std::max( 0, opc.flags2 );
1112 idx = std::min( idx,
1114 opcPtIt->second.special_variants.size() ) - 1 );
1115 const auto& variant = opcPtIt->second.special_variants[idx];
1118 variant.decal_name, variant.pin_type );
1121 if( !powerStyle.empty() )
1127 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
1140 bool pinUp = ( powerStyle ==
"VCC" || powerStyle ==
"PWR_TRIANGLE" );
1142 std::to_string( opc.id ), sheetSignals, pos, pinUp,
1147 wxString netName = wxString::FromUTF8( opc.signal_name );
1149 if( netName.StartsWith( wxT(
"/" ) ) )
1150 netName = wxT(
"~{" ) + netName.Mid( 1 ) + wxT(
"}" );
1155 wxString pwrRef = wxString::Format( wxT(
"#PWR%03d" ), pwrIndex++ );
1156 symbol->
SetRef( &ctx.path, pwrRef );
1160 ctx.screen->Append( symbolPtr.release() );
1172 if( !sheetContexts.empty() )
1174 SCH_SCREEN* textScreen = sheetContexts.begin()->second.screen;
1178 if( textItem.content.empty() )
1190 if( !sheetContexts.empty() )
1192 SCH_SCREEN* linesScreen = sheetContexts.begin()->second.screen;
1199 double ox = linesItem.origin.x;
1200 double oy = linesItem.origin.y;
1229 && prim.
points.size() == 2 )
1250 linesScreen->
Append( rect );
1252 else if( prim.
points.size() >= 2 )
1254 for(
size_t p = 0; p + 1 < prim.
points.size(); p++ )
1272 if( prim.
points[p].arc.has_value() )
1282 double sx = start.
x -
center.x;
1283 double sy = start.
y -
center.y;
1286 double radius = std::sqrt( sx * sx + sy * sy );
1288 double mx = sx + ex;
1289 double my = sy + ey;
1290 double mlen = std::sqrt( mx * mx + my * my );
1297 +
static_cast<int>(
radius * mx / mlen );
1299 +
static_cast<int>(
radius * my / mlen );
1304 +
static_cast<int>( -sy *
radius
1305 / std::max(
radius, 1.0 ) );
1307 +
static_cast<int>( sx *
radius
1308 / std::max(
radius, 1.0 ) );
1324 linesScreen->
Append( arc );
1332 linesScreen->
Append( line );
1341 if( textItem.
content.empty() )