Implement explicit polygon construction for most pad shapes

Fixes https://gitlab.com/kicad/code/kicad/-/issues/8650
This commit is contained in:
david-beinder 2021-07-04 14:48:32 +02:00 committed by jean-pierre charras
parent 395602b12a
commit 8b3ccab0a3
10 changed files with 214 additions and 218 deletions

View File

@ -754,7 +754,7 @@ void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize
{ {
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
// TransformRoundRectToPolygon creates only one convex polygon // TransformRoundRectToPolygon creates only one convex polygon
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );

View File

@ -1430,7 +1430,7 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS
{ {
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
outline.Inflate( -GetCurrentLineWidth()/2, 16 ); outline.Inflate( -GetCurrentLineWidth()/2, 16 );
@ -1722,7 +1722,7 @@ void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const w
#endif #endif
{ {
TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient, TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
aCornerRadius, aChamferRatio, aChamferPositions, aCornerRadius, aChamferRatio, aChamferPositions, 0,
GetPlotterArcHighDef(), ERROR_INSIDE ); GetPlotterArcHighDef(), ERROR_INSIDE );
// Build the corner list // Build the corner list
@ -1769,7 +1769,7 @@ void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const w
// Build the chamfered polygon (4 to 8 corners ) // Build the chamfered polygon (4 to 8 corners )
TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0, TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
aChamferRatio, aChamferPositions, aChamferRatio, aChamferPositions, 0,
GetPlotterArcHighDef(), ERROR_INSIDE ); GetPlotterArcHighDef(), ERROR_INSIDE );
// Build the corner list // Build the corner list

View File

@ -754,7 +754,7 @@ void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSiz
} }
TransformRoundChamferedRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius, TransformRoundChamferedRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius,
0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
// TransformRoundRectToPolygon creates only one convex polygon // TransformRoundRectToPolygon creates only one convex polygon
std::vector<wxPoint> cornerList; std::vector<wxPoint> cornerList;

View File

@ -210,7 +210,7 @@ void PSLIKE_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius, TransformRoundChamferedRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius,
0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
std::vector< wxPoint > cornerList; std::vector< wxPoint > cornerList;
// TransformRoundRectToPolygon creates only one convex polygon // TransformRoundRectToPolygon creates only one convex polygon

View File

