Don't whack original pad shape when converting to custom.

Rectangles and circles convert easily, but a chamfered rectangle
(for instance) needs more careful handling.
This commit is contained in:
Jeff Young 2020-06-19 14:07:50 +01:00
parent fe1e835471
commit b779715996
4 changed files with 119 additions and 51 deletions

View File

@ -65,7 +65,7 @@ namespace KIGFX
class PAD_CS_PRIMITIVE class PAD_CS_PRIMITIVE
{ {
public: public:
STROKE_T m_Shape; /// S_SEGMENT, S_ARC, S_CIRCLE, S_POLYGON only (same as DRAWSEGMENT) STROKE_T m_Shape; /// S_SEGMENT, S_RECT, S_ARC, S_CIRCLE, S_POLYGON only (same as DRAWSEGMENT)
int m_Thickness; /// thickness of segment or outline int m_Thickness; /// thickness of segment or outline
/// For filled S_CIRCLE shape, thickness = 0. /// For filled S_CIRCLE shape, thickness = 0.
// if thickness is not = 0 S_CIRCLE shape is a ring // if thickness is not = 0 S_CIRCLE shape is a ring

View File

@ -1538,6 +1538,13 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
FormatInternalUnits( primitive.m_Thickness ).c_str() ); FormatInternalUnits( primitive.m_Thickness ).c_str() );
break; break;
case S_RECT:
m_out->Print( nested_level, "(gr_rect (start %s) (end %s) (width %s))",
FormatInternalUnits( primitive.m_Start ).c_str(),
FormatInternalUnits( primitive.m_End ).c_str(),
FormatInternalUnits( primitive.m_Thickness ).c_str() );
break;
case S_ARC: // Arc with rounded ends case S_ARC: // Arc with rounded ends
m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s) (width %s))", m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s) (width %s))",
FormatInternalUnits( primitive.m_Start ).c_str(), FormatInternalUnits( primitive.m_Start ).c_str(),

View File

