Refactor poly outline generator to handle multiple outlines.

Note: disjoint outlines are handled in footprint courtyards, but not
for board outlines.

Fixes https://gitlab.com/kicad/code/kicad/issues/7660
This commit is contained in:
Jeff Young 2022-10-14 20:30:56 +01:00
parent 6357c83a8c
commit 6f9bc0980b
3 changed files with 156 additions and 421 deletions

View File

@ -137,427 +137,59 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const VECTOR2I& aPoint,
* Build a polygon (with holes) from a PCB_SHAPE list, which is expected to be a closed main
* outline with perhaps closed inner outlines. These closed inner outlines are considered as
* holes in the main outline.
* @param aSegList the initial list of drawsegments (only lines, circles and arcs).
* @param aShapeList the initial list of SHAPEs (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon.
* @param aErrorMax is the max error distance when polygonizing a curve (internal units)
* @param aChainingEpsilon is the max error distance when polygonizing a curve (internal units)
* @param aAllowDisjoint indicates multiple top-level outlines are allowed
* @param aErrorHandler = an optional error handler
*/
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
int aErrorMax, int aChainingEpsilon,
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SET& aPolygons,
int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint,
OUTLINE_ERROR_HANDLER* aErrorHandler )
{
if( aSegList.size() == 0 )
if( aShapeList.size() == 0 )
return true;
bool polygonComplete = false;
bool selfIntersecting = false;
wxString msg;
PCB_SHAPE* graphic = nullptr;
std::set<PCB_SHAPE*> startCandidates( aSegList.begin(), aSegList.end() );
std::set<PCB_SHAPE*> startCandidates( aShapeList.begin(), aShapeList.end() );
// Find edge point with minimum x, this should be in the outer polygon
// which will define the perimeter polygon polygon.
VECTOR2I xmin = VECTOR2I( INT_MAX, 0 );
int xmini = 0;
for( size_t i = 0; i < aSegList.size(); i++ )
{
graphic = (PCB_SHAPE*) aSegList[i];
graphic->ClearFlags( SKIP_STRUCT );
switch( graphic->GetShape() )
{
case SHAPE_T::RECT:
case SHAPE_T::SEGMENT:
{
if( graphic->GetStart().x < xmin.x )
{
xmin = graphic->GetStart();
xmini = i;
}
if( graphic->GetEnd().x < xmin.x )
{
xmin = graphic->GetEnd();
xmini = i;
}
}
break;
case SHAPE_T::ARC:
{
VECTOR2I pstart = graphic->GetStart();
VECTOR2I center = graphic->GetCenter();
EDA_ANGLE angle = -graphic->GetArcAngle();
double radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
for( int step = 1; step<=steps; ++step )
{
EDA_ANGLE rotation = ( angle * step ) / steps;
VECTOR2I pt = pstart;
RotatePoint( pt, center, rotation );
if( pt.x < xmin.x )
{
xmin = pt;
xmini = i;
}
}
}
break;
case SHAPE_T::CIRCLE:
{
VECTOR2I pt = graphic->GetCenter();
// pt has minimum x point
pt.x -= graphic->GetRadius();
// when the radius <= 0, this is a mal-formed circle. Skip it
if( graphic->GetRadius() > 0 && pt.x < xmin.x )
{
xmin = pt;
xmini = i;
}
}
break;
case SHAPE_T::BEZIER:
{
graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
for( const VECTOR2I& pt : graphic->GetBezierPoints() )
{
if( pt.x < xmin.x )
{
xmin = pt;
xmini = i;
}
}
}
break;
case SHAPE_T::POLY:
{
const SHAPE_POLY_SET& poly = graphic->GetPolyShape();
EDA_ANGLE orientation = ANGLE_0;
VECTOR2I offset = VECTOR2I( 0, 0 );
if( graphic->GetParentFootprint() )
{
orientation = graphic->GetParentFootprint()->GetOrientation();
offset = graphic->GetParentFootprint()->GetPosition();
}
for( auto iter = poly.CIterate(); iter; iter++ )
{
VECTOR2I pt = *iter;
RotatePoint( pt, orientation );
pt += offset;
if( pt.x < xmin.x )
{
xmin.x = pt.x;
xmin.y = pt.y;
xmini = i;
}
}
}
break;
default:
break;
}
}
// Keep a list of where the various segments came from so after doing our combined-polygon
// Keep a list of where the various shapes came from so after doing our combined-polygon
// tests we can still report errors against the individual graphic items.
std::map<std::pair<VECTOR2I, VECTOR2I>, PCB_SHAPE*> segOwners;
std::map<std::pair<VECTOR2I, VECTOR2I>, PCB_SHAPE*> shapeOwners;
auto fetchOwner =
[&]( const SEG& seg ) -> PCB_SHAPE*
{
auto it = segOwners.find( std::make_pair( seg.A, seg.B ) );
return it == segOwners.end() ? nullptr : it->second;
auto it = shapeOwners.find( std::make_pair( seg.A, seg.B ) );
return it == shapeOwners.end() ? nullptr : it->second;
};
// Grab the left most point, assume its on the board's perimeter, and see if we can put
// enough graphics together by matching endpoints to formulate a cohesive polygon.
PCB_SHAPE* prevGraphic = nullptr;
VECTOR2I prevPt;
graphic = (PCB_SHAPE*) aSegList[xmini];
graphic->SetFlags( SKIP_STRUCT );
startCandidates.erase( graphic );
std::vector<SHAPE_LINE_CHAIN> contours;
// Output the outline perimeter as polygon.
if( graphic->GetShape() == SHAPE_T::CIRCLE )
{
TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(),
ARC_LOW_DEF, ERROR_INSIDE );
polygonComplete = true;
}
else if( graphic->GetShape() == SHAPE_T::RECT )
{
std::vector<VECTOR2I> pts = graphic->GetRectCorners();
aPolygons.NewOutline();
for( const VECTOR2I& pt : pts )
aPolygons.Append( pt );
segOwners[ std::make_pair( pts[0], pts[1] ) ] = graphic;
segOwners[ std::make_pair( pts[1], pts[2] ) ] = graphic;
segOwners[ std::make_pair( pts[2], pts[3] ) ] = graphic;
segOwners[ std::make_pair( pts[3], pts[0] ) ] = graphic;
polygonComplete = true;
}
else if( graphic->GetShape() == SHAPE_T::POLY )
{
EDA_ANGLE orientation = ANGLE_0;
VECTOR2I offset = VECTOR2I( 0, 0 );
if( graphic->GetParentFootprint() )
{
orientation = graphic->GetParentFootprint()->GetOrientation();
offset = graphic->GetParentFootprint()->GetPosition();
}
aPolygons.NewOutline();
bool first = true;
for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
{
VECTOR2I pt = *it;
RotatePoint( pt, orientation );
pt += offset;
aPolygons.Append( pt );
if( first )
first = false;
else
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
polygonComplete = true;
}
else
{
// Polygon start point. Arbitrarily choose an end of the segment and build the polygon
// from there.
VECTOR2I startPt = graphic->GetEnd();
prevPt = startPt;
aPolygons.NewOutline();
aPolygons.Append( prevPt );
// Do not append the other end point yet of this 'graphic', this first 'graphic' might
// be an arc or a curve.
for(;;)
{
switch( graphic->GetShape() )
{
case SHAPE_T::RECT:
case SHAPE_T::CIRCLE:
{
// As a non-first item, closed shapes can't be anything but self-intersecting
if( aErrorHandler )
{
wxASSERT( prevGraphic );
(*aErrorHandler)( _( "(self-intersecting)" ), prevGraphic, graphic, prevPt );
}
selfIntersecting = true;
// A closed shape will finish where it started, so no point in updating prevPt
}
break;
case SHAPE_T::SEGMENT:
{
VECTOR2I nextPt;
// Use the line segment end point furthest away from prevPt as we assume the
// other end to be ON prevPt or very close to it.
if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
nextPt = graphic->GetEnd();
else
nextPt = graphic->GetStart();
aPolygons.Append( nextPt );
segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
prevPt = nextPt;
}
break;
case SHAPE_T::ARC:
{
// We do not support arcs in polygons, so approximate an arc with a series of
// short lines and put those line segments into the !same! PATH.
VECTOR2I pstart = graphic->GetStart();
VECTOR2I pend = graphic->GetEnd();
VECTOR2I pcenter = graphic->GetCenter();
EDA_ANGLE angle = -graphic->GetArcAngle();
double radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
{
wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
angle = -angle;
std::swap( pstart, pend );
}
// Create intermediate points between start and end:
for( int step = 1; step < steps; ++step )
{
EDA_ANGLE rotation = ( angle * step ) / steps;
VECTOR2I pt = pstart;
RotatePoint( pt, pcenter, rotation );
aPolygons.Append( pt );
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
// Append the last arc end point
aPolygons.Append( pend );
segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
prevPt = pend;
}
break;
case SHAPE_T::BEZIER:
{
// We do not support Bezier curves in polygons, so approximate with a series
// of short lines and put those line segments into the !same! PATH.
VECTOR2I nextPt;
bool first = true;
bool reverse = false;
// Use the end point furthest away from
// prevPt as we assume the other end to be ON prevPt or
// very close to it.
if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
{
nextPt = graphic->GetEnd();
}
else
{
nextPt = graphic->GetStart();
reverse = true;
}
if( reverse )
{
for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
{
const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
aPolygons.Append( pt );
if( first )
first = false;
else
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
}
else
{
for( const VECTOR2I& pt : graphic->GetBezierPoints() )
{
aPolygons.Append( pt );
if( first )
first = false;
else
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
}
prevPt = nextPt;
}
break;
default:
UNIMPLEMENTED_FOR( graphic->SHAPE_T_asString() );
return false;
}
// Get next closest segment.
PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
{
prevGraphic = graphic;
graphic = nextGraphic;
graphic->SetFlags( SKIP_STRUCT );
startCandidates.erase( graphic );
continue;
}
// Finished, or ran into trouble...
if( close_enough( startPt, prevPt, aChainingEpsilon ) )
{
polygonComplete = true;
break;
}
else if( nextGraphic ) // encountered already-used segment, but not at the start
{
if( aErrorHandler )
(*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
polygonComplete = false;
break;
}
else // encountered discontinuity
{
if( aErrorHandler )
(*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
polygonComplete = false;
break;
}
}
}
int holeNum = -1;
for( PCB_SHAPE* shape : startCandidates )
shape->ClearFlags( SKIP_STRUCT );
while( startCandidates.size() )
{
int hole = aPolygons.NewHole();
bool firstPt = true;
holeNum++;
graphic = (PCB_SHAPE*) *startCandidates.begin();
graphic->SetFlags( SKIP_STRUCT );
startCandidates.erase( startCandidates.begin() );
// Both circles and polygons on the edge cuts layer are closed items that do not
// connect to other elements, so we process them independently
contours.emplace_back();
int ii = contours.size() - 1;
bool firstPt = true;
// Circles, rects and polygons are closed shapes unto themselves (and do not combine
// with other shapes), so process them separately.
if( graphic->GetShape() == SHAPE_T::POLY )
{
EDA_ANGLE orientation = ANGLE_0;
@ -575,15 +207,17 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
RotatePoint( pt, orientation );
pt += offset;
aPolygons.Append( pt, -1, hole );
contours[ ii ].Append( pt );
if( firstPt )
firstPt = false;
else
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
contours[ ii ].SetClosed( true );
}
else if( graphic->GetShape() == SHAPE_T::CIRCLE )
{
@ -600,15 +234,17 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
{
nextPt = start;
RotatePoint( nextPt, center, ANGLE_360 * step / steps );
aPolygons.Append( nextPt, -1, hole );
contours[ ii ].Append( nextPt );
if( firstPt )
firstPt = false;
else
segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
shapeOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
prevPt = nextPt;
}
contours[ ii ].SetClosed( true );
}
else if( graphic->GetShape() == SHAPE_T::RECT )
{
@ -616,15 +252,17 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
for( const VECTOR2I& pt : pts )
{
aPolygons.Append( pt, -1, hole );
contours[ ii ].Append( pt );
if( firstPt )
firstPt = false;
else
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
contours[ ii ].SetClosed( true );
}
else
{
@ -633,13 +271,30 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
VECTOR2I startPt = graphic->GetEnd();
prevPt = startPt;
aPolygons.Append( prevPt, -1, hole );
contours[ ii ].Append( prevPt );
// do not append the other end point yet, this first 'graphic' might be an arc
for(;;)
{
switch( graphic->GetShape() )
{
case SHAPE_T::RECT:
case SHAPE_T::CIRCLE:
{
// As a non-first item, closed shapes can't be anything but self-intersecting
if( aErrorHandler )
{
wxASSERT( prevGraphic );
(*aErrorHandler)( _( "(self-intersecting)" ), prevGraphic, graphic, prevPt );
}
selfIntersecting = true;
// A closed shape will finish where it started, so no point in updating prevPt
break;
}
case SHAPE_T::SEGMENT:
{
VECTOR2I nextPt;
@ -652,8 +307,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
else
nextPt = graphic->GetStart();
aPolygons.Append( nextPt, -1, hole );
segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
contours[ ii ].Append( nextPt );
shapeOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
prevPt = nextPt;
}
break;
@ -685,14 +340,14 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
RotatePoint( pt, pcenter, rotation );
aPolygons.Append( pt, -1, hole );
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
contours[ ii ].Append( pt );
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
// Append the last arc end point
aPolygons.Append( pend, -1, hole );
segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
contours[ ii ].Append( pend );
shapeOwners[ std::make_pair( prevPt, pend ) ] = graphic;
prevPt = pend;
}
break;
@ -722,8 +377,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
{
const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
aPolygons.Append( pt, -1, hole );
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
contours[ ii ].Append( pt );
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
}
@ -731,8 +386,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
{
for( const VECTOR2I& pt : graphic->GetBezierPoints() )
{
aPolygons.Append( pt, -1, hole );
segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
contours[ ii ].Append( pt );
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
prevPt = pt;
}
}
@ -748,10 +403,11 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Get next closest segment.
PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aShapeList, aChainingEpsilon );
if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
{
prevGraphic = graphic;
graphic = nextGraphic;
graphic->SetFlags( SKIP_STRUCT );
startCandidates.erase( graphic );
@ -762,6 +418,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
if( close_enough( startPt, prevPt, aChainingEpsilon ) )
{
contours[ ii ].SetClosed( true );
break;
}
else if( nextGraphic ) // encountered already-used segment, but not at the start
@ -769,7 +426,6 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
if( aErrorHandler )
(*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
polygonComplete = false;
break;
}
else // encountered discontinuity
@ -777,15 +433,93 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
if( aErrorHandler )
(*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
polygonComplete = false;
break;
}
}
}
}
if( !polygonComplete )
return false;
for( const SHAPE_LINE_CHAIN& contour : contours )
{
if( !contour.IsClosed() )
return false;
}
// First, collect the parents of each contour
//
std::map<int, std::vector<int>> contourToParentIndexesMap;
for( size_t ii = 0; ii < contours.size(); ++ii )
{
VECTOR2I firstPt = contours[ii].GetPoint( 0 );
std::vector<int> parents;
for( size_t jj = 0; jj < contours.size(); ++jj )
{
if( jj == ii )
continue;
const SHAPE_LINE_CHAIN& parentCandidate = contours[jj];
if( parentCandidate.PointInside( firstPt ) )
parents.push_back( jj );
}
contourToParentIndexesMap[ii] = parents;
}
// Next add those that are top-level outlines to the SHAPE_POLY_SET
//
std::map<int, int> contourToOutlineIdxMap;
for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
{
if( parentIndexes.size() %2 == 0 )
{
// Even number of parents; top-level outline
if( !aAllowDisjoint && !aPolygons.IsEmpty() )
{
if( aErrorHandler )
{
BOARD_ITEM* a = fetchOwner( aPolygons.Outline( 0 ).GetSegment( 0 ) );
BOARD_ITEM* b = fetchOwner( contours[ contourIndex ].GetSegment( 0 ) );
if( a && b )
{
(*aErrorHandler)( _( "(multiple board outlines not supported)" ), a, b,
contours[ contourIndex ].GetPoint( 0 ) );
}
}
return false;
}
aPolygons.AddOutline( contours[ contourIndex ] );
contourToOutlineIdxMap[ contourIndex ] = aPolygons.OutlineCount() - 1;
}
}
// And finally add the holes
//
for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
{
if( parentIndexes.size() %2 == 1 )
{
// Odd number of parents; we're a hole in the parent which has one fewer parents
// than we have.
const SHAPE_LINE_CHAIN& hole = contours[ contourIndex ];
for( int parentContourIdx : parentIndexes )
{
if( contourToParentIndexesMap[ parentContourIdx ].size() == parentIndexes.size() - 1 )
{
int outlineIdx = contourToOutlineIdxMap[ parentContourIdx ];
aPolygons.AddHole( hole, outlineIdx );
break;
}
}
}
}
// All of the silliness that follows is to work around the segment iterator while checking
// for collisions.
@ -860,7 +594,7 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aE
if( segList.size() )
{
success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
aErrorHandler );
false, aErrorHandler );
}
if( !success || !aOutlines.OutlineCount() )
@ -1085,7 +819,7 @@ bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, in
if( !segList.empty() )
{
success = ConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon,
aErrorHandler );
false, aErrorHandler );
}
// A closed outline was found on Edge_Cuts

View File

@ -34,18 +34,19 @@ const std::function<void( const wxString& msg, BOARD_ITEM* itemA, BOARD_ITEM* it
/**
* Function ConvertOutlineToPolygon
* build a polygon (with holes) from a PCB_SHAPE list, which is expected to be
* a outline, therefore a closed main outline with perhaps closed inner outlines.
* These closed inner outlines are considered as holes in the main outline
* @param aSegList the initial list of drawsegments (only lines, circles and arcs).
* build a polygon set (with holes) from a PCB_SHAPE list, which is expected to be one or more
* top-level closed outlines, with zero or more holes in each. Optionally, it can be limited to
* a single top-level closed outline.
* @param aShapeList the initial list of drawsegments (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon.
* @param aErrorMax is the max error distance when polygonizing a curve (internal units)
* @param aChainingEpsilon is the max distance from one endPt to the next startPt (internal units)
* @param aAllowDisjoint indicates multiple top-level outlines are allowed
* @param aErrorHandler = an optional error handler
*/
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
int aErrorMax, int aChainingEpsilon,
OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SET& aPolygons,
int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint,
OUTLINE_ERROR_HANDLER* aErrorHandler );
/**

View File

@ -2222,7 +2222,7 @@ void FOOTPRINT::BuildCourtyardCaches( OUTLINE_ERROR_HANDLER* aErrorHandler )
int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
if( ConvertOutlineToPolygon( list_front, m_courtyard_cache_front, errorMax, chainingEpsilon,
aErrorHandler ) )
true, aErrorHandler ) )
{
// Touching courtyards, or courtyards -at- the clearance distance are legal.
m_courtyard_cache_front.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
@ -2235,7 +2235,7 @@ void FOOTPRINT::BuildCourtyardCaches( OUTLINE_ERROR_HANDLER* aErrorHandler )
}
if( ConvertOutlineToPolygon( list_back, m_courtyard_cache_back, errorMax, chainingEpsilon,
aErrorHandler ) )
true, aErrorHandler ) )
{
// Touching courtyards, or courtyards -at- the clearance distance are legal.
m_courtyard_cache_back.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );