Gracefully handle dots in the outline path.
Fixes https://gitlab.com/kicad/code/kicad/issues/6854
This commit is contained in:
parent
3aca0e1853
commit
e1f76ea2ab
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue