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:
parent
4d40ea2586
commit
b73481dd49
libs/kimath/src/geometry
qa/tests/libs/kimath/geometry
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 },
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user