diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index 37feb92717..88175bd38c 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -1921,6 +1921,142 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, } +std::vector 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( 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 + { + std::vector matching; + + for( BOARD_ITEM* item : footprint->GraphicalItems() ) + { + PCB_SHAPE* other = dynamic_cast( 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 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( 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& aErrorHandler ) const diff --git a/pcbnew/pad.h b/pcbnew/pad.h index e9db76940a..1a18962c33 100644 --- a/pcbnew/pad.h +++ b/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 Recombine( bool aIsDryRun, int aMaxError ); + wxString GetClass() const override { return wxT( "PAD" ); diff --git a/pcbnew/pcb_io/altium/altium_pcb.cpp b/pcbnew/pcb_io/altium/altium_pcb.cpp index a149543926..4d52b36d9a 100644 --- a/pcbnew/pcb_io/altium/altium_pcb.cpp +++ b/pcbnew/pcb_io/altium/altium_pcb.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -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,70 +2588,14 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT* polySet.AddHole( hole_linechain ); } - if( aLayer == F_Cu || aLayer == B_Cu ) - { - std::unique_ptr pad = std::make_unique( aFootprint ); + std::unique_ptr shape = std::make_unique( aFootprint, SHAPE_T::POLY ); - LSET padLayers; - padLayers.set( aLayer ); + shape->SetPolyShape( polySet ); + shape->SetFilled( true ); + shape->SetLayer( aLayer ); + shape->SetStroke( STROKE_PARAMS( 0 ) ); - 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 shape = std::make_unique( aFootprint, SHAPE_T::POLY ); - - shape->SetPolyShape( polySet ); - shape->SetFilled( true ); - shape->SetLayer( aLayer ); - shape->SetStroke( STROKE_PARAMS( 0 ) ); - - aFootprint->Add( shape.release(), ADD_MODE::APPEND ); - } + aFootprint->Add( shape.release(), ADD_MODE::APPEND ); } diff --git a/pcbnew/tools/pad_tool.cpp b/pcbnew/tools/pad_tool.cpp index 7ff1c096b2..c89680ee2d 100644 --- a/pcbnew/tools/pad_tool.cpp +++ b/pcbnew/tools/pad_tool.cpp @@ -848,140 +848,11 @@ void PAD_TOOL::explodePad( PAD* aPad, PCB_LAYER_ID* aLayer, BOARD_COMMIT& aCommi std::vector 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( 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 - { - std::vector matching; - - for( BOARD_ITEM* item : footprint->GraphicalItems() ) - { - PCB_SHAPE* other = dynamic_cast( 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 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( 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 ); }