KiCad PCB EDA Suite
test_circle.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
22 #include <geometry/circle.h>
23 #include <geometry/seg.h> // for SEG
24 #include <geometry/shape.h> // for MIN_PRECISION_IU
25 
26 
27 bool CompareLength( int aLengthA, int aLengthB )
28 {
29  if( aLengthA > ( aLengthB + SHAPE::MIN_PRECISION_IU ) )
30  return false;
31  else if( aLengthA < ( aLengthB - SHAPE::MIN_PRECISION_IU ) )
32  return false;
33  else
34  return true;
35 }
36 
37 bool CompareVector2I( const VECTOR2I& aVecA, const VECTOR2I& aVecB )
38 {
39  if( !CompareLength(aVecA.x, aVecB.x) )
40  return false;
41  else if( !CompareLength( aVecA.y, aVecB.y ) )
42  return false;
43  else
44  return true;
45 }
46 
47 
48 BOOST_AUTO_TEST_SUITE( Circle )
49 
50 
54 BOOST_AUTO_TEST_CASE( ParameterCtorMod )
55 {
56  const VECTOR2I center( 10, 20 );
57  const int radius = 10;
58 
59  // Build a circle referencing the previous values
60  CIRCLE circle( center, radius );
61 
62  BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 10, 20 ) );
63  BOOST_CHECK_EQUAL( circle.Radius, 10 );
64 
65  // Modify the parameters
66  circle.Center += VECTOR2I( 10, 10 );
67  circle.Radius += 20;
68 
69  // Check the parameters were modified
70  BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 20, 30 ) );
71  BOOST_CHECK_EQUAL( circle.Radius, 30 );
72 }
73 
74 
79 {
80  std::string m_case_name;
84 };
85 
86 // clang-format off
90 static const std::vector<CIR_PT_PT_CASE> nearest_point_cases = {
91  {
92  "on center",
93  { { 10, 10 }, 20 },
94  { 10, 10 },
95  { 30, 10 }, // special case: when at the circle return a point on the x axis
96  },
97  {
98  "inside",
99  { { 10, 10 }, 20 },
100  { 10, 20 },
101  { 10, 30 },
102  },
103  {
104  "outside",
105  { { 10, 10 }, 20 },
106  { 10, 50 },
107  { 10, 30 },
108  },
109  {
110  "angled",
111  { { 10, 10 }, 20 },
112  { 50, 50 },
113  { 24, 24 },
114  },
115 };
116 // clang-format on
117 
118 
119 BOOST_AUTO_TEST_CASE( NearestPoint )
120 {
121  for( const auto& c : nearest_point_cases )
122  {
123  BOOST_TEST_CONTEXT( c.m_case_name )
124  {
125  VECTOR2I ret = c.m_circle.NearestPoint( c.m_point );
126  BOOST_CHECK_EQUAL( ret, c.m_exp_result );
127  }
128  }
129 }
130 
131 
136 {
137  std::string m_case_name;
140  std::vector<VECTOR2I> m_exp_result;
141 };
142 
143 // clang-format off
147 static const std::vector<CIR_CIR_VECPT_CASE> intersect_circle_cases = {
148  {
149  "two point aligned",
150  { { 10, 10 }, 20 },
151  { { 10, 45 }, 20 },
152  {
153  { 0, 27 },
154  { 21, 27 },
155  },
156  },
157  {
158  "two point angled",
159  { { 10, 10 }, 20 },
160  { { 20, 20 }, 20 },
161  {
162  { 2, 28 },
163  { 28, 2 },
164  },
165  },
166  {
167  "tangent aligned",
168  { { 10, 10 }, 20 },
169  { { 10, 50 }, 20 },
170  {
171  { 10, 30 },
172  },
173  },
174  {
175  "no intersection",
176  { { 10, 10 }, 20 },
177  { { 10, 51 }, 20 },
178  {
179  //no points
180  },
181  },
182 };
183 // clang-format on
184 
185 
186 BOOST_AUTO_TEST_CASE( IntersectCircle )
187 {
188  for( const auto& c : intersect_circle_cases )
189  {
190  BOOST_TEST_CONTEXT( c.m_case_name + " Case 1" )
191  {
192  std::vector<VECTOR2I> ret1 = c.m_circle1.Intersect( c.m_circle2 );
193  BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret1.size() );
194  KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret1, CompareVector2I );
195  }
196 
197  BOOST_TEST_CONTEXT( c.m_case_name + " Case 2" )
198  {
199  // Test the other direction
200  std::vector<VECTOR2I> ret2 = c.m_circle2.Intersect( c.m_circle1 );
201  BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret2.size() );
202  KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret2, CompareVector2I );
203  }
204  }
205 }
206 
207 
212 {
213  std::string m_case_name;
216  std::vector<VECTOR2I> m_exp_result;
217 };
218 
219 // clang-format off
223 static const std::vector<SEG_SEG_VECPT_CASE> intersect_line_cases = {
224  {
225  "two point aligned",
226  { { 0, 0 }, 20 },
227  { { 10, 45 }, {10, 40} },
228  {
229  { 10, -17 },
230  { 10, 17 },
231  },
232  },
233  {
234  "two point angled",
235  { { 0, 0 }, 20 },
236  { { -20, -40 }, {20, 40} },
237  {
238  { 8, 17 },
239  { -8, -17 },
240  },
241  },
242  {
243  "tangent",
244  { { 0, 0 }, 20 },
245  { { 20, 0 }, {20, 40} },
246  {
247  { 20, 0 }
248  },
249  },
250  {
251  "no intersection",
252  { { 0, 0 }, 20 },
253  { { 25, 0 }, {25, 40} },
254  {
255  //no points
256  },
257  },
258 };
259 // clang-format on
260 
261 
262 BOOST_AUTO_TEST_CASE( IntersectLine )
263 {
264  for( const auto& c : intersect_line_cases )
265  {
266  BOOST_TEST_CONTEXT( c.m_case_name )
267  {
268  std::vector<VECTOR2I> ret = c.m_circle.Intersect( c.m_seg );
269  BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
270  KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
271  }
272  }
273 }
274 
279 {
280  std::string m_case_name;
285 };
286 
287 // clang-format off
291 static const std::vector<CIR_SEG_VECPT_CASE> construct_tan_tan_pt_cases = {
292  {
293  "90 degree segs, point on seg",
294  { { 0, 0 }, { 0, 1000 } },
295  { { 0, 0 }, { 1000, 0 } },
296  { 0, 400 },
297  { { 400, 400} , 400 }, // result from simple geometric inference
298  },
299  {
300  "90 degree segs, point floating",
301  { { 0, 0 }, { 0, 1000 } },
302  { { 0, 0 }, { 1000, 0 } },
303  { 200, 100 },
304  { { 500, 500} , 500 }, // result from LibreCAD 2.2.0-rc2
305  },
306  {
307  "45 degree segs, point on seg",
308  { { 0, 0 }, { 1000, 0 } },
309  { { 0, 0 }, { 1000, 1000 } },
310  { 400, 0 },
311  { { 400, 166} , 166 },// result from LibreCAD 2.2.0-rc2
312  },
313  {
314  "45 degree segs, point floating",
315  { { 0, 0 }, { 1000000, 0 } },
316  { { 0, 0 }, { 1000000, 1000000 } },
317  { 200000, 100000 },
318  { { 332439, 137701} , 137701 }, // result from LibreCAD 2.2.0-rc2
319  },
320  {
321  "135 degree segs, point on seg",
322  { { 0, 0 }, { 1000000, 0 } },
323  { { 0, 0 }, { -1000000, 1000000 } },
324  { 400000, 0 },
325  { { 400009, 965709 } , 965709 }, // ammended to get the test to pass
326  //{ { 400000, 965686 } , 965686 }, // result from LibreCAD 2.2.0-rc2
327  },
328  {
329  "135 degree segs, point floating",
330  { { 0, 0 }, { 1000, 0 } },
331  { { 0, 0 }, { -1000, 1000 } },
332  { 200, 100 },
333  { { 814, 1964} , 1964 }, // ammended to get the test to pass
334  //{ { 822, 1985} , 1985 }, // result from LibreCAD 2.2.0-rc2
335  },
336  {
337  "point on intersection",
338  { { 10, 0 }, { 1000, 0 } },
339  { { 10, 0 }, { -1000, 1000 } },
340  { 10, 0 },
341  { { 10, 0} , 0 }, // special case: radius=0
342  },
343 };
344 // clang-format on
345 
346 
347 BOOST_AUTO_TEST_CASE( ConstructFromTanTanPt )
348 {
349  for( const auto& c : construct_tan_tan_pt_cases )
350  {
351  BOOST_TEST_CONTEXT( c.m_case_name )
352  {
353  CIRCLE circle;
354  circle.ConstructFromTanTanPt( c.m_segA, c.m_segB, c.m_pt );
355  BOOST_CHECK_MESSAGE( CompareVector2I( c.m_exp_result.Center, circle.Center ),
356  "\nCenter point mismatch: "
357  << "\n Got: " << circle.Center
358  << "\n Expected: " << c.m_exp_result.Center );
359 
360  BOOST_CHECK_MESSAGE( CompareLength( c.m_exp_result.Radius, circle.Radius ),
361  "\nRadius mismatch: "
362  << "\n Got: " << circle.Radius
363  << "\n Expected: " << c.m_exp_result.Radius );
364  }
365  }
366 }
367 
368 BOOST_AUTO_TEST_SUITE_END()
Struct to hold test cases for a given circle, a point and an expected return point.
Definition: test_circle.cpp:78
Struct to hold test cases for two lines, a point and an expected returned circle.
std::vector< VECTOR2I > m_exp_result
static const std::vector< CIR_CIR_VECPT_CASE > intersect_circle_cases
Test cases for #CIRCLE::Intersect( const CIRCLE& aCircle )
Define a general 2D-vector/point.
Definition: vector2d.h:61
static const std::vector< CIR_PT_PT_CASE > nearest_point_cases
Test cases for CIRCLE::NearestPoint.
Definition: test_circle.cpp:90
BOOST_AUTO_TEST_CASE(ParameterCtorMod)
Checks whether the construction of a circle referencing external parameters works and that the parame...
Definition: test_circle.cpp:54
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Constructs this circle such that it is tangent to the given segmetns and passes through the given poi...
Definition: circle.cpp:51
static const std::vector< SEG_SEG_VECPT_CASE > intersect_line_cases
Test cases for #CIRCLE::Intersect( const SEG& aSeg )
std::string m_case_name
bool CompareVector2I(const VECTOR2I &aVecA, const VECTOR2I &aVecB)
Definition: test_circle.cpp:37
VECTOR2I m_exp_result
Definition: test_circle.cpp:83
std::vector< VECTOR2I > m_exp_result
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:122
std::string m_case_name
VECTOR2I m_point
Definition: test_circle.cpp:82
int Radius
Public to make access simpler.
Definition: circle.h:36
Class Circle Represents basic circle geometry with utility geometry functions.
Definition: circle.h:33
bool CompareLength(int aLengthA, int aLengthB)
Definition: test_circle.cpp:27
void CheckUnorderedMatches(const EXP_CONT &aExpected, const FOUND_CONT &aFound, MATCH_PRED aMatchPredicate)
Check that a container of "found" objects matches a container of "expected" objects.
#define BOOST_TEST_CONTEXT(A)
std::string m_case_name
Definition: seg.h:41
static const std::vector< CIR_SEG_VECPT_CASE > construct_tan_tan_pt_cases
Test cases for #CIRCLE::Intersect( const SEG& aSeg )
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:37
Struct to hold test cases for a circle, a line and an expected vector of points.
Struct to hold test cases for two circles, and an vector of points.
std::string m_case_name
Definition: test_circle.cpp:80