@ -83,9 +83,7 @@ void PAD_CS_PRIMITIVE::Move( wxPoint aMoveVector )
m_Ctrl2 += aMoveVector; m_Ctrl2 += aMoveVector;
for( auto& corner : m_Poly ) for( auto& corner : m_Poly )
{
corner += aMoveVector; corner += aMoveVector;
}
} }
@ -101,6 +99,25 @@ void PAD_CS_PRIMITIVE::Rotate( const wxPoint& aRotCentre, double aAngle )
RotatePoint( &m_End, aRotCentre, aAngle ); RotatePoint( &m_End, aRotCentre, aAngle );
break; break;
case S_RECT:
if( KiROUND( aAngle ) % 900 == 0 )
{
RotatePoint( &m_Start, aRotCentre, aAngle );
RotatePoint( &m_End, aRotCentre, aAngle );
break;
}
// Convert non-cartesian-rotated rect to a diamond
m_Shape = S_POLYGON;
m_Poly.clear();
m_Poly.emplace_back( m_Start );
m_Poly.emplace_back( m_End.x, m_Start.y );
m_Poly.emplace_back( m_End );
m_Poly.emplace_back( m_Start.x, m_End.y );
KI_FALLTHROUGH;
case S_POLYGON: case S_POLYGON:
for( auto& pt : m_Poly ) for( auto& pt : m_Poly )
RotatePoint( &pt, aRotCentre, aAngle ); RotatePoint( &pt, aRotCentre, aAngle );
@ -112,10 +129,8 @@ void PAD_CS_PRIMITIVE::Rotate( const wxPoint& aRotCentre, double aAngle )
RotatePoint( &m_End, aRotCentre, aAngle ); RotatePoint( &m_End, aRotCentre, aAngle );
RotatePoint( &m_Ctrl1, aRotCentre, aAngle ); RotatePoint( &m_Ctrl1, aRotCentre, aAngle );
RotatePoint( &m_Ctrl2, aRotCentre, aAngle ); RotatePoint( &m_Ctrl2, aRotCentre, aAngle );
break; break;
case S_RECT:
default: default:
// un-handled edge transform // un-handled edge transform
wxASSERT_MSG( false, wxT( "PAD_CS_PRIMITIVE::Rotate not implemented for " wxASSERT_MSG( false, wxT( "PAD_CS_PRIMITIVE::Rotate not implemented for "
@ -146,8 +161,8 @@ void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool
AddPrimitivePoly( points, aThickness, aMergePrimitives ); AddPrimitivePoly( points, aThickness, aMergePrimitives );
} }
void D_PAD::AddPrimitivePoly( void D_PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness,
const std::vector<wxPoint>& aPoly, int aThickness, bool aMergePrimitives ) bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_POLYGON ); PAD_CS_PRIMITIVE shape( S_POLYGON );
shape.m_Poly = aPoly; shape.m_Poly = aPoly;
@ -159,8 +174,8 @@ void D_PAD::AddPrimitivePoly(
} }
void D_PAD::AddPrimitiveSegment( void D_PAD::AddPrimitiveSegment( wxPoint aStart, wxPoint aEnd, int aThickness,
wxPoint aStart, wxPoint aEnd, int aThickness, bool aMergePrimitives ) bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_SEGMENT ); PAD_CS_PRIMITIVE shape( S_SEGMENT );
shape.m_Start = aStart; shape.m_Start = aStart;
@ -173,8 +188,8 @@ void D_PAD::AddPrimitiveSegment(
} }
void D_PAD::AddPrimitiveArc( void D_PAD::AddPrimitiveArc( wxPoint aCenter, wxPoint aStart, int aArcAngle, int aThickness,
wxPoint aCenter, wxPoint aStart, int aArcAngle, int aThickness, bool aMergePrimitives ) bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_ARC ); PAD_CS_PRIMITIVE shape( S_ARC );
shape.m_Start = aCenter; shape.m_Start = aCenter;
@ -189,7 +204,7 @@ void D_PAD::AddPrimitiveArc(
void D_PAD::AddPrimitiveCurve( wxPoint aStart, wxPoint aEnd, wxPoint aCtrl1, wxPoint aCtrl2, void D_PAD::AddPrimitiveCurve( wxPoint aStart, wxPoint aEnd, wxPoint aCtrl1, wxPoint aCtrl2,
int aThickness, bool aMergePrimitives ) int aThickness, bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_CURVE ); PAD_CS_PRIMITIVE shape( S_CURVE );
shape.m_Start = aStart; shape.m_Start = aStart;
@ -204,8 +219,8 @@ void D_PAD::AddPrimitiveCurve( wxPoint aStart, wxPoint aEnd, wxPoint aCtrl1, wxP
} }
void D_PAD::AddPrimitiveCircle( void D_PAD::AddPrimitiveCircle( wxPoint aCenter, int aRadius, int aThickness,
wxPoint aCenter, int aRadius, int aThickness, bool aMergePrimitives ) bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_CIRCLE ); PAD_CS_PRIMITIVE shape( S_CIRCLE );
shape.m_Start = aCenter; shape.m_Start = aCenter;
@ -253,10 +268,8 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
{ {
SHAPE_POLY_SET aux_polyset; SHAPE_POLY_SET aux_polyset;
for( unsigned cnt = 0; cnt < m_basicShapes.size(); ++cnt ) for( PAD_CS_PRIMITIVE& bshape : m_basicShapes )
{ {
const PAD_CS_PRIMITIVE& bshape = m_basicShapes[cnt];
switch( bshape.m_Shape ) switch( bshape.m_Shape )
{ {
case S_CURVE: case S_CURVE:
@ -276,32 +289,42 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
case S_SEGMENT: // usual segment : line with rounded ends case S_SEGMENT: // usual segment : line with rounded ends
{ {
TransformSegmentToPolygon( TransformSegmentToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, aError,
aux_polyset, bshape.m_Start, bshape.m_End, aError, bshape.m_Thickness ); bshape.m_Thickness );
break; break;
} }
case S_ARC: // Arc with rounded ends case S_ARC: // Arc with rounded ends
{ {
TransformArcToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle, TransformArcToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
aError, bshape.m_Thickness ); aError, bshape.m_Thickness );
break; break;
} }
case S_CIRCLE: // ring or circle case S_CIRCLE: // ring or circle
{ {
if( bshape.m_Thickness ) // ring if( bshape.m_Thickness ) // ring
TransformRingToPolygon( TransformRingToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError,
aux_polyset, bshape.m_Start, bshape.m_Radius, aError, bshape.m_Thickness ); bshape.m_Thickness );
else // Filled circle else // Filled circle
TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError ); TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError );
break; break;
} }
case S_RECT:
bshape.m_Poly.clear();
bshape.m_Poly.emplace_back( bshape.m_Start );
bshape.m_Poly.emplace_back( bshape.m_End.x, bshape.m_Start.y );
bshape.m_Poly.emplace_back( bshape.m_End );
bshape.m_Poly.emplace_back( bshape.m_Start.x, bshape.m_End.y );
KI_FALLTHROUGH;
case S_POLYGON: // polygon case S_POLYGON: // polygon
{
if( bshape.m_Poly.size() < 2 ) if( bshape.m_Poly.size() < 2 )
break; // Malformed polygon. break; // Malformed polygon.
{
// Insert the polygon: // Insert the polygon:
const std::vector< wxPoint>& poly = bshape.m_Poly; const std::vector< wxPoint>& poly = bshape.m_Poly;
aux_polyset.NewOutline(); aux_polyset.NewOutline();
@ -311,24 +334,29 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
SHAPE_POLY_SET polyset; SHAPE_POLY_SET polyset;
polyset.NewOutline(); polyset.NewOutline();
for( unsigned ii = 0; ii < poly.size(); ii++ ) for( const wxPoint& pt : poly )
{ polyset.Append( pt.x, pt.y );
polyset.Append( poly[ii].x, poly[ii].y );
}
int numSegs = std::max( int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 );
GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 );
polyset.Inflate( bshape.m_Thickness / 2, numSegs ); polyset.Inflate( bshape.m_Thickness / 2, numSegs );
aux_polyset.Append( polyset ); aux_polyset.Append( polyset );
} }
else else
for( unsigned ii = 0; ii < poly.size(); ii++ ) {
aux_polyset.Append( poly[ii].x, poly[ii].y ); for( const wxPoint& pt : poly )
aux_polyset.Append( pt.x, pt.y );
} }
if( bshape.m_Shape == S_RECT )
bshape.m_Poly.clear();
}
break; break;
default: default:
// un-handled primitive
wxASSERT_MSG( false, wxT( "D_PAD::buildCustomPadPolygon not implemented for "
+ BOARD_ITEM::ShowShape( bshape.m_Shape ) ) );
break; break;
} }
} }
@ -390,8 +418,9 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
return aMergedPolygon->OutlineCount() <= 1; return aMergedPolygon->OutlineCount() <= 1;
} }
void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon, void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
wxPoint aPosition, double aRotation ) const wxPoint aPosition, double aRotation ) const
{ {
if( aMergedPolygon->OutlineCount() == 0 ) if( aMergedPolygon->OutlineCount() == 0 )
return; return;
@ -402,6 +431,7 @@ void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon
aMergedPolygon->Move( VECTOR2I( aPosition ) ); aMergedPolygon->Move( VECTOR2I( aPosition ) );
} }
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos ) bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
{ {
SHAPE_POLY_SET poly; SHAPE_POLY_SET poly;
@ -430,9 +460,9 @@ bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) ); stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) ); stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
auto center = bbox.Centre(); VECTOR2I center = bbox.Centre();
auto minDist = std::numeric_limits<int64_t>::max(); int64_t minDist = std::numeric_limits<int64_t>::max();
int64_t minDistEdge; int64_t minDistEdge;
if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE ) if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
@ -446,22 +476,23 @@ bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() ); OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
for ( int y = 0; y < stepsY ; y++ ) for( int y = 0; y < stepsY ; y++ )
{ {
for ( int x = 0; x < stepsX; x++ ) for( int x = 0; x < stepsX; x++ )
{ {
VECTOR2I p = bbox.GetPosition(); VECTOR2I p = bbox.GetPosition();
p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) ); p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) ); p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
if ( poly.Contains(p) ) if( poly.Contains(p) )
{ {
auto dist = (center - p).EuclideanNorm(); int dist = (center - p).EuclideanNorm();
auto distEdge = poly.COutline(0).Distance( p, true ); int distEdge = poly.COutline(0).Distance( p, true );
if ( distEdge >= minDistEdge )
if( distEdge >= minDistEdge )
{ {
if ( dist < minDist ) if( dist < minDist )
{ {
bestAnchor = p; bestAnchor = p;
minDist = dist; minDist = dist;
@ -471,7 +502,7 @@ bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
} }
} }
if ( bestAnchor ) if( bestAnchor )
{ {
aPos = *bestAnchor; aPos = *bestAnchor;
return true; return true;

View File

@ -466,7 +466,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
shape.m_Ctrl2 = em->GetBezControl2(); shape.m_Ctrl2 = em->GetBezControl2();
shape.m_Poly = em->BuildPolyPointsList(); shape.m_Poly = em->BuildPolyPointsList();
shapes.push_back(shape); shapes.push_back( shape );
break; break;
} }
@ -501,19 +501,49 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
return 0; return 0;
} }
double refOrientation = 0.0; double deltaAngle = 0.0;
if( refPad ) if( refPad && refPad->GetShape() == PAD_SHAPE_CUSTOM )
{
// it's already a pad anchor
}
else if( refPad )
{ {
pad.reset( static_cast<D_PAD*>( refPad->Clone() ) ); pad.reset( static_cast<D_PAD*>( refPad->Clone() ) );
if( refPad->GetShape() == PAD_SHAPE_RECT ) if( refPad->GetShape() == PAD_SHAPE_RECT )
{
pad->SetAnchorPadShape( PAD_SHAPE_RECT ); pad->SetAnchorPadShape( PAD_SHAPE_RECT );
deltaAngle = 0.0;
}
else if( refPad->GetShape() == PAD_SHAPE_CIRCLE )
{
pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE );
deltaAngle = 0.0;
}
else
{
// Create a new minimally-sized circular anchor and convert existing pad
// to a polygon primitive
pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE );
int r = refPad->GetDrillSize().x + Millimeter2iu( 0.2 );
pad->SetSize( wxSize( r, r ) );
pad->SetOffset( wxPoint( 0, 0 ) );
// ignore the pad offset for the moment. Makes more trouble than it's worth. SHAPE_POLY_SET existingOutline;
pad->SetOffset( wxPoint( 0, 0 ) ); int maxError = board()->GetDesignSettings().m_MaxError;
refOrientation = pad->GetOrientation(); refPad->TransformShapeWithClearanceToPolygon( existingOutline, 0, maxError );
pad->SetOrientation( 0.0 );
PAD_CS_PRIMITIVE shape( S_POLYGON );
for( auto ii = existingOutline.Iterate(); ii; ii++ )
shape.m_Poly.emplace_back( ii->x, ii->y );
shapes.push_back( shape );
deltaAngle = refPad->GetOrientation();
pad->SetOrientation( 0.0 );
}
} }
else else
{ {
@ -562,7 +592,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
for( auto& shape : shapes ) for( auto& shape : shapes )
{ {
shape.Move( wxPoint( -anchor->x, -anchor->y ) ); shape.Move( wxPoint( -anchor->x, -anchor->y ) );
shape.Rotate( wxPoint( 0, 0 ), -refOrientation ); shape.Rotate( wxPoint( 0, 0 ), -deltaAngle );
} }
pad->SetPosition( wxPoint( anchor->x, anchor->y ) ); pad->SetPosition( wxPoint( anchor->x, anchor->y ) );
@ -570,7 +600,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
pad->ClearFlags(); pad->ClearFlags();
bool result = pad->MergePrimitivesAsPolygon(); bool result = pad->MergePrimitivesAsPolygon();
pad->Rotate( wxPoint( anchor->x, anchor->y ), refOrientation ); pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle );
if( !result ) if( !result )
{ {