qa tests for CIRCLE geometry library

This commit is contained in:
Roberto Fernandez Bautista 2021-01-06 01:35:52 +00:00 committed by Jon Evans
parent b6a50d2f74
commit 6ed5ed52b8
5 changed files with 362 additions and 8 deletions

View File

@ -123,6 +123,11 @@ protected:
class SHAPE : public SHAPE_BASE
{
public:
/**
* @brief This is the minimum precision for all the points in a shape
*/
static const int MIN_PRECISION_IU = 4;
/**
* Constructor
*

View File

@ -35,10 +35,6 @@ class SHAPE_LINE_CHAIN;
class SHAPE_ARC : public SHAPE
{
public:
/**
* @brief This is the minimum precision for all the points in the arc shape.
*/
static const int MIN_PRECISION_IU = 4;
SHAPE_ARC() :
SHAPE( SH_ARC ),

View File

@ -20,7 +20,7 @@
#include <geometry/circle.h>
#include <geometry/seg.h>
#include <geometry/shape_arc.h> // for MIN_PRECISION_IU
#include <geometry/shape.h> // for MIN_PRECISION_IU
#include <math/util.h> // for KiROUND
#include <math/vector2d.h> // for VECTOR2I
#include <math.h> // for sqrt
@ -293,12 +293,12 @@ std::vector<VECTOR2I> CIRCLE::Intersect( const SEG& aLine ) const
VECTOR2I m = aLine.LineProject( Center );
int64_t om = ( m - Center ).EuclideanNorm();
if( om > ( (int64_t) Radius + SHAPE_ARC::MIN_PRECISION_IU ) )
if( om > ( (int64_t) Radius + SHAPE::MIN_PRECISION_IU ) )
{
return retval; // does not intersect
}
else if( om <= ( (int64_t) Radius + SHAPE_ARC::MIN_PRECISION_IU )
&& om >= ( (int64_t) Radius - SHAPE_ARC::MIN_PRECISION_IU ) )
else if( om <= ( (int64_t) Radius + SHAPE::MIN_PRECISION_IU )
&& om >= ( (int64_t) Radius - SHAPE::MIN_PRECISION_IU ) )
{
retval.push_back( m );
return retval; //tangent

View File

@ -28,6 +28,7 @@ set( KIMATH_SRCS
test_kimath.cpp
geometry/test_fillet.cpp
geometry/test_circle.cpp
geometry/test_segment.cpp
geometry/test_shape_compound_collision.cpp
geometry/test_shape_arc.cpp

View File

@ -0,0 +1,352 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
* Copyright (C) 2021 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <unit_test_utils/unit_test_utils.h>
#include <geometry/circle.h>
#include <geometry/seg.h> // for SEG
#include <geometry/shape.h> // for MIN_PRECISION_IU
bool CompareVector2I( const VECTOR2I& aVecA, const VECTOR2I& aVecB )
{
if( aVecA.x > ( aVecB.x + SHAPE::MIN_PRECISION_IU ) )
return false;
else if( aVecA.x < ( aVecB.x - SHAPE::MIN_PRECISION_IU ) )
return false;
else if( aVecA.y > ( aVecB.y + SHAPE::MIN_PRECISION_IU ) )
return false;
else if( aVecA.y < ( aVecB.y - SHAPE::MIN_PRECISION_IU ) )
return false;
else
return true;
}
BOOST_AUTO_TEST_SUITE( Circle )
/**
* Checks whether the construction of a circle referencing external parameters works
* and that the parameters can be modified directly.
*/
BOOST_AUTO_TEST_CASE( ParameterCtorMod )
{
const VECTOR2I center( 10, 20 );
const int radius = 10;
// Build a circle referencing the previous values
CIRCLE circle( center, radius );
BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 10, 20 ) );
BOOST_CHECK_EQUAL( circle.Radius, 10 );
// Modify the parameters
circle.Center += VECTOR2I( 10, 10 );
circle.Radius += 20;
// Check the parameters were modified
BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 20, 30 ) );
BOOST_CHECK_EQUAL( circle.Radius, 30 );
}
/**
* Struct to hold test cases for a given circle, a point and an expected return point
*/
struct CIR_PT_PT_CASE
{
std::string m_case_name;
CIRCLE m_circle;
VECTOR2I m_point;
VECTOR2I m_exp_result;
};
// clang-format off
/**
* Test cases for #CIRCLE::NearestPoint
*/
static const std::vector<CIR_PT_PT_CASE> nearest_point_cases = {
{
"on center",
{ { 10, 10 }, 20 },
{ 10, 10 },
{ 30, 10 }, // special case: when at the circle return a point on the x axis
},
{
"inside",
{ { 10, 10 }, 20 },
{ 10, 20 },
{ 10, 30 },
},
{
"outside",
{ { 10, 10 }, 20 },
{ 10, 50 },
{ 10, 30 },
},
{
"angled",
{ { 10, 10 }, 20 },
{ 50, 50 },
{ 24, 24 },
},
};
// clang-format on
BOOST_AUTO_TEST_CASE( NearestPoint )
{
for( const auto& c : nearest_point_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
VECTOR2I ret = c.m_circle.NearestPoint( c.m_point );
BOOST_CHECK_EQUAL( ret, c.m_exp_result );
}
}
}
/**
* Struct to hold test cases for two circles, and an vector of points
*/
struct CIR_CIR_VECPT_CASE
{
std::string m_case_name;
CIRCLE m_circle1;
CIRCLE m_circle2;
std::vector<VECTOR2I> m_exp_result;
};
// clang-format off
/**
* Test cases for #CIRCLE::Intersect( const CIRCLE& aCircle )
*/
static const std::vector<CIR_CIR_VECPT_CASE> intersect_circle_cases = {
{
"two point aligned",
{ { 10, 10 }, 20 },
{ { 10, 45 }, 20 },
{
{ 0, 27 },
{ 21, 27 },
},
},
{
"two point angled",
{ { 10, 10 }, 20 },
{ { 20, 20 }, 20 },
{
{ 2, 28 },
{ 28, 2 },
},
},
{
"tangent aligned",
{ { 10, 10 }, 20 },
{ { 10, 50 }, 20 },
{
{ 10, 30 },
},
},
{
"no intersection",
{ { 10, 10 }, 20 },
{ { 10, 51 }, 20 },
{
//no points
},
},
};
// clang-format on
BOOST_AUTO_TEST_CASE( IntersectCircle )
{
for( const auto& c : intersect_circle_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name + " Case 1" )
{
std::vector<VECTOR2I> ret1 = c.m_circle1.Intersect( c.m_circle2 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret1.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret1, CompareVector2I );
}
BOOST_TEST_CONTEXT( c.m_case_name + " Case 2" )
{
// Test the other direction
std::vector<VECTOR2I> ret2 = c.m_circle2.Intersect( c.m_circle1 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret2.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret2, CompareVector2I );
}
}
}
/**
* Struct to hold test cases for a circle, a line and an expected vector of points
*/
struct SEG_SEG_VECPT_CASE
{
std::string m_case_name;
CIRCLE m_circle;
SEG m_seg;
std::vector<VECTOR2I> m_exp_result;
};
// clang-format off
/**
* Test cases for #CIRCLE::Intersect( const SEG& aSeg )
*/
static const std::vector<SEG_SEG_VECPT_CASE> intersect_line_cases = {
{
"two point aligned",
{ { 0, 0 }, 20 },
{ { 10, 45 }, {10, 40} },
{
{ 10, -17 },
{ 10, 17 },
},
},
{
"two point angled",
{ { 0, 0 }, 20 },
{ { -20, -40 }, {20, 40} },
{
{ 8, 17 },
{ -8, -17 },
},
},
{
"tangent",
{ { 0, 0 }, 20 },
{ { 20, 0 }, {20, 40} },
{
{ 20, 0 }
},
},
{
"no intersection",
{ { 0, 0 }, 20 },
{ { 25, 0 }, {25, 40} },
{
//no points
},
},
};
// clang-format on
BOOST_AUTO_TEST_CASE( IntersectLine )
{
for( const auto& c : intersect_line_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
std::vector<VECTOR2I> ret = c.m_circle.Intersect( c.m_seg );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
}
}
}
/**
* Struct to hold test cases for two lines, a point and an expected returned circle
*/
struct CIR_SEG_VECPT_CASE
{
std::string m_case_name;
SEG m_segA;
SEG m_segB;
VECTOR2I m_pt;
CIRCLE m_exp_result;
};
// clang-format off
/**
* Test cases for #CIRCLE::Intersect( const SEG& aSeg )
*/
static const std::vector<CIR_SEG_VECPT_CASE> construct_tan_tan_pt_cases = {
{
"90 degree segs, point on seg",
{ { 0, 0 }, { 0, 1000 } },
{ { 0, 0 }, { 1000, 0 } },
{ 0, 400 },
{ { 400, 400} , 400 },
},
{
"90 degree segs, point floating",
{ { 0, 0 }, { 0, 1000 } },
{ { 0, 0 }, { 1000, 0 } },
{ 400, 400 },
{ { 1356, 1356} , 1356 },
},
{
"45 degree segs, point on seg",
{ { 0, 0 }, { 1000, 0 } },
{ { 0, 0 }, { 1000, 1000 } },
{ 400, 0 },
{ { 400, 166} , 165 },
},
{
"45 degree segs, point floating",
{ { 0, 0 }, { 1000, 0 } },
{ { 0, 0 }, { 1000, 1000 } },
{ 200, 100 },
{ { 331, 137} , 137 },
},
{
"135 degree segs, point on seg",
{ { 0, 0 }, { 1000, 0 } },
{ { 0, 0 }, { -1000, 1000 } },
{ 400, 0 },
{ { 394, 950} , 950 },
},
{
"135 degree segs, point floating",
{ { 0, 0 }, { 1000, 0 } },
{ { 0, 0 }, { -1000, 1000 } },
{ 200, 100 },
{ { 814, 1964} , 1964 },
},
{
"point on intersection",
{ { 10, 0 }, { 1000, 0 } },
{ { 10, 0 }, { -1000, 1000 } },
{ 10, 0 },
{ { 10, 0} , 0 }, // special case: radius=0
},
};
// clang-format on
BOOST_AUTO_TEST_CASE( ConstructFromTanTanPt )
{
for( const auto& c : construct_tan_tan_pt_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
CIRCLE circle;
circle.ConstructFromTanTanPt( c.m_segA, c.m_segB, c.m_pt );
BOOST_CHECK_EQUAL( c.m_exp_result.Center, circle.Center );
BOOST_CHECK_EQUAL( c.m_exp_result.Radius, circle.Radius );
}
}
}
BOOST_AUTO_TEST_SUITE_END()