QA: Add unit test of SHAPE_ARC
Test a few "centre-point-angle" cases and add some generic geom code for testing vectors and boxes are within tolerance (since rounding often creeps in). Much of the arc-checking code will be useful to other construction methods (e.g. point-point-point). There are expected failures for the bbox code when the arc passes though, but does not end on, a quadrant point (0, 90, 180, 270). This is due to a defective implementation of SHAPE_ARC::BBox() that does not take into account the quadrant points. This will be fixed as a follow-up.
This commit is contained in:
parent
170ff66cbb
commit
dc71a73178
|
@ -46,6 +46,7 @@ set( common_srcs
|
|||
libeval/test_numeric_evaluator.cpp
|
||||
|
||||
geometry/test_fillet.cpp
|
||||
geometry/test_shape_arc.cpp
|
||||
|
||||
view/test_zoom_controller.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <geometry/shape_arc.h>
|
||||
|
||||
#include <unit_test_utils/geometry.h>
|
||||
#include <unit_test_utils/numeric.h>
|
||||
#include <unit_test_utils/unit_test_utils.h>
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( ShapeArc )
|
||||
|
||||
/**
|
||||
* All properties of an arc (depending on how it's constructed, some of these
|
||||
* might be the same as the constructor params)
|
||||
*/
|
||||
struct ARC_PROPERTIES
|
||||
{
|
||||
VECTOR2I m_center_point;
|
||||
VECTOR2I m_start_point;
|
||||
VECTOR2I m_end_point;
|
||||
double m_center_angle;
|
||||
double m_start_angle;
|
||||
double m_end_angle;
|
||||
int m_radius;
|
||||
BOX2I m_bbox;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check a #SHAPE_ARC against a given set of geometric properties
|
||||
*/
|
||||
static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps )
|
||||
{
|
||||
// Angular error - not this can get quite large for very small arcs,
|
||||
// as the integral position rounding has a relatively greater effect
|
||||
const double angle_tol_deg = 0.01;
|
||||
|
||||
// Position error - rounding to nearest integer
|
||||
const int pos_tol = 1;
|
||||
|
||||
BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
|
||||
( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
|
||||
BOOST_CHECK_PREDICATE(
|
||||
KI_TEST::IsVecWithinTol<VECTOR2I>, ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
|
||||
BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
|
||||
( aArc.GetCenter() )( aProps.m_center_point )( pos_tol ) );
|
||||
BOOST_CHECK_PREDICATE( KI_TEST::IsWithin<double>,
|
||||
( aArc.GetCentralAngle() )( aProps.m_center_angle )( angle_tol_deg ) );
|
||||
BOOST_CHECK_PREDICATE( KI_TEST::IsWithin<double>,
|
||||
( aArc.GetStartAngle() )( aProps.m_start_angle )( angle_tol_deg ) );
|
||||
BOOST_CHECK_PREDICATE( KI_TEST::IsWithin<double>,
|
||||
( aArc.GetEndAngle() )( aProps.m_end_angle )( angle_tol_deg ) );
|
||||
BOOST_CHECK_PREDICATE(
|
||||
KI_TEST::IsWithin<double>, ( aArc.GetRadius() )( aProps.m_radius )( pos_tol ) );
|
||||
|
||||
/// Check the chord agrees
|
||||
const auto chord = aArc.GetChord();
|
||||
|
||||
BOOST_CHECK_PREDICATE(
|
||||
KI_TEST::IsVecWithinTol<VECTOR2I>, ( chord.A )( aProps.m_start_point )( pos_tol ) );
|
||||
BOOST_CHECK_PREDICATE(
|
||||
KI_TEST::IsVecWithinTol<VECTOR2I>, ( chord.B )( aProps.m_end_point )( pos_tol ) );
|
||||
|
||||
/// All arcs are solid
|
||||
BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
|
||||
|
||||
BOOST_CHECK_PREDICATE(
|
||||
KI_TEST::IsBoxWithinTol<BOX2I>, ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
|
||||
|
||||
/// Collisions will be checked elsewhere.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an arcs geometry and other class functions
|
||||
*/
|
||||
static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps )
|
||||
{
|
||||
// Check the original arc
|
||||
CheckArcGeom( aArc, aProps );
|
||||
|
||||
// Test the Clone function (also tests copy-ctor)
|
||||
std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
|
||||
|
||||
BOOST_CHECK_EQUAL( new_shape->Type(), SH_ARC );
|
||||
|
||||
SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
|
||||
|
||||
BOOST_REQUIRE( new_arc != nullptr );
|
||||
|
||||
/// Should have identical geom props
|
||||
CheckArcGeom( *new_arc, aProps );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check correct handling of filter strings (as used by WX)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( NullCtor )
|
||||
{
|
||||
auto arc = SHAPE_ARC();
|
||||
|
||||
BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
|
||||
|
||||
static ARC_PROPERTIES null_props{
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
CheckArc( arc, null_props );
|
||||
}
|
||||
|
||||
/**
|
||||
* Info to set up an arc by centre, start point and angle
|
||||
*
|
||||
* In future there may be more ways to set this up, so keep it separate
|
||||
*/
|
||||
struct ARC_CENTRE_PT_ANGLE
|
||||
{
|
||||
VECTOR2I m_center_point;
|
||||
VECTOR2I m_start_point;
|
||||
double m_center_angle;
|
||||
};
|
||||
|
||||
struct ARC_CPA_CASE
|
||||
{
|
||||
/// The text context name
|
||||
std::string m_ctx_name;
|
||||
|
||||
/// Geom of the arc
|
||||
ARC_CENTRE_PT_ANGLE m_geom;
|
||||
|
||||
/// Arc line width
|
||||
int m_width;
|
||||
|
||||
/// Expected properties
|
||||
ARC_PROPERTIES m_properties;
|
||||
};
|
||||
|
||||
static const std::vector<ARC_CPA_CASE> arc_cases = {
|
||||
{
|
||||
"C(0,0) 180 + 90 degree",
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ -100, 0 },
|
||||
90,
|
||||
},
|
||||
0,
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ -100, 0 },
|
||||
{ 0, -100 },
|
||||
90,
|
||||
180,
|
||||
270,
|
||||
100,
|
||||
{ { -100, -100 }, { 100, 100 } },
|
||||
},
|
||||
},
|
||||
{
|
||||
"C(100,200) 0 - 30 degree",
|
||||
{
|
||||
{ 100, 200 },
|
||||
{ 300, 200 },
|
||||
-30,
|
||||
},
|
||||
0,
|
||||
{
|
||||
{ 100, 200 },
|
||||
{ 300, 200 },
|
||||
{ 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
|
||||
-30,
|
||||
0,
|
||||
330,
|
||||
200,
|
||||
{ { 100, 100 }, { 200, 100 } },
|
||||
},
|
||||
},
|
||||
{
|
||||
// This is a "fan shape" which includes the top quadrant point,
|
||||
// so it exercises the bounding box code (centre and end points
|
||||
// do not contain the top quadrant)
|
||||
"C(0,0) 30 + 120 degree",
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ 17320, 10000 },
|
||||
120,
|
||||
},
|
||||
0,
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ 17320, 10000 },
|
||||
{ -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
|
||||
120,
|
||||
30,
|
||||
150,
|
||||
20000,
|
||||
// bbox defined by: centre, top quadrant point, two endpoints
|
||||
{ { -17320, 0 }, { 17320 * 2, 20000 } },
|
||||
},
|
||||
},
|
||||
{
|
||||
// An arc that covers three quadrant points (L/R, bottom)
|
||||
"C(0,0) 150 + 240 degree",
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ -17320, 10000 },
|
||||
240,
|
||||
},
|
||||
0,
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ -17320, 10000 },
|
||||
{ 17320, 10000 },
|
||||
240,
|
||||
150,
|
||||
30,
|
||||
20000,
|
||||
// bbox defined by: L/R quads, bottom quad and start/end
|
||||
{ { -20000, -20000 }, { 40000, 30000 } },
|
||||
},
|
||||
},
|
||||
{
|
||||
// Same as above but reverse direction
|
||||
"C(0,0) 30 - 300 degree",
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ 17320, 10000 },
|
||||
-240,
|
||||
},
|
||||
0,
|
||||
{
|
||||
{ 0, 0 },
|
||||
{ 17320, 10000 },
|
||||
{ -17320, 10000 },
|
||||
-240,
|
||||
30,
|
||||
150,
|
||||
20000,
|
||||
// bbox defined by: L/R quads, bottom quad and start/end
|
||||
{ { -20000, -20000 }, { 40000, 30000 } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef HAVE_EXPECTED_FAILURES
|
||||
// One of the bbox tests fails
|
||||
|
||||
BOOST_AUTO_TEST_CASE( BasicCPAGeom, *boost::unit_test::expected_failures( 6 ) )
|
||||
{
|
||||
for( const auto& c : arc_cases )
|
||||
{
|
||||
BOOST_TEST_CONTEXT( c.m_ctx_name )
|
||||
{
|
||||
|
||||
const auto this_arc = SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
|
||||
c.m_geom.m_center_angle, c.m_width };
|
||||
|
||||
CheckArc( this_arc, c.m_properties );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_EXPECTED_FAILURES
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
#ifndef QA_UNIT_TEST_UTILS_GEOM__H
|
||||
#define QA_UNIT_TEST_UTILS_GEOM__H
|
||||
|
||||
#include <unit_test_utils/numeric.h>
|
||||
#include <unit_test_utils/unit_test_utils.h>
|
||||
|
||||
#include <math/box2.h>
|
||||
#include <math/vector2d.h>
|
||||
|
||||
/**
|
||||
* Printer for BOX2I type
|
||||
*/
|
||||
template <> struct BOOST_PRINT::print_log_value<BOX2I>
|
||||
{
|
||||
void operator()( std::ostream& os, const BOX2I& aBox )
|
||||
{
|
||||
os << "BOX[ " << aBox.GetOrigin() << " + " << aBox.GetSize() << " ]";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace KI_TEST
|
||||
{
|
||||
|
||||
/**
|
||||
* Check that both x and y of a vector are within expected error
|
||||
*/
|
||||
template <typename VEC>
|
||||
bool IsVecWithinTol( const VEC& aVec, const VEC& aExp, typename VEC::coord_type aTol )
|
||||
{
|
||||
return IsWithin<typename VEC::coord_type>( aVec.x, aExp.x, aTol )
|
||||
&& IsWithin<typename VEC::coord_type>( aVec.y, aExp.y, aTol );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a box is close enough to another box
|
||||
*/
|
||||
template <typename BOX>
|
||||
bool IsBoxWithinTol( const BOX& aBox, const BOX& aExp, typename BOX::coord_type aTol )
|
||||
{
|
||||
using VEC = VECTOR2<typename BOX::coord_type>;
|
||||
return IsVecWithinTol<VEC>( aBox.GetPosition(), aExp.GetPosition(), aTol )
|
||||
&& IsVecWithinTol<VEC>( aBox.GetSize(), aExp.GetSize(), aTol * 2 );
|
||||
}
|
||||
|
||||
} // namespace KI_TEST
|
||||
|
||||
#endif // QA_UNIT_TEST_UTILS_GEOM__H
|
Loading…
Reference in New Issue