Altium PCB: support solder/paste mask expansion rules.

- Writes expansion values to board design settings
- Imports footprint regions on Cu layers as pads
- Adds support for holes in non-Cu polygons in footprints
This commit is contained in:
Alex Shvartzkop 2023-07-21 20:02:59 +05:00
parent 8e1466a35a
commit acc03e91f3
4 changed files with 120 additions and 19 deletions

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -533,6 +534,13 @@ ARULE6::ARULE6( ALTIUM_PARSER& aReader )
else if( rulekind == wxT( "PasteMaskExpansion" ) )
{
kind = ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION;
pastemaskExpansion = ALTIUM_PARSER::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "0" ) );
}
else if( rulekind == wxT( "SolderMaskExpansion" ) )
{
kind = ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION;
soldermaskExpansion =
ALTIUM_PARSER::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "4mil" ) );
}
else if( rulekind == wxT( "PlaneClearance" ) )
{

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -105,8 +105,9 @@ enum class ALTIUM_RULE_KIND
HOLE_TO_HOLE_CLEARANCE = 5,
WIDTH = 6,
PASTE_MASK_EXPANSION = 7,
PLANE_CLEARANCE = 8,
POLYGON_CONNECT = 9,
SOLDER_MASK_EXPANSION = 8,
PLANE_CLEARANCE = 9,
POLYGON_CONNECT = 10,
};
enum class ALTIUM_CONNECT_STYLE
@ -502,6 +503,12 @@ struct ARULE6
// ALTIUM_RULE_KIND::PLANE_CLEARANCE
int planeclearanceClearance;
// ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION
int soldermaskExpansion;
// ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION
int pastemaskExpansion;
// ALTIUM_RULE_KIND::POLYGON_CONNECT
int32_t polygonconnectAirgapwidth;
int32_t polygonconnectReliefconductorwidth;

View File

@ -771,7 +771,7 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
case ALTIUM_RECORD::REGION:
{
AREGION6 region( parser, false );
ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region );
ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
break;
}
case ALTIUM_RECORD::MODEL:
@ -1866,6 +1866,15 @@ void ALTIUM_PCB::ParseRules6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile
} );
}
const ARULE6* soldermaskRule = GetRuleDefault( ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION );
const ARULE6* pastemaskRule = GetRuleDefault( ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION );
if( soldermaskRule )
m_board->GetDesignSettings().m_SolderMaskExpansion = soldermaskRule->soldermaskExpansion;
if( pastemaskRule )
m_board->GetDesignSettings().m_SolderPasteMargin = pastemaskRule->pastemaskExpansion;
if( reader.GetRemainingBytes() != 0 )
THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
}
@ -1894,11 +1903,12 @@ void ALTIUM_PCB::ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aA
const CFB::COMPOUND_FILE_ENTRY* aEntry )
{
if( m_progressReporter )
m_progressReporter->Report( _( "Loading zones..." ) );
m_progressReporter->Report( _( "Loading polygons..." ) );
ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
/* TODO: use Header section of file */
for( int primitiveIndex = 0; reader.GetRemainingBytes() >= 4; primitiveIndex++ )
{
checkpoint();
AREGION6 elem( reader, true );
@ -1912,7 +1922,7 @@ void ALTIUM_PCB::ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aA
else
{
FOOTPRINT* footprint = HelperGetFootprint( elem.component );
ConvertShapeBasedRegions6ToFootprintItem( footprint, elem );
ConvertShapeBasedRegions6ToFootprintItem( footprint, elem, primitiveIndex );
}
}
@ -1974,7 +1984,8 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToBoardItem( const AREGION6& aElem )
void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItem( FOOTPRINT* aFootprint,
const AREGION6& aElem )
const AREGION6& aElem,
const int aPrimitiveIndex )
{
if( aElem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || aElem.is_keepout )
{
@ -2010,7 +2021,8 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItem( FOOTPRINT* aFoot
if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
{
for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
aPrimitiveIndex );
}
}
else
@ -2062,7 +2074,8 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToBoardItemOnLayer( const AREGION6& aE
void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
const AREGION6& aElem,
PCB_LAYER_ID aLayer )
PCB_LAYER_ID aLayer,
const int aPrimitiveIndex )
{
SHAPE_LINE_CHAIN linechain;
HelperShapeLineChainFromAltiumVertices( linechain, aElem.outline );
@ -2076,15 +2089,85 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
return;
}
FP_SHAPE* shape = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
SHAPE_POLY_SET polySet;
polySet.AddOutline( linechain );
shape->SetPolyShape( linechain );
shape->SetFilled( true );
shape->SetLayer( aLayer );
shape->SetStroke( STROKE_PARAMS( 0 ) );
for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
{
SHAPE_LINE_CHAIN hole_linechain;
HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
HelperShapeSetLocalCoord( shape );
aFootprint->Add( shape, ADD_MODE::APPEND );
if( hole_linechain.PointCount() < 3 )
continue;
polySet.AddHole( hole_linechain );
}
if( aLayer == F_Cu || aLayer == B_Cu )
{
PAD* pad = new PAD( aFootprint );
LSET padLayers;
padLayers.set( aLayer );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetAttribute( PAD_ATTRIB::SMD );
pad->SetShape( PAD_SHAPE::CUSTOM );
int anchorSize = 1;
VECTOR2I anchorPos = linechain.CPoint( 0 );
pad->SetShape( PAD_SHAPE::CUSTOM );
pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
pad->SetSize( { anchorSize, anchorSize } );
pad->SetPosition( anchorPos );
SHAPE_POLY_SET shapePolys = polySet;
shapePolys.Move( -anchorPos );
pad->AddPrimitivePoly( shapePolys, 0, true );
auto& map = m_extendedPrimitiveInformationMaps[ALTIUM_RECORD::REGION];
auto it = map.find( aPrimitiveIndex );
if( it != map.end() )
{
const AEXTENDED_PRIMITIVE_INFORMATION& info = it->second;
if( info.pastemaskexpansionmode == ALTIUM_MODE::MANUAL )
{
pad->SetLocalSolderPasteMargin(
info.pastemaskexpansionmanual ? info.pastemaskexpansionmanual : 1 );
}
if( info.soldermaskexpansionmode == ALTIUM_MODE::MANUAL )
{
pad->SetLocalSolderMaskMargin(
info.soldermaskexpansionmanual ? info.soldermaskexpansionmanual : 1 );
}
if( info.pastemaskexpansionmode != ALTIUM_MODE::NONE )
padLayers.set( aLayer == F_Cu ? F_Paste : B_Paste );
if( info.soldermaskexpansionmode != ALTIUM_MODE::NONE )
padLayers.set( aLayer == F_Cu ? F_Mask : B_Mask );
}
pad->SetLayerSet( padLayers );
aFootprint->Add( pad, ADD_MODE::APPEND );
}
else
{
PCB_SHAPE* shape = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
shape->SetPolyShape( polySet );
shape->SetFilled( true );
shape->SetLayer( aLayer );
shape->SetStroke( STROKE_PARAMS( 0 ) );
HelperShapeSetLocalCoord(shape);
aFootprint->Add( shape, ADD_MODE::APPEND );
}
}

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -196,11 +197,13 @@ private:
void ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ConvertShapeBasedRegions6ToBoardItem( const AREGION6& aElem );
void ConvertShapeBasedRegions6ToFootprintItem( FOOTPRINT* aFootprint, const AREGION6& aElem );
void ConvertShapeBasedRegions6ToFootprintItem( FOOTPRINT* aFootprint, const AREGION6& aElem,
const int aPrimitiveIndex );
void ConvertShapeBasedRegions6ToBoardItemOnLayer( const AREGION6& aElem, PCB_LAYER_ID aLayer );
void ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
const AREGION6& aElem,
PCB_LAYER_ID aLayer );
PCB_LAYER_ID aLayer,
const int aPrimitiveIndex );
void ParseExtendedPrimitiveInformationData( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,