KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_segment.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) 2017 CERN
5 * @author Alejandro GarcĂ­a Montoro <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <boost/test/data/test_case.hpp>
28
29#include <geometry/seg.h>
30
31namespace
32{
33
42bool SegCollideCorrect( const SEG& aSegA, const SEG& aSegB, int aClearance, bool aExp )
43{
44 const bool AtoB = aSegA.Collide( aSegB, aClearance );
45 const bool BtoA = aSegB.Collide( aSegA, aClearance );
46
47 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
48
49 if( AtoB != BtoA )
50 {
51 std::stringstream ss;
52 ss << "Segment collision is not the same in both directions: expected " << aExp << ", got "
53 << AtoB << " & " << BtoA;
54 BOOST_TEST_INFO( ss.str() );
55 }
56 else if( !ok )
57 {
58 std::stringstream ss;
59 ss << "Collision incorrect: expected " << aExp << ", got " << AtoB;
60 BOOST_TEST_INFO( ss.str() );
61 }
62
63 return ok;
64}
65
66
74bool SegDistanceCorrect( const SEG& aSegA, const SEG& aSegB, int aExp )
75{
76 const int AtoB = aSegA.Distance( aSegB );
77 const int BtoA = aSegB.Distance( aSegA );
78
79 bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
80
81 if( AtoB != BtoA )
82 {
83 std::stringstream ss;
84 ss << "Segment distance is not the same in both directions: expected " << aExp << ", got "
85 << AtoB << " & " << BtoA;
86 BOOST_TEST_INFO( ss.str() );
87 }
88 else if( !ok )
89 {
90 std::stringstream ss;
91 ss << "Distance incorrect: expected " << aExp << ", got " << AtoB;
92 BOOST_TEST_INFO( ss.str() );
93 }
94
95 // Sanity check: the collision should be consistent with the distance
96 ok = ok && SegCollideCorrect( aSegA, aSegB, 0, aExp == 0 );
97
98 return ok;
99}
100
108bool SegVecDistanceCorrect( const SEG& aSeg, const VECTOR2I& aVec, int aExp )
109{
110 const SEG::ecoord squaredDistance = aSeg.SquaredDistance( aVec );
111 BOOST_REQUIRE( squaredDistance >= 0 );
112
113 const int dist = aSeg.Distance( aVec );
114
115 bool ok = ( dist == aExp );
116
117 if( !ok )
118 {
119 std::stringstream ss;
120 ss << "Distance incorrect: expected " << aExp << ", got " << dist;
121 BOOST_TEST_INFO( ss.str() );
122 }
123
124 return ok;
125}
126
134bool SegCollinearCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
135{
136 const bool AtoB = aSegA.Collinear( aSegB );
137 const bool BtoA = aSegB.Collinear( aSegA );
138
139 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
140
141 if( AtoB != BtoA )
142 {
143 std::stringstream ss;
144 ss << "Segment collinearity is not the same in both directions: expected " << aExp
145 << ", got " << AtoB << " & " << BtoA;
146 BOOST_TEST_INFO( ss.str() );
147 }
148 else if( !ok )
149 {
150 std::stringstream ss;
151 ss << "Collinearity incorrect: expected " << aExp << ", got " << AtoB;
152 BOOST_TEST_INFO( ss.str() );
153 }
154
155 return ok;
156}
157
166bool SegParallelCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
167{
168 const bool AtoB = aSegA.ApproxParallel( aSegB );
169 const bool BtoA = aSegB.ApproxParallel( aSegA );
170
171 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
172
173 if( AtoB != BtoA )
174 {
175 std::stringstream ss;
176 ss << "Segment parallelism is not the same in both directions: expected " << aExp
177 << ", got AtoB: " << AtoB << " BtoA:" << BtoA;
178 BOOST_TEST_INFO( ss.str() );
179 }
180 else if( !ok )
181 {
182 std::stringstream ss;
183 ss << "Parallelism incorrect: expected " << aExp << ", got " << AtoB;
184 BOOST_TEST_INFO( ss.str() );
185 }
186
187 return ok;
188}
189
198bool SegPerpendicularCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
199{
200 const bool AtoB = aSegA.ApproxPerpendicular( aSegB );
201 const bool BtoA = aSegB.ApproxPerpendicular( aSegA );
202
203 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
204
205 if( AtoB != BtoA )
206 {
207 std::stringstream ss;
208 ss << "Segment perpendicularity is not the same in both directions: expected " << aExp
209 << ", got AtoB: " << AtoB << " BtoA:" << BtoA;
210 BOOST_TEST_INFO( ss.str() );
211 }
212 else if( !ok )
213 {
214 std::stringstream ss;
215 ss << "Perpendicularity incorrect: expected " << aExp << ", got " << AtoB;
216 BOOST_TEST_INFO( ss.str() );
217 }
218
219 return ok;
220}
221
222} // namespace
223
224BOOST_AUTO_TEST_SUITE( Segment )
225
226
230BOOST_AUTO_TEST_CASE( EndpointCtorMod )
231{
232 const VECTOR2I pointA{ 10, 20 };
233 const VECTOR2I pointB{ 100, 200 };
234
235 // Build a segment referencing the previous points
236 SEG segment( pointA, pointB );
237
238 BOOST_CHECK_EQUAL( pointA, VECTOR2I( 10, 20 ) );
239 BOOST_CHECK_EQUAL( pointB, VECTOR2I( 100, 200 ) );
240
241 // Modify the ends of the segments
242 segment.A += VECTOR2I( 10, 10 );
243 segment.B += VECTOR2I( 100, 100 );
244
245 // Check that the ends in segment are modified
246 BOOST_CHECK_EQUAL( segment.A, VECTOR2I( 20, 30 ) );
247 BOOST_CHECK_EQUAL( segment.B, VECTOR2I( 200, 300 ) );
248}
249
251{
255};
256
257
258// clang-format off
259static const std::vector<SEG_SEG_DISTANCE_CASE> seg_seg_dist_cases = {
260 {
261 "Parallel, 10 apart",
262 { { 0, 0 }, { 10, 0 } },
263 { { 0, 10 }, { 10, 10 } },
264 10,
265 },
266 {
267 "Non-parallel, 10 apart",
268 { { 0, -5 }, { 10, 0 } },
269 { { 0, 10 }, { 10, 10 } },
270 10,
271 },
272 {
273 "Co-incident",
274 { { 0, 0 }, { 30, 0 } },
275 { { 10, 0 }, { 20, 0 } },
276 0,
277 },
278 {
279 "Crossing",
280 { { 0, -10 }, { 0, 10 } },
281 { { -20, 0 }, { 20, 0 } },
282 0,
283 },
284 {
285 "T-junction",
286 { { 0, -10 }, { 0, 10 } },
287 { { -20, 0 }, { 0, 0 } },
288 0,
289 },
290 {
291 "T-junction (no touch)",
292 { { 0, -10 }, { 0, 10 } },
293 { { -20, 0 }, { -2, 0 } },
294 2,
295 },
296};
297// clang-format on
298
299
300BOOST_DATA_TEST_CASE( SegSegDistance, boost::unit_test::data::make( seg_seg_dist_cases ), c )
301{
302 BOOST_CHECK_PREDICATE( SegDistanceCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_dist ) );
303}
304
305
307{
311};
312
313
314// clang-format off
315static const std::vector<SEG_VECTOR_DISTANCE_CASE> seg_vec_dist_cases = {
316 {
317 "On endpoint",
318 { { 0, 0 }, { 10, 0 } },
319 { 0, 0 },
320 0,
321 },
322 {
323 "On segment",
324 { { 0, 0 }, { 10, 0 } },
325 { 3, 0 },
326 0,
327 },
328 {
329 "At side",
330 { { 0, 0 }, { 10, 0 } },
331 { 3, 2 },
332 2,
333 },
334 {
335 "At end (collinear)",
336 { { 0, 0 }, { 10, 0 } },
337 { 12, 0 },
338 2,
339 },
340 {
341 "At end (not collinear)",
342 { { 0, 0 }, { 1000, 0 } },
343 { 1000 + 200, 200 },
344 282, // sqrt(200^2 + 200^2) = 282.8, rounded to nearest
345 },
346 {
347 "Issue 18473 (inside hit with rounding error)",
348 { { 187360000, 42510000 }, { 105796472, 42510000 } },
349 { 106645000, 42510000 },
350 0,
351 },
352 {
353 "Straight line x distance",
354 { { 187360000, 42510000 }, { 105796472, 42510000 } },
355 { 197360000, 42510000 },
356 10000000,
357 },
358 {
359 "Straight line -x distance",
360 { { 187360000, 42510000 }, { 105796472, 42510000 } },
361 { 104796472, 42510000 },
362 1000000,
363 },
364};
365// clang-format on
366
367
368BOOST_DATA_TEST_CASE( SegVecDistance, boost::unit_test::data::make( seg_vec_dist_cases ), c )
369{
370 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( c.m_seg )( c.m_vec )( c.m_exp_dist ) );
371}
372
373
379{
384};
385
386
387// clang-format off
388static const std::vector<SEG_SEG_COLLIDE_CASE> seg_seg_coll_cases = {
389 {
390 "Parallel, 10 apart, 5 clear",
391 { { 0, 0 }, { 10, 0 } },
392 { { 0, 10 }, { 10, 10 } },
393 5,
394 false,
395 },
396 {
397 "Parallel, 10 apart, 10 clear",
398 { { 0, 0 }, { 10, 0 } },
399 { { 0, 10 }, { 10, 10 } },
400 10,
401 false,
402 },
403 {
404 "Parallel, 10 apart, 11 clear",
405 { { 0, 0 }, { 10, 0 } },
406 { { 0, 10 }, { 10, 10 } },
407 11,
408 true,
409 },
410 {
411 "T-junction, 2 apart, 2 clear",
412 { { 0, -10 }, { 0, 0 } },
413 { { -20, 0 }, { -2, 0 } },
414 2,
415 false,
416 },
417 {
418 "T-junction, 2 apart, 3 clear",
419 { { 0, -10 }, { 0, 0 } },
420 { { -20, 0 }, { -2, 0 } },
421 3,
422 true,
423 },
424};
425// clang-format on
426
427
428BOOST_DATA_TEST_CASE( SegSegCollision, boost::unit_test::data::make( seg_seg_coll_cases ), c )
429{
430 BOOST_CHECK_PREDICATE( SegCollideCorrect,
431 ( c.m_seg_a )( c.m_seg_b )( c.m_clearance )( c.m_exp_coll ) );
432}
433
434
439{
443};
444
445// clang-format off
449static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_collinear_cases = {
450 {
451 "coincident",
452 { { 0, 0 }, { 10, 0 } },
453 { { 0, 0 }, { 10, 0 } },
454 true,
455 },
456 {
457 "end-to-end",
458 { { 0, 0 }, { 10, 0 } },
459 { { 10, 0 }, { 20, 0 } },
460 true,
461 },
462 {
463 "In segment",
464 { { 0, 0 }, { 10, 0 } },
465 { { 4, 0 }, { 7, 0 } },
466 true,
467 },
468 {
469 "At side, parallel",
470 { { 0, 0 }, { 10, 0 } },
471 { { 4, 1 }, { 7, 1 } },
472 false,
473 },
474 {
475 "crossing",
476 { { 0, 0 }, { 10, 0 } },
477 { { 5, -5 }, { 5, 5 } },
478 false,
479 },
480};
481// clang-format on
482
483
484BOOST_DATA_TEST_CASE( SegSegCollinear, boost::unit_test::data::make( seg_vec_collinear_cases ), c )
485{
486 BOOST_CHECK_PREDICATE( SegCollinearCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
487}
488
489
490// clang-format off
494static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_parallel_cases = {
495 {
496 "coincident",
497 { { 0, 0 }, { 10, 0 } },
498 { { 0, 0 }, { 10, 0 } },
499 true,
500 },
501 {
502 "end-to-end",
503 { { 0, 0 }, { 10, 0 } },
504 { { 10, 0 }, { 20, 0 } },
505 true,
506 },
507 {
508 "In segment",
509 { { 0, 0 }, { 10, 0 } },
510 { { 4, 0 }, { 7, 0 } },
511 true,
512 },
513 {
514 "At side, parallel",
515 { { 0, 0 }, { 10, 0 } },
516 { { 4, 1 }, { 7, 1 } },
517 true,
518 },
519 {
520 "crossing",
521 { { 0, 0 }, { 10, 0 } },
522 { { 5, -5 }, { 5, 5 } },
523 false,
524 },
525};
526// clang-format on
527
528
529BOOST_DATA_TEST_CASE( SegSegParallel, boost::unit_test::data::make( seg_vec_parallel_cases ), c )
530{
531 BOOST_CHECK_PREDICATE( SegParallelCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
532}
533
534
535// clang-format off
539static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_perpendicular_cases = {
540 {
541 "coincident",
542 { { 0, 0 }, { 10, 0 } },
543 { { 0, 0 }, { 10, 0 } },
544 false,
545 },
546 {
547 "end-to-end",
548 { { 0, 0 }, { 10, 0 } },
549 { { 10, 0 }, { 20, 0 } },
550 false,
551 },
552 {
553 "In segment",
554 { { 0, 0 }, { 10, 0 } },
555 { { 4, 0 }, { 7, 0 } },
556 false,
557 },
558 {
559 "At side, parallel",
560 { { 0, 0 }, { 10, 0 } },
561 { { 4, 1 }, { 7, 1 } },
562 false,
563 },
564 {
565 "crossing 45 deg",
566 { { 0, 0 }, { 10, 0 } },
567 { { 0, 0 }, { 5, 5 } },
568 false,
569 },
570 {
571 "very nearly perpendicular",
572 { { 0, 0 }, { 10, 0 } },
573 { { 0, 0 }, { 1, 10 } },
574 true, //allow error margin of 1 IU
575 },
576 {
577 "not really perpendicular",
578 { { 0, 0 }, { 10, 0 } },
579 { { 0, 0 }, { 3, 10 } },
580 false,
581 },
582 {
583 "perpendicular",
584 { { 0, 0 }, { 10, 0 } },
585 { { 0, 0 }, { 0, 10 } },
586 true,
587 },
588 {
589 "perpendicular not intersecting",
590 { { 0, 0 }, { 10, 0 } },
591 { { 15, 5 }, { 15, 10 } },
592 true,
593 },
594};
595// clang-format on
596
597
598BOOST_DATA_TEST_CASE( SegSegPerpendicular,
599 boost::unit_test::data::make( seg_vec_perpendicular_cases ), c )
600{
601 BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
602}
603
604
609{
612};
613
614
615// clang-format off
619static const std::vector<SEG_VEC_CASE> segment_and_point_cases = {
620 {
621 "Horizontal: point on edge of seg",
622 { { 0, 0 }, { 10, 0 } },
623 { 0, 0 },
624 },
625 {
626 "Horizontal: point in middle of seg",
627 { { 0, 0 }, { 10, 0 } },
628 { 5, 0 },
629 },
630 {
631 "Horizontal: point outside seg",
632 { { 0, 0 }, { 10, 0 } },
633 { 20, 20 },
634 },
635 {
636 "Vertical: point on edge of seg",
637 { { 0, 0 }, { 0, 10 } },
638 { 0, 0 },
639 },
640 {
641 "Vertical: point in middle of seg",
642 { { 0, 0 }, { 0, 10 } },
643 { 0, 5 },
644 },
645 {
646 "Vertical: point outside seg",
647 { { 0, 0 }, { 0, 10 } },
648 { 20, 20 },
649 },
650};
651// clang-format on
652
653
654BOOST_DATA_TEST_CASE( SegCreateParallel, boost::unit_test::data::make( segment_and_point_cases ),
655 c )
656{
657 const SEG perpendicular = c.m_seg.ParallelSeg( c.m_vec );
658
659 BOOST_CHECK_PREDICATE( SegParallelCorrect, (perpendicular) ( c.m_seg )( true ) );
660 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, (perpendicular) ( c.m_vec )( 0 ) );
661}
662
663BOOST_DATA_TEST_CASE( SegCreatePerpendicular,
664 boost::unit_test::data::make( segment_and_point_cases ), c )
665{
666 const SEG perpendicular = c.m_seg.PerpendicularSeg( c.m_vec );
667
668 BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, (perpendicular) ( c.m_seg )( true ) );
669 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, (perpendicular) ( c.m_vec )( 0 ) );
670}
671
672BOOST_AUTO_TEST_CASE( LineDistance )
673{
674 SEG seg( { 0, 0 }, { 10, 0 } );
675
676 BOOST_TEST( seg.LineDistance( { 5, 0 } ) == 0 );
677 BOOST_TEST( seg.LineDistance( { 5, 8 } ) == 8 );
678}
679
680BOOST_AUTO_TEST_CASE( LineDistanceSided )
681{
682 SEG seg( { 0, 0 }, { 10, 0 } );
683
684 BOOST_TEST( seg.LineDistance( { 5, 8 }, true ) == 8 );
685 BOOST_TEST( seg.LineDistance( { 5, -8 }, true ) == -8 );
686}
687
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition: seg.cpp:75
VECTOR2I::extended_type ecoord
Definition: seg.h:44
VECTOR2I B
Definition: seg.h:50
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition: seg.cpp:289
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition: seg.cpp:489
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
Definition: seg.h:276
SEG ParallelSeg(const VECTOR2I &aP) const
Compute a segment parallel to this one, passing through point aP.
Definition: seg.cpp:274
bool ApproxPerpendicular(const SEG &aSeg) const
Definition: seg.cpp:501
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
SEG PerpendicularSeg(const VECTOR2I &aP) const
Compute a segment perpendicular to this one, passing through point aP.
Definition: seg.cpp:265
A named data-driven test case.
Struct to hold general cases for collinearity, parallelism and perpendicularity.
Test cases for collisions (with clearance, for no clearance, it's just a SEG_SEG_DISTANCE_CASE of 0)
Struct to hold cases for operations with a SEG, and a VECTOR2I.
VECTOR2I m_vec
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
BOOST_TEST(contains==c.ExpectedContains)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static const std::vector< SEG_SEG_BOOLEAN_CASE > seg_vec_perpendicular_cases
Test cases for perpendicularity.
BOOST_AUTO_TEST_CASE(EndpointCtorMod)
Checks whether the construction of a segment referencing external points works and that the endpoints...
static const std::vector< SEG_SEG_BOOLEAN_CASE > seg_vec_collinear_cases
Test cases for collinearity.
static const std::vector< SEG_VEC_CASE > segment_and_point_cases
Test cases to create segments passing through a point.
BOOST_DATA_TEST_CASE(SegSegPerpendicular, boost::unit_test::data::make(seg_vec_perpendicular_cases), c)
static const std::vector< SEG_SEG_DISTANCE_CASE > seg_seg_dist_cases
static const std::vector< SEG_VECTOR_DISTANCE_CASE > seg_vec_dist_cases
static const std::vector< SEG_SEG_COLLIDE_CASE > seg_seg_coll_cases
static const std::vector< SEG_SEG_BOOLEAN_CASE > seg_vec_parallel_cases
Test cases for parallelism.
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695