Altium import: handle polygons on copper
In Altium, copper polys will be connected automatically to their
associated pads. In KiCad, we need to do the equivalent when parsing,
which is to combine the joining copper into the underlying pad. We also
don't want to treat copper polys as proxy pads without the original
anymore.
(cherry picked from commit 7102d9f72a
)
This commit is contained in:
parent
b589da63d8
commit
33b93a3824
136
pcbnew/pad.cpp
136
pcbnew/pad.cpp
|
@ -1726,6 +1726,142 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
|
|||
}
|
||||
|
||||
|
||||
std::vector<PCB_SHAPE*> PAD::Recombine( bool aIsDryRun, int maxError )
|
||||
{
|
||||
FOOTPRINT* footprint = GetParentFootprint();
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
item->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
auto findNext =
|
||||
[&]( PCB_LAYER_ID aLayer ) -> PCB_SHAPE*
|
||||
{
|
||||
SHAPE_POLY_SET padPoly;
|
||||
TransformShapeToPolygon( padPoly, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
|
||||
|
||||
if( !shape || ( shape->GetFlags() & SKIP_STRUCT ) )
|
||||
continue;
|
||||
|
||||
if( shape->GetLayer() != aLayer )
|
||||
continue;
|
||||
|
||||
if( shape->IsProxyItem() ) // Pad number (and net name) box
|
||||
return shape;
|
||||
|
||||
SHAPE_POLY_SET drawPoly;
|
||||
shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
drawPoly.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( !drawPoly.IsEmpty() )
|
||||
return shape;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto findMatching =
|
||||
[&]( PCB_SHAPE* aShape ) -> std::vector<PCB_SHAPE*>
|
||||
{
|
||||
std::vector<PCB_SHAPE*> matching;
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
PCB_SHAPE* other = dynamic_cast<PCB_SHAPE*>( item );
|
||||
|
||||
if( !other || ( other->GetFlags() & SKIP_STRUCT ) )
|
||||
continue;
|
||||
|
||||
if( GetLayerSet().test( other->GetLayer() )
|
||||
&& aShape->Compare( other ) == 0 )
|
||||
{
|
||||
matching.push_back( other );
|
||||
}
|
||||
}
|
||||
|
||||
return matching;
|
||||
};
|
||||
|
||||
PCB_LAYER_ID layer;
|
||||
std::vector<PCB_SHAPE*> mergedShapes;
|
||||
|
||||
if( IsOnLayer( F_Cu ) )
|
||||
layer = F_Cu;
|
||||
else if( IsOnLayer( B_Cu ) )
|
||||
layer = B_Cu;
|
||||
else
|
||||
layer = *GetLayerSet().UIOrder();
|
||||
|
||||
// If there are intersecting items to combine, we need to first make sure the pad is a
|
||||
// custom-shape pad.
|
||||
if( !aIsDryRun && findNext( layer ) && GetShape() != PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
if( GetShape() == PAD_SHAPE::CIRCLE || GetShape() == PAD_SHAPE::RECTANGLE )
|
||||
{
|
||||
// Use the existing pad as an anchor
|
||||
SetAnchorPadShape( GetShape() );
|
||||
SetShape( PAD_SHAPE::CUSTOM );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new circular anchor and convert existing pad to a polygon primitive
|
||||
SHAPE_POLY_SET existingOutline;
|
||||
TransformShapeToPolygon( existingOutline, layer, 0, maxError, ERROR_INSIDE );
|
||||
|
||||
int minExtent = std::min( GetSize().x, GetSize().y );
|
||||
SetAnchorPadShape( PAD_SHAPE::CIRCLE );
|
||||
SetSize( VECTOR2I( minExtent, minExtent ) );
|
||||
SetShape( PAD_SHAPE::CUSTOM );
|
||||
|
||||
PCB_SHAPE* shape = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
|
||||
shape->SetFilled( true );
|
||||
shape->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
|
||||
shape->SetPolyShape( existingOutline );
|
||||
shape->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
|
||||
shape->Move( - ShapePos() );
|
||||
AddPrimitive( shape );
|
||||
}
|
||||
}
|
||||
|
||||
while( PCB_SHAPE* fpShape = findNext( layer ) )
|
||||
{
|
||||
fpShape->SetFlags( SKIP_STRUCT );
|
||||
|
||||
mergedShapes.push_back( fpShape );
|
||||
|
||||
if( !aIsDryRun )
|
||||
{
|
||||
PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate() );
|
||||
|
||||
primitive->SetParent( nullptr );
|
||||
primitive->Move( - ShapePos() );
|
||||
primitive->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
|
||||
|
||||
AddPrimitive( primitive );
|
||||
}
|
||||
|
||||
// See if there are other shapes that match and mark them for delete. (KiCad won't
|
||||
// produce these, but old footprints from other vendors have them.)
|
||||
for( PCB_SHAPE* other : findMatching( fpShape ) )
|
||||
{
|
||||
other->SetFlags( SKIP_STRUCT );
|
||||
mergedShapes.push_back( other );
|
||||
}
|
||||
}
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
item->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
if( !aIsDryRun )
|
||||
ClearFlags( ENTERED );
|
||||
|
||||
return mergedShapes;
|
||||
}
|
||||
|
||||
|
||||
void PAD::CheckPad( UNITS_PROVIDER* aUnitsProvider,
|
||||
const std::function<void( int aErrorCode,
|
||||
const wxString& aMsg )>& aErrorHandler ) const
|
||||
|
|
10
pcbnew/pad.h
10
pcbnew/pad.h
|
@ -640,6 +640,16 @@ public:
|
|||
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
||||
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
||||
|
||||
|
||||
/**
|
||||
* Recombines the pad with other graphical shapes in the footprint
|
||||
*
|
||||
* @param aIsDryRun if true, the pad will not be recombined but the operation will still be logged
|
||||
* @param aMaxError the maximum error to allow for converting arcs to polygons
|
||||
* @return a list of shapes that were recombined
|
||||
*/
|
||||
std::vector<PCB_SHAPE*> Recombine( bool aIsDryRun, int aMaxError );
|
||||
|
||||
wxString GetClass() const override
|
||||
{
|
||||
return wxT( "PAD" );
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <core/profile.h>
|
||||
#include <pad_shapes.h>
|
||||
#include <string_utils.h>
|
||||
#include <tools/pad_tool.h>
|
||||
#include <zone.h>
|
||||
|
||||
#include <board_stackup_manager/stackup_predefined_prms.h>
|
||||
|
@ -807,6 +808,11 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( ALTIUM_COMPOUND_FILE& altiumLibFile,
|
|||
// Auto-position reference and value
|
||||
footprint->AutoPositionFields();
|
||||
|
||||
for( PAD* pad : footprint->Pads() )
|
||||
{
|
||||
pad->Recombine( false, ARC_HIGH_DEF );
|
||||
}
|
||||
|
||||
if( parser.HasParsingError() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( wxT( "%s stream was not parsed correctly" ),
|
||||
|
@ -2468,71 +2474,14 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
|
|||
polySet.AddHole( hole_linechain );
|
||||
}
|
||||
|
||||
if( aLayer == F_Cu || aLayer == B_Cu )
|
||||
{
|
||||
PAD* pad = new PAD( aFootprint );
|
||||
std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
|
||||
|
||||
LSET padLayers;
|
||||
padLayers.set( aLayer );
|
||||
shape->SetPolyShape( polySet );
|
||||
shape->SetFilled( true );
|
||||
shape->SetLayer( aLayer );
|
||||
shape->SetStroke( STROKE_PARAMS( 0 ) );
|
||||
|
||||
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
|
||||
pad->SetAttribute( PAD_ATTRIB::SMD );
|
||||
pad->SetShape( PAD_SHAPE::CUSTOM );
|
||||
pad->SetThermalSpokeAngle( ANGLE_90 );
|
||||
|
||||
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 ) );
|
||||
|
||||
aFootprint->Add( shape, ADD_MODE::APPEND );
|
||||
}
|
||||
aFootprint->Add( shape.release(), ADD_MODE::APPEND );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -846,140 +846,11 @@ void PAD_TOOL::explodePad( PAD* aPad, PCB_LAYER_ID* aLayer, BOARD_COMMIT& aCommi
|
|||
std::vector<PCB_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun )
|
||||
{
|
||||
int maxError = board()->GetDesignSettings().m_MaxError;
|
||||
FOOTPRINT* footprint = aPad->GetParentFootprint();
|
||||
|
||||
// Don't leave an object in the point editor that might no longer exist after recombining.
|
||||
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
item->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
auto findNext =
|
||||
[&]( PCB_LAYER_ID aLayer ) -> PCB_SHAPE*
|
||||
{
|
||||
SHAPE_POLY_SET padPoly;
|
||||
aPad->TransformShapeToPolygon( padPoly, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
|
||||
|
||||
if( !shape || ( shape->GetFlags() & SKIP_STRUCT ) )
|
||||
continue;
|
||||
|
||||
if( shape->GetLayer() != aLayer )
|
||||
continue;
|
||||
|
||||
if( shape->IsProxyItem() ) // Pad number (and net name) box
|
||||
return shape;
|
||||
|
||||
SHAPE_POLY_SET drawPoly;
|
||||
shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
drawPoly.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
if( !drawPoly.IsEmpty() )
|
||||
return shape;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto findMatching =
|
||||
[&]( PCB_SHAPE* aShape ) -> std::vector<PCB_SHAPE*>
|
||||
{
|
||||
std::vector<PCB_SHAPE*> matching;
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
PCB_SHAPE* other = dynamic_cast<PCB_SHAPE*>( item );
|
||||
|
||||
if( !other || ( other->GetFlags() & SKIP_STRUCT ) )
|
||||
continue;
|
||||
|
||||
if( aPad->GetLayerSet().test( other->GetLayer() )
|
||||
&& aShape->Compare( other ) == 0 )
|
||||
{
|
||||
matching.push_back( other );
|
||||
}
|
||||
}
|
||||
|
||||
return matching;
|
||||
};
|
||||
|
||||
PCB_LAYER_ID layer;
|
||||
std::vector<PCB_SHAPE*> mergedShapes;
|
||||
|
||||
if( aPad->IsOnLayer( F_Cu ) )
|
||||
layer = F_Cu;
|
||||
else if( aPad->IsOnLayer( B_Cu ) )
|
||||
layer = B_Cu;
|
||||
else
|
||||
layer = *aPad->GetLayerSet().UIOrder();
|
||||
|
||||
// If there are intersecting items to combine, we need to first make sure the pad is a
|
||||
// custom-shape pad.
|
||||
if( !aIsDryRun && findNext( layer ) && aPad->GetShape() != PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
if( aPad->GetShape() == PAD_SHAPE::CIRCLE || aPad->GetShape() == PAD_SHAPE::RECTANGLE )
|
||||
{
|
||||
// Use the existing pad as an anchor
|
||||
aPad->SetAnchorPadShape( aPad->GetShape() );
|
||||
aPad->SetShape( PAD_SHAPE::CUSTOM );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new circular anchor and convert existing pad to a polygon primitive
|
||||
SHAPE_POLY_SET existingOutline;
|
||||
aPad->TransformShapeToPolygon( existingOutline, layer, 0, maxError, ERROR_INSIDE );
|
||||
|
||||
int minExtent = std::min( aPad->GetSize().x, aPad->GetSize().y );
|
||||
aPad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
|
||||
aPad->SetSize( VECTOR2I( minExtent, minExtent ) );
|
||||
aPad->SetShape( PAD_SHAPE::CUSTOM );
|
||||
|
||||
PCB_SHAPE* shape = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
|
||||
shape->SetFilled( true );
|
||||
shape->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
|
||||
shape->SetPolyShape( existingOutline );
|
||||
shape->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
|
||||
shape->Move( - aPad->ShapePos() );
|
||||
aPad->AddPrimitive( shape );
|
||||
}
|
||||
}
|
||||
|
||||
while( PCB_SHAPE* fpShape = findNext( layer ) )
|
||||
{
|
||||
fpShape->SetFlags( SKIP_STRUCT );
|
||||
|
||||
mergedShapes.push_back( fpShape );
|
||||
|
||||
if( !aIsDryRun )
|
||||
{
|
||||
PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate() );
|
||||
|
||||
primitive->SetParent( nullptr );
|
||||
primitive->Move( - aPad->ShapePos() );
|
||||
primitive->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
|
||||
|
||||
aPad->AddPrimitive( primitive );
|
||||
}
|
||||
|
||||
// See if there are other shapes that match and mark them for delete. (KiCad won't
|
||||
// produce these, but old footprints from other vendors have them.)
|
||||
for( PCB_SHAPE* other : findMatching( fpShape ) )
|
||||
{
|
||||
other->SetFlags( SKIP_STRUCT );
|
||||
mergedShapes.push_back( other );
|
||||
}
|
||||
}
|
||||
|
||||
for( BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
item->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
if( !aIsDryRun )
|
||||
aPad->ClearFlags( ENTERED );
|
||||
|
||||
return mergedShapes;
|
||||
return aPad->Recombine( aIsDryRun, maxError );
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue