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:
parent
8e1466a35a
commit
acc03e91f3
|
@ -2,6 +2,7 @@
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* 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) 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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" ) )
|
else if( rulekind == wxT( "PasteMaskExpansion" ) )
|
||||||
{
|
{
|
||||||
kind = ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION;
|
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" ) )
|
else if( rulekind == wxT( "PlaneClearance" ) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* 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) 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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,
|
HOLE_TO_HOLE_CLEARANCE = 5,
|
||||||
WIDTH = 6,
|
WIDTH = 6,
|
||||||
PASTE_MASK_EXPANSION = 7,
|
PASTE_MASK_EXPANSION = 7,
|
||||||
PLANE_CLEARANCE = 8,
|
SOLDER_MASK_EXPANSION = 8,
|
||||||
POLYGON_CONNECT = 9,
|
PLANE_CLEARANCE = 9,
|
||||||
|
POLYGON_CONNECT = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ALTIUM_CONNECT_STYLE
|
enum class ALTIUM_CONNECT_STYLE
|
||||||
|
@ -502,6 +503,12 @@ struct ARULE6
|
||||||
// ALTIUM_RULE_KIND::PLANE_CLEARANCE
|
// ALTIUM_RULE_KIND::PLANE_CLEARANCE
|
||||||
int planeclearanceClearance;
|
int planeclearanceClearance;
|
||||||
|
|
||||||
|
// ALTIUM_RULE_KIND::SOLDER_MASK_EXPANSION
|
||||||
|
int soldermaskExpansion;
|
||||||
|
|
||||||
|
// ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION
|
||||||
|
int pastemaskExpansion;
|
||||||
|
|
||||||
// ALTIUM_RULE_KIND::POLYGON_CONNECT
|
// ALTIUM_RULE_KIND::POLYGON_CONNECT
|
||||||
int32_t polygonconnectAirgapwidth;
|
int32_t polygonconnectAirgapwidth;
|
||||||
int32_t polygonconnectReliefconductorwidth;
|
int32_t polygonconnectReliefconductorwidth;
|
||||||
|
|
|
@ -771,7 +771,7 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile
|
||||||
case ALTIUM_RECORD::REGION:
|
case ALTIUM_RECORD::REGION:
|
||||||
{
|
{
|
||||||
AREGION6 region( parser, false );
|
AREGION6 region( parser, false );
|
||||||
ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region );
|
ConvertShapeBasedRegions6ToFootprintItem( footprint.get(), region, primitiveIndex );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ALTIUM_RECORD::MODEL:
|
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 )
|
if( reader.GetRemainingBytes() != 0 )
|
||||||
THROW_IO_ERROR( wxT( "Rules6 stream is not fully parsed" ) );
|
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 )
|
const CFB::COMPOUND_FILE_ENTRY* aEntry )
|
||||||
{
|
{
|
||||||
if( m_progressReporter )
|
if( m_progressReporter )
|
||||||
m_progressReporter->Report( _( "Loading zones..." ) );
|
m_progressReporter->Report( _( "Loading polygons..." ) );
|
||||||
|
|
||||||
ALTIUM_PARSER reader( aAltiumPcbFile, aEntry );
|
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();
|
checkpoint();
|
||||||
AREGION6 elem( reader, true );
|
AREGION6 elem( reader, true );
|
||||||
|
@ -1912,7 +1922,7 @@ void ALTIUM_PCB::ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aA
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FOOTPRINT* footprint = HelperGetFootprint( elem.component );
|
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,
|
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 )
|
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 )
|
if( aElem.subpolyindex == ALTIUM_POLYGON_NONE )
|
||||||
{
|
{
|
||||||
for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
|
for( PCB_LAYER_ID klayer : GetKicadLayersToIterate( aElem.layer ) )
|
||||||
ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer );
|
ConvertShapeBasedRegions6ToFootprintItemOnLayer( aFootprint, aElem, klayer,
|
||||||
|
aPrimitiveIndex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2062,7 +2074,8 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToBoardItemOnLayer( const AREGION6& aE
|
||||||
|
|
||||||
void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
|
void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
|
||||||
const AREGION6& aElem,
|
const AREGION6& aElem,
|
||||||
PCB_LAYER_ID aLayer )
|
PCB_LAYER_ID aLayer,
|
||||||
|
const int aPrimitiveIndex )
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN linechain;
|
SHAPE_LINE_CHAIN linechain;
|
||||||
HelperShapeLineChainFromAltiumVertices( linechain, aElem.outline );
|
HelperShapeLineChainFromAltiumVertices( linechain, aElem.outline );
|
||||||
|
@ -2076,15 +2089,85 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FP_SHAPE* shape = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
|
SHAPE_POLY_SET polySet;
|
||||||
|
polySet.AddOutline( linechain );
|
||||||
|
|
||||||
shape->SetPolyShape( linechain );
|
for( const std::vector<ALTIUM_VERTICE>& hole : aElem.holes )
|
||||||
shape->SetFilled( true );
|
{
|
||||||
shape->SetLayer( aLayer );
|
SHAPE_LINE_CHAIN hole_linechain;
|
||||||
shape->SetStroke( STROKE_PARAMS( 0 ) );
|
HelperShapeLineChainFromAltiumVertices( hole_linechain, hole );
|
||||||
|
|
||||||
HelperShapeSetLocalCoord( shape );
|
if( hole_linechain.PointCount() < 3 )
|
||||||
aFootprint->Add( shape, ADD_MODE::APPEND );
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* 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) 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -196,11 +197,13 @@ private:
|
||||||
void ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
void ParseShapeBasedRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
||||||
const CFB::COMPOUND_FILE_ENTRY* aEntry );
|
const CFB::COMPOUND_FILE_ENTRY* aEntry );
|
||||||
void ConvertShapeBasedRegions6ToBoardItem( const AREGION6& aElem );
|
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 ConvertShapeBasedRegions6ToBoardItemOnLayer( const AREGION6& aElem, PCB_LAYER_ID aLayer );
|
||||||
void ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
|
void ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* aFootprint,
|
||||||
const AREGION6& aElem,
|
const AREGION6& aElem,
|
||||||
PCB_LAYER_ID aLayer );
|
PCB_LAYER_ID aLayer,
|
||||||
|
const int aPrimitiveIndex );
|
||||||
void ParseExtendedPrimitiveInformationData( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
void ParseExtendedPrimitiveInformationData( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
||||||
const CFB::COMPOUND_FILE_ENTRY* aEntry );
|
const CFB::COMPOUND_FILE_ENTRY* aEntry );
|
||||||
void ParseRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
void ParseRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
||||||
|
|
Loading…
Reference in New Issue