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" );
/**
* 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
* 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.
* @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
// approximating it, since aLimit is non-exact anyway except when zero.
return close_ness( aLeft, aRight ) <= aLimit;
return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( 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.
*
* @param aReference is the reference point
* @param aRef is the reference point
* @param aFirst is the first 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
// approximating to find the closest to the reference.
return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
return ( aRef - aFirst ).SquaredEuclideanNorm() < ( aRef - aSecond ).SquaredEuclideanNorm();
}
@ -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,
const std::vector<PCB_SHAPE*>& aList, unsigned aLimit )
{
unsigned min_d = INT_MAX;
int ndx_min = 0;
// find the point closest to aPoint and perhaps exactly matching aPoint.
for( size_t i = 0; i < aList.size(); ++i )
// Look for an unused, exact hit
for( PCB_SHAPE* graphic : aList )
{
PCB_SHAPE* graphic = aList[i];
if( graphic == aShape )
if( graphic == aShape || ( graphic->GetFlags() & SKIP_STRUCT ) != 0 )
continue;
unsigned d;
switch( graphic->GetShape() )
{
case S_ARC:
if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
{
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;
default:
if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
{
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() );
if( d < min_d )
d_sq = ( pt - graphic->GetArcEnd() ).SquaredEuclideanNorm();
if( d_sq < closest_dist_sq )
{
min_d = d;
ndx_min = i;
closest_dist_sq = d_sq;
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() );
if( d < min_d )
d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
if( d_sq < closest_dist_sq )
{
min_d = d;
ndx_min = i;
closest_dist_sq = d_sq;
closest_graphic = graphic;
}
}
}
if( min_d <= aLimit )
{
return aList[ndx_min];
}
return NULL;
return closest_graphic; // Note: will be nullptr if nothing within aLimit
}
@ -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
// 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();
else
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
// very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
if( closer_to_first( prevPt, graphic->GetStart(), 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
// very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
nextPt = graphic->GetEnd();
else
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
// very close to it.
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
{
nextPt = graphic->GetEnd();
}