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
{
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
/// For filled S_CIRCLE shape, thickness = 0.
// 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() );
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
m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s) (width %s))",
FormatInternalUnits( primitive.m_Start ).c_str(),

View File

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

View File

@ -466,7 +466,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
shape.m_Ctrl2 = em->GetBezControl2();
shape.m_Poly = em->BuildPolyPointsList();
shapes.push_back(shape);
shapes.push_back( shape );
break;
}
@ -501,19 +501,49 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
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() ) );
if( refPad->GetShape() == 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.
pad->SetOffset( wxPoint( 0, 0 ) );
refOrientation = pad->GetOrientation();
pad->SetOrientation( 0.0 );
SHAPE_POLY_SET existingOutline;
int maxError = board()->GetDesignSettings().m_MaxError;
refPad->TransformShapeWithClearanceToPolygon( existingOutline, 0, maxError );
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
{
@ -562,7 +592,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
for( auto& shape : shapes )
{
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 ) );
@ -570,7 +600,7 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
pad->ClearFlags();
bool result = pad->MergePrimitivesAsPolygon();
pad->Rotate( wxPoint( anchor->x, anchor->y ), refOrientation );
pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle );
if( !result )
{