diff --git a/common/plotters/DXF_plotter.cpp b/common/plotters/DXF_plotter.cpp index da91a5aef1..7fe8ff1ba1 100644 --- a/common/plotters/DXF_plotter.cpp +++ b/common/plotters/DXF_plotter.cpp @@ -754,7 +754,7 @@ void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize { SHAPE_POLY_SET outline; 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 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index 85ae7c0027..b465f5377b 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -1430,7 +1430,7 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS { SHAPE_POLY_SET outline; 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 ); outline.Inflate( -GetCurrentLineWidth()/2, 16 ); @@ -1722,7 +1722,7 @@ void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const w #endif { TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient, - aCornerRadius, aChamferRatio, aChamferPositions, + aCornerRadius, aChamferRatio, aChamferPositions, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); // 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 ) TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0, - aChamferRatio, aChamferPositions, + aChamferRatio, aChamferPositions, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); // Build the corner list diff --git a/common/plotters/HPGL_plotter.cpp b/common/plotters/HPGL_plotter.cpp index 5d779e600d..1ff18b09a0 100644 --- a/common/plotters/HPGL_plotter.cpp +++ b/common/plotters/HPGL_plotter.cpp @@ -754,7 +754,7 @@ void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSiz } 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 std::vector cornerList; diff --git a/common/plotters/PS_plotter.cpp b/common/plotters/PS_plotter.cpp index 49ac0b877b..64dfbf2c11 100644 --- a/common/plotters/PS_plotter.cpp +++ b/common/plotters/PS_plotter.cpp @@ -210,7 +210,7 @@ void PSLIKE_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS SHAPE_POLY_SET outline; TransformRoundChamferedRectToPolygon( outline, aPadPos, size, aOrient, aCornerRadius, - 0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); + 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE ); std::vector< wxPoint > cornerList; // TransformRoundRectToPolygon creates only one convex polygon diff --git a/libs/kimath/include/convert_basic_shapes_to_polygon.h b/libs/kimath/include/convert_basic_shapes_to_polygon.h index 850c565537..269f503b1e 100644 --- a/libs/kimath/include/convert_basic_shapes_to_polygon.h +++ b/libs/kimath/include/convert_basic_shapes_to_polygon.h @@ -113,11 +113,29 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCenter, i void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, 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 rounded corners arcs to multiple straight lines. This will generate at least + * Convert rounded corners arcs to multiple straight lines. This will generate at least * 16 segments per circle. * * @param aCornerBuffer is a buffer to store the polygon. @@ -125,7 +143,7 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo * @param aSize is the size of the rectangle. * @param aCornerRadius is the radius of rounded corners (can be 0). * @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: * - 0 = no chamfer * - 1 = TOP_LEFT @@ -133,13 +151,14 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo * - 4 = BOTTOM_LEFT * - 8 = BOTTOM_RIGHT * 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 aErrorLoc determines if the approximation error be placed outside or inside the polygon. */ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, const wxSize& aSize, double aRotation, int aCornerRadius, - double aChamferRatio, int aChamferCorners, + double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc ); /** diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index cb68741a70..52e9d5bbfa 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -26,6 +26,7 @@ */ #include // for max, min +#include // for bitset::count #include // for atan2 #include // 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} -void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxSize& aSize, - int aCornerRadius, int aError, ERROR_LOC aErrorLoc ) +struct ROUNDED_CORNER +{ + 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& 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 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& 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; + wxSize size( aSize / 2 ); - wxPoint centers[4]; - wxSize size( aSize / 2 ); + std::vector corners; + 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 ) ); - size.x -= aCornerRadius; - size.y -= aCornerRadius; + if( aDeltaY == size.x || aDeltaX == size.y ) + CornerListRemoveDuplicates( corners ); - // 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 ); + CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc ); - centers[0] = wxPoint( -size.x, size.y ); - centers[1] = wxPoint( size.x, size.y ); - centers[2] = wxPoint( size.x, -size.y ); - centers[3] = wxPoint( -size.x, -size.y ); + if( aRotation != 0.0 ) + outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) ); - int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 ); - - // Choppy corners on rounded-corner rectangles look awful so enforce a minimum of - // 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: + outline.Move( VECTOR2I( aPosition ) ); aCornerBuffer.Append( outline ); } void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, - const wxSize& aSize, double aRotation, - int aCornerRadius, double aChamferRatio, - int aChamferCorners, int aError, ERROR_LOC aErrorLoc ) + const wxSize& aSize, double aRotation, int aCornerRadius, + double aChamferRatio, int aChamferCorners, int aInflate, + int aError, ERROR_LOC aErrorLoc ) { 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 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 ) { - // Now we have the round rect outline, in position 0,0 orientation 0.0. - // Chamfer the corner(s). - int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y ); - - SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer - - int corner_id[4] = + int shorterSide = std::min( aSize.x, aSize.y ); + int chamfer = aChamferRatio * shorterSide; + 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 }; + + for( int cc = 0, pos = 0; cc < 4; cc++, pos++ ) { - RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, - 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 ) + if( !( aChamferCorners & chamId[cc] ) ) continue; - VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 ); + corners[pos].m_radius = 0; + + if( chamfer == 0 ) + continue; - if( aCornerRadius ) - { - // We recreate a rectangular area covering the full rounded corner - // (max size = aSize/2) to rebuild the corner before chamfering, to be sure - // the rounded corner shape does not overlap the chamfered corner shape: - chamfered_corner.RemoveAllContours(); - chamfered_corner.NewOutline(); - chamfered_corner.Append( 0, 0 ); - chamfered_corner.Append( 0, signY[ii] * aSize.y / 2 ); - 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 - chamfered_corner.RemoveAllContours(); - 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 ); + corners.insert( corners.begin() + pos + 1, corners[pos] ); + corners[pos].m_position.x += sign[( 2 * cc ) & 7] * chamfer; + corners[pos].m_position.y += sign[( 2 * cc - 2 ) & 7] * chamfer; + corners[pos + 1].m_position.x += sign[( 2 * cc + 1 ) & 7] * chamfer; + corners[pos + 1].m_position.y += sign[( 2 * cc - 1 ) & 7] * chamfer; + pos++; } + + if( chamferCnt > 1 && 2 * chamfer >= shorterSide ) + CornerListRemoveDuplicates( corners ); } - // Rotate and move the outline: + CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc ); + if( aRotation != 0.0 ) outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) ); outline.Move( VECTOR2I( aPosition ) ); - - // Add the outline: aCornerBuffer.Append( outline ); } diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 027db2396d..47f4d8f046 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -629,37 +629,9 @@ void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 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; - outline.NewOutline(); - - 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 ); - } - + TransformTrapezoidToPolygon( outline, padShapePos, m_size, angle, ddx, ddy, + aClearanceValue, aError, aErrorLoc ); aCornerBuffer.Append( outline ); } break; @@ -667,51 +639,14 @@ void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, case PAD_SHAPE::CHAMFERED_RECT: case PAD_SHAPE::ROUNDRECT: { - int radius = GetRoundRectCornerRadius(); - wxSize shapesize( m_size ); - 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; - } + bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT; SHAPE_POLY_SET outline; - TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, radius, - chamferRatio, + TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, angle, + GetRoundRectCornerRadius(), + doChamfer ? GetChamferRectRatio() : 0, doChamfer ? GetChamferPositions() : 0, - aError, aErrorLoc ); - + aClearanceValue, aError, aErrorLoc ); aCornerBuffer.Append( outline ); } break; diff --git a/pcbnew/exporters/export_gencad.cpp b/pcbnew/exporters/export_gencad.cpp index 87b7ff73b8..defe9bdde2 100644 --- a/pcbnew/exporters/export_gencad.cpp +++ b/pcbnew/exporters/export_gencad.cpp @@ -582,7 +582,7 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb ) TransformRoundChamferedRectToPolygon( outline, pad->GetPosition(), pad->GetSize(), pad->GetOrientation(), pad->GetRoundRectCornerRadius(), - pad->GetChamferRectRatio(), pad->GetChamferPositions(), maxError, + pad->GetChamferRectRatio(), pad->GetChamferPositions(), 0, maxError, ERROR_INSIDE ); for( int jj = 0; jj < outline.OutlineCount(); ++jj ) diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index b1d1751514..295b53ba1e 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -430,7 +430,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize(), m_orient, GetRoundRectCornerRadius(), GetChamferRectRatio(), - GetChamferPositions(), maxError, ERROR_INSIDE ); + GetChamferPositions(), 0, maxError, ERROR_INSIDE ); add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) ); } diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 29183aff4f..db1f643a61 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -500,6 +500,7 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, PAD* aPad ) 0, rradius, aPad->GetChamferRectRatio(), doChamfer ? aPad->GetChamferPositions() : 0, + 0, aBoard->GetDesignSettings().m_MaxError, ERROR_INSIDE ); SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 );