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 <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
* 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
@ -454,40 +485,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 );
}

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 )
{
// 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,
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 );

View File

@ -791,11 +791,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;

View File

@ -530,16 +530,12 @@ 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
{
outline.InflateWithLinkedHoles( aDrawInfo.m_Mask_margin.x,
segmentToCircleCount, SHAPE_POLY_SET::PM_FAST );
// 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 )
// ( can happen with CUSTOM pads and negative margins )
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{
poly = &outline.Outline( jj );
@ -548,7 +544,6 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
(wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0,
aDrawInfo.m_Color, aDrawInfo.m_Color );
}
}
if( aDrawInfo.m_PadClearance )
{

View File

@ -846,7 +846,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

View File

@ -451,7 +451,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();