pcbnew: tools for converting between custom-shaped pads and graphical shapes
This commit is contained in:
parent
bcde2a77e2
commit
ac095b6724
|
@ -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.SetEnd( p_end );
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,9 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate()
|
|||
|
||||
break;
|
||||
|
||||
case S_POLYGON:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// Check start and end are not the same.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
Add( PCB_ACTIONS::applyPadSettings );
|
||||
Add( PCB_ACTIONS::pushPadSettings );
|
||||
|
||||
|
||||
// show modedit-specific items
|
||||
if( m_editingFootprint )
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue