46#include <wx/filename.h>
104 std::ifstream file( aFileName.fn_str() );
106 if( !file.is_open() )
111 if( std::getline( file, line ) )
113 if( line.find(
"!PADS-" ) != std::string::npos )
122 const std::map<std::string, UTF8>* aProperties,
PROJECT* aProject )
126 std::unique_ptr<BOARD> board( aAppendToMe ? aAppendToMe :
new BOARD() );
138 parser.
Parse( aFileName );
140 catch(
const std::exception& e )
142 THROW_IO_ERROR( wxString::Format(
"Error parsing PADS file: %s", e.what() ) );
187 return board.release();
193 const auto& nets =
m_parser->GetNets();
195 for(
const auto& pads_net : nets )
198 for(
const auto& pads_net : nets )
200 for(
const auto&
pin : pads_net.pins )
202 std::string key =
pin.ref_des +
"." +
pin.pin_name;
207 const auto& route_nets =
m_parser->GetRoutes();
209 for(
const auto& route : route_nets )
211 for(
const auto&
pin : route.pins )
213 std::string key =
pin.ref_des +
"." +
pin.pin_name;
220 for(
const auto& route : route_nets )
223 for(
const auto& pour_def :
m_parser->GetPours() )
226 for(
const auto& copper :
m_parser->GetCopperShapes() )
232 const auto& reuse_blocks =
m_parser->GetReuseBlocks();
234 for(
const auto& [blockName, block] : reuse_blocks )
236 for(
const std::string& partName : block.part_names )
246 const auto& decals =
m_parser->GetPartDecals();
247 const auto& part_types =
m_parser->GetPartTypes();
248 const auto& partInstanceAttrs =
m_parser->GetPartInstanceAttrs();
249 const auto& parts =
m_parser->GetParts();
251 for(
const auto& pads_part : parts )
260 path.push_back( symbolUuid );
264 std::string decal_name = pads_part.decal;
270 if( !pads_part.explicit_decal )
272 auto part_type_it = part_types.find( decal_name );
274 if( part_type_it != part_types.end() )
275 decal_name = part_type_it->second.decal_name;
280 std::stringstream ss( decal_name );
282 std::vector<std::string> decal_list;
284 while( std::getline( ss, segment,
':' ) )
286 decal_list.push_back( segment );
289 std::string actual_decal_name;
290 bool found_valid_decal =
false;
292 if( pads_part.alt_decal_index >= 0
293 &&
static_cast<size_t>( pads_part.alt_decal_index ) < decal_list.size() )
295 const std::string& alt_decal = decal_list[pads_part.alt_decal_index];
297 if( decals.find( alt_decal ) != decals.end() )
299 actual_decal_name = alt_decal;
300 found_valid_decal =
true;
304 if( !found_valid_decal )
306 for(
const std::string& decal : decal_list )
308 if( decals.find( decal ) != decals.end() )
310 actual_decal_name = decal;
311 found_valid_decal =
true;
317 if( found_valid_decal )
319 decal_name = actual_decal_name;
326 footprint->
SetValue( pads_part.decal );
328 if( !pads_part.alternate_decals.empty() )
332 for(
size_t i = 0; i < pads_part.alternate_decals.size(); ++i )
335 alternates += wxT(
", " );
337 alternates += wxString::FromUTF8( pads_part.alternate_decals[i] );
344 footprint->
Add( field );
347 auto partCoordScaler = [&](
double val,
bool is_x ) {
360 long long origin_nm =
static_cast<long long>( std::round( origin *
m_scaleFactor ) );
361 long long val_nm =
static_cast<long long>( std::round( val * part_factor ) );
363 long long res_nm = val_nm - origin_nm;
368 return static_cast<int>( std::clamp<long long>( res_nm, INT_MIN, INT_MAX ) );
372 partCoordScaler( pads_part.location.y,
false ) ) );
386 auto ptIt = part_types.find( pads_part.decal );
388 if( ptIt != part_types.end() )
389 partType = &ptIt->second;
391 const std::map<std::string, std::string>* instanceAttrs =
nullptr;
392 auto iaIt = partInstanceAttrs.find( pads_part.name );
394 if( iaIt != partInstanceAttrs.end() )
395 instanceAttrs = &iaIt->second;
397 auto applyAttributes = [&](
const std::vector<PADS_IO::ATTRIBUTE>& attrs,
398 std::function<int(
double)> scaler )
400 for(
const auto& attr : attrs )
403 bool ownsField =
false;
405 if( attr.name ==
"Ref.Des." )
409 else if( attr.name ==
"Part Type" || attr.name ==
"VALUE" )
411 field = &footprint->
Value();
415 std::string attrValue;
419 auto valIt = instanceAttrs->find( attr.name );
421 if( valIt != instanceAttrs->end() )
422 attrValue = valIt->second;
425 if( attrValue.empty() && partType )
427 auto valIt = partType->
attributes.find( attr.name );
430 attrValue = valIt->second;
433 if( !attrValue.empty() )
436 wxString::FromUTF8( attr.name ) );
437 field->
SetText( wxString::FromUTF8( attrValue ) );
456 int scaledSize = scaler( attr.height );
468 VECTOR2I offset( scaler( attr.x ), -scaler( attr.y ) );
484 if( attr.hjust ==
"LEFT" )
486 else if( attr.hjust ==
"RIGHT" )
491 if( attr.vjust ==
"UP" )
493 else if( attr.vjust ==
"DOWN" )
499 footprint->
Add( field );
503 auto decal_it = decals.find( decal_name );
505 if( decal_it != decals.end() )
507 auto decalScaler = [&](
double val ) {
511 const std::string& units = decal_it->second.units;
513 if( units ==
"M" || units ==
"D" || units ==
"MILS" || units ==
"MIL" )
515 else if( units ==
"MM" || units ==
"METRIC" )
517 else if( units ==
"I" || units ==
"INCHES" || units ==
"INCH" )
523 applyAttributes( decal_it->second.attributes, decalScaler );
530 wxString::Format(
_(
"Footprint '%s' not found in decal list, part skipped" ),
536 auto partScaler = [&](
double val ) {
542 if( pads_part.units ==
"M" )
return KiROUND( val );
547 applyAttributes( pads_part.attributes, partScaler );
563 blockField->
SetText( wxString::FromUTF8( blockIt->second ) );
564 footprint->
Add( blockField );
567 if( decal_it == decals.end() )
576 auto decalScaler = [&](
double val ) {
581 || decal.
units ==
"MIL" )
583 else if( decal.
units ==
"MM" || decal.
units ==
"METRIC" )
585 else if( decal.
units ==
"I" || decal.
units ==
"INCHES" || decal.
units ==
"INCH" )
594 const std::string& shape = layer_def.
shape;
599 if( shape ==
"R" || shape ==
"C" || shape ==
"A" || shape ==
"RT" )
604 else if( shape ==
"S" || shape ==
"ST" )
609 else if( shape ==
"O" || shape ==
"OT" )
612 pad->SetSize( kicad_layer, size );
614 else if( shape ==
"RF" )
617 pad->SetSize( kicad_layer, size );
624 pad->SetOffset( kicad_layer, pad_offset );
627 else if( shape ==
"OF" )
630 pad->SetSize( kicad_layer, size );
637 pad->SetOffset( kicad_layer, pad_offset );
640 else if( shape ==
"RC" || shape ==
"OC" )
643 pad->SetSize( kicad_layer, size );
647 double min_dim = std::min( size.
x, size.
y );
649 double ratio = ( min_dim > 0 ) ? (
radius / min_dim ) : 0.25;
650 ratio = std::min( ratio, 0.5 );
651 pad->SetRoundRectRadiusRatio( kicad_layer, ratio );
655 pad->SetRoundRectRadiusRatio( kicad_layer, 0.25 );
663 pad->SetOffset( kicad_layer, pad_offset );
677 for(
size_t term_idx = 0; term_idx < decal.
terminals.size(); ++term_idx )
679 const auto& term = decal.
terminals[term_idx];
683 pad->SetNumber( term.name );
685 VECTOR2I pad_pos( decalScaler( term.x ), -decalScaler( term.y ) );
691 int pin_num =
static_cast<int>( term_idx + 1 );
693 auto stack_it = decal.
pad_stacks.find( pin_num );
698 if( stack_it != decal.
pad_stacks.end() && !stack_it->second.empty() )
700 const std::vector<PADS_IO::PAD_STACK_LAYER>& stack = stack_it->second;
704 double slot_length = 0.0;
705 double slot_orientation = 0.0;
706 double pad_rotation = 0.0;
708 for(
const auto& layer_def : stack )
710 if( layer_def.
drill > 0 )
712 drill = layer_def.
drill;
713 plated = layer_def.
plated;
723 auto mapPadsLayer = [&](
int pads_layer ) ->
PCB_LAYER_ID {
724 if( pads_layer == -2 || pads_layer == 1 )
726 else if( pads_layer == -1
727 || pads_layer ==
m_parser->GetParameters().layer_count )
729 else if( pads_layer > 1
730 && pads_layer < m_parser->GetParameters().layer_count )
732 int inner_idx = pads_layer - 2;
734 if( inner_idx >= 0 && inner_idx < 30 )
741 bool has_explicit_layers =
false;
743 for(
const auto& layer_def : stack )
745 if( layer_def.
layer == -2 || layer_def.
layer == -1
746 || layer_def.
layer == 1
747 || layer_def.
layer ==
m_parser->GetParameters().layer_count )
749 has_explicit_layers =
true;
754 for(
const auto& layer_def : stack )
756 if( layer_def.
layer == 0 )
758 if( !has_explicit_layers )
762 convertPadShape( layer_def,
pad,
F_Cu, part_orient );
777 if( layer_def.
sizeA <= 0 )
783 if( layer_def.
shape ==
"RT" || layer_def.
shape ==
"ST"
784 || layer_def.
shape ==
"RA" || layer_def.
shape ==
"SA" )
793 layer_set.
set( kicad_layer );
794 convertPadShape( layer_def,
pad, kicad_layer, part_orient );
798 if( layer_set.none() )
801 convertPadShape( stack[0],
pad,
F_Cu, part_orient );
804 if( slot_length > 0 && slot_length != drill )
808 int drillMinor = decalScaler( drill );
809 int drillMajor = decalScaler( slot_length );
814 double relAngle = slot_orientation - pad_rotation;
816 relAngle = fmod( relAngle, 360.0 );
821 bool vertical = ( relAngle > 45.0 && relAngle < 135.0 )
822 || ( relAngle > 225.0 && relAngle < 315.0 );
825 pad->SetDrillSize(
VECTOR2I( drillMinor, drillMajor ) );
827 pad->SetDrillSize(
VECTOR2I( drillMajor, drillMinor ) );
832 decalScaler( drill ) ) );
849 pad->SetLayerSet( layer_set );
860 std::string pinKey = pads_part.name +
"." + term.name;
873 for(
const auto& item : decal.
items )
875 if( item.points.empty() )
882 if( item.layer == 0 )
892 if( mapped_layer ==
B_Cu )
899 shape_layer = mapped_layer;
908 _(
"Skipping decal item on unmapped layer %d" ), item.layer ),
914 bool is_circle = ( item.type ==
"CIRCLE" );
915 bool is_closed = ( item.type ==
"CLOSED" || is_circle );
919 if( is_circle && item.points.size() >= 2 )
924 double x1 = item.points[0].x;
925 double y1 = item.points[0].y;
926 double x2 = item.points[1].x;
927 double y2 = item.points[1].y;
929 double cx = ( x1 + x2 ) / 2.0;
930 double cy = ( y1 + y2 ) / 2.0;
932 double radius = std::sqrt( ( x2 - x1 ) * ( x2 - x1 )
933 + ( y2 - y1 ) * ( y2 - y1 ) )
945 shape->
SetEnd( fp_pos + pt_on_circle );
949 footprint->
Add( shape );
954 if( item.points.size() < 2 )
957 for(
size_t i = 0; i < item.points.size() - 1; ++i )
971 VECTOR2I start( decalScaler( p1.
x ), -decalScaler( p1.
y ) );
976 std::swap( start,
end );
990 VECTOR2I start( decalScaler( p1.
x ), -decalScaler( p1.
y ) );
1001 footprint->
Add( shape );
1004 if( is_closed && item.points.size() > 2 )
1018 -decalScaler( pFirst.
arc.
cy ) );
1019 VECTOR2I start( decalScaler( pLast.
x ), -decalScaler( pLast.
y ) );
1020 VECTOR2I end( decalScaler( pFirst.
x ), -decalScaler( pFirst.
y ) );
1023 std::swap( start,
end );
1037 VECTOR2I start( decalScaler( pLast.
x ), -decalScaler( pLast.
y ) );
1038 VECTOR2I end( decalScaler( pFirst.
x ), -decalScaler( pFirst.
y ) );
1048 footprint->
Add( shape );
1053 if( pads_part.bottom_layer )
1063 const auto& reuse_blocks =
m_parser->GetReuseBlocks();
1065 if( reuse_blocks.empty() )
1068 std::map<std::string, PCB_GROUP*> blockGroups;
1070 for(
const auto& [blockName, block] : reuse_blocks )
1072 if( !block.instances.empty() || !block.part_names.empty() )
1075 group->SetName( wxString::FromUTF8( blockName ) );
1077 blockGroups[blockName] =
group;
1083 for(
PCB_FIELD* field : fp->GetFields() )
1085 if( field->GetName() == wxT(
"PADS_Reuse_Block" ) )
1087 std::string blockName = field->GetText().ToStdString();
1088 auto groupIt = blockGroups.find( blockName );
1090 if( groupIt != blockGroups.end() )
1092 groupIt->second->AddItem( fp );
1104 const auto& test_points =
m_parser->GetTestPoints();
1106 for(
const auto&
tp : test_points )
1112 footprint->
SetValue( wxString::FromUTF8(
tp.symbol_name ) );
1125 pad->SetNumber( wxT(
"1" ) );
1126 pad->SetPosition( pos );
1133 if( !
tp.net_name.empty() )
1148 tpField->
SetText( wxString::FromUTF8(
tp.type ) );
1149 footprint->
Add( tpField );
1158 const auto& texts =
m_parser->GetTexts();
1160 for(
const auto& pads_text : texts )
1169 _(
"Text on unmapped layer %d assigned to Comments layer" ),
1176 text->SetText( pads_text.content );
1180 int scaledSize =
scaleSize( pads_text.height );
1185 text->SetTextSize(
VECTOR2I( charWidth, charHeight ) );
1187 if( pads_text.width > 0 )
1191 text->SetTextAngle( textAngle );
1199 text->SetPosition( pos + textShift );
1201 if( pads_text.hjust ==
"LEFT" )
1203 else if( pads_text.hjust ==
"RIGHT" )
1208 if( pads_text.vjust ==
"UP" )
1210 else if( pads_text.vjust ==
"DOWN" )
1215 text->SetKeepUpright(
false );
1216 text->SetLayer( textLayer );
1224 const auto& routes =
m_parser->GetRoutes();
1225 std::set<std::pair<int, int>> placedThroughVias;
1227 for(
const auto& route : routes )
1234 for(
const auto& track_def : route.tracks )
1236 if( track_def.points.size() < 2 )
1246 _(
"Skipping track on non-copper layer %d" ), track_def.layer ),
1254 for(
size_t i = 0; i < track_def.points.size() - 1; ++i )
1265 if( ( start -
end ).EuclideanNorm() < 1000 )
1291 for(
const auto& via_def : route.vias )
1297 auto it =
m_parser->GetViaDefs().find( via_def.name );
1299 if( it !=
m_parser->GetViaDefs().end() )
1301 switch( it->second.via_type )
1314 auto key = std::make_pair( pos.
x, pos.
y );
1316 if( placedThroughVias.count( key ) )
1319 placedThroughVias.insert( key );
1324 via->SetPosition( pos );
1326 if( it !=
m_parser->GetViaDefs().end() )
1342 via->SetLayerPair( startLayer, endLayer );
1343 via->SetViaType( viaType );
1374 const auto& copperShapes =
m_parser->GetCopperShapes();
1380 return cs.outline.size() == 2 && !cs.outline[1].is_arc
1381 && !cs.filled && !cs.is_cutout;
1387 auto tryFormRectangle = [&](
size_t idx,
VECTOR2I& minCorner,
VECTOR2I& maxCorner ) ->
bool
1389 if( idx + 3 >= copperShapes.size() )
1392 const auto& c0 = copperShapes[idx];
1393 const auto& c1 = copperShapes[idx + 1];
1394 const auto& c2 = copperShapes[idx + 2];
1395 const auto& c3 = copperShapes[idx + 3];
1397 if( !isRectCandidate( c0 ) || !isRectCandidate( c1 )
1398 || !isRectCandidate( c2 ) || !isRectCandidate( c3 ) )
1403 if( c1.net_name != c0.net_name || c2.net_name != c0.net_name
1404 || c3.net_name != c0.net_name )
1409 if( c1.layer != c0.layer || c2.layer != c0.layer || c3.layer != c0.layer )
1416 for(
int i = 0; i < 4; ++i )
1419 scaleCoord( segs[i]->outline[0].y,
false ) );
1421 scaleCoord( segs[i]->outline[1].y,
false ) );
1425 for(
int i = 0; i < 4; ++i )
1430 if( s.
x != e.
x && s.
y != e.
y )
1435 for(
int i = 0; i < 3; ++i )
1437 if( pts[i * 2 + 1] != pts[( i + 1 ) * 2] )
1442 if( pts[7] != pts[0] )
1446 int minX = pts[0].
x, maxX = pts[0].
x;
1447 int minY = pts[0].
y, maxY = pts[0].
y;
1449 for(
int i = 0; i < 8; ++i )
1451 minX = std::min( minX, pts[i].x );
1452 maxX = std::max( maxX, pts[i].x );
1453 minY = std::min( minY, pts[i].y );
1454 maxY = std::max( maxY, pts[i].y );
1457 minCorner =
VECTOR2I( minX, minY );
1458 maxCorner =
VECTOR2I( maxX, maxY );
1462 for(
size_t idx = 0; idx < copperShapes.size(); ++idx )
1464 const auto& copper = copperShapes[idx];
1466 if( copper.outline.size() < 2 )
1469 if( copper.is_cutout )
1479 _(
"COPPER item on unmapped layer %d defaulting to F.Cu" ),
1494 if( tryFormRectangle( idx, minCorner, maxCorner ) )
1499 rect->
SetEnd( maxCorner );
1511 for(
size_t i = 0; i < copper.outline.size() - 1; ++i )
1513 const auto& p1 = copper.outline[i];
1514 const auto& p2 = copper.outline[i + 1];
1519 if( ( start -
end ).EuclideanNorm() < 1000 )
1545 if( !copper.net_name.empty() )
1550 if( copper.outline.size() < 3 )
1568 for(
size_t i = 0; i < copper.outline.size() - 1; ++i )
1570 const auto& p1 = copper.outline[i];
1571 const auto& p2 = copper.outline[i + 1];
1576 if( ( start -
end ).EuclideanNorm() < 1000 )
1613 const auto& clusters =
m_parser->GetClusters();
1615 if( clusters.empty() )
1618 std::map<std::string, const PADS_IO::CLUSTER*> netToClusterMap;
1620 for(
const auto& cluster : clusters )
1622 for(
const std::string& netName : cluster.net_names )
1625 netToClusterMap[converted] = &cluster;
1629 std::map<int, PCB_GROUP*> clusterGroups;
1631 for(
const auto& cluster : clusters )
1634 group->SetName( wxString::FromUTF8( cluster.name ) );
1636 clusterGroups[cluster.id] =
group;
1645 std::string netName = net->
GetNetname().ToStdString();
1646 auto clusterIt = netToClusterMap.find( netName );
1648 if( clusterIt != netToClusterMap.end() )
1650 int clusterId = clusterIt->second->id;
1651 auto groupIt = clusterGroups.find( clusterId );
1653 if( groupIt != clusterGroups.end() )
1655 groupIt->second->AddItem( track );
1665 const auto& pours =
m_parser->GetPours();
1666 const auto& params =
m_parser->GetParameters();
1670 auto isValidPoly = [](
const std::vector<PADS_IO::ARC_POINT>& pts )
1672 if( pts.size() >= 3 )
1675 if( pts.size() == 1 && pts[0].is_arc
1676 &&
std::abs( pts[0].arc.delta_angle ) >= 359.0 )
1686 int maxPriority = 0;
1688 for(
const auto& pour_def : pours )
1690 if( pour_def.priority > maxPriority )
1691 maxPriority = pour_def.priority;
1695 std::map<std::string, ZONE*> pourZoneMap;
1698 std::map<std::string, std::string> hatoutToParent;
1701 for(
const auto& pour_def : pours )
1705 hatoutToParent[pour_def.name] = pour_def.owner_pour;
1715 if( pour_def.points.size() < 3 )
1725 _(
"Skipping pour on unmapped layer %d" ), pour_def.layer ),
1738 if( pour_def.is_cutout )
1746 zone->
SetZoneName( wxString::Format( wxT(
"Cutout_%s" ), pour_def.owner_pour ) );
1756 int kicadPriority = maxPriority - pour_def.priority + 1;
1766 pourZoneMap[pour_def.name] = zone;
1771 for(
const auto& pour_def : pours )
1776 if( !isValidPoly( pour_def.points ) )
1779 auto zoneIt = pourZoneMap.find( pour_def.owner_pour );
1781 if( zoneIt == pourZoneMap.end() )
1784 ZONE* zone = zoneIt->second;
1809 for(
const auto& void_def : pours )
1814 if( !isValidPoly( void_def.points ) )
1819 auto parentIt = hatoutToParent.find( void_def.owner_pour );
1821 if( parentIt == hatoutToParent.end() )
1824 if( parentIt->second != pour_def.owner_pour )
1844 const auto& pts = polyline.points;
1846 if( pts.size() < 2 )
1849 for(
size_t i = 0; i < pts.size() - 1; ++i )
1879 if( polyline.closed && pts.size() > 2 )
1884 bool needsClosing = (
std::abs( pLast.
x - pFirst.
x ) > 0.001
1915 const auto& dimensions =
m_parser->GetDimensions();
1917 for(
const auto& dim : dimensions )
1919 if( dim.points.size() < 2 )
1933 if( dim.is_horizontal )
1943 if( dim.is_horizontal )
1945 double heightOffset = dim.crossbar_pos - dim.points[0].y;
1946 int height = -
scaleSize( heightOffset );
1951 double heightOffset = dim.crossbar_pos - dim.points[0].x;
1965 if( dim.text_height > 0 )
1967 int scaledSize =
scaleSize( dim.text_height );
1974 if( dim.text_width > 0 )
1978 if( !dim.text.empty() )
1986 if( dim.rotation != 0.0 )
1997 const auto& keepouts =
m_parser->GetKeepouts();
1998 int keepoutIndex = 0;
2000 for(
const auto& ko : keepouts )
2002 if( ko.outline.size() < 3 )
2008 if( ko.layers.empty() )
2012 else if( ko.layers.size() == 1 )
2021 _(
"Skipping keepout on unmapped layer %d" ), ko.layers[0] ),
2034 for(
int layer : ko.layers )
2039 layerSet.
set( mappedLayer );
2042 if( layerSet.none() )
2070 zone->
SetZoneName( wxString::Format( wxT(
"%s_%d" ), typeName, ++keepoutIndex ) );
2076 if( ko.outline.size() > 2 )
2078 const auto& first = ko.outline.front();
2079 const auto& last = ko.outline.back();
2081 if(
std::abs( first.x - last.x ) > 0.001 ||
std::abs( first.y - last.y ) > 0.001 )
2097 const auto& pts = graphic.points;
2104 if( pts.size() == 1 && pts[0].is_arc
2105 &&
std::abs( pts[0].arc.delta_angle - 360.0 ) < 0.1 )
2120 if( pts.size() < 2 )
2123 for(
size_t i = 0; i < pts.size() - 1; ++i )
2151 if( graphic.closed && pts.size() > 2 )
2156 bool needsClosing = (
std::abs( pLast.
x - pFirst.
x ) > 0.001
2187 wxFileName fn( aFileName );
2188 fn.SetExt( wxT(
"kicad_dru" ) );
2190 wxString customRules = wxT(
"(version 1)\n" );
2192 const auto& diffPairs =
m_parser->GetDiffPairs();
2194 for(
const auto& dp : diffPairs )
2196 if( dp.name.empty() || ( dp.gap <= 0 && dp.width <= 0 ) )
2199 wxString ruleName = wxString::Format( wxT(
"DiffPair_%s" ), wxString::FromUTF8( dp.name ) );
2201 if( dp.gap > 0 && !dp.positive_net.empty() && !dp.negative_net.empty() )
2206 wxString gapStr = wxString::FromUTF8(
FormatDouble2Str( gapMm ) ) + wxT(
"mm" );
2208 customRules += wxString::Format(
2209 wxT(
"\n(rule \"%s_gap\"\n" )
2210 wxT(
" (condition \"A.NetName == '%s' && B.NetName == '%s'\")\n" )
2211 wxT(
" (constraint clearance (min %s)))\n" ),
2212 ruleName, posNet, negNet, gapStr );
2216 if( customRules.length() > 15 )
2218 wxFile rulesFile( fn.GetFullPath(), wxFile::write );
2220 if( rulesFile.IsOpened() )
2221 rulesFile.Write( customRules );
2231 size_t trackCount = 0;
2232 size_t viaCount = 0;
2242 m_reporter->Report( wxString::Format(
_(
"Imported %zu footprints, %d nets, %zu tracks,"
2243 " %zu vias, %zu zones" ),
2246 trackCount, viaCount,
2253 const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2255 std::map<wxString, PCB_LAYER_ID> layer_map;
2259 layer_map[layer.Name] = layer.AutoMapLayer;
2269 return static_cast<int>( std::clamp<int64_t>( nm, INT_MIN, INT_MAX ) );
2277 long long origin_nm =
static_cast<long long>( std::round( origin *
m_scaleFactor ) );
2278 long long val_nm =
static_cast<long long>( std::round( aVal *
m_scaleFactor ) );
2280 long long result = aIsX ? ( val_nm - origin_nm ) : ( origin_nm - val_nm );
2281 return static_cast<int>( std::clamp<long long>(
result, INT_MIN, INT_MAX ) );
2289 if(
info.padsLayerNum == aPadsLayer )
2306 if( aNetName.empty() )
2337 const std::vector<PADS_IO::ARC_POINT>& aPts )
2343 if( aPts.size() == 1 && aPts[0].is_arc
2344 &&
std::abs( aPts[0].arc.delta_angle ) >= 359.0 )
2350 constexpr int NUM_SEGS = 36;
2352 for(
int i = 0; i < NUM_SEGS; i++ )
2354 double angle = 2.0 *
M_PI * i / NUM_SEGS;
2364 for(
size_t i = 1; i < aPts.size(); i++ )
2366 const auto& pt = aPts[i];
2373 for(
int j = 1; j < arcPoly.
PointCount(); j++ )
2395 std::swap( start,
end );
2411 double startAngleRad = atan2( aPrev.
y - aCurr.
arc.
cy, aPrev.
x - aCurr.
arc.
cx );
2414 double midX = aCurr.
arc.
cx + aCurr.
arc.
radius * cos( midAngleRad );
2415 double midY = aCurr.
arc.
cy + aCurr.
arc.
radius * sin( midAngleRad );
2427 std::vector<PADS_IO::LAYER_INFO> padsLayerInfos =
m_parser->GetLayerInfos();
2453 for(
const auto& padsInfo : padsLayerInfos )
2456 info.padsLayerNum = padsInfo.number;
2457 info.name = padsInfo.name;
2462 info.type = convertLayerType( padsInfo.layer_type );
2464 std::string lowerName = padsInfo.name;
2465 std::transform( lowerName.begin(), lowerName.end(), lowerName.begin(),
2466 [](
unsigned char c ){ return std::tolower( c ); } );
2468 bool isBottom = lowerName.find(
"bottom" ) != std::string::npos
2469 || lowerName.find(
"bot" ) != std::string::npos;
2481 if( padsInfo.number == 1 )
2483 else if( padsInfo.number ==
m_parser->GetParameters().layer_count )
2492 info.required = padsInfo.required;
2496 std::vector<INPUT_LAYER_DESC> inputDescs =
2502 int copperLayerCount =
m_parser->GetParameters().layer_count;
2504 if( copperLayerCount < 1 )
2505 copperLayerCount = 2;
2507 m_loadBoard->SetCopperLayerCount( copperLayerCount );
2515 switch(
m_parser->GetParameters().units )
2537 const auto& designRules =
m_parser->GetDesignRules();
2557 if( defaultNetclass )
2559 defaultNetclass->SetClearance(
scaleSize( designRules.default_clearance ) );
2560 defaultNetclass->SetTrackWidth(
scaleSize( designRules.default_track_width ) );
2561 defaultNetclass->SetViaDiameter(
scaleSize( designRules.default_via_size ) );
2562 defaultNetclass->SetViaDrill(
scaleSize( designRules.default_via_drill ) );
2565 const auto& viaDefs =
m_parser->GetViaDefs();
2567 if( !viaDefs.empty() )
2571 const std::string& defaultViaName =
m_parser->GetParameters().default_signal_via;
2572 auto defaultIt = viaDefs.find( defaultViaName );
2574 if( defaultIt == viaDefs.end() )
2575 defaultIt = viaDefs.begin();
2577 int viaDia =
scaleSize( defaultIt->second.size );
2578 int viaDrill =
scaleSize( defaultIt->second.drill );
2583 if( defaultNetclass )
2585 defaultNetclass->SetViaDiameter( viaDia );
2586 defaultNetclass->SetViaDrill( viaDrill );
2589 for(
const auto& [
name, def] : viaDefs )
2593 const auto& netClasses =
m_parser->GetNetClasses();
2595 for(
const auto& nc : netClasses )
2597 if( nc.name.empty() )
2600 wxString ncName = wxString::FromUTF8( nc.name );
2601 std::shared_ptr<NETCLASS> netclass = std::make_shared<NETCLASS>( ncName );
2603 if( nc.clearance > 0 )
2604 netclass->SetClearance(
scaleSize( nc.clearance ) );
2606 if( nc.track_width > 0 )
2607 netclass->SetTrackWidth(
scaleSize( nc.track_width ) );
2609 if( nc.via_size > 0 )
2610 netclass->SetViaDiameter(
scaleSize( nc.via_size ) );
2612 if( nc.via_drill > 0 )
2613 netclass->SetViaDrill(
scaleSize( nc.via_drill ) );
2615 if( nc.diff_pair_width > 0 )
2616 netclass->SetDiffPairWidth(
scaleSize( nc.diff_pair_width ) );
2618 if( nc.diff_pair_gap > 0 )
2619 netclass->SetDiffPairGap(
scaleSize( nc.diff_pair_gap ) );
2623 for(
const std::string& netName : nc.net_names )
2630 const auto& diffPairs =
m_parser->GetDiffPairs();
2632 for(
const auto& dp : diffPairs )
2634 if( dp.name.empty() )
2637 wxString dpClassName =
2638 wxString::Format( wxT(
"DiffPair_%s" ), wxString::FromUTF8( dp.name ) );
2639 std::shared_ptr<NETCLASS> dpNetclass = std::make_shared<NETCLASS>( dpClassName );
2642 dpNetclass->SetDiffPairGap(
scaleSize( dp.gap ) );
2646 dpNetclass->SetDiffPairWidth(
scaleSize( dp.width ) );
2647 dpNetclass->SetTrackWidth(
scaleSize( dp.width ) );
2652 if( !dp.positive_net.empty() )
2658 if( !dp.negative_net.empty() )
2668 const auto& boardOutlines =
m_parser->GetBoardOutlines();
2670 if( !boardOutlines.empty() )
2672 double min_x = std::numeric_limits<double>::max();
2673 double max_x = std::numeric_limits<double>::lowest();
2674 double min_y = std::numeric_limits<double>::max();
2675 double max_y = std::numeric_limits<double>::lowest();
2677 for(
const auto& outline : boardOutlines )
2679 for(
const auto& pt : outline.points )
2681 min_x = std::min( min_x, pt.x );
2682 max_x = std::max( max_x, pt.x );
2683 min_y = std::min( min_y, pt.y );
2684 max_y = std::max( max_y, pt.y );
2688 if( min_x < max_x && min_y < max_y )
2697 std::vector<const PADS_IO::LAYER_INFO*> copperLayerInfos;
2699 for(
const auto& li : padsLayerInfos )
2702 copperLayerInfos.push_back( &li );
2705 bool hasStackupData =
false;
2707 for(
const auto* li : copperLayerInfos )
2709 if( li->layer_thickness > 0.0 || li->dielectric_constant > 0.0 )
2711 hasStackupData =
true;
2716 if( hasStackupData )
2723 std::map<PCB_LAYER_ID, const PADS_IO::LAYER_INFO*> copperInfoMap;
2725 for(
const auto* li : copperLayerInfos )
2730 copperInfoMap[kicadLayer] = li;
2740 auto it = copperInfoMap.find( item->GetBrdLayerId() );
2742 if( it != copperInfoMap.end() )
2744 prevCopperInfo = it->second;
2746 if( it->second->copper_thickness > 0.0 )
2747 item->SetThickness(
scaleSize( it->second->copper_thickness ) );
2752 if( prevCopperInfo )
2763 item->SetColor( wxT(
"White" ) );
2767 item->SetColor( wxT(
"Green" ) );
@ BS_ITEM_TYPE_SILKSCREEN
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERMASK
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BASE_SET & set(size_t pos)
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
void SetCustomTrackWidth(int aWidth)
Sets custom width for track (i.e.
int m_CopperEdgeClearance
void SetCustomViaSize(int aSize)
Set custom size for via diameter (i.e.
int m_SolderMaskExpansion
BOARD_STACKUP & GetStackupDescriptor()
void SetCustomViaDrill(int aDrill)
Sets custom size for via drill (i.e.
void SetBoardThickness(int aThickness)
ZONE_SETTINGS & GetDefaultZoneSettings()
std::vector< VIA_DIMENSION > m_ViasDimensionsList
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Manage one layer needed to make a physical board.
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
int BuildBoardThicknessFromStackup() const
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Information pertinent to a Pcbnew printed circuit board.
void SetCenter(const VECTOR2I &aCenter)
void SetStart(const VECTOR2I &aStart)
void SetShape(SHAPE_T aShape)
void SetEnd(const VECTOR2I &aEnd)
void SetWidth(int aWidth)
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
virtual void SetVisible(bool aVisible)
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
void SetKeepUpright(bool aKeepUpright)
virtual void SetText(const wxString &aText)
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
virtual void RegisterCallback(LAYER_MAPPING_HANDLER aLayerMappingHandler)
Register a different handler to be called when mapping of input layers to KiCad layers occurs.
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
A logical library item identifier and consists of various portions much like a URI.
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
LSET is a set of PCB_LAYER_IDs.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Handle the data for a net.
const wxString & GetNetname() const
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
void SetNetclass(const wxString &netclassName, std::shared_ptr< NETCLASS > &netclass)
Sets the given netclass Calling user is responsible for resetting the effective netclass calculation ...
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
void Parse(const wxString &aFileName)
Maps PADS layer numbers and names to KiCad layer IDs.
Converts PADS file format units to KiCad internal units (nanometers).
static constexpr double MILS_TO_NM
static constexpr double INCHES_TO_NM
static constexpr double BASIC_TO_NM
static constexpr double MM_TO_NM
void Update()
Update the dimension's cached text and geometry.
void SetOverrideTextEnabled(bool aOverride)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
void SetOverrideText(const wxString &aValue)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
A set of BOARD_ITEMs (i.e., without duplicates).
const IO_FILE_DESC GetBoardFileDesc() const override
Returns board file description for the PCB_IO.
int scaleSize(double aVal) const
std::vector< PADS_LAYER_INFO > m_layerInfos
SHAPE_ARC makeMidpointArc(const PADS_IO::ARC_POINT &aPrev, const PADS_IO::ARC_POINT &aCurr, int aWidth)
Build a SHAPE_ARC from two consecutive PADS points using the midpoint approach.
std::map< std::string, std::string > m_partToBlockMap
int scaleCoord(double aVal, bool aIsX) const
void setPcbShapeArc(PCB_SHAPE *aShape, const PADS_IO::ARC_POINT &aPrev, const PADS_IO::ARC_POINT &aCurr)
Configure a PCB_SHAPE as an arc from two consecutive PADS points using board-level scaleCoord.
std::map< std::string, std::string > m_pinToNetMap
PCB_LAYER_ID getMappedLayer(int aPadsLayer) const
void appendArcPoints(SHAPE_LINE_CHAIN &aChain, const std::vector< PADS_IO::ARC_POINT > &aPts)
Interpolate arc segments from an ARC_POINT vector into polyline vertices on a SHAPE_LINE_CHAIN.
std::map< wxString, PCB_LAYER_ID > m_layer_map
PADS layer names to KiCad layers.
void generateDrcRules(const wxString &aFileName)
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
const IO_FILE_DESC GetLibraryDesc() const override
Get the descriptor for the library container that this IO plugin operates on.
void ensureNet(const std::string &aNetName)
PADS_LAYER_MAPPER m_layerMapper
PADS_UNIT_CONVERTER m_unitConverter
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
const PADS_IO::PARSER * m_parser
void loadReuseBlockGroups()
virtual bool CanReadBoard(const wxString &aFileName) const
Checks if this PCB_IO can read the specified board file.
PCB_IO(const wxString &aName)
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStroke(const STROKE_PARAMS &aStroke) override
virtual void SetPosition(const VECTOR2I &aPos) override
void SetEnd(const VECTOR2I &aEnd)
void SetStart(const VECTOR2I &aStart)
virtual void SetWidth(int aWidth)
Container for project specific data.
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsPolygonSelfIntersecting(int aPolygonIndex) const
Check whether the aPolygonIndex-th polygon in the set is self intersecting.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
Simple container to manage line stroke parameters.
Handle a list of polygons defining a copper zone.
void SetDoNotAllowPads(bool aEnable)
void SetMinThickness(int aMinThickness)
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
SHAPE_POLY_SET * Outline()
void SetIsRuleArea(bool aEnable)
void SetDoNotAllowTracks(bool aEnable)
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
void SetIsFilled(bool isFilled)
void SetLayerSet(const LSET &aLayerSet) override
void SetDoNotAllowVias(bool aEnable)
void SetThermalReliefGap(int aThermalReliefGap)
void SetDoNotAllowFootprints(bool aEnable)
void SetDoNotAllowZoneFills(bool aEnable)
void SetAssignedPriority(unsigned aPriority)
void SetPadConnection(ZONE_CONNECTION aPadConnection)
void SetZoneName(const wxString &aName)
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
double m_PadsPcbTextWidthScale
PADS text width scale factor for PCB imports.
double m_PadsPcbTextHeightScale
PADS text height scale factor for PCB imports.
int m_PcbImportMinObjectSizeNm
Minimum object size in nanometers for PCB imports.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
@ LEFT_RIGHT
Flip left to right (around the Y axis)
wxString ConvertInvertedNetName(const std::string &aNetName)
Convert a PADS net name to KiCad format, handling inverted signal notation.
KIID GenerateDeterministicUuid(const std::string &aIdentifier)
Generate a deterministic KIID from a PADS component identifier.
@ NONE
No thermal relief defined.
@ BURIED
Via spans only inner layers.
@ THROUGH
Via spans all copper layers.
@ BLIND
Via starts at top or bottom and ends at inner layer.
@ MICROVIA
Single-layer blind via (typically HDI)
@ ROUTE
Routing keepout (traces)
@ PLACEMENT
Component placement keepout.
@ COPPER
Copper pour keepout.
@ VOIDOUT
Void/empty region (VOIDOUT)
@ HATCHED
Hatched pour (HATOUT)
PADS_LAYER_FUNCTION
Layer types from PADS LAYER_TYPE field.
@ ASSEMBLY
Assembly drawing.
@ ROUTING
Copper routing layer.
@ PASTE_MASK
Solder paste mask.
@ MIXED
Mixed signal/plane.
@ UNASSIGNED
Unassigned layer.
@ DOCUMENTATION
Documentation layer.
@ SILK_SCREEN
Silkscreen/legend.
@ PLANE
Power/ground plane.
@ SOLDER_MASK
Solder mask.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Common utilities and types for parsing PADS file formats.
PADS_LAYER_TYPE
PADS layer types.
@ MILS
Thousandths of an inch (1 mil = 0.001 inch)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
Class to handle a set of BOARD_ITEMs.
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
Container that describes file type info.
wxString m_Description
Description shown in the file picker dialog.
std::vector< std::string > m_FileExtensions
Filter used for file pickers if m_IsFile is true.
A point that may be either a line endpoint or an arc segment.
ARC arc
Arc parameters (only valid when is_arc is true)
bool is_arc
True if this segment is an arc, false for line.
double y
Endpoint Y coordinate.
double x
Endpoint X coordinate.
double cx
Center X coordinate.
double delta_angle
Arc sweep angle in degrees (positive = CCW)
double cy
Center Y coordinate.
A copper shape from the LINES section (type=COPPER).
A 2D graphic line/shape from the LINES section (type=LINES).
double layer_thickness
Dielectric thickness (BASIC units)
double dielectric_constant
Relative permittivity (Er)
double drill
Drill hole diameter (0 for SMD)
std::string shape
Shape code: R, S, A, O, OF, RF, RT, ST, RA, SA, RC, OC.
bool plated
True if drill is plated (PTH vs NPTH)
double rotation
Pad rotation angle in degrees.
double slot_orientation
Slot orientation in degrees (0-179.999)
double slot_length
Slot length.
double finger_offset
Finger pad offset along orientation axis.
double sizeB
Secondary size (height for rectangles/ovals)
double corner_radius
Corner radius magnitude (always positive)
double sizeA
Primary size (diameter or width)
std::vector< DECAL_ITEM > items
std::vector< TERMINAL > terminals
std::map< int, std::vector< PAD_STACK_LAYER > > pad_stacks
std::map< std::string, std::string > attributes
Attribute name-value pairs from {...} block.
A polyline that may contain arc segments.
bool has_mask_front
Stack includes top soldermask opening (layer 25)
int start_layer
First PADS layer number in via span.
int end_layer
Last PADS layer number in via span.
bool has_mask_back
Stack includes bottom soldermask opening (layer 28)
Information about a single PADS layer.
@ USER
The field ID hasn't been set yet; field is invalid.
wxString result
Test unit parsing edge cases and error handling.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
VECTOR2< int32_t > VECTOR2I
@ THERMAL
Use thermal relief for pads.