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
This commit is contained in:
jean-pierre charras 2019-05-09 16:23:18 +02:00
parent f7c042a357
commit a155289f70
7 changed files with 85 additions and 57 deletions

View File

@ -32,11 +32,42 @@
#include <common.h> #include <common.h>
#include <convert_basic_shapes_to_polygon.h> #include <convert_basic_shapes_to_polygon.h>
/**
* 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 * Function TransformCircleToPolygon
* convert a circle to a polygon, using multiple straight lines * 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 aCenter = the center of the circle
* @param aRadius = the radius of the circle * @param aRadius = the radius of the circle
* @param aCircleToSegmentsCount = the number of segments to approximate a circle * @param aCircleToSegmentsCount = the number of segments to approximate a circle
@ -454,40 +485,14 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
return; return;
} }
aCornerBuffer.NewOutline(); SHAPE_POLY_SET buffer;
// Draw the inner circle of the ring TransformCircleToPolygon( buffer, aCentre, outer_radius, aCircleToSegmentsCount );
int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
for( int ii = 0; ii < 3600; ii += delta ) // Build the hole:
{ buffer.NewHole();
curr_point.x = inner_radius; TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius, aCircleToSegmentsCount );
curr_point.y = 0;
RotatePoint( &curr_point, ii );
curr_point += aCentre;
aCornerBuffer.Append( curr_point.x, curr_point.y );
}
// Draw the last point of inner circle buffer.Fracture( SHAPE_POLY_SET::PM_FAST );
aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y ); aCornerBuffer.Append( buffer );
// 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 );
} }

View File

@ -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 ) void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount )
{ {
// A static table to avoid repetitive calculations of the coefficient // A static table to avoid repetitive calculations of the coefficient

View File

@ -830,11 +830,23 @@ class SHAPE_POLY_SET : public SHAPE
void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
POLYGON_MODE aFastMode ); 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 ); void Inflate( int aFactor, int aCircleSegmentsCount );
///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the outer ring ///> Performs outline inflation/deflation, using round corners.
///> to the inner holes ///> 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 ///> For aFastMode meaning, see function booleanOp
void Fracture( POLYGON_MODE aFastMode ); void Fracture( POLYGON_MODE aFastMode );

View File

@ -791,11 +791,12 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_CUSTOM: case PAD_SHAPE_CUSTOM:
{ {
int clearance = KiROUND( aClearanceValue * aCorrectionFactor ); int clearance = KiROUND( aClearanceValue * aCorrectionFactor );
SHAPE_POLY_SET outline; // Will contain the corners in board coordinates SHAPE_POLY_SET outline; // Will contain the corners in board coordinates
outline.Append( m_customShapeAsPolygon ); outline.Append( m_customShapeAsPolygon );
CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() );
outline.Simplify( SHAPE_POLY_SET::PM_FAST );
outline.Inflate( clearance, aCircleToSegmentsCount ); outline.Inflate( clearance, aCircleToSegmentsCount );
outline.Fracture( SHAPE_POLY_SET::PM_FAST );
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );
} }
break; break;

View File

@ -530,24 +530,19 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
const int segmentToCircleCount = ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF; const int segmentToCircleCount = ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF;
if( aDrawInfo.m_Mask_margin.x ) if( aDrawInfo.m_Mask_margin.x )
{ outline.InflateWithLinkedHoles( aDrawInfo.m_Mask_margin.x,
SHAPE_POLY_SET clearance_outline; segmentToCircleCount, SHAPE_POLY_SET::PM_FAST );
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 );
GRClosedPoly( aClipBox, aDC, poly->PointCount(), // Draw the polygon: only one polygon is expected
(wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0, // However we provide a multi polygon shape drawing
aDrawInfo.m_Color, aDrawInfo.m_Color ); // ( 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 ) if( aDrawInfo.m_PadClearance )

View File

@ -846,7 +846,10 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() ); outline.Append( aPad->GetCustomShapeAsPolygon() );
const int segmentToCircleCount = ARC_APPROX_SEGMENTS_COUNT_HIGH_DEF; 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 ); m_gal->DrawPolygon( outline );
} }
else else

View File

@ -451,7 +451,11 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
D_PAD dummy( *pad ); D_PAD dummy( *pad );
SHAPE_POLY_SET shape; SHAPE_POLY_SET shape;
pad->MergePrimitivesAsPolygon( &shape, 64 ); 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.DeletePrimitivesList();
dummy.AddPrimitive( shape, 0 ); dummy.AddPrimitive( shape, 0 );
dummy.MergePrimitivesAsPolygon(); dummy.MergePrimitivesAsPolygon();