@ -113,6 +113,24 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCenter, i
void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd,
int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount = 0 ); int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount = 0 );
/**
* Convert a rectangle or trapezoid to a polygon.
*
* This will generate at least 16 segments per circle (when using inflate).
*
* @param aCornerBuffer is a buffer to store the polygon.
* @param aPosition is the coordinate of the center of the rectangle.
* @param aSize is the size of the rectangle.
* @param aDeltaX is the delta for trapezoids in X direction
* @param aDeltaY is the delta for trapezoids in Y direction
* @param aInflate is the (positive) shape inflation or 0
* @param aError is the IU allowed for error in approximation.
* @param aErrorLoc determines if the approximation error be placed outside or inside the polygon.
*/
void TransformTrapezoidToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const wxPoint& aPosition, const wxSize& aSize,
double aRotation, int aDeltaX, int aDeltaY, int aInflate,
int aError, ERROR_LOC aErrorLoc );
/** /**
* Convert a rectangle with rounded corners and/or chamfered corners to a polygon. * Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
@ -125,7 +143,7 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
* @param aSize is the size of the rectangle. * @param aSize is the size of the rectangle.
* @param aCornerRadius is the radius of rounded corners (can be 0). * @param aCornerRadius is the radius of rounded corners (can be 0).
* @param aRotation is the rotation in 0.1 degrees of the rectangle. * @param aRotation is the rotation in 0.1 degrees of the rectangle.
* @param aChamferRatio is the ratio between smaller rect size and chamfer value. * @param aChamferRatio is the ratio between smaller rect side and chamfer value.
* @param aChamferCorners is the identifier of the corners to chamfer: * @param aChamferCorners is the identifier of the corners to chamfer:
* - 0 = no chamfer * - 0 = no chamfer
* - 1 = TOP_LEFT * - 1 = TOP_LEFT
@ -133,13 +151,14 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
* - 4 = BOTTOM_LEFT * - 4 = BOTTOM_LEFT
* - 8 = BOTTOM_RIGHT * - 8 = BOTTOM_RIGHT
* One can have more than one chamfered corner by ORing the corner identifiers. * One can have more than one chamfered corner by ORing the corner identifiers.
* @param aInflate is the (positive) shape inflation or 0
* @param aError is the IU allowed for error in approximation. * @param aError is the IU allowed for error in approximation.
* @param aErrorLoc determines if the approximation error be placed outside or inside the polygon. * @param aErrorLoc determines if the approximation error be placed outside or inside the polygon.
*/ */
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const wxPoint& aPosition, const wxSize& aSize, const wxPoint& aPosition, const wxSize& aSize,
double aRotation, int aCornerRadius, double aRotation, int aCornerRadius,
double aChamferRatio, int aChamferCorners, double aChamferRatio, int aChamferCorners, int aInflate,
int aError, ERROR_LOC aErrorLoc ); int aError, ERROR_LOC aErrorLoc );
/** /**

View File

@ -26,6 +26,7 @@
*/ */
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <bitset> // for bitset::count
#include <math.h> // for atan2 #include <math.h> // for atan2
#include <type_traits> // for swap #include <type_traits> // for swap
@ -226,169 +227,209 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
} }
// Return a polygon representing a round rect centered at {0,0} struct ROUNDED_CORNER
void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxSize& aSize, {
int aCornerRadius, int aError, ERROR_LOC aErrorLoc ) VECTOR2I m_position;
int m_radius;
ROUNDED_CORNER( int x, int y ) : m_position( VECTOR2I( x, y ) ), m_radius( 0 ) {}
ROUNDED_CORNER( int x, int y, int radius ) : m_position( VECTOR2I( x, y ) ), m_radius( radius ) {}
};
// Corner List requirements: no concave shape, corners in clockwise order, no duplicate corners
void CornerListToPolygon( SHAPE_POLY_SET& outline, std::vector<ROUNDED_CORNER>& aCorners,
int aInflate, int aError, ERROR_LOC aErrorLoc )
{
assert( aInflate >= 0 );
outline.NewOutline();
VECTOR2I incoming = aCorners[0].m_position - aCorners.back().m_position;
for( int n = 0, count = aCorners.size(); n < count; n++ )
{
ROUNDED_CORNER& cur = aCorners[n];
ROUNDED_CORNER& next = aCorners[( n + 1 ) % count];
VECTOR2I outgoing = next.m_position - cur.m_position;
if( !( aInflate || cur.m_radius ) )
outline.Append( cur.m_position );
else
{
VECTOR2I position = cur.m_position;
int radius = cur.m_radius;
double cosNum = (double) incoming.x * outgoing.x + (double) incoming.y * outgoing.y;
double cosDen = (double) incoming.EuclideanNorm() * outgoing.EuclideanNorm();
double angle = acos( cosNum / cosDen );
double tanAngle2 = tan( ( M_PI - angle ) / 2 );
if( aInflate )
{
radius += aInflate;
position += incoming.Resize( aInflate / tanAngle2 )
+ incoming.Perpendicular().Resize( -aInflate );
}
// Ensure 16+ segments per 360° and ensure first & last segment are the same size
int numSegs = std::max( 16, GetArcToSegmentCount( radius, aError, 360.0 ) );
int angDelta = 3600 / numSegs;
int targetAngle = RAD2DECIDEG( angle );
int angPos = ( angDelta + ( targetAngle % angDelta ) ) / 2;
double centerProjection = radius / tanAngle2;
VECTOR2I arcStart = position - incoming.Resize( centerProjection );
VECTOR2I arcEnd = position + outgoing.Resize( centerProjection );
VECTOR2I arcCenter = arcStart + incoming.Perpendicular().Resize( radius );
if( aErrorLoc == ERROR_INSIDE )
{
outline.Append( arcStart );
VECTOR2I zeroRef = arcStart - arcCenter;
for( ; angPos < targetAngle; angPos += angDelta )
{
VECTOR2I pt = zeroRef;
RotatePoint( pt, -angPos );
outline.Append( pt + arcCenter );
}
outline.Append( arcEnd );
}
else
{
// The outer radius should be radius+aError, recalculate because numSegs is clamped
int actualDeltaRadius = CircleToEndSegmentDeltaRadius( radius, numSegs );
int radiusExtend = GetCircleToPolyCorrection( actualDeltaRadius );
VECTOR2I arcExStart = arcStart + incoming.Perpendicular().Resize( -radiusExtend );
VECTOR2I arcExEnd = arcEnd + outgoing.Perpendicular().Resize( -radiusExtend );
// A larger radius will create "ears", so we intersect the first and last segment
// of the rounded corner with the non-rounded outline
SEG inSeg( position - incoming, position );
SEG outSeg( position, position + outgoing );
VECTOR2I zeroRef = arcExStart - arcCenter;
VECTOR2I pt = zeroRef;
RotatePoint( pt, -angPos );
pt += arcCenter;
OPT<VECTOR2I> intersect = inSeg.Intersect( SEG( arcExStart, pt ) );
outline.Append( intersect.has_value() ? intersect.get() : arcStart );
outline.Append( pt );
angPos += angDelta;
for( ; angPos < targetAngle; angPos += angDelta )
{
pt = zeroRef;
RotatePoint( pt, -angPos );
pt += arcCenter;
outline.Append( pt );
}
intersect = outSeg.Intersect( SEG( pt, arcExEnd ) );
outline.Append( intersect.has_value() ? intersect.get() : arcEnd );
}
}
incoming = outgoing;
}
}
void CornerListRemoveDuplicates( std::vector<ROUNDED_CORNER>& aCorners )
{
VECTOR2I prev = aCorners[0].m_position;
for( int pos = aCorners.size() - 1; pos >= 0; pos-- )
{
if( aCorners[pos].m_position == prev )
aCorners.erase( aCorners.begin() + pos );
else
prev = aCorners[pos].m_position;
}
}
void TransformTrapezoidToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition,
const wxSize& aSize, double aRotation, int aDeltaX, int aDeltaY,
int aInflate, int aError, ERROR_LOC aErrorLoc )
{ {
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
wxPoint centers[4];
wxSize size( aSize / 2 ); wxSize size( aSize / 2 );
size.x -= aCornerRadius; std::vector<ROUNDED_CORNER> corners;
size.y -= aCornerRadius; corners.reserve( 4 );
corners.push_back( ROUNDED_CORNER( -size.x + aDeltaY, -size.y - aDeltaX ) );
corners.push_back( ROUNDED_CORNER( size.x - aDeltaY, -size.y + aDeltaX ) );
corners.push_back( ROUNDED_CORNER( size.x + aDeltaY, size.y - aDeltaX ) );
corners.push_back( ROUNDED_CORNER( -size.x - aDeltaY, size.y + aDeltaX ) );
// Ensure size is > 0, to avoid generating unusable shapes which can crash kicad. if( aDeltaY == size.x || aDeltaX == size.y )
size.x = std::max( 1, size.x ); CornerListRemoveDuplicates( corners );
size.y = std::max( 1, size.y );
centers[0] = wxPoint( -size.x, size.y ); CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
centers[1] = wxPoint( size.x, size.y );
centers[2] = wxPoint( size.x, -size.y );
centers[3] = wxPoint( -size.x, -size.y );
int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 ); if( aRotation != 0.0 )
outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) );
// Choppy corners on rounded-corner rectangles look awful so enforce a minimum of outline.Move( VECTOR2I( aPosition ) );
// 4 segments per corner.
if( numSegs < 16 )
numSegs = 16;
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
int radius = aCornerRadius;
if( aErrorLoc == ERROR_OUTSIDE )
{
// The outer radius should be radius+aError
// Recalculate the actual approx error, as it can be smaller than aError
// because numSegs is clamped to a minimal value
int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
radius += GetCircleToPolyCorrection( actual_delta_radius );
}
auto genArc =
[&]( const wxPoint& aCenter, int aStart, int aEnd )
{
for( int angle = aStart + delta; angle < aEnd; angle += delta )
{
wxPoint pt( -radius, 0 );
RotatePoint( &pt, angle );
pt += aCenter;
outline.Append( pt.x, pt.y );
}
};
outline.NewOutline();
outline.Append( centers[0] + wxPoint( -radius, 0 ) );
genArc( centers[0], 0, 900 );
outline.Append( centers[0] + wxPoint( 0, radius ) );
outline.Append( centers[1] + wxPoint( 0, radius ) );
genArc( centers[1], 900, 1800 );
outline.Append( centers[1] + wxPoint( radius, 0 ) );
outline.Append( centers[2] + wxPoint( radius, 0 ) );
genArc( centers[2], 1800, 2700 );
outline.Append( centers[2] + wxPoint( 0, -radius ) );
outline.Append( centers[3] + wxPoint( 0, -radius ) );
genArc( centers[3], 2700, 3600 );
outline.Append( centers[3] + wxPoint( -radius, 0 ) );
outline.Outline( 0 ).SetClosed( true );
// The created outlines are bigger than the actual outlines, due to the fact
// the corner radius is bigger than the initial value when building a shape outside the
// actual shape.
// However the bounding box shape does not need to be bigger: only rounded corners must
// be modified.
// So clamp the too big shape by the actual bounding box
if( aErrorLoc == ERROR_OUTSIDE )
{
SHAPE_POLY_SET bbox;
bbox.NewOutline();
wxSize bbox_size = aSize/2;
bbox.Append( wxPoint( -bbox_size.x, -bbox_size.y ) );
bbox.Append( wxPoint( bbox_size.x, -bbox_size.y ) );
bbox.Append( wxPoint( bbox_size.x, bbox_size.y ) );
bbox.Append( wxPoint( -bbox_size.x, bbox_size.y ) );
bbox.Outline( 0 ).SetClosed( true );
outline.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_FAST );
// The result is a convex polygon, no need to simplify or fracture.
}
// Add the outline:
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );
} }
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition,
const wxSize& aSize, double aRotation, const wxSize& aSize, double aRotation, int aCornerRadius,
int aCornerRadius, double aChamferRatio, double aChamferRatio, int aChamferCorners, int aInflate,
int aChamferCorners, int aError, ERROR_LOC aErrorLoc ) int aError, ERROR_LOC aErrorLoc )
{ {
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
TransformRoundRectToPolygon( outline, aSize, aCornerRadius, aError, aErrorLoc ); wxSize size( aSize / 2 );
int chamferCnt = std::bitset<8>( aChamferCorners ).count();
// Ensure size is > 0, to avoid generating unusable shapes which can crash kicad.
size.x = std::max( 1, size.x );
size.y = std::max( 1, size.y );
std::vector<ROUNDED_CORNER> corners;
corners.reserve( 4 + chamferCnt );
corners.push_back( ROUNDED_CORNER( -size.x, -size.y, aCornerRadius ) );
corners.push_back( ROUNDED_CORNER( size.x, -size.y, aCornerRadius ) );
corners.push_back( ROUNDED_CORNER( size.x, size.y, aCornerRadius ) );
corners.push_back( ROUNDED_CORNER( -size.x, size.y, aCornerRadius ) );
if( aChamferCorners ) if( aChamferCorners )
{ {
// Now we have the round rect outline, in position 0,0 orientation 0.0. int shorterSide = std::min( aSize.x, aSize.y );
// Chamfer the corner(s). int chamfer = aChamferRatio * shorterSide;
int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y ); int chamId[4] = { RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT,
RECT_CHAMFER_BOTTOM_RIGHT, RECT_CHAMFER_BOTTOM_LEFT };
int sign[8] = { 0, 1, -1, 0, 0, -1, 1, 0 };
SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer for( int cc = 0, pos = 0; cc < 4; cc++, pos++ )
int corner_id[4] =
{ {
RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, if( !( aChamferCorners & chamId[cc] ) )
RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT
};
// Depending on the corner position, signX[] and signY[] give the sign of chamfer
// coordinates relative to the corner position
// The first corner is the top left corner, then top right, bottom left and bottom right
int signX[4] = {1, -1, 1,-1 };
int signY[4] = {1, 1, -1,-1 };
for( int ii = 0; ii < 4; ii++ )
{
if( (corner_id[ii] & aChamferCorners) == 0 )
continue; continue;
VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 ); corners[pos].m_radius = 0;
if( aCornerRadius ) if( chamfer == 0 )
{ continue;
// We recreate a rectangular area covering the full rounded corner
// (max size = aSize/2) to rebuild the corner before chamfering, to be sure corners.insert( corners.begin() + pos + 1, corners[pos] );
// the rounded corner shape does not overlap the chamfered corner shape: corners[pos].m_position.x += sign[( 2 * cc ) & 7] * chamfer;
chamfered_corner.RemoveAllContours(); corners[pos].m_position.y += sign[( 2 * cc - 2 ) & 7] * chamfer;
chamfered_corner.NewOutline(); corners[pos + 1].m_position.x += sign[( 2 * cc + 1 ) & 7] * chamfer;
chamfered_corner.Append( 0, 0 ); corners[pos + 1].m_position.y += sign[( 2 * cc - 1 ) & 7] * chamfer;
chamfered_corner.Append( 0, signY[ii] * aSize.y / 2 ); pos++;
chamfered_corner.Append( signX[ii] * aSize.x / 2, signY[ii] * aSize.y / 2 );
chamfered_corner.Append( signX[ii] * aSize.x / 2, 0 );
chamfered_corner.Move( corner_pos );
outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
} }
// Now chamfer this corner if( chamferCnt > 1 && 2 * chamfer >= shorterSide )
chamfered_corner.RemoveAllContours(); CornerListRemoveDuplicates( corners );
chamfered_corner.NewOutline();
chamfered_corner.Append( 0, 0 );
chamfered_corner.Append( 0, signY[ii] * chamfer_value );
chamfered_corner.Append( signX[ii] * chamfer_value, 0 );
chamfered_corner.Move( corner_pos );
outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
} }
// Rotate and move the outline: CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
if( aRotation != 0.0 ) if( aRotation != 0.0 )
outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) ); outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) );
outline.Move( VECTOR2I( aPosition ) ); outline.Move( VECTOR2I( aPosition ) );
// Add the outline:
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );
} }

