Don't require callers to jump through hoops.

Fixes KICAD-Y6J.
Fixes KICAD-Y6H.
Fixes KICAD-Y6G.
This commit is contained in:
Jeff Young 2025-07-31 15:57:36 +01:00
parent b0663d84cb
commit 736d3c2d26
3 changed files with 36 additions and 75 deletions

View File

@ -64,8 +64,7 @@ static bool cmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
} }
void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list )
bool aGenerateNPTH_list )
{ {
HOLE_INFO new_hole; HOLE_INFO new_hole;
@ -75,9 +74,9 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
// build hole list for vias // build hole list for vias
if( ! aGenerateNPTH_list ) // vias are always plated ! if( !aGenerateNPTH_list ) // vias are always plated !
{ {
for( auto track : m_pcb->Tracks() ) for( PCB_TRACK* track : m_pcb->Tracks() )
{ {
if( track->Type() != PCB_VIA_T ) if( track->Type() != PCB_VIA_T )
continue; continue;
@ -99,7 +98,7 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
new_hole.m_Hole_Orient = ANGLE_0; new_hole.m_Hole_Orient = ANGLE_0;
new_hole.m_Hole_Diameter = hole_sz; new_hole.m_Hole_Diameter = hole_sz;
new_hole.m_Hole_NotPlated = false; new_hole.m_Hole_NotPlated = false;
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Shape = 0; // hole shape: round
new_hole.m_Hole_Pos = via->GetStart(); new_hole.m_Hole_Pos = via->GetStart();
@ -109,29 +108,19 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false ); new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false );
new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false ); new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false );
new_hole.m_Hole_Top_Covered = new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Top_Layer ).value_or( false );
via->Padstack().IsCovered( new_hole.m_Hole_Top_Layer ).value_or( false ); new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Bot_Covered = new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Top_Layer ).value_or( false );
via->Padstack().IsCovered( new_hole.m_Hole_Bottom_Layer ).value_or( false ); new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Top_Plugged = new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( new_hole.m_Hole_Top_Layer ).value_or( false );
via->Padstack().IsPlugged( new_hole.m_Hole_Top_Layer ).value_or( false ); new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Bot_Plugged =
via->Padstack().IsPlugged( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Top_Tented =
via->Padstack().IsTented( new_hole.m_Hole_Top_Layer ).value_or( false );
new_hole.m_Hole_Bot_Tented =
via->Padstack().IsTented( new_hole.m_Hole_Bottom_Layer ).value_or( false );
// LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
// Remember: top layer = 0 and bottom layer = 31 for through hole vias // Remember: top layer = 0 and bottom layer = 31 for through hole vias
// Any captured via should be from aLayerPair.first to aLayerPair.second exactly. // Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
if( new_hole.m_Hole_Top_Layer != aLayerPair.first || if( new_hole.m_Hole_Top_Layer != aLayerPair.first || new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
continue; continue;
m_holeListBuffer.push_back( new_hole ); m_holeListBuffer.push_back( new_hole );
@ -159,9 +148,8 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
new_hole.m_ItemParent = pad; new_hole.m_ItemParent = pad;
new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB::NPTH); new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB::NPTH);
new_hole.m_HoleAttribute = new_hole.m_Hole_NotPlated new_hole.m_HoleAttribute = new_hole.m_Hole_NotPlated ? HOLE_ATTRIBUTE::HOLE_MECHANICAL
? HOLE_ATTRIBUTE::HOLE_MECHANICAL : HOLE_ATTRIBUTE::HOLE_PAD;
: HOLE_ATTRIBUTE::HOLE_PAD;
new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Orient = pad->GetOrientation();
new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Shape = 0; // hole shape: round
@ -169,11 +157,8 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
// Convert oblong holes that are actually circular into drill hits // Convert oblong holes that are actually circular into drill hits
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE && if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
pad->GetDrillSizeX() != pad->GetDrillSizeY() )
{
new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Shape = 1; // oval flag set
}
new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Size = pad->GetDrillSize();
new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
@ -300,7 +285,9 @@ const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, b
wxString extend; wxString extend;
if( aNPTH ) if( aNPTH )
{
extend = wxT( "-NPTH" ); extend = wxT( "-NPTH" );
}
else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) ) else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{ {
if( !aMerge_PTH_NPTH ) if( !aMerge_PTH_NPTH )
@ -378,8 +365,7 @@ const wxString GENDRILL_WRITER_BASE::getProtectionFileName( DRILL_LAYER_PAIR aPa
} }
bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, REPORTER * aReporter )
REPORTER * aReporter )
{ {
wxFileName fn; wxFileName fn;
wxString msg; wxString msg;
@ -390,8 +376,7 @@ bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory,
if( !m_merge_PTH_NPTH ) if( !m_merge_PTH_NPTH )
hole_sets.emplace_back( F_Cu, B_Cu ); hole_sets.emplace_back( F_Cu, B_Cu );
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin(); for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
it != hole_sets.end(); ++it )
{ {
DRILL_LAYER_PAIR pair = *it; DRILL_LAYER_PAIR pair = *it;
// For separate drill files, the last layer pair is the NPTH drill file. // For separate drill files, the last layer pair is the NPTH drill file.
@ -439,9 +424,9 @@ bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory,
} }
const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( DRILL_LAYER_PAIR aLayerPair,
DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHoleType, TYPE_FILE aHoleType,
bool aCompatNCdrill ) const bool aCompatNCdrill ) const
{ {
// Build a wxString containing the .FileFunction attribute for drill files. // Build a wxString containing the .FileFunction attribute for drill files.
// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*% // %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*%
@ -488,9 +473,7 @@ const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString(
if( aHoleType == NPTH_FILE ) if( aHoleType == NPTH_FILE )
text << wxT( ",NPTH" ); text << wxT( ",NPTH" );
else if( aHoleType == MIXED_FILE ) // only for Excellon format else if( aHoleType == MIXED_FILE ) // only for Excellon format
{ ; // write nothing
// write nothing
}
else if( layer1 == toplayer && layer2 == bottomlayer ) else if( layer1 == toplayer && layer2 == bottomlayer )
text << wxT( ",PTH" ); text << wxT( ",PTH" );
else if( layer1 == toplayer || layer2 == bottomlayer ) else if( layer1 == toplayer || layer2 == bottomlayer )

View File

@ -1358,7 +1358,7 @@ std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
if( IsBackLayer( aSide ) ) if( IsBackLayer( aSide ) )
return m_backMaskProps.has_solder_mask; return m_backMaskProps.has_solder_mask;
wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" ); return false;
} }
std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const
@ -1369,7 +1369,7 @@ std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const
if( IsBackLayer( aSide ) ) if( IsBackLayer( aSide ) )
return m_backMaskProps.has_covering; return m_backMaskProps.has_covering;
wxCHECK_MSG( false, std::nullopt, "IsCovered expects a front or back layer" ); return false;
} }
std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const
@ -1380,7 +1380,7 @@ std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const
if( IsBackLayer( aSide ) ) if( IsBackLayer( aSide ) )
return m_backMaskProps.has_plugging; return m_backMaskProps.has_plugging;
wxCHECK_MSG( false, std::nullopt, "IsPlugged expects a front or back layer" ); return false;
} }
std::optional<bool> PADSTACK::IsCapped() const std::optional<bool> PADSTACK::IsCapped() const

