QA: Test arc to polylines - this has a bug

Add unit test of SHAPE_ARC::ConvertToPolyline.

This function has a bug when the arc is of zero radius. This
test shows the bug, but does not fix it yet.
This commit is contained in:
John Beard 2019-04-16 19:16:57 +01:00
parent 427f0aed78
commit ce84c19a38
2 changed files with 175 additions and 7 deletions

View File

@ -31,6 +31,7 @@
#include <geometry/shape_poly_set.h>
#include <unit_test_utils/numeric.h>
#include <unit_test_utils/unit_test_utils.h>
/**
* @brief Utility functions for testing geometry functions.
@ -81,7 +82,7 @@ bool IsInQuadrant( const VECTOR2<T>& aPoint, QUADRANT aQuadrant )
/*
* @Brief Check if both ends of a segment are in Quadrant 1
*/
bool SegmentCompletelyInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
inline bool SegmentCompletelyInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
{
return IsInQuadrant( aSeg.A, aQuadrant)
&& IsInQuadrant( aSeg.B, aQuadrant );
@ -90,7 +91,7 @@ bool SegmentCompletelyInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
/*
* @brief Check if at least one end of the segment is in Quadrant 1
*/
bool SegmentEndsInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
inline bool SegmentEndsInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
{
return IsInQuadrant( aSeg.A, aQuadrant )
|| IsInQuadrant( aSeg.B, aQuadrant );
@ -99,14 +100,66 @@ bool SegmentEndsInQuadrant( const SEG& aSeg, QUADRANT aQuadrant )
/*
* @brief Check if a segment is entirely within a certain radius of a point.
*/
bool SegmentCompletelyWithinRadius( const SEG& aSeg, const VECTOR2I& aPt,
const int aRadius )
inline bool SegmentCompletelyWithinRadius( const SEG& aSeg, const VECTOR2I& aPt, const int aRadius )
{
// This is true iff both ends of the segment are within the radius
return ( ( aSeg.A - aPt ).EuclideanNorm() < aRadius )
&& ( ( aSeg.B - aPt ).EuclideanNorm() < aRadius );
}
/**
* Check that two points are the given distance apart, within the given tolerance.
*
* @tparam T the dimension type
* @param aPtA the first point
* @param aPtB the second point
* @param aExpDist the expected distance
* @param aTol the permitted tolerance
*/
template <typename T>
bool IsPointAtDistance( const VECTOR2<T>& aPtA, const VECTOR2<T>& aPtB, T aExpDist, T aTol )
{
const int dist = ( aPtB - aPtA ).EuclideanNorm();
const bool ok = KI_TEST::IsWithin( dist, aExpDist, aTol );
if( !ok )
{
BOOST_TEST_INFO( "Points not at expected distance: distance is " << dist << ", expected "
<< aExpDist );
}
return ok;
}
/**
* Predicate for checking a set of points is within a certain tolerance of
* a circle
* @param aPoints the points to check
* @param aCentre the circle centre
* @param aRad the circle radius
* @param aTolEnds the tolerance for the endpoint-centre distance
* @return true if predicate met
*/
template <typename T>
bool ArePointsNearCircle(
const std::vector<VECTOR2<T>>& aPoints, const VECTOR2<T>& aCentre, T aRad, T aTol )
{
bool ok = true;
for( unsigned i = 0; i < aPoints.size(); ++i )
{
if( !IsPointAtDistance( aPoints[i], aCentre, aRad, aTol ) )
{
BOOST_TEST_INFO( "Point " << i << " " << aPoints[i] << " is not within tolerance ("
<< aTol << ") of radius (" << aRad << ") from centre point "
<< aCentre );
ok = false;
}
}
return ok;
}
/*
* @brief Check if two vectors are perpendicular
*
@ -135,7 +188,7 @@ bool ArePerpendicular( const VECTOR2<T>& a, const VECTOR2<T>& b, double aToleran
* @param aSize: the side width (must be divisible by 2 if want to avoid rounding)
* @param aCentre: the centre of the square
*/
SHAPE_LINE_CHAIN MakeSquarePolyLine( int aSize, const VECTOR2I& aCentre )
inline SHAPE_LINE_CHAIN MakeSquarePolyLine( int aSize, const VECTOR2I& aCentre )
{
SHAPE_LINE_CHAIN polyLine;
@ -154,8 +207,7 @@ SHAPE_LINE_CHAIN MakeSquarePolyLine( int aSize, const VECTOR2I& aCentre )
/*
* @brief Fillet every polygon in a set and return a new set
*/
SHAPE_POLY_SET FilletPolySet( SHAPE_POLY_SET& aPolySet, int aRadius,
int aError )
inline SHAPE_POLY_SET FilletPolySet( SHAPE_POLY_SET& aPolySet, int aRadius, int aError )
{
SHAPE_POLY_SET filletedPolySet;
@ -169,6 +221,27 @@ SHAPE_POLY_SET FilletPolySet( SHAPE_POLY_SET& aPolySet, int aRadius,
return filletedPolySet;
}
} // namespace GEOM_TEST
BOOST_TEST_PRINT_NAMESPACE_OPEN
{
template <>
struct print_log_value<SHAPE_LINE_CHAIN>
{
inline void operator()( std::ostream& os, const SHAPE_LINE_CHAIN& c )
{
os << "SHAPE_LINE_CHAIN: " << c.PointCount() << " points: [\n";
for( int i = 0; i < c.PointCount(); ++i )
{
os << " " << i << ": " << c.CPoint( i ) << "\n";
}
os << "]";
}
};
}
BOOST_TEST_PRINT_NAMESPACE_CLOSE
#endif // GEOM_TEST_UTILS_H

View File

@ -23,10 +23,13 @@
#include <geometry/shape_arc.h>
#include <geometry/shape_line_chain.h>
#include <unit_test_utils/geometry.h>
#include <unit_test_utils/numeric.h>
#include <unit_test_utils/unit_test_utils.h>
#include "geom_test_utils.h"
BOOST_AUTO_TEST_SUITE( ShapeArc )
@ -281,4 +284,96 @@ BOOST_AUTO_TEST_CASE( BasicCPAGeom )
}
}
struct ARC_TO_POLYLINE_CASE
{
std::string m_ctx_name;
ARC_CENTRE_PT_ANGLE m_geom;
};
/**
* Predicate for checking a polyline has all the points on (near) a circle of
* given centre and radius
* @param aPolyline the polyline to check
* @param aCentre the circle centre
* @param aRad the circle radius
* @param aTolEnds the tolerance for the endpoint-centre distance
* @return true if predicate met
*/
bool ArePolylinePointsNearCircle(
const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre, int aRad, int aTolEnds )
{
std::vector<VECTOR2I> points;
for( int i = 0; i < aPolyline.PointCount(); ++i )
{
points.push_back( aPolyline.CPoint( i ) );
}
return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolEnds );
}
#ifdef HAVE_EXPECTED_FAILURES
// Failure in zero-radius case
BOOST_AUTO_TEST_CASE( ArcToPolyline, *boost::unit_test::expected_failures( 1 ) )
{
const std::vector<ARC_TO_POLYLINE_CASE> cases = {
{
"Zero rad",
{
{ 0, 0 },
{ 0, 0 },
180,
},
},
{
"Semicircle",
{
{ 0, 0 },
{ -10, 0 },
180,
},
},
{
"Larger semicircle",
{
{ 0, 0 },
{ -1000, 0 },
180,
},
},
};
const int width = 0;
const double accuracy = 1.0;
for( const auto& c : cases )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
c.m_geom.m_center_angle, width };
const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
const int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
const int tol = 2;
BOOST_CHECK_PREDICATE( ArePolylinePointsNearCircle,
( chain )( c.m_geom.m_center_point )( radius )( tol ) );
// TODO: check midpoints are near circle too
}
}
}
#endif // HAVE_EXPECTED_FAILURES
BOOST_AUTO_TEST_SUITE_END()