From 7265f843587724df48be10a979e804bc0eb29be7 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sat, 14 Nov 2020 08:50:58 +0100 Subject: [PATCH] fix PAD::TransformShapeWithClearanceToPolygon() incorrect calculation in some cases. For round rect pads, when the arc error was OUTSIDE, the shape was too big and creates bad shape for chamfered round rect pads. For chamfered pads, when the clearance value was not 0, the chamfer was not at the right place. --- .../src/convert_basic_shapes_to_polygon.cpp | 49 ++++++++++++++----- ...board_items_to_polygon_shape_transform.cpp | 39 +++++++++++++-- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index f01c5a77d1..59e84aa710 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -192,6 +192,8 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxSize& aSize, int aCornerRadius, int aError, ERROR_LOC aErrorLoc ) { + SHAPE_POLY_SET outline; + wxPoint centers[4]; wxSize size( aSize / 2 ); @@ -228,26 +230,51 @@ void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxSize& a wxPoint pt( -radius, 0 ); RotatePoint( &pt, angle ); pt += aCenter; - aCornerBuffer.Append( pt.x, pt.y ); + outline.Append( pt.x, pt.y ); } }; - aCornerBuffer.NewOutline(); + outline.NewOutline(); - aCornerBuffer.Append( centers[0] + wxPoint( -radius, 0 ) ); + outline.Append( centers[0] + wxPoint( -radius, 0 ) ); genArc( centers[0], 0, 900 ); - aCornerBuffer.Append( centers[0] + wxPoint( 0, radius ) ); - aCornerBuffer.Append( centers[1] + wxPoint( 0, radius ) ); + outline.Append( centers[0] + wxPoint( 0, radius ) ); + outline.Append( centers[1] + wxPoint( 0, radius ) ); genArc( centers[1], 900, 1800 ); - aCornerBuffer.Append( centers[1] + wxPoint( radius, 0 ) ); - aCornerBuffer.Append( centers[2] + wxPoint( radius, 0 ) ); + outline.Append( centers[1] + wxPoint( radius, 0 ) ); + outline.Append( centers[2] + wxPoint( radius, 0 ) ); genArc( centers[2], 1800, 2700 ); - aCornerBuffer.Append( centers[2] + wxPoint( 0, -radius ) ); - aCornerBuffer.Append( centers[3] + wxPoint( 0, -radius ) ); + outline.Append( centers[2] + wxPoint( 0, -radius ) ); + outline.Append( centers[3] + wxPoint( 0, -radius ) ); genArc( centers[3], 2700, 3600 ); - aCornerBuffer.Append( centers[3] + wxPoint( -radius, 0 ) ); + outline.Append( centers[3] + wxPoint( -radius, 0 ) ); - aCornerBuffer.Outline( 0 ).SetClosed( true ); + 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 ); } diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 23697c0acb..dd0d8d7441 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -641,13 +641,44 @@ void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxSize shapesize( m_size ); bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT; - radius += aClearanceValue; - shapesize.x += aClearanceValue * 2; - shapesize.y += aClearanceValue * 2; + 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; TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, radius, - doChamfer ? GetChamferRectRatio() : 0.0, + chamferRatio, doChamfer ? GetChamferPositions() : 0, aError, aErrorLoc );