@ -26,79 +26,110 @@
# include <vector>
# include <algorithm>
# include <limits>
# include <pcb_shape.h>
# include <geometry/circle.h>
# include <nanoflann.hpp>
struct PCB_SHAPE_ENDPOINTS_ADAPTOR
{
std : : vector < std : : pair < VECTOR2I , PCB_SHAPE * > > endpoints ;
PCB_SHAPE_ENDPOINTS_ADAPTOR ( const std : : vector < PCB_SHAPE * > & shapes )
{
endpoints . reserve ( shapes . size ( ) * 2 ) ;
for ( PCB_SHAPE * shape : shapes )
{
endpoints . emplace_back ( shape - > GetStart ( ) , shape ) ;
endpoints . emplace_back ( shape - > GetEnd ( ) , shape ) ;
}
}
// Required by nanoflann
size_t kdtree_get_point_count ( ) const { return endpoints . size ( ) ; }
// Returns the dim'th component of the idx'th point
double kdtree_get_pt ( const size_t idx , const size_t dim ) const
{
if ( dim = = 0 )
return static_cast < double > ( endpoints [ idx ] . first . x ) ;
else
return static_cast < double > ( endpoints [ idx ] . first . y ) ;
}
template < class BBOX >
bool kdtree_get_bbox ( BBOX & ) const
{
return false ;
}
} ;
using KDTree = nanoflann : : KDTreeSingleIndexAdaptor < nanoflann : : L2_Simple_Adaptor < double , PCB_SHAPE_ENDPOINTS_ADAPTOR > ,
PCB_SHAPE_ENDPOINTS_ADAPTOR ,
2 /* dim */ > ;
/**
* Searches for a PCB_SHAPE matching a given end point or start point in a list .
* @ param aShape The starting shape .
* @ param aPoint The starting or ending point to search for .
* @ param aList The list to remove from .
* @ param aLimit is the distance from \ a aPoint that still constitutes a valid find .
* @ param kdTree The KD - tree for efficient nearest neighbor search .
* @ param adaptor The adaptor containing the endpoints data .
* @ param aChainingEpsilon is the distance from \ a aPoint that still constitutes a valid find .
* @ return PCB_SHAPE * - The first PCB_SHAPE that has a start or end point matching
* aPoint , otherwise NULL if none .
*/
static PCB_SHAPE * findNext ( PCB_SHAPE * aShape , const VECTOR2I & aPoint ,
const std : : vector < PCB_SHAPE * > & aList , unsigned aLimit )
static PCB_SHAPE * findNext ( PCB_SHAPE * aShape , const VECTOR2I & aPoint , const KDTree & kdTree ,
const PCB_SHAPE_ENDPOINTS_ADAPTOR & adaptor , double aChainingEpsilon )
{
// Look for an unused, exact hit
for ( PCB_SHAPE * graphic : aList )
{
if ( graphic = = aShape | | ( graphic - > GetFlags ( ) & SKIP_STRUCT ) ! = 0 )
continue ;
const double query_pt [ 2 ] = { static_cast < double > ( aPoint . x ) , static_cast < double > ( aPoint . y ) } ;
if ( aPoint = = graphic - > GetStart ( ) | | aPoint = = graphic - > GetEnd ( ) )
return graphic ;
}
uint32_t indices [ 2 ] ;
double distances [ 2 ] ;
kdTree . knnSearch ( query_pt , 2 , indices , distances ) ;
if ( distances [ 0 ] = = std : : numeric_limits < double > : : max ( ) )
return nullptr ;
// Search again for anything that's close.
VECTOR2I pt ( aPoint ) ;
SEG : : ecoord closest_dist_sq = SEG : : Square ( aLimit ) ;
PCB_SHAPE * closest_graphic = nullptr ;
SEG : : ecoord d_sq ;
// Find the closest valid candidate
PCB_SHAPE * closest_graphic = nullptr ;
double closest_dist_sq = aChainingEpsilon * aChainingEpsilon ;
for ( PCB_SHAPE * graphic : aList )
for ( size_t i = 0 ; i < 2 ; + + i )
{
if ( graphic = = aShape | | ( graphic - > GetFlags ( ) & SKIP_STRUCT ) ! = 0 )
if ( distances [ i ] = = std : : numeric_limits < double > : : max ( ) )
continue ;
d_sq = ( pt - graphic - > GetStart ( ) ) . SquaredEuclideanNorm ( ) ;
PCB_SHAPE * candidate = adaptor . endpoints [ indices [ i ] ] . second ;
if ( d_sq < closest_dist_sq )
{
closest_dist_sq = d_sq ;
closest_graphic = graphic ;
}
if ( candidate = = aShape )
continue ;
d_sq = ( pt - graphic - > GetEnd ( ) ) . SquaredEuclideanNorm ( ) ;
if ( candidate - > GetFlags ( ) & SKIP_STRUCT )
continue ;
if ( d_sq < closest_dist_sq )
if ( distances [ i ] < closest_dist_sq )
{
closest_dist_sq = d_sq ;
closest_graphic = graphic ;
closest_dist_sq = distances [ i ] ;
closest_graphic = candidate ;
}
}
return closest_graphic ; // Note: will be nullptr if nothing within aLimit
return closest_graphic ;
}
void ConnectBoardShapes ( std : : vector < PCB_SHAPE * > & aShapeList ,
std : : vector < std : : unique_ptr < PCB_SHAPE > > & aNewShapes , int aChainingEpsilon )
void ConnectBoardShapes ( std : : vector < PCB_SHAPE * > & aShapeList , int aChainingEpsilon )
{
if ( aShapeList . size ( ) = = 0 )
return ;
( void ) aNewShapes ;
#if 0
// Not used, but not removed, just in case
auto close_enough = [ ] ( const VECTOR2I & aLeft , const VECTOR2I & aRight , unsigned aLimit ) - > bool
{
return ( aLeft - aRight ) . SquaredEuclideanNorm ( ) < = SEG : : Square ( aLimit ) ;
} ;
# endif
// Pre-build KD-tree
PCB_SHAPE_ENDPOINTS_ADAPTOR adaptor ( aShapeList ) ;
KDTree kdTree ( 2 , adaptor ) ;
auto closer_to_first = [ ] ( const VECTOR2I & aRef , const VECTOR2I & aFirst ,
const VECTOR2I & aSecond ) - > bool
@ -288,6 +319,55 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
success = true ;
}
else if ( shape0 = = SHAPE_T : : BEZIER & & shape1 = = SHAPE_T : : BEZIER )
{
PCB_SHAPE * bez0 = aPrevShape ;
PCB_SHAPE * bez1 = aShape ;
VECTOR2I pts0 [ 2 ] = { bez0 - > GetStart ( ) , bez0 - > GetEnd ( ) } ;
VECTOR2I pts1 [ 2 ] = { bez1 - > GetStart ( ) , bez1 - > GetEnd ( ) } ;
SEG : : ecoord d [ 4 ] ;
d [ 0 ] = ( pts0 [ 0 ] - pts1 [ 0 ] ) . SquaredEuclideanNorm ( ) ;
d [ 1 ] = ( pts0 [ 0 ] - pts1 [ 1 ] ) . SquaredEuclideanNorm ( ) ;
d [ 2 ] = ( pts0 [ 1 ] - pts1 [ 0 ] ) . SquaredEuclideanNorm ( ) ;
d [ 3 ] = ( pts0 [ 1 ] - pts1 [ 1 ] ) . SquaredEuclideanNorm ( ) ;
int idx = std : : min_element ( d , d + 4 ) - d ;
int i0 = idx / 2 ;
int i1 = idx % 2 ;
VECTOR2I middle = ( pts0 [ i0 ] + pts1 [ i1 ] ) / 2 ;
// Adjust first bezier curve
if ( i0 = = 0 )
{
VECTOR2I delta = middle - bez0 - > GetStart ( ) ;
bez0 - > SetStart ( middle ) ;
bez0 - > SetBezierC1 ( bez0 - > GetBezierC1 ( ) + delta ) ;
}
else
{
VECTOR2I delta = middle - bez0 - > GetEnd ( ) ;
bez0 - > SetEnd ( middle ) ;
bez0 - > SetBezierC2 ( bez0 - > GetBezierC2 ( ) + delta ) ;
}
// Adjust second bezier curve
if ( i1 = = 0 )
{
VECTOR2I delta = middle - bez1 - > GetStart ( ) ;
bez1 - > SetStart ( middle ) ;
bez1 - > SetBezierC1 ( bez1 - > GetBezierC1 ( ) + delta ) ;
}
else
{
VECTOR2I delta = middle - bez1 - > GetEnd ( ) ;
bez1 - > SetEnd ( middle ) ;
bez1 - > SetBezierC2 ( bez1 - > GetBezierC2 ( ) + delta ) ;
}
success = true ;
}
return success ;
} ;
@ -317,7 +397,7 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
{
// Get next closest segment.
PCB_SHAPE * nextGraphic =
findNext ( curr_graphic , prevPt , aShapeList , aChainingEpsilon ) ;
findNext ( curr_graphic , prevPt , kdTree , adaptor , aChainingEpsilon ) ;
if ( ! nextGraphic )
break ;
@ -336,8 +416,8 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
const VECTOR2I ptEnd = graphic - > GetEnd ( ) ;
const VECTOR2I ptStart = graphic - > GetStart ( ) ;
PCB_SHAPE * grAtEnd = findNext ( graphic , ptEnd , aShapeList , aChainingEpsilon ) ;
PCB_SHAPE * grAtStart = findNext ( graphic , ptStart , aShapeList , aChainingEpsilon ) ;
PCB_SHAPE * grAtEnd = findNext ( graphic , ptEnd , kdTree , adaptor , aChainingEpsilon ) ;
PCB_SHAPE * grAtStart = findNext ( graphic , ptStart , kdTree , adaptor , aChainingEpsilon ) ;
bool beginFromEndPt = true ;