Rewrite broken collision routine SHAPE_ARC::Collide( SEG& aSeg )

This commit is contained in:
Roberto Fernandez Bautista 2021-11-12 15:40:22 +00:00
parent 9b43689a76
commit d47bd3a04d
3 changed files with 100 additions and 56 deletions

View File

@ -231,67 +231,24 @@ bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I
if( aSeg.A == aSeg.B )
return Collide( aSeg.A, aClearance, aActual, aLocation );
int minDist = aClearance + m_width / 2;
VECTOR2I center = GetCenter();
ecoord dist_sq;
ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
VECTOR2I nearest;
CIRCLE circle( center, GetRadius() );
VECTOR2I ab = ( aSeg.B - aSeg.A );
VECTOR2I ac = ( center - aSeg.A );
// Possible points of the collision are:
// 1. Intersetion of the segment with the full circle
// 2. Closest point on the segment to the center of the circle
// 3. End points of the segment
ecoord lenAbSq = ab.SquaredEuclideanNorm();
double lambda = (double) ac.Dot( ab ) / (double) lenAbSq;
std::vector<VECTOR2I> candidatePts = circle.Intersect( aSeg );
if( lambda >= 0.0 && lambda <= 1.0 )
candidatePts.push_back( aSeg.NearestPoint( center ) );
candidatePts.push_back( aSeg.A );
candidatePts.push_back( aSeg.B );
for( const VECTOR2I& candidate : candidatePts )
{
VECTOR2I p;
p.x = (double) aSeg.A.x * lambda + (double) aSeg.B.x * (1.0 - lambda);
p.y = (double) aSeg.A.y * lambda + (double) aSeg.B.y * (1.0 - lambda);
dist_sq = ( m_start - p ).SquaredEuclideanNorm();
if( dist_sq < closest_dist_sq )
{
closest_dist_sq = dist_sq;
nearest = p;
}
dist_sq = ( m_end - p ).SquaredEuclideanNorm();
if( dist_sq < closest_dist_sq )
{
closest_dist_sq = dist_sq;
nearest = p;
}
}
dist_sq = aSeg.SquaredDistance( m_start );
if( dist_sq < closest_dist_sq )
{
closest_dist_sq = dist_sq;
nearest = m_start;
}
dist_sq = aSeg.SquaredDistance( m_end );
if( dist_sq < closest_dist_sq )
{
closest_dist_sq = dist_sq;
nearest = m_end;
}
if( closest_dist_sq == 0 || closest_dist_sq < SEG::Square( minDist ) )
{
if( aLocation )
*aLocation = nearest;
if( aActual )
*aActual = std::max( 0, (int) sqrt( closest_dist_sq ) - m_width / 2 );
return true;
if( Collide( candidate, aClearance, aActual, aLocation ) )
return true;
}
return false;

View File

@ -844,6 +844,31 @@ BOOST_AUTO_TEST_CASE( CollideArc )
}
BOOST_AUTO_TEST_CASE( CollideArcToShapeLineChain )
{
SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
VECTOR2I( 197822958, 136722959 ), 250000 );
SHAPE_LINE_CHAIN lc( { VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
VECTOR2I( 192255720, 113265480 ), VECTOR2I( 203682188, 124691948 ),
VECTOR2I( 203682188, 140332188 ), VECTOR2I( 206000000, 142650000 ) },
false );
SHAPE* arc_sh = &arc;
SHAPE* lc_sh = &lc;
BOOST_CHECK_EQUAL( arc_sh->Collide( &lc, 100000 ), true );
BOOST_CHECK_EQUAL( lc_sh->Collide( &arc, 100000 ), true );
SEG seg( VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ) );
BOOST_CHECK_EQUAL( arc.Collide( seg, 0 ), true );
}
BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
{
SHAPE_ARC arc( VECTOR2I( 73843527, 74355869 ), VECTOR2I( 71713528, 72965869 ), -76.36664803,

View File

@ -225,8 +225,70 @@ int playground_main_func( int argc, char* argv[] )
return 0;
}
int drawShapes( int argc, char* argv[] )
{
SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
VECTOR2I( 197822958, 136722959 ), 250000 );
SHAPE_LINE_CHAIN lc( { /* VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
VECTOR2I( 192255720, 113265480 ),*/
VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ),
/* VECTOR2I( 206000000, 142650000 ) */ },
false );
auto frame = new PNS_LOG_VIEWER_FRAME( nullptr );
Pgm().App().SetTopWindow( frame ); // wxApp gets a face.
frame->Show();
overlay = frame->GetOverlay();
overlay->SetIsFill( false );
overlay->SetLineWidth( arc.GetWidth() );
overlay->SetStrokeColor( RED );
overlay->Arc( arc );
overlay->SetLineWidth( arc.GetWidth() / 20 );
overlay->SetStrokeColor( GREEN );
overlay->Polyline( lc );
overlay->SetLineWidth( 80000.0 );
overlay->SetStrokeColor( CYAN );
for( int i = 0; i < lc.PointCount(); ++i )
{
int mult = ( i % 2 ) ? 1 : -1;
overlay->AnnotatedPoint( lc.GetPoint( i ), arc.GetWidth() * 2 );
overlay->SetGlyphSize( { 800000.0, 800000.0 } );
overlay->BitmapText( wxString::Format( "x=%d, y=%d", lc.GetPoint( i ).x, lc.GetPoint( i ).y ),
lc.GetPoint( i ) + VECTOR2I( 0, mult*arc.GetWidth() * 4 ), 0 );
}
arc.Collide( &lc, 100000 );
BOX2I vp = arc.BBox();
vp.Merge( lc.BBox() );
vp.Inflate( (800000 + arc.GetWidth() * 4 )*2);
frame->GetPanel()->GetView()->SetViewport( BOX2D( vp.GetOrigin(), vp.GetSize() ) );
overlay = nullptr;
return 0;
}
static bool registered = UTILITY_REGISTRY::Register( {
"playground",
"Geometry/drawing playground",
playground_main_func,
} );
static bool registered1 = UTILITY_REGISTRY::Register( {
"drawShapes",
"drawing shapes",
drawShapes,
} );