pcbnew: tools for converting between custom-shaped pads and graphical shapes

This commit is contained in:
Tomasz Włostowski 2017-10-19 23:16:06 +02:00
parent bcde2a77e2
commit ac095b6724
13 changed files with 490 additions and 71 deletions

View File

@ -79,9 +79,9 @@ void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
break;
case S_POLYGON:
for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
for( auto iter = m_Poly.Iterate(); iter; iter++ )
{
RotatePoint( &m_PolyPoints[ii], aRotCentre, aAngle);
RotatePoint( *iter, VECTOR2I(aRotCentre), aAngle);
}
break;
@ -411,10 +411,12 @@ const EDA_RECT DRAWSEGMENT::GetBoundingBox() const
{
wxPoint p_end;
MODULE* module = GetParentModule();
bool first = true;
for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
for( auto iter = m_Poly.CIterate(); iter; iter++ )
{
wxPoint pt = m_PolyPoints[ii];
wxPoint pt ( iter->x, iter->y );
if( module ) // Transform, if we belong to a module
{
@ -422,19 +424,27 @@ const EDA_RECT DRAWSEGMENT::GetBoundingBox() const
pt += module->GetPosition();
}
if( ii == 0 )
if( first )
{
p_end = pt;
bbox.SetX( pt.x );
bbox.SetY( pt.y );
first = false;
}
else
{
bbox.SetX( std::min( bbox.GetX(), pt.x ) );
bbox.SetY( std::min( bbox.GetY(), pt.y ) );
p_end.x = std::max( p_end.x, pt.x );
p_end.y = std::max( p_end.y, pt.y );
bbox.SetX( std::min( bbox.GetX(), pt.x ) );
bbox.SetY( std::min( bbox.GetY(), pt.y ) );
p_end.x = std::max( p_end.x, pt.x );
p_end.y = std::max( p_end.y, pt.y );
}
}
bbox.SetEnd( p_end );
}
break;
}
default:
break;
}
@ -508,6 +518,18 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
break;
case S_POLYGON: // not yet handled
{
#define MAX_DIST_IN_MM 0.25
int distmax = Millimeter2iu( 0.25 );
SHAPE_POLY_SET::VERTEX_INDEX dummy;
auto poly = m_Poly;
if( poly.CollideVertex( VECTOR2I( aPosition ), dummy, distmax ) )
return true;
if( poly.CollideEdge( VECTOR2I( aPosition ), dummy, distmax ) )
return true;
}
break;
default:
@ -710,3 +732,26 @@ void DRAWSEGMENT::computeArcBBox( EDA_RECT& aBBox ) const
angle -= 900;
}
}
void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
{
m_Poly.RemoveAllContours();
m_Poly.NewOutline();
for ( auto p : aPoints )
{
m_Poly.Append( p.x, p.y );
}
}
const std::vector<wxPoint> DRAWSEGMENT::GetPolyPoints() const
{
std::vector<wxPoint> rv;
for ( auto iter = m_Poly.CIterate(); iter; iter++ )
{
rv.push_back( wxPoint( iter->x, iter->y ) );
}
return rv;
}

View File

@ -36,6 +36,7 @@
#include <trigo.h>
#include <common.h>
#include <geometry/shape_poly_set.h>
class LINE_READER;
class EDA_DRAW_FRAME;
@ -57,7 +58,7 @@ protected:
wxPoint m_BezierC2; ///< Bezier Control Point 2
std::vector<wxPoint> m_BezierPoints;
std::vector<wxPoint> m_PolyPoints;
SHAPE_POLY_SET m_Poly;
// Computes the bounding box for an arc
void computeArcBBox( EDA_RECT& aBBox ) const;
@ -165,19 +166,18 @@ public:
// Accessors:
const std::vector<wxPoint>& GetBezierPoints() const { return m_BezierPoints; }
const std::vector<wxPoint>& GetPolyPoints() const { return m_PolyPoints; }
// same accessor, to add/change corners of the polygon
std::vector<wxPoint>& GetPolyPoints() { return m_PolyPoints; }
const std::vector<wxPoint> GetPolyPoints() const;
SHAPE_POLY_SET& GetPolyShape() { return m_Poly; }
const SHAPE_POLY_SET& GetPolyShape() const { return m_Poly; }
void SetPolyShape( const SHAPE_POLY_SET& aShape ) { m_Poly = aShape; }
void SetBezierPoints( const std::vector<wxPoint>& aPoints )
{
m_BezierPoints = aPoints;
}
void SetPolyPoints( const std::vector<wxPoint>& aPoints )
{
m_PolyPoints = aPoints;
}
void SetPolyPoints( const std::vector<wxPoint>& aPoints );
void Draw( EDA_DRAW_PANEL* panel, wxDC* DC,
GR_DRAWMODE aDrawMode, const wxPoint& aOffset = ZeroOffset ) override;

