KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
5 * Copyright The 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 <boost/test/data/test_case.hpp>
23
24#include <geometry/circle.h>
25#include <geometry/seg.h> // for SEG
26#include <geometry/shape.h> // for MIN_PRECISION_IU
27
28const int MIN_PRECISION_45DEG = KiROUND( (double) SHAPE::MIN_PRECISION_IU * 0.7071 );
29
30bool CompareLength( int aLengthA, int aLengthB )
31{
32 if( aLengthA > ( aLengthB + SHAPE::MIN_PRECISION_IU ) )
33 return false;
34 else if( aLengthA < ( aLengthB - SHAPE::MIN_PRECISION_IU ) )
35 return false;
36 else
37 return true;
38}
39
40bool CompareVector2I( const VECTOR2I& aVecA, const VECTOR2I& aVecB )
41{
42 if( !CompareLength(aVecA.x, aVecB.x) )
43 return false;
44 else if( !CompareLength( aVecA.y, aVecB.y ) )
45 return false;
46 else
47 return true;
48}
49
50
52
53
57BOOST_AUTO_TEST_CASE( ParameterCtorMod )
58{
59 const VECTOR2I center( 10, 20 );
60 const int radius = 10;
61
62 // Build a circle referencing the previous values
63 CIRCLE circle( center, radius );
64
65 BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 10, 20 ) );
66 BOOST_CHECK_EQUAL( circle.Radius, 10 );
67
68 // Modify the parameters
69 circle.Center += VECTOR2I( 10, 10 );
70 circle.Radius += 20;
71
72 // Check the parameters were modified
73 BOOST_CHECK_EQUAL( circle.Center, VECTOR2I( 20, 30 ) );
74 BOOST_CHECK_EQUAL( circle.Radius, 30 );
75}
76
77
82{
86};
87
88// clang-format off
92static const std::vector<CIR_PT_BOOL_CASE> contains_cases = {
93 {
94 "on center",
95 { { 100, 100 }, 200 },
96 { 100, 100 },
97 false,
98 },
99 {
100 "0 deg",
101 { { 100, 100 }, 200 },
102 { 300, 100 },
103 true,
104 },
105 {
106 "0 deg, allowed tolerance pos",
107 { { 100, 100 }, 200 },
108 { 100, 300 + SHAPE::MIN_PRECISION_IU },
109 true,
110 },
111 {
112 "0 deg, allowed tolerance neg",
113 { { 100, 100 }, 200 },
114 { 100, 300 - SHAPE::MIN_PRECISION_IU },
115 true,
116 },
117 {
118 "0 deg, allowed tolerance pos + 1",
119 { { 100, 100 }, 200 },
120 { 100, 300 + SHAPE::MIN_PRECISION_IU + 1 },
121 false,
122 },
123 {
124 "0 deg, allowed tolerance neg - 1",
125 { { 100, 100 }, 200 },
126 { 100, 300 - SHAPE::MIN_PRECISION_IU - 1 },
127 false,
128 },
129 {
130 "45 deg",
131 { { 100, 100 }, 200 },
132 { 241, 241 },
133 true,
134 },
135 {
136 "45 deg, allowed tolerance pos",
137 { { 100, 100 }, 200 },
139 true,
140 },
141 {
142 "45 deg, allowed tolerance pos + 1",
143 { { 100, 100 }, 200 },
144 { 241 + MIN_PRECISION_45DEG + 1, 241 + MIN_PRECISION_45DEG + 1 },
145 false,
146 },
147 {
148 "90 deg",
149 { { 100, 100 }, 200 },
150 { 100, 300 },
151 true,
152 },
153 {
154 "180 deg",
155 { { 100, 100 }, 200 },
156 { -100, 100 },
157 true,
158 },
159 {
160 "270 deg",
161 { { 100, 100 }, 200 },
162 { 100, -100 },
163 true,
164 },
165};
166// clang-format on
167
168
169BOOST_DATA_TEST_CASE( Contains, boost::unit_test::data::make( contains_cases ), c )
170{
171 bool ret = c.m_circle.Contains( c.m_point );
172 BOOST_CHECK_EQUAL( ret, c.m_exp_result );
173}
174
175
180{
184};
185
186// clang-format off
190static const std::vector<CIR_PT_PT_CASE> nearest_point_cases = {
191 {
192 "on center",
193 { { 10, 10 }, 20 },
194 { 10, 10 },
195 { 30, 10 }, // special case: when at the circle return a point on the x axis
196 },
197 {
198 "inside",
199 { { 10, 10 }, 20 },
200 { 10, 20 },
201 { 10, 30 },
202 },
203 {
204 "outside",
205 { { 10, 10 }, 20 },
206 { 10, 50 },
207 { 10, 30 },
208 },
209 {
210 "angled",
211 { { 10, 10 }, 20 },
212 { 50, 50 },
213 { 24, 24 },
214 },
215};
216// clang-format on
217
218
219BOOST_DATA_TEST_CASE( NearestPoint, boost::unit_test::data::make( nearest_point_cases ), c )
220{
221 VECTOR2I ret = c.m_circle.NearestPoint( c.m_point );
222 BOOST_CHECK_EQUAL( ret, c.m_exp_result );
223}
224
225
230{
233 std::vector<VECTOR2I> m_exp_result;
234};
235
236// clang-format off
240static const std::vector<CIR_CIR_VECPT_CASE> intersect_circle_cases = {
241 {
242 "two point aligned",
243 { { 10, 10 }, 20 },
244 { { 10, 45 }, 20 },
245 {
246 { 0, 27 },
247 { 21, 27 },
248 },
249 },
250 {
251 "two point angled",
252 { { 10, 10 }, 20 },
253 { { 20, 20 }, 20 },
254 {
255 { 2, 28 },
256 { 28, 2 },
257 },
258 },
259 {
260 "tangent aligned, external",
261 { { 10, 10 }, 20 },
262 { { 10, 50 }, 20 },
263 {
264 { 10, 30 },
265 },
266 },
267 {
268 "tangent aligned, internal",
269 { { 10, 10 }, 40 },
270 { { 10, 30 }, 20 },
271 {
272 { 10, 50 },
273 },
274 },
275 {
276 "no intersection",
277 { { 10, 10 }, 20 },
278 { { 10, 51 }, 20 },
279 {
280 //no points
281 },
282 },
283 {
284 "KiROUND overflow 1",
285 { { 44798001, -94001999 }, 200001 },
286 { { 44797999, -94001999 }, 650001 },
287 {
288 //no points
289 },
290 },
291 {
292 "KiROUND overflow 2",
293 { { 50747999, -92402001 }, 650001 },
294 { { 50748001, -92402001 }, 200001 },
295 {
296 //no points
297 },
298 },
299 {
300 "KiROUND overflow 3",
301 { { 43947999, -92402001 }, 650001 },
302 { { 43948001, -92402001 }, 200001 },
303 {
304 //no points
305 },
306 },
307 {
308 "KiROUND overflow 4",
309 { { 46497999, -94001999 }, 200001 },
310 { { 46498001, -94001999 }, 650001 },
311 {
312 //no points
313 },
314 },
315 {
316 "Co-centered, same radius", // Exercise d=0
317 { { 205999999, 136367974 }, 3742026 },
318 { { 205999999, 136367974 }, 3742026 },
319 {
320 //no points
321 },
322 },
323};
324// clang-format on
325
326
327BOOST_DATA_TEST_CASE( IntersectCircle, boost::unit_test::data::make( intersect_circle_cases ), c )
328{
329 BOOST_TEST_CONTEXT( c.m_CaseName + " Case 1" )
330 {
331 std::vector<VECTOR2I> ret1 = c.m_circle1.Intersect( c.m_circle2 );
332 BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret1.size() );
333 KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret1, CompareVector2I );
334 }
335
336 BOOST_TEST_CONTEXT( c.m_CaseName + " Case 2" )
337 {
338 // Test the other direction
339 std::vector<VECTOR2I> ret2 = c.m_circle2.Intersect( c.m_circle1 );
340 BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret2.size() );
341 KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret2, CompareVector2I );
342 }
343}
344
345
349struct SEG_SEG_VECPT_CASE : public KI_TEST::NAMED_CASE
350{
351 CIRCLE m_circle;
352 SEG m_seg;
353 std::vector<VECTOR2I> m_exp_result;
354};
355
356// clang-format off
360static const std::vector<SEG_SEG_VECPT_CASE> intersect_seg_cases = {
361 {
362 "two point aligned",
363 { { 0, 0 }, 20 },
364 { { 10, -40 }, {10, 40} },
365 {
366 { 10, -17 },
367 { 10, 17 },
368 },
369 },
370 {
371 "two point angled",
372 { { 0, 0 }, 20 },
373 { { -20, -40 }, {20, 40} },
374 {
375 { 8, 17 },
376 { -8, -17 },
377 },
378 },
379 {
380 "tangent",
381 { { 0, 0 }, 20 },
382 { { 20, 0 }, {20, 40} },
383 {
384 { 20, 0 }
385 },
386 },
387 {
388 "no intersection",
389 { { 0, 0 }, 20 },
390 { { 25, 0 }, {25, 40} },
391 {
392 //no points
393 },
394 },
395 {
396 "no intersection: seg end points inside circle",
397 { { 0, 0 }, 20 },
398 { { 0, 10 }, {0, -10} },
399 {
400 //no points
401 },
402 },
403};
404// clang-format on
405
406
407BOOST_DATA_TEST_CASE( Intersect, boost::unit_test::data::make( intersect_seg_cases ), c )
408{
409 std::vector<VECTOR2I> ret = c.m_circle.Intersect( c.m_seg );
410 BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
411 KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
412}
413
414
415// clang-format off
419static const std::vector<SEG_SEG_VECPT_CASE> intersect_line_cases = {
420 {
421 "two point aligned",
422 { { 0, 0 }, 20 },
423 { { 10, 45 }, {10, 40} },
424 {
425 { 10, -17 },
426 { 10, 17 },
427 },
428 },
429 {
430 "two point angled",
431 { { 0, 0 }, 20 },
432 { { -20, -40 }, {20, 40} },
433 {
434 { 8, 17 },
435 { -8, -17 },
436 },
437 },
438 {
439 "tangent",
440 { { 0, 0 }, 20 },
441 { { 20, 0 }, {20, 40} },
442 {
443 { 20, 0 }
444 },
445 },
446 {
447 "no intersection",
448 { { 0, 0 }, 20 },
449 { { 25, 0 }, {25, 40} },
450 {
451 //no points
452 },
453 },
454 {
455 "intersection, seg end points inside circle",
456 { { 0, 0 }, 20 },
457 { { 0, 10 }, {0, -10} },
458 {
459 { 0, 20 },
460 { 0, -20 }
461 },
462 },
463};
464// clang-format on
465
466
467BOOST_DATA_TEST_CASE( IntersectLine, boost::unit_test::data::make( intersect_line_cases ), c )
468{
469 std::vector<VECTOR2I> ret = c.m_circle.IntersectLine( c.m_seg );
470 BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
471 KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
472}
473
474
475
480{
485};
486
487// clang-format off
491static const std::vector<CIR_SEG_VECPT_CASE> construct_tan_tan_pt_cases = {
492 {
493 "90 degree segs, point on seg",
494 { { 0, 0 }, { 0, 1000 } },
495 { { 0, 0 }, { 1000, 0 } },
496 { 0, 400 },
497 { { 400, 400} , 400 }, // result from simple geometric inference
498 },
499 {
500 "90 degree segs, point floating",
501 { { 0, 0 }, { 0, 1000 } },
502 { { 0, 0 }, { 1000, 0 } },
503 { 200, 100 },
504 { { 500, 500} , 500 }, // result from LibreCAD 2.2.0-rc2
505 },
506 {
507 "45 degree segs, point on seg",
508 { { 0, 0 }, { 1000, 0 } },
509 { { 0, 0 }, { 1000, 1000 } },
510 { 400, 0 },
511 { { 400, 166} , 166 },// result from LibreCAD 2.2.0-rc2
512 },
513 {
514 "45 degree segs, point floating",
515 { { 0, 0 }, { 1000000, 0 } },
516 { { 0, 0 }, { 1000000, 1000000 } },
517 { 200000, 100000 },
518 { { 332439, 137701} , 137701 }, // result from LibreCAD 2.2.0-rc2
519 },
520 {
521 "135 degree segs, point on seg",
522 { { 0, 0 }, { 1000000, 0 } },
523 { { 0, 0 }, { -1000000, 1000000 } },
524 { 400000, 0 },
525 { { 400009, 965709 } , 965709 }, // amended to get the test to pass
526 //{ { 400000, 965686 } , 965686 }, // result from LibreCAD 2.2.0-rc2
527 },
528 {
529 "135 degree segs, point floating",
530 { { 0, 0 }, { 1000, 0 } },
531 { { 0, 0 }, { -1000, 1000 } },
532 { 200, 100 },
533 { { 814, 1964} , 1964 }, // amended to get the test to pass
534 //{ { 822, 1985} , 1985 }, // result from LibreCAD 2.2.0-rc2
535 },
536 {
537 "point on intersection",
538 { { 10, 0 }, { 1000, 0 } },
539 { { 10, 0 }, { -1000, 1000 } },
540 { 10, 0 },
541 { { 10, 0} , 0 }, // special case: radius=0
542 },
543};
544// clang-format on
545
546
547BOOST_DATA_TEST_CASE( ConstructFromTanTanPt,
548 boost::unit_test::data::make( construct_tan_tan_pt_cases ), c )
549{
550 CIRCLE circle;
551 circle.ConstructFromTanTanPt( c.m_segA, c.m_segB, c.m_pt );
552 BOOST_CHECK_MESSAGE( CompareVector2I( c.m_exp_result.Center, circle.Center ),
553 "\nCenter point mismatch: " << "\n Got: " << circle.Center
554 << "\n Expected: "
555 << c.m_exp_result.Center );
556
557 BOOST_CHECK_MESSAGE( CompareLength( c.m_exp_result.Radius, circle.Radius ),
558 "\nRadius mismatch: " << "\n Got: " << circle.Radius
559 << "\n Expected: " << c.m_exp_result.Radius );
560}
561
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:130
int Radius
Public to make access simpler.
Definition: circle.h:129
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Construct this circle such that it is tangent to the given segments and passes through the given poin...
Definition: circle.cpp:51
Definition: seg.h:42
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:131
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.
Struct to hold test cases for two circles, and an vector of points.
std::vector< VECTOR2I > m_exp_result
Struct to hold test cases for a given circle, a point and an expected return boolean.
Definition: test_circle.cpp:82
Struct to hold test cases for a given circle, a point and an expected return point.
VECTOR2I m_point
VECTOR2I m_exp_result
Struct to hold test cases for two lines, a point and an expected returned circle.
A named data-driven test case.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
bool CompareLength(int aLengthA, int aLengthB)
Definition: test_circle.cpp:30
static const std::vector< CIR_SEG_VECPT_CASE > construct_tan_tan_pt_cases
Test cases for #CIRCLE::Intersect( const SEG& aSeg )
BOOST_AUTO_TEST_CASE(ParameterCtorMod)
Checks whether the construction of a circle referencing external parameters works and that the parame...
Definition: test_circle.cpp:57
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
bool CompareVector2I(const VECTOR2I &aVecA, const VECTOR2I &aVecB)
Definition: test_circle.cpp:40
static const std::vector< CIR_PT_PT_CASE > nearest_point_cases
Test cases for CIRCLE::NearestPoint.
static const std::vector< CIR_CIR_VECPT_CASE > intersect_circle_cases
Test cases for #CIRCLE::Intersect( const CIRCLE& aCircle )
static const std::vector< SEG_SEG_VECPT_CASE > intersect_line_cases
Test cases for #CIRCLE::IntersectLine( const SEG& aSeg )
static const std::vector< SEG_SEG_VECPT_CASE > intersect_seg_cases
Test cases for #CIRCLE::Intersect( const SEG& aSeg )
BOOST_DATA_TEST_CASE(ConstructFromTanTanPt, boost::unit_test::data::make(construct_tan_tan_pt_cases), c)
const int MIN_PRECISION_45DEG
Definition: test_circle.cpp:28
static const std::vector< CIR_PT_BOOL_CASE > contains_cases
Test cases for CIRCLE::Contains.
Definition: test_circle.cpp:92
BOOST_AUTO_TEST_SUITE_END()
VECTOR2I center
int radius
BOOST_TEST_CONTEXT("Test Clearance")
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695