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.
This commit is contained in:
parent
f97ac5ce73
commit
7102d9f72a
136
pcbnew/pad.cpp
136
pcbnew/pad.cpp
|
@ -1921,6 +1921,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
|
@ -733,6 +733,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" );
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <pcb_track.h>
|
||||
#include <core/profile.h>
|
||||
#include <string_utils.h>
|
||||
#include <tools/pad_tool.h>
|
||||
#include <zone.h>
|
||||
|
||||
#include <board_stackup_manager/stackup_predefined_prms.h>
|
||||
|
@ -813,6 +814,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" ),
|
||||
|
@ -2582,61 +2588,6 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
|
|||
polySet.AddHole( hole_linechain );
|
||||
}
|
||||
|
||||
if( aLayer == F_Cu || aLayer == B_Cu )
|
||||
{
|
||||
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
|
||||
|
||||
LSET padLayers;
|
||||
padLayers.set( aLayer );
|
||||
|
||||
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.release(), ADD_MODE::APPEND );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aFootprint, SHAPE_T::POLY );
|
||||
|
||||
shape->SetPolyShape( polySet );
|
||||
|
@ -2646,7 +2597,6 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
|
|||
|
||||
aFootprint->Add( shape.release(), ADD_MODE::APPEND );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ALTIUM_PCB::ParseRegions6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
|
||||
|
|
|
@ -848,140 +848,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