View File

@ -1766,37 +1766,25 @@ void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
std::vector<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>> new_layers; std::vector<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>> new_layers;
if( via->Padstack().IsFilled().value_or( false ) ) if( via->Padstack().IsFilled().value_or( false ) )
{
new_layers.emplace_back( auxLayerType::FILLING, via->TopLayer(), via->BottomLayer() ); new_layers.emplace_back( auxLayerType::FILLING, via->TopLayer(), via->BottomLayer() );
}
if( via->Padstack().IsCapped().value_or( false ) ) if( via->Padstack().IsCapped().value_or( false ) )
{
new_layers.emplace_back( auxLayerType::CAPPING, via->TopLayer(), via->BottomLayer() ); new_layers.emplace_back( auxLayerType::CAPPING, via->TopLayer(), via->BottomLayer() );
}
for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } ) for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } )
{ {
if( via->Padstack().IsPlugged( layer ).value_or( false ) ) if( via->Padstack().IsPlugged( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::PLUGGING, layer, UNDEFINED_LAYER ); new_layers.emplace_back( auxLayerType::PLUGGING, layer, UNDEFINED_LAYER );
}
if( via->Padstack().IsCovered( layer ).value_or( false ) ) if( via->Padstack().IsCovered( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::COVERING, layer, UNDEFINED_LAYER ); new_layers.emplace_back( auxLayerType::COVERING, layer, UNDEFINED_LAYER );
}
if( via->Padstack().IsTented( layer ).value_or( false ) ) if( via->Padstack().IsTented( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::TENTING, layer, UNDEFINED_LAYER ); new_layers.emplace_back( auxLayerType::TENTING, layer, UNDEFINED_LAYER );
}
} }
for( auto& tuple : new_layers ) for( auto& tuple : new_layers )
{
m_auxilliary_Layers[tuple].push_back( via ); m_auxilliary_Layers[tuple].push_back( via );
}
} }
for( const auto& [layers, vec] : m_auxilliary_Layers ) for( const auto& [layers, vec] : m_auxilliary_Layers )
@ -1844,19 +1832,15 @@ void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
if( std::get<2>( layers ) == UNDEFINED_LAYER ) if( std::get<2>( layers ) == UNDEFINED_LAYER )
{ {
addAttribute( node, "name", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) ); addAttribute( node, "name", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) );
addAttribute( node, "side", addAttribute( node, "side", IsFrontLayer( std::get<1>( layers ) ) ? "TOP" : "BOTTOM" );
IsFrontLayer( std::get<1>( layers ) ) ? "TOP" : "BOTTOM" );
} }
else else
{ {
addAttribute( addAttribute( node, "name",
node, "name", genLayersString( std::get<1>( layers ), std::get<2>( layers ), TO_UTF8( name ) ) );
genLayersString( std::get<1>( layers ), std::get<2>( layers ), TO_UTF8( name ) ) );
const bool first_external = const bool first_external = std::get<1>( layers ) == F_Cu || std::get<1>( layers ) == B_Cu;
std::get<1>( layers ) == F_Cu || std::get<1>( layers ) == B_Cu; const bool second_external = std::get<2>( layers ) == F_Cu || std::get<2>( layers ) == B_Cu;
const bool second_external =
std::get<2>( layers ) == F_Cu || std::get<2>( layers ) == B_Cu;
if( first_external ) if( first_external )
{ {
@ -1874,10 +1858,8 @@ void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
} }
wxXmlNode* spanNode = appendNode( node, "SPAN" ); wxXmlNode* spanNode = appendNode( node, "SPAN" );
addAttribute( spanNode, "fromLayer", addAttribute( spanNode, "fromLayer", genLayerString( std::get<1>( layers ), "LAYER" ) );
genLayerString( std::get<1>( layers ), "LAYER" ) ); addAttribute( spanNode, "toLayer", genLayerString( std::get<2>( layers ), "LAYER" ) );
addAttribute( spanNode, "toLayer",
genLayerString( std::get<2>( layers ), "LAYER" ) );
} }
} }
} }
@ -1903,8 +1885,7 @@ void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
m_last_padstack = insertNode( stepNode, "NonstandardAttribute" ); m_last_padstack = insertNode( stepNode, "NonstandardAttribute" );
addAttribute( m_last_padstack, "name", "FOOTPRINT_COUNT" ); addAttribute( m_last_padstack, "name", "FOOTPRINT_COUNT" );
addAttribute( m_last_padstack, "type", "INTEGER" ); addAttribute( m_last_padstack, "type", "INTEGER" );
addAttribute( m_last_padstack, "value", addAttribute( m_last_padstack, "value", wxString::Format( "%zu", m_board->Footprints().size() ) );
wxString::Format( "%zu", m_board->Footprints().size() ) );
generateLayerFeatures( stepNode ); generateLayerFeatures( stepNode );
generateLayerSetDrill( stepNode ); generateLayerSetDrill( stepNode );
@ -2087,12 +2068,10 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
} }
if( aVia->Padstack().IsFilled().value_or( false ) ) if( aVia->Padstack().IsFilled().value_or( false ) )
addPadShape( UNDEFINED_LAYER, aVia, addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "FILLING" ), true );
genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "FILLING" ), true );
if( aVia->Padstack().IsCapped().value_or( false ) ) if( aVia->Padstack().IsCapped().value_or( false ) )
addPadShape( UNDEFINED_LAYER, aVia, addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "CAPPING" ), true );
genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "CAPPING" ), true );
for( PCB_LAYER_ID layer : { aVia->TopLayer(), aVia->BottomLayer() } ) for( PCB_LAYER_ID layer : { aVia->TopLayer(), aVia->BottomLayer() } )
{ {
@ -2232,8 +2211,7 @@ bool PCB_IO_IPC2581::addOutlineNode( wxXmlNode* aParentNode, const SHAPE_POLY_SE
bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet, bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
int aOutline, FILL_T aFillType, int aWidth, int aOutline, FILL_T aFillType, int aWidth, LINE_STYLE aDashType )
LINE_STYLE aDashType )
{ {
if( aPolySet.OutlineCount() < ( aOutline + 1 ) ) if( aPolySet.OutlineCount() < ( aOutline + 1 ) )
return false; return false;