Gracefully handle dots in the outline path.

Fixes https://gitlab.com/kicad/code/kicad/issues/6854
This commit is contained in:
Jeff Young 2020-12-27 01:00:41 +00:00
parent 3aca0e1853
commit e1f76ea2ab
1 changed files with 59 additions and 74 deletions

View File

@ -47,25 +47,6 @@
*/ */
const wxChar* traceBoardOutline = wxT( "KICAD_BOARD_OUTLINE" ); const wxChar* traceBoardOutline = wxT( "KICAD_BOARD_OUTLINE" );
/**
* Function close_ness
* is a non-exact distance (also called Manhattan distance) used to approximate
* the distance between two points.
* The distance is very in-exact, but can be helpful when used
* to pick between alternative neighboring points.
* @param aLeft is the first point
* @param aRight is the second point
* @return unsigned - a measure of proximity that the caller knows about, in BIU,
* but remember it is only an approximation.
*/
static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
{
// Don't need an accurate distance calculation, just something
// approximating it, for relative ordering.
return unsigned( std::abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
}
/** /**
* Function close_enough * Function close_enough
* is a local and tunable method of qualifying the proximity of two points. * is a local and tunable method of qualifying the proximity of two points.
@ -75,27 +56,23 @@ static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
* @param aLimit is a measure of proximity that the caller knows about. * @param aLimit is a measure of proximity that the caller knows about.
* @return bool - true if the two points are close enough, else false. * @return bool - true if the two points are close enough, else false.
*/ */
inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit ) bool close_enough( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
{ {
// We don't use an accurate distance calculation, just something return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
// approximating it, since aLimit is non-exact anyway except when zero.
return close_ness( aLeft, aRight ) <= aLimit;
} }
/** /**
* Function close_st * Function closer_to_first
* Local method which qualifies whether the start or end point of a segment is closest to a point. * Local method which qualifies whether the start or end point of a segment is closest to a point.
* *
* @param aReference is the reference point * @param aRef is the reference point
* @param aFirst is the first point * @param aFirst is the first point
* @param aSecond is the second point * @param aSecond is the second point
* @return bool - true if the the first point is closest to the reference, otherwise false. * @return bool - true if the first point is closest to the reference, otherwise false.
*/ */
inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond ) bool closer_to_first( VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond )
{ {
// We don't use an accurate distance calculation, just something return ( aRef - aFirst ).SquaredEuclideanNorm() < ( aRef - aSecond ).SquaredEuclideanNorm();
// approximating to find the closest to the reference.
return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
} }
@ -111,70 +88,78 @@ inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wx
static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint, static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
const std::vector<PCB_SHAPE*>& aList, unsigned aLimit ) const std::vector<PCB_SHAPE*>& aList, unsigned aLimit )
{ {
unsigned min_d = INT_MAX; // Look for an unused, exact hit
int ndx_min = 0; for( PCB_SHAPE* graphic : aList )
// find the point closest to aPoint and perhaps exactly matching aPoint.
for( size_t i = 0; i < aList.size(); ++i )
{ {
PCB_SHAPE* graphic = aList[i]; if( graphic == aShape || ( graphic->GetFlags() & SKIP_STRUCT ) != 0 )
if( graphic == aShape )
continue; continue;
unsigned d;
switch( graphic->GetShape() ) switch( graphic->GetShape() )
{ {
case S_ARC: case S_ARC:
if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() ) if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
{
return graphic; return graphic;
}
d = close_ness( aPoint, graphic->GetArcStart() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
d = close_ness( aPoint, graphic->GetArcEnd() );
if( d < min_d )
{
min_d = d;
ndx_min = i;
}
break; break;
default: default:
if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() ) if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
{
return graphic; return graphic;
}
}
// Search again for anything that's close, even something already used. (The latter is
// important for error reporting.)
VECTOR2I pt( aPoint );
SEG::ecoord closest_dist_sq = SEG::Square( aLimit );
PCB_SHAPE* closest_graphic = nullptr;
SEG::ecoord d_sq;
for( PCB_SHAPE* graphic : aList )
{
if( graphic == aShape )
continue;
switch( graphic->GetShape() )
{
case S_ARC:
d_sq = ( pt - graphic->GetArcStart() ).SquaredEuclideanNorm();
if( d_sq < closest_dist_sq )
{
closest_dist_sq = d_sq;
closest_graphic = graphic;
} }
d = close_ness( aPoint, graphic->GetStart() ); d_sq = ( pt - graphic->GetArcEnd() ).SquaredEuclideanNorm();
if( d < min_d )
if( d_sq < closest_dist_sq )
{ {
min_d = d; closest_dist_sq = d_sq;
ndx_min = i; closest_graphic = graphic;
}
break;
default:
d_sq = ( pt - graphic->GetStart() ).SquaredEuclideanNorm();
if( d_sq < closest_dist_sq )
{
closest_dist_sq = d_sq;
closest_graphic = graphic;
} }
d = close_ness( aPoint, graphic->GetEnd() ); d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
if( d < min_d )
if( d_sq < closest_dist_sq )
{ {
min_d = d; closest_dist_sq = d_sq;
ndx_min = i; closest_graphic = graphic;
} }
} }
} }
if( min_d <= aLimit ) return closest_graphic; // Note: will be nullptr if nothing within aLimit
{
return aList[ndx_min];
}
return NULL;
} }
@ -439,7 +424,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Use the line segment end point furthest away from prevPt as we assume // 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. // the other end to be ON prevPt or very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
nextPt = graphic->GetEnd(); nextPt = graphic->GetEnd();
else else
nextPt = graphic->GetStart(); nextPt = graphic->GetStart();
@ -502,7 +487,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// prevPt as we assume the other end to be ON prevPt or // prevPt as we assume the other end to be ON prevPt or
// very close to it. // very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
{ {
nextPt = graphic->GetEnd(); nextPt = graphic->GetEnd();
} }
@ -697,7 +682,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// prevPt as we assume the other end to be ON prevPt or // prevPt as we assume the other end to be ON prevPt or
// very close to it. // very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
nextPt = graphic->GetEnd(); nextPt = graphic->GetEnd();
else else
nextPt = graphic->GetStart(); nextPt = graphic->GetStart();
@ -758,7 +743,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// prevPt as we assume the other end to be ON prevPt or // prevPt as we assume the other end to be ON prevPt or
// very close to it. // very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
{ {
nextPt = graphic->GetEnd(); nextPt = graphic->GetEnd();
} }