View File

@ -202,7 +202,12 @@ void EDGE_MODULE::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode,
{
// We must compute absolute coordinates from m_PolyPoints
// which are relative to module position, orientation 0
std::vector<wxPoint> points = m_PolyPoints;
std::vector<wxPoint> points;
for( auto iter = m_Poly.CIterate(); iter; iter++ )
{
points.push_back( wxPoint( iter->x,iter->y ) );
}
for( unsigned ii = 0; ii < points.size(); ii++ )
{
@ -300,8 +305,11 @@ void EDGE_MODULE::Flip( const wxPoint& aCentre )
case S_POLYGON:
// polygon corners coordinates are always relative to the
// footprint position, orientation 0
for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
MIRROR( m_PolyPoints[ii].y, 0 );
for( auto iter = m_Poly.Iterate(); iter; iter++ )
{
MIRROR( iter->y, 0 );
}
break;
}
// DRAWSEGMENT items are not usually on copper layers, but
@ -338,12 +346,12 @@ void EDGE_MODULE::Mirror( wxPoint aCentre, bool aMirrorAroundXAxis )
case S_POLYGON:
// polygon corners coordinates are always relative to the
// footprint position, orientation 0
for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
for( auto iter = m_Poly.Iterate(); iter; iter++ )
{
if( aMirrorAroundXAxis )
MIRROR( m_PolyPoints[ii].y, aCentre.y );
MIRROR( iter->y, aCentre.y );
else
MIRROR( m_PolyPoints[ii].x, aCentre.x );
MIRROR( iter->x, aCentre.x );
}
}
@ -378,8 +386,8 @@ void EDGE_MODULE::Move( const wxPoint& aMoveVector )
case S_POLYGON:
// polygon corners coordinates are always relative to the
// footprint position, orientation 0
for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
m_PolyPoints[ii] += aMoveVector;
for( auto iter = m_Poly.Iterate(); iter; iter++ )
*iter += VECTOR2I( aMoveVector );
}
SetDrawCoord();

View File

@ -272,13 +272,16 @@ public:
* a filled circle or ring ( if thickness == 0, this is a filled circle, else a ring)
* a arc
*/
void AddPrimitive( std::vector<wxPoint>& aPoly, int aThickness ); ///< add a polygonal basic shape
void AddPrimitive( const SHAPE_POLY_SET& aPoly, int aThickness ); ///< add a polygonal basic shape
void AddPrimitive( const std::vector<wxPoint>& aPoly, int aThickness ); ///< add a polygonal basic shape
void AddPrimitive( wxPoint aStart, wxPoint aEnd, int aThickness ); ///< segment basic shape
void AddPrimitive( wxPoint aCenter, int aRadius, int aThickness ); ///< ring or circle basic shape
void AddPrimitive( wxPoint aCenter, wxPoint aStart,
int aArcAngle, int aThickness ); ///< arc basic shape
bool GetBestAnchorPosition( VECTOR2I& aPos );
/**
* Merge all basic shapes, converted to a polygon in one polygon,
* in m_customShapeAsPolygon
@ -726,6 +729,9 @@ private:
*/
int boundingRadius() const;
bool buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon,
int aCircleToSegmentsCount );
private: // Private variable members:
// Actually computed and cached on demand by the accessor

View File

@ -66,7 +66,18 @@ void PAD_CS_PRIMITIVE::ExportTo( DRAWSEGMENT* aTarget )
* add a free shape to the shape list.
* the shape is a polygon (can be with thick outline), segment, circle or arc
*/
void D_PAD::AddPrimitive( std::vector<wxPoint>& aPoly, int aThickness )
void D_PAD::AddPrimitive( const SHAPE_POLY_SET& aPoly, int aThickness )
{
std::vector<wxPoint> points;
for( auto iter = aPoly.CIterate(); iter; iter++ )
points.push_back( wxPoint( iter->x, iter->y ) );
AddPrimitive( points, aThickness );
}
void D_PAD::AddPrimitive( const std::vector<wxPoint>& aPoly, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_POLYGON );
shape.m_Poly = aPoly;
@ -135,38 +146,10 @@ void D_PAD::DeletePrimitivesList()
}
/* Merge all basic shapes, converted to a polygon in one polygon,
* return true if OK, false in there is more than one polygon
* in aMergedPolygon
*/
bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
int aCircleToSegmentsCount )
bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon,
int aCircleToSegmentsCount )
{
// if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
if( !aMergedPolygon )
aMergedPolygon = &m_customShapeAsPolygon;
aMergedPolygon->RemoveAllContours();
// Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
// The anchor pad is always at 0,0
switch( GetAnchorPadShape() )
{
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0,0 ), GetSize().x/2,
aCircleToSegmentsCount );
break;
case PAD_SHAPE_RECT:
{
SHAPE_RECT rect( -GetSize().x/2, -GetSize().y/2, GetSize().x, GetSize().y );
aMergedPolygon->AddOutline( rect.Outline() );
}
break;
}
SHAPE_POLY_SET aux_polyset;
for( unsigned cnt = 0; cnt < m_basicShapes.size(); ++cnt )
@ -212,7 +195,9 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
polyset.NewOutline();
for( unsigned ii = 0; ii < poly.size(); ii++ )
{
polyset.Append( poly[ii].x, poly[ii].y );
}
polyset.Inflate( bshape.m_Thickness/2, 32 );
@ -230,13 +215,79 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
}
}
// Merge all polygons:
aux_polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
// Merge all polygons, if more than one, pick the largest (area-wise)
if( aux_polyset.OutlineCount() )
{
if( aux_polyset.OutlineCount() >= 2)
{
int bestOutline = 0;
double maxArea = 0.0;
for( int i = 0; i < aux_polyset.OutlineCount(); i++ )
{
double area = aux_polyset.COutline(i).Area();
if ( area > maxArea )
{
maxArea = area;
bestOutline = i;
}
}
if( bestOutline != 0 )
aux_polyset.Polygon( 0 ) = aux_polyset.Polygon( bestOutline );
for (int i = 1; i < aux_polyset.OutlineCount(); i++ )
{
aux_polyset.DeletePolygon( i );
}
}
aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_FAST );
aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_FAST );
}
return aMergedPolygon->OutlineCount() <= 1;
}
/* Merge all basic shapes, converted to a polygon in one polygon,
* return true if OK, false in there is more than one polygon
* in aMergedPolygon
*/
bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
int aCircleToSegmentsCount )
{
// if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
if( !aMergedPolygon )
aMergedPolygon = &m_customShapeAsPolygon;
aMergedPolygon->RemoveAllContours();
// Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
// The anchor pad is always at 0,0
switch( GetAnchorPadShape() )
{
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0,0 ), GetSize().x/2,
aCircleToSegmentsCount );
break;
case PAD_SHAPE_RECT:
{
SHAPE_RECT rect( -GetSize().x/2, -GetSize().y/2, GetSize().x, GetSize().y );
aMergedPolygon->AddOutline( rect.Outline() );
}
break;
}
if ( !buildCustomPadPolygon( aMergedPolygon, aCircleToSegmentsCount ) )
return false;
m_boundingRadius = -1; // The current bouding radius is no more valid.
return aMergedPolygon->OutlineCount() <= 1;
@ -265,3 +316,79 @@ void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon
}
}
}
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
{
SHAPE_POLY_SET poly;
if ( !buildCustomPadPolygon( &poly, 16 ) )
return false;
const int minSteps = 10;
const int maxSteps = 50;
int stepsX, stepsY;
auto bbox = poly.BBox();
if( bbox.GetWidth() < bbox.GetHeight() )
{
stepsX = minSteps;
stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
}
else
{
stepsY = minSteps;
stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
}
stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
auto center = bbox.Centre();
auto minDist = std::numeric_limits<int64_t>::max();
int64_t minDistEdge;
if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
{
minDistEdge = GetSize().x;
}
else
{
minDistEdge = std::max( GetSize().x, GetSize().y );
}
boost::optional<VECTOR2I> bestAnchor;
for ( int y = 0; y < stepsY ; y++ )
for ( int x = 0; x < stepsX; x++ )
{
VECTOR2I p = bbox.GetPosition();
p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
if ( poly.Contains(p) )
{
auto dist = (center - p).EuclideanNorm();
auto distEdge = poly.COutline(0).Distance( p, true );
if ( distEdge >= minDistEdge )
{
if ( dist < minDist )
{
bestAnchor = p;
minDist = dist;
}
}
}
}
if ( bestAnchor )
{
aPos = *bestAnchor;
return true;
}
return false;
}