View File

@ -629,37 +629,9 @@ void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0; int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0;
int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0; int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0;
wxPoint corners[4];
corners[0] = wxPoint( -dx - ddy, dy + ddx );
corners[1] = wxPoint( dx + ddy, dy - ddx );
corners[2] = wxPoint( dx - ddy, -dy + ddx );
corners[3] = wxPoint( -dx + ddy, -dy - ddx );
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
outline.NewOutline(); TransformTrapezoidToPolygon( outline, padShapePos, m_size, angle, ddx, ddy,
aClearanceValue, aError, aErrorLoc );
for( wxPoint& corner : corners )
{
RotatePoint( &corner, angle );
corner += padShapePos;
outline.Append( corner.x, corner.y );
}
if( aClearanceValue )
{
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
pad_min_seg_per_circle_count );
int clearance = aClearanceValue;
if( aErrorLoc == ERROR_OUTSIDE )
{
int actual_error = CircleToEndSegmentDeltaRadius( clearance, numSegs );
clearance += GetCircleToPolyCorrection( actual_error );
}
outline.Inflate( clearance, numSegs );
}
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );
} }
break; break;
@ -667,51 +639,14 @@ void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE::CHAMFERED_RECT: case PAD_SHAPE::CHAMFERED_RECT:
case PAD_SHAPE::ROUNDRECT: case PAD_SHAPE::ROUNDRECT:
{ {
int radius = GetRoundRectCornerRadius();
wxSize shapesize( m_size );
bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT; bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT;
double chamferRatio = doChamfer ? GetChamferRectRatio() : 0.0;
if( aClearanceValue )
{
radius += aClearanceValue;
shapesize.x += aClearanceValue * 2;
shapesize.y += aClearanceValue * 2;
// The chamfer position (the 45 deg line on corner) must be
// offsetted by aClearanceValue from the base shape chamfer pos
// So we recalculate the chamferRatio to do that
//
// the chamfered shape is square with widet = w, and a corner dist from center
// is w*1.414 / 2 = w*0.707
// the distance from corner to chamfer line is ch = chamfer_size/707
// the distance from center to chamfer line is
// d = w*707 - ch/707
// so we have:
// base shape: d1 = w1*707 - ch1/707 = 0.707 * ( w1 - w1*chamferRatio)
// shape with clearance: d2 = w2*707 - ch2/707 = d1 + aClearanceValue
const double rootsq_2 = 1.41421356237/2;
int d1 = rootsq_2 * std::min( m_size.x, m_size.y ) * ( 1 - GetChamferRectRatio() );
int d2 = d1 + aClearanceValue;
// d2 = 0.707 * w2 * ( 1 - chamferRatio2 )
// 1 - d2 / ( 0.707 * w2 ) = chamferRatio2
chamferRatio = 1.0 - d2 / ( rootsq_2 * std::min( shapesize.x, shapesize.y ) );
// Ensure chamferRatio = 0.0 ... 0.5
if( chamferRatio < 0.0 )
chamferRatio = 0.0;
if( chamferRatio > 0.5 )
chamferRatio = 0.5;
}
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, radius, TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, angle,
chamferRatio, GetRoundRectCornerRadius(),
doChamfer ? GetChamferRectRatio() : 0,
doChamfer ? GetChamferPositions() : 0, doChamfer ? GetChamferPositions() : 0,
aError, aErrorLoc ); aClearanceValue, aError, aErrorLoc );
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );
} }
break; break;

