diff --git a/pcbnew/plugins/altium/altium_parser_pcb.cpp b/pcbnew/plugins/altium/altium_parser_pcb.cpp index 35ff48b95c..613f23704a 100644 --- a/pcbnew/plugins/altium/altium_parser_pcb.cpp +++ b/pcbnew/plugins/altium/altium_parser_pcb.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Thomas Pointhuber + * 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" ) ) { diff --git a/pcbnew/plugins/altium/altium_parser_pcb.h b/pcbnew/plugins/altium/altium_parser_pcb.h index 031c4a4b2d..987220ac2f 100644 --- a/pcbnew/plugins/altium/altium_parser_pcb.h +++ b/pcbnew/plugins/altium/altium_parser_pcb.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Thomas Pointhuber - * 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; diff --git a/pcbnew/plugins/altium/altium_pcb.cpp b/pcbnew/plugins/altium/altium_pcb.cpp index cea3bd1cf4..8a9bba3bc5 100644 --- a/pcbnew/plugins/altium/altium_pcb.cpp +++ b/pcbnew/plugins/altium/altium_pcb.cpp @@ -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& 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 ); + } } diff --git a/pcbnew/plugins/altium/altium_pcb.h b/pcbnew/plugins/altium/altium_pcb.h index 3cec9c0ae7..a6d261b4b6 100644 --- a/pcbnew/plugins/altium/altium_pcb.h +++ b/pcbnew/plugins/altium/altium_pcb.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2019-2020 Thomas Pointhuber + * 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,