View File

@ -312,6 +312,9 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate()
break;
case S_POLYGON:
break;
default:
// Check start and end are not the same.

View File

@ -329,7 +329,7 @@ void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
case S_POLYGON: // polygon
{
std::vector<wxPoint>& poly = dummySegment.GetPolyPoints();
std::vector<wxPoint> poly = dummySegment.GetPolyPoints();
GRClosedPoly( NULL, &dc, poly.size(), &poly[0], /* filled */ true,
primitive.m_Thickness, hcolor, hcolor );
}
@ -1160,11 +1160,9 @@ void DIALOG_PAD_PROPERTIES::redraw()
case S_POLYGON: // polygon
{
std::vector<wxPoint>& poly = dummySegment->GetPolyPoints();
for( unsigned ii = 0; ii < poly.size(); ii++ )
for( auto iter = dummySegment->GetPolyShape().Iterate(); iter; iter++ )
{
poly[ii] += m_dummyPad->GetPosition();
(*iter) += VECTOR2I( m_dummyPad->GetPosition() );
}
}
break;

View File

@ -253,7 +253,7 @@ MODULE* try_load_footprint( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFil
}
MODULE* FOOTPRINT_EDIT_FRAME::Import_Module()
MODULE* FOOTPRINT_EDIT_FRAME::Import_Module( const wxString& aName )
{
wxString lastOpenedPathForLoading = m_mruPath;
wxConfigBase* config = Kiface().KifaceSettings();
@ -261,7 +261,12 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module()
if( config )
config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading );
wxFileName fn = getFootprintFilenameFromUser( this, lastOpenedPathForLoading );
wxFileName fn;
if( aName != wxT("") )
fn = aName;
else
fn = getFootprintFilenameFromUser( this, lastOpenedPathForLoading );
if( !fn.IsOk() )
return NULL;

View File

@ -634,7 +634,7 @@ MODULE* PCB_EDIT_FRAME::Create_MuWavePolygonShape()
module->GraphicalItemsList().PushFront( edge );
// Get the corner buffer of the polygonal edge
std::vector<wxPoint>& polyPoints = edge->GetPolyPoints();
std::vector<wxPoint> polyPoints;
polyPoints.reserve( PolyEdges.size() + 2 );
// Init start point coord:
@ -670,6 +670,7 @@ MODULE* PCB_EDIT_FRAME::Create_MuWavePolygonShape()
break;
}
edge->SetPolyPoints( polyPoints );
PolyEdges.clear();
module->CalculateBoundingBox();
GetBoard()->m_Status_Pcb = 0;

View File