View File

@ -582,7 +582,7 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
TransformRoundChamferedRectToPolygon( outline, pad->GetPosition(), pad->GetSize(), TransformRoundChamferedRectToPolygon( outline, pad->GetPosition(), pad->GetSize(),
pad->GetOrientation(), pad->GetRoundRectCornerRadius(), pad->GetOrientation(), pad->GetRoundRectCornerRadius(),
pad->GetChamferRectRatio(), pad->GetChamferPositions(), maxError, pad->GetChamferRectRatio(), pad->GetChamferPositions(), 0, maxError,
ERROR_INSIDE ); ERROR_INSIDE );
for( int jj = 0; jj < outline.OutlineCount(); ++jj ) for( int jj = 0; jj < outline.OutlineCount(); ++jj )

View File

@ -430,7 +430,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize(), m_orient, TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize(), m_orient,
GetRoundRectCornerRadius(), GetChamferRectRatio(), GetRoundRectCornerRadius(), GetChamferRectRatio(),
GetChamferPositions(), maxError, ERROR_INSIDE ); GetChamferPositions(), 0, maxError, ERROR_INSIDE );
add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) ); add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) );
} }

View File

@ -500,6 +500,7 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, PAD* aPad )
0, rradius, 0, rradius,
aPad->GetChamferRectRatio(), aPad->GetChamferRectRatio(),
doChamfer ? aPad->GetChamferPositions() : 0, doChamfer ? aPad->GetChamferPositions() : 0,
0,
aBoard->GetDesignSettings().m_MaxError, aBoard->GetDesignSettings().m_MaxError,
ERROR_INSIDE ); ERROR_INSIDE );
SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 ); SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 );