Fix and simplify SHAPE_LINE_CHAIN::Append( SHAPE_ARC ) + QA tests

This commit is contained in:
Roberto Fernandez Bautista 2024-01-07 10:47:47 +01:00 committed by Alex Shvartzkop
parent 34942290a2
commit 673e23c2e6
3 changed files with 98 additions and 14 deletions

View File

@ -477,6 +477,9 @@ const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy,
VECTOR2I c = GetCenter();
EDA_ANGLE ca = GetCentralAngle();
SEG startToEnd( GetP0(), GetP1() );
double halfAccuracy = std::max( 1.0, aAccuracy / 2 );
int n;
// To calculate the arc to segment count, use the external radius instead of the radius.
@ -484,7 +487,8 @@ const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy,
double external_radius = r+(m_width/2);
double effectiveAccuracy;
if( external_radius < aAccuracy/2 ) // Should be a very rare case
if( external_radius < halfAccuracy
|| startToEnd.Distance( GetArcMid() ) < halfAccuracy ) // Should be a very rare case
{
// In this case, the arc is approximated by one segment, with a effective error
// between -aAccuracy/2 and +aAccuracy/2, as expected.

View File

@ -1302,28 +1302,19 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc )
void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc, double aAccuracy )
{
SEG startToEnd( aArc.GetP0(), aArc.GetP1() );
SHAPE_LINE_CHAIN chain = aArc.ConvertToPolyline( aAccuracy );
if( startToEnd.Distance( aArc.GetArcMid() ) < 1 )
if( chain.PointCount() > 2 )
{
// Not really a valid arc. Add as a straight line segment instead
Append( aArc.GetP0() );
Append( aArc.GetP1() );
}
else
{
SHAPE_LINE_CHAIN chain = aArc.ConvertToPolyline( aAccuracy );
// @todo should the below 4 LOC be moved to SHAPE_ARC::ConvertToPolyline ?
chain.m_arcs.push_back( aArc );
chain.m_arcs.back().SetWidth( 0 );
for( auto& sh : chain.m_shapes )
sh.first = 0;
Append( chain );
}
Append( chain );
assert( m_shapes.size() == m_points.size() );
}

View File

@ -346,6 +346,95 @@ BOOST_AUTO_TEST_CASE( NextShape )
}
BOOST_AUTO_TEST_CASE( AppendArc )
{
BOOST_TEST_CONTEXT( "Case 1: Arc mid point nearly collinear" )
{
SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 2499 ), VECTOR2I( -100000, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( -100000, 0 ) ); //arc end
BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( -100000, 0 ) ); //arc end
}
BOOST_TEST_CONTEXT( "Case 2: Arc = Large Circle" )
{
SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 100000, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 1 );
BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
BOOST_CHECK_EQUAL( chain.GetPoint( 9 ), VECTOR2I( 100000, 0 ) ); //arc end
BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( 100000, 0 ) ); //arc end
}
BOOST_TEST_CONTEXT( "Case 3: Arc = Small Circle (approximate to point)" )
{
SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 2499, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
}
BOOST_TEST_CONTEXT( "Case 3: Small Arc (approximate to segment)" )
{
SHAPE_ARC arc( VECTOR2I( 1767, 0 ), VECTOR2I( 2499, 2499 ), VECTOR2I( 0, 1767 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 1767, 0 ) ); //arc start
BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 0, 1767 ) ); //arc end
}
BOOST_TEST_CONTEXT( "Case 4: Arc = null arc (all points coincident)" )
{
SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
}
BOOST_TEST_CONTEXT( "Case 5: Arc = infinite radius (all points very close)" )
{
SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2500, 0 ), VECTOR2I( 2501, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 2501, 0 ) ); //arc end
}
BOOST_TEST_CONTEXT( "Case 6: Arc = large radius (all points very close)" )
{
SHAPE_ARC arc( VECTOR2I( -100000, 0 ), VECTOR2I( 0, 1 ), VECTOR2I( 100000, 0 ), 0 );
SHAPE_LINE_CHAIN chain;
chain.Append( arc, 5000 );
BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( -100000, 0 ) ); //arc start
BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 100000, 0 ) ); //arc end
}
}
// Test special case where the last arc in the chain has a shared point with the first arc
BOOST_AUTO_TEST_CASE( ArcWrappingToStartSharedPoints )
{