@ -295,12 +295,13 @@ bool EDIT_TOOL::Init()
menu.AddItem( PCB_ACTIONS::properties, SELECTION_CONDITIONS::Count( 1 )
|| SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::Tracks ) );
menu.AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty );
menu.AddSeparator();
menu.AddItem( PCB_ACTIONS::copyToClipboard, SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::cutToClipboard, SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::pasteFromClipboard );
@ -308,6 +309,8 @@ bool EDIT_TOOL::Init()
// Mirror only available in modedit
menu.AddItem( PCB_ACTIONS::mirror, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::createPadFromShapes, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
menu.AddItem( PCB_ACTIONS::explodePadToShapes, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
// Footprint actions
menu.AddItem( PCB_ACTIONS::editFootprintInFpEditor,

View File

@ -59,6 +59,14 @@ TOOL_ACTION PCB_ACTIONS::placePad( "pcbnew.ModuleEditor.placePad",
AS_GLOBAL, 0,
_( "Add Pad" ), _( "Add a pad" ), NULL, AF_ACTIVATE );
TOOL_ACTION PCB_ACTIONS::createPadFromShapes( "pcbnew.ModuleEditor.createPadFromShapes",
AS_CONTEXT, 0,
_( "Create Pad from Selected Shapes" ), _( "Creates a custom-shaped pads from a set of selected shapes" ) );
TOOL_ACTION PCB_ACTIONS::explodePadToShapes( "pcbnew.ModuleEditor.explodePadToShapes",
AS_CONTEXT, 0,
_( "Explode Selected Pad to Graphical Shapes" ), _( "Converts a custom-shaped pads to a set of graphical shapes" ) );
TOOL_ACTION PCB_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads",
AS_GLOBAL, 0,
_( "Enumerate Pads" ), _( "Enumerate pads" ), pad_enumerate_xpm, AF_ACTIVATE );
@ -321,10 +329,209 @@ int MODULE_EDITOR_TOOLS::ModuleEdgeOutlines( const TOOL_EVENT& aEvent )
return 0;
}
int MODULE_EDITOR_TOOLS::ExplodePadToShapes( const TOOL_EVENT& aEvent )
{
SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
BOARD_COMMIT commit( frame() );
if( selection.Size() != 1 )
return 0;
if( selection[0]->Type() != PCB_PAD_T )
return 0;
auto pad = static_cast<D_PAD*>( selection[0] );
if( pad->GetShape() != PAD_SHAPE_CUSTOM )
return 0;
commit.Modify( pad );
wxPoint anchor = pad->GetPosition();
for( auto prim : pad->GetPrimitives() )
{
auto ds = new EDGE_MODULE( board()->m_Modules );
ds->SetLayer( pad->GetLayer() );
ds->SetShape( prim.m_Shape );
ds->SetStart( prim.m_Start + anchor );
ds->SetEnd( prim.m_End + anchor );
ds->SetWidth( prim.m_Thickness );
for( auto&p : prim.m_Poly )
p += anchor;
ds->SetPolyPoints( prim.m_Poly );
ds->SetAngle( prim.m_ArcAngle );
commit.Add( ds );
}
pad->SetShape( pad->GetAnchorPadShape() );
commit.Push( _("Explode pad to shapes") );
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
return 0;
}
int MODULE_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
{
SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
std::unique_ptr<D_PAD> pad ( new D_PAD ( board()->m_Modules ) );
D_PAD *refPad = nullptr;
bool multipleRefPadsFound = false;
bool illegalItemsFound = false;
std::vector<PAD_CS_PRIMITIVE> shapes;
BOARD_COMMIT commit( frame() );
for( auto item : selection )
{
switch( item->Type() )
{
case PCB_PAD_T:
{
if( refPad )
multipleRefPadsFound = true;
refPad = static_cast<D_PAD*>( item );
break;
}
case PCB_MODULE_EDGE_T:
{
auto em = static_cast<EDGE_MODULE*> ( item );
PAD_CS_PRIMITIVE shape( em->GetShape() );
shape.m_Start = em->GetStart();
shape.m_End = em->GetEnd();
shape.m_Radius = em->GetRadius();
shape.m_Thickness = em->GetWidth();
shape.m_ArcAngle = em->GetAngle();
for ( auto p : em->GetPolyPoints() )
shape.m_Poly.push_back(p);
shapes.push_back(shape);
break;
}
default:
{
illegalItemsFound = true;
break;
}
}
}
if( refPad && selection.Size() == 1 )
{
// don't convert a pad into itself...
return 0;
}
if( multipleRefPadsFound )
{
DisplayErrorMessage( frame(), _("Cannot convert items to a custom-shaped pad: selection contains more than one reference pad. ") );
return 0;
}
if( illegalItemsFound )
{
DisplayErrorMessage( frame(), _("Cannot convert items to a custom-shaped pad: selection contains unsupported items. Only graphical lines, circles, arcs and polygons are allowed.") );
return 0;
}
if( refPad )
{
pad.reset( static_cast<D_PAD*>( refPad->Clone() ) );
}
else
{
pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE );
pad->SetAttribute( PAD_ATTRIB_SMD );
pad->SetLayerSet( D_PAD::SMDMask() );
pad->SetSize ( wxSize( 10000, 10000 ) );
pad->IncrementPadName( true, true );
}
pad->SetPrimitives( shapes );
pad->SetShape ( PAD_SHAPE_CUSTOM );
boost::optional<VECTOR2I> anchor;
VECTOR2I tmp;
if( refPad )
{
anchor = pad->GetPosition();
}
else if( pad->GetBestAnchorPosition( tmp ) )
{
anchor = tmp;
}
if( !anchor )
{
DisplayErrorMessage( frame(), _("Cannot convert items to a custom-shaped pad: unable to determine the anchor point position. Consider adding a small anchor pad to the selection and try again.") );
return 0;
}
// relocate the shapes, they are relative to the anchor pad position
for( auto& shape : shapes )
{
shape.m_Start.x -= anchor->x;
shape.m_Start.y -= anchor->y;
shape.m_End.x -= anchor->x;
shape.m_End.y -= anchor->y;
for( auto&p : shape.m_Poly )
{
p.x -= anchor->x;
p.y -= anchor->y;
}
}
pad->SetPosition( wxPoint( anchor->x, anchor->y ) );
pad->SetPrimitives( shapes );
bool result = pad->MergePrimitivesAsPolygon();
if( !result )
{
DisplayErrorMessage( frame(), _("Cannot convert items to a custom-shaped pad: selected items do not form a single solid shape.") );
return 0;
}
auto padPtr = pad.release();
commit.Add( padPtr );
for ( auto item : selection )
{
commit.Remove( item );
}
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, padPtr );
commit.Push(_("Create Pad From Selected Shapes") );
return 0;
}
void MODULE_EDITOR_TOOLS::setTransitions()
{
Go( &MODULE_EDITOR_TOOLS::PlacePad, PCB_ACTIONS::placePad.MakeEvent() );
Go( &MODULE_EDITOR_TOOLS::CreatePadFromShapes, PCB_ACTIONS::createPadFromShapes.MakeEvent() );
Go( &MODULE_EDITOR_TOOLS::ExplodePadToShapes, PCB_ACTIONS::explodePadToShapes.MakeEvent() );
Go( &MODULE_EDITOR_TOOLS::EnumeratePads, PCB_ACTIONS::enumeratePads.MakeEvent() );
Go( &MODULE_EDITOR_TOOLS::ModuleTextOutlines, PCB_ACTIONS::moduleTextOutlines.MakeEvent() );
Go( &MODULE_EDITOR_TOOLS::ModuleEdgeOutlines, PCB_ACTIONS::moduleEdgeOutlines.MakeEvent() );

View File

@ -82,6 +82,21 @@ public:
*/
int ModuleEdgeOutlines( const TOOL_EVENT& aEvent );
/**
* Function CreatePadFromShapes()
*
* Creates a custom-shaped pad from a set of selected graphical shapes
*/
int CreatePadFromShapes( const TOOL_EVENT& aEvent );
/**
* Function ExplodePadToShapes()
*
* Breaks apart a complex-shaped part into a set of graphical shapes
*/
int ExplodePadToShapes( const TOOL_EVENT& aEvent );
///> Sets up handlers for various events.
void setTransitions() override;

View File

@ -80,6 +80,7 @@ public:
Add( PCB_ACTIONS::applyPadSettings );
Add( PCB_ACTIONS::pushPadSettings );
// show modedit-specific items
if( m_editingFootprint )
{