From c489659a20951c345417acb046d1bf1dc17d721e Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 9 May 2019 16:23:18 +0200 Subject: [PATCH] Custom pads: fix incorrect shape of masks when the shape is a polygon with holes and the mask margin is < 0. This is mainly the solder paste layer that shows this issue. This is due to the fact SHAPE_POLY_SET::Inflate does not work fine with polygons with linked holes. SHAPE_POLY_SET::InflateWithLinkedHoles it added to fix this issue. Fixes: lp:1828287 https://bugs.launchpad.net/kicad/+bug/1828287 --- common/convert_basic_shapes_to_polygon.cpp | 73 ++++++++++--------- common/geometry/shape_poly_set.cpp | 8 ++ include/geometry/shape_poly_set.h | 18 ++++- ...board_items_to_polygon_shape_transform.cpp | 3 +- pcbnew/pad_draw_functions.cpp | 29 +++----- pcbnew/pcb_painter.cpp | 5 +- pcbnew/plot_board_layers.cpp | 6 +- 7 files changed, 85 insertions(+), 57 deletions(-) diff --git a/common/convert_basic_shapes_to_polygon.cpp b/common/convert_basic_shapes_to_polygon.cpp index cf68cb2286..d6a00c5801 100644 --- a/common/convert_basic_shapes_to_polygon.cpp +++ b/common/convert_basic_shapes_to_polygon.cpp @@ -32,11 +32,42 @@ #include #include +/** + * Function TransformCircleToPolygon + * convert a circle to a polygon, using multiple straight lines + * @param aBuffer = a SHAPE_LINE_CHAIN to store the polygon corners + * @param aCenter = the center of the circle + * @param aRadius = the radius of the circle + * @param aCircleToSegmentsCount = the number of segments to approximate a circle + * Note: the polygon is inside the circle, so if you want to have the polygon + * outside the circle, you should give aRadius calculated with a correction factor + */ +void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer, + wxPoint aCenter, int aRadius, + int aCircleToSegmentsCount ) +{ + wxPoint corner_position; + double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree + double halfstep = delta/2; // the starting value for rot angles + + for( int ii = 0; ii < aCircleToSegmentsCount; ii++ ) + { + corner_position.x = aRadius; + corner_position.y = 0; + double angle = (ii * delta) + halfstep; + RotatePoint( &corner_position, angle ); + corner_position += aCenter; + aBuffer.Append( corner_position.x, corner_position.y ); + } + + aBuffer.SetClosed( true ); +} + /** * Function TransformCircleToPolygon * convert a circle to a polygon, using multiple straight lines - * @param aCornerBuffer = a buffer to store the polygon + * @param aCornerBuffer = a SHAPE_POLY_SET to store the polygon * @param aCenter = the center of the circle * @param aRadius = the radius of the circle * @param aCircleToSegmentsCount = the number of segments to approximate a circle @@ -397,40 +428,14 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, return; } - aCornerBuffer.NewOutline(); + SHAPE_POLY_SET buffer; - // Draw the inner circle of the ring - int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + TransformCircleToPolygon( buffer, aCentre, outer_radius, aCircleToSegmentsCount ); - for( int ii = 0; ii < 3600; ii += delta ) - { - curr_point.x = inner_radius; - curr_point.y = 0; - RotatePoint( &curr_point, ii ); - curr_point += aCentre; - aCornerBuffer.Append( curr_point.x, curr_point.y ); - } + // Build the hole: + buffer.NewHole(); + TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius, aCircleToSegmentsCount ); - // Draw the last point of inner circle - aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); - - // Draw the outer circle of the ring - // the first point creates also a segment from the inner to the outer polygon - for( int ii = 0; ii < 3600; ii += delta ) - { - curr_point.x = outer_radius; - curr_point.y = 0; - RotatePoint( &curr_point, -ii ); - curr_point += aCentre; - aCornerBuffer.Append( curr_point.x, curr_point.y ); - } - - // Draw the last point of outer circle - aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y ); - - // And connect the outer polygon to the inner polygon,. - // because a segment from inner to the outer polygon was already created, - // the final polygon is the inner and the outer outlines connected by - // 2 overlapping segments - aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); + buffer.Fracture( SHAPE_POLY_SET::PM_FAST ); + aCornerBuffer.Append( buffer ); } diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 13cc9a1a55..1de93f328b 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -544,6 +544,14 @@ void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, } +void SHAPE_POLY_SET::InflateWithLinkedHoles( int aFactor, int aCircleSegmentsCount, POLYGON_MODE aFastMode ) +{ + Simplify( aFastMode ); + Inflate( aFactor, aCircleSegmentsCount ); + Fracture( aFastMode ); +} + + void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) { // A static table to avoid repetitive calculations of the coefficient diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 57a6dc9a6f..460ebeb78f 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -826,11 +826,23 @@ class SHAPE_POLY_SET : public SHAPE void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); - ///> Performs outline inflation/deflation, using round corners. + /** Performs outline inflation/deflation, using round corners. + * Polygons cna have holes, but not linked holes with main outlines, + * if aFactor < 0. + * When aFactor is < 0 a bad shape can result from these extra-segments used to + * link holes to main outlines + * Use InflateWithLinkedHoles for these polygons, especially if aFactor < 0 + */ void Inflate( int aFactor, int aCircleSegmentsCount ); - ///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the outer ring - ///> to the inner holes + ///> Performs outline inflation/deflation, using round corners. + ///> Polygons can have holes, and/or linked holes with main outlines. + ///> The resulting polygons are laso polygons with linked holes to main outlines + ///> For aFastMode meaning, see function booleanOp + void InflateWithLinkedHoles( int aFactor, int aCircleSegmentsCount, POLYGON_MODE aFastMode ); + + ///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" + ///> connecting the outer ring to the inner holes ///> For aFastMode meaning, see function booleanOp void Fracture( POLYGON_MODE aFastMode ); diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 925e8da4c6..e1c5cdc00e 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -787,11 +787,12 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, case PAD_SHAPE_CUSTOM: { int clearance = KiROUND( aClearanceValue * aCorrectionFactor ); - SHAPE_POLY_SET outline; // Will contain the corners in board coordinates outline.Append( m_customShapeAsPolygon ); CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); + outline.Simplify( SHAPE_POLY_SET::PM_FAST ); outline.Inflate( clearance, aCircleToSegmentsCount ); + outline.Fracture( SHAPE_POLY_SET::PM_FAST ); aCornerBuffer.Append( outline ); } break; diff --git a/pcbnew/pad_draw_functions.cpp b/pcbnew/pad_draw_functions.cpp index 5a0b5ffd47..c9935f9469 100644 --- a/pcbnew/pad_draw_functions.cpp +++ b/pcbnew/pad_draw_functions.cpp @@ -546,24 +546,19 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) const int segmentToCircleCount = ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF; if( aDrawInfo.m_Mask_margin.x ) - { - SHAPE_POLY_SET clearance_outline; - clearance_outline.Append( outline ); - clearance_outline.Inflate( aDrawInfo.m_Mask_margin.x, segmentToCircleCount ); - } - else - { - // Draw the polygon: only one polygon is expected - // However we provide a multi polygon shape drawing - // ( for the future or to show a non expected shape ) - for( int jj = 0; jj < outline.OutlineCount(); ++jj ) - { - poly = &outline.Outline( jj ); + outline.InflateWithLinkedHoles( aDrawInfo.m_Mask_margin.x, + segmentToCircleCount, SHAPE_POLY_SET::PM_FAST ); - GRClosedPoly( aClipBox, aDC, poly->PointCount(), - (wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0, - aDrawInfo.m_Color, aDrawInfo.m_Color ); - } + // Draw the polygon: only one polygon is expected + // However we provide a multi polygon shape drawing + // ( can happen with CUSTOM pads and negative margins ) + for( int jj = 0; jj < outline.OutlineCount(); ++jj ) + { + poly = &outline.Outline( jj ); + + GRClosedPoly( aClipBox, aDC, poly->PointCount(), + (wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0, + aDrawInfo.m_Color, aDrawInfo.m_Color ); } if( aDrawInfo.m_PadClearance ) diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index cc2a60c130..3960aded40 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -840,7 +840,10 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) SHAPE_POLY_SET outline; outline.Append( aPad->GetCustomShapeAsPolygon() ); const int segmentToCircleCount = ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF; - outline.Inflate( custom_margin, segmentToCircleCount ); + // outline polygon can have holes linked to the main outline. + // So use InflateWithLinkedHoles(), not Inflate() that can create + // bad shapes if custom_margin is < 0 + outline.InflateWithLinkedHoles( custom_margin, segmentToCircleCount, SHAPE_POLY_SET::PM_FAST ); m_gal->DrawPolygon( outline ); } else diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 166c913191..96890ef980 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -461,7 +461,11 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, D_PAD dummy( *pad ); SHAPE_POLY_SET shape; pad->MergePrimitivesAsPolygon( &shape, 64 ); - shape.Inflate( margin.x, ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF ); + // shape polygon can have holes linked to the main outline. + // So use InflateWithLinkedHoles(), not Inflate() that can create + // bad shapes if margin.x is < 0 + shape.InflateWithLinkedHoles( margin.x, ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF, + SHAPE_POLY_SET::PM_FAST ); dummy.DeletePrimitivesList(); dummy.AddPrimitive( shape, 0 ); dummy.MergePrimitivesAsPolygon();