7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-20 00:21:31 +00:00

Update SHAPE_ARC::IntersectionPoints()

Force it to handle a number of edge cases from Arc-Arc collision test.
Unify and simplify the Collide Arc-Arc.  Add additional arc collision
QA test
This commit is contained in:
Seth Hillbrand 2025-01-13 18:29:30 -08:00
parent 4d40ea2586
commit b73481dd49
3 changed files with 106 additions and 96 deletions
libs/kimath/src/geometry
qa/tests/libs/kimath/geometry

View File

@ -454,7 +454,7 @@ bool SHAPE_ARC::NearestPoints( const SHAPE_ARC& aArc, VECTOR2I& aPtA, VECTOR2I&
VECTOR2I center1 = GetCenter();
VECTOR2I center2 = aArc.GetCenter();
int64_t dist = center1.SquaredDistance( center2 );
int64_t center_dist_sq = center1.SquaredDistance( center2 );
// Start by checking endpoints
std::vector<VECTOR2I> pts1 = { m_start, m_end };
@ -471,44 +471,110 @@ bool SHAPE_ARC::NearestPoints( const SHAPE_ARC& aArc, VECTOR2I& aPtA, VECTOR2I&
aDistSq = distSq;
aPtA = pt1;
aPtB = pt2;
if( aDistSq == 0 )
return true;
}
}
}
// If the centers are the same, the end points are always the closest
// or at least equidistant
if( dist > 0 )
for( const VECTOR2I& pt : pts1 )
{
CIRCLE circle1( center1, GetRadius() );
CIRCLE circle2( center2, aArc.GetRadius() );
// First check for intersections on the circles
std::vector<VECTOR2I> pts = circle1.Intersect( circle2 );
for( const VECTOR2I& pt : pts )
if( aArc.sliceContainsPoint( pt ) )
{
if( sliceContainsPoint( pt ) && aArc.sliceContainsPoint( pt ) )
{
aPtA = pt;
aPtB = pt;
aDistSq = 0;
CIRCLE circle( center2, aArc.GetRadius() );
aPtA = circle.NearestPoint( pt );
aPtB = pt;
aDistSq = aPtA.SquaredDistance( aPtB );
if( center_dist_sq == 0 || aDistSq == 0 )
return true;
}
}
}
for( const VECTOR2I& pt : pts2 )
{
if( sliceContainsPoint( pt ) )
{
CIRCLE circle( center1, GetRadius() );
aPtA = pt;
aPtB = circle.NearestPoint( pt );
aDistSq = aPtA.SquaredDistance( aPtB );
if( center_dist_sq == 0 || aDistSq == 0 )
return true;
}
}
// The remaining checks are require the arcs to be on non-concentric circles
if( center_dist_sq == 0 )
return true;
CIRCLE circle1( center1, GetRadius() );
CIRCLE circle2( center2, aArc.GetRadius() );
// First check for intersections on the circles
std::vector<VECTOR2I> intersections = circle1.Intersect( circle2 );
for( const VECTOR2I& pt : intersections )
{
if( sliceContainsPoint( pt ) && aArc.sliceContainsPoint( pt ) )
{
aPtA = pt;
aPtB = pt;
aDistSq = 0;
return true;
}
}
// Check for the closest points on the circles
VECTOR2I pt1 = circle1.NearestPoint( center2 );
VECTOR2I pt2 = circle2.NearestPoint( center1 );
bool pt1InSlice = sliceContainsPoint( pt1 );
bool pt2InSlice = aArc.sliceContainsPoint( pt2 );
if( pt1InSlice && pt2InSlice )
{
int64_t distSq = pt1.SquaredDistance( pt2 );
if( distSq < aDistSq )
{
aDistSq = distSq;
aPtA = pt1;
aPtB = pt2;
}
// Check for the closest points on the circles
VECTOR2I pt1 = circle1.NearestPoint( center2 );
VECTOR2I pt2 = circle2.NearestPoint( center1 );
return true;
}
if( sliceContainsPoint( pt1 ) && aArc.sliceContainsPoint( pt2 ) )
// Check the endpoints of arc 1 against the nearest point on arc 2
if( pt2InSlice )
{
for( const VECTOR2I& pt : pts1 )
{
int64_t distSq = pt1.SquaredDistance( pt2 );
int64_t distSq = pt.SquaredDistance( pt2 );
if( distSq < aDistSq )
{
aDistSq = distSq;
aPtA = pt;
aPtB = pt2;
}
}
}
// Check the endpoints of arc 2 against the nearest point on arc 1
if( pt1InSlice )
{
for( const VECTOR2I& pt : pts2 )
{
int64_t distSq = pt.SquaredDistance( pt1 );
if( distSq < aDistSq )
{
aDistSq = distSq;
aPtA = pt1;
aPtB = pt2;
aPtB = pt;
}
}
}

View File

@ -738,84 +738,23 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_ARC& aB, int aClear
aA.TypeName(),
aB.TypeName() ) );
SEG mediatrix( aA.GetCenter(), aB.GetCenter() );
VECTOR2I ptA, ptB;
int64_t dist_sq = std::numeric_limits<int64_t>::max();
aA.NearestPoints( aB, ptA, ptB, dist_sq );
int dual_width = ( aA.GetWidth() + aB.GetWidth() ) / 2;
std::vector<VECTOR2I> ips;
// Basic case - arcs intersect
if( aA.Intersect( aB, &ips ) > 0 )
if( dist_sq < SEG::Square( aClearance + dual_width ) )
{
if( aActual )
*aActual = 0;
if( aLocation )
*aLocation = ips[0]; // Pick the first intersection point
*aLocation = ( ptA + ptB ) / 2;
if( aActual )
*aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) - dual_width ) );
return true;
}
// Arcs don't intersect, build a list of points to check
std::vector<VECTOR2I> ptsA;
std::vector<VECTOR2I> ptsB;
bool cocentered = ( mediatrix.A == mediatrix.B );
// 1: Interior points of both arcs, which are on the line segment between the two centres
if( !cocentered )
{
aA.IntersectLine( mediatrix, &ptsA );
aB.IntersectLine( mediatrix, &ptsB );
}
// 2: Check arc end points
ptsA.push_back( aA.GetP0() );
ptsA.push_back( aA.GetP1() );
ptsB.push_back( aB.GetP0() );
ptsB.push_back( aB.GetP1() );
// 3: Endpoint of one and "projected" point on the other, which is on the
// line segment through that endpoint and the centre of the other arc
aA.IntersectLine( SEG( aB.GetP0(), aA.GetCenter() ), &ptsA );
aA.IntersectLine( SEG( aB.GetP1(), aA.GetCenter() ), &ptsA );
aB.IntersectLine( SEG( aA.GetP0(), aB.GetCenter() ), &ptsB );
aB.IntersectLine( SEG( aA.GetP1(), aB.GetCenter() ), &ptsB );
double minDist = std::numeric_limits<double>::max();
SEG minDistSeg;
bool rv = false;
int widths = ( aA.GetWidth() / 2 ) + ( aB.GetWidth() / 2 );
// @todo performance could be improved by only checking certain points (e.g only check end
// points against other end points or their corresponding "projected" points)
for( const VECTOR2I& ptA : ptsA )
{
for( const VECTOR2I& ptB : ptsB )
{
SEG candidateMinDist( ptA, ptB );
int dist = candidateMinDist.Length() - widths;
if( dist < aClearance )
{
if( !rv || dist < minDist )
{
minDist = dist;
minDistSeg = candidateMinDist;
}
rv = true;
}
}
}
if( rv && aActual )
*aActual = std::max( 0, minDistSeg.Length() - widths );
if( rv && aLocation )
*aLocation = minDistSeg.Center();
return rv;
return false;
}

View File

@ -771,8 +771,8 @@ static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
0,
false },
{ "case 6: Coincident centers, colliding due to arc thickness",
{ 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.2 },
{ 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.2 },
{ 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
{ 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
0,
true },
{ "case 7: Single intersection",
@ -815,6 +815,11 @@ static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
{ 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
0,
true },
{ "case 15: Arcs separated by clearance",
{ 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
{ 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
0.15,
false },
};