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 (C) 2019-2021 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
28#include <geometry/seg.h>
29
38bool SegCollideCorrect( const SEG& aSegA, const SEG& aSegB, int aClearance, bool aExp )
39{
40 const bool AtoB = aSegA.Collide( aSegB, aClearance );
41 const bool BtoA = aSegB.Collide( aSegA, aClearance );
42
43 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
44
45 if( AtoB != BtoA )
46 {
47 std::stringstream ss;
48 ss << "Segment collision is not the same in both directions: expected " << aExp << ", got "
49 << AtoB << " & " << BtoA;
50 BOOST_TEST_INFO( ss.str() );
51 }
52 else if( !ok )
53 {
54 std::stringstream ss;
55 ss << "Collision incorrect: expected " << aExp << ", got " << AtoB;
56 BOOST_TEST_INFO( ss.str() );
57 }
58
59 return ok;
60}
61
62
70bool SegDistanceCorrect( const SEG& aSegA, const SEG& aSegB, int aExp )
71{
72 const int AtoB = aSegA.Distance( aSegB );
73 const int BtoA = aSegB.Distance( aSegA );
74
75 bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
76
77 if( AtoB != BtoA )
78 {
79 std::stringstream ss;
80 ss << "Segment distance is not the same in both directions: expected " << aExp << ", got "
81 << AtoB << " & " << BtoA;
82 BOOST_TEST_INFO( ss.str() );
83 }
84 else if( !ok )
85 {
86 std::stringstream ss;
87 ss << "Distance incorrect: expected " << aExp << ", got " << AtoB;
88 BOOST_TEST_INFO( ss.str() );
89 }
90
91 // Sanity check: the collision should be consistent with the distance
92 ok = ok && SegCollideCorrect( aSegA, aSegB, 0, aExp == 0 );
93
94 return ok;
95}
96
104bool SegVecDistanceCorrect( const SEG& aSeg, const VECTOR2I& aVec, int aExp )
105{
106 const SEG::ecoord squaredDistance = aSeg.SquaredDistance( aVec );
107 BOOST_REQUIRE( squaredDistance >= 0 );
108
109 const int dist = aSeg.Distance( aVec );
110
111 bool ok = ( dist == aExp );
112
113 if( !ok )
114 {
115 std::stringstream ss;
116 ss << "Distance incorrect: expected " << aExp << ", got " << dist;
117 BOOST_TEST_INFO( ss.str() );
118 }
119
120 return ok;
121}
122
130bool SegCollinearCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
131{
132 const bool AtoB = aSegA.Collinear( aSegB );
133 const bool BtoA = aSegB.Collinear( aSegA );
134
135 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
136
137 if( AtoB != BtoA )
138 {
139 std::stringstream ss;
140 ss << "Segment collinearity is not the same in both directions: expected " << aExp
141 << ", got " << AtoB << " & " << BtoA;
142 BOOST_TEST_INFO( ss.str() );
143 }
144 else if( !ok )
145 {
146 std::stringstream ss;
147 ss << "Collinearity incorrect: expected " << aExp << ", got " << AtoB;
148 BOOST_TEST_INFO( ss.str() );
149 }
150
151 return ok;
152}
153
162bool SegParallelCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
163{
164 const bool AtoB = aSegA.ApproxParallel( aSegB );
165 const bool BtoA = aSegB.ApproxParallel( aSegA );
166
167 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
168
169 if( AtoB != BtoA )
170 {
171 std::stringstream ss;
172 ss << "Segment parallelism is not the same in both directions: expected " << aExp
173 << ", got AtoB: " << AtoB << " BtoA:" << BtoA;
174 BOOST_TEST_INFO( ss.str() );
175 }
176 else if( !ok )
177 {
178 std::stringstream ss;
179 ss << "Parallelism incorrect: expected " << aExp << ", got " << AtoB;
180 BOOST_TEST_INFO( ss.str() );
181 }
182
183 return ok;
184}
185
194bool SegPerpendicularCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
195{
196 const bool AtoB = aSegA.ApproxPerpendicular( aSegB );
197 const bool BtoA = aSegB.ApproxPerpendicular( aSegA );
198
199 const bool ok = ( AtoB == aExp ) && ( BtoA == aExp );
200
201 if( AtoB != BtoA )
202 {
203 std::stringstream ss;
204 ss << "Segment perpendicularity is not the same in both directions: expected " << aExp
205 << ", got AtoB: " << AtoB << " BtoA:" << BtoA;
206 BOOST_TEST_INFO( ss.str() );
207 }
208 else if( !ok )
209 {
210 std::stringstream ss;
211 ss << "Perpendicularity incorrect: expected " << aExp << ", got " << AtoB;
212 BOOST_TEST_INFO( ss.str() );
213 }
214
215 return ok;
216}
217
218BOOST_AUTO_TEST_SUITE( Segment )
219
220
224BOOST_AUTO_TEST_CASE( EndpointCtorMod )
225{
226 const VECTOR2I pointA{ 10, 20 };
227 const VECTOR2I pointB{ 100, 200 };
228
229 // Build a segment referencing the previous points
230 SEG segment( pointA, pointB );
231
232 BOOST_CHECK_EQUAL( pointA, VECTOR2I( 10, 20 ) );
233 BOOST_CHECK_EQUAL( pointB, VECTOR2I( 100, 200 ) );
234
235 // Modify the ends of the segments
236 segment.A += VECTOR2I( 10, 10 );
237 segment.B += VECTOR2I( 100, 100 );
238
239 // Check that the ends in segment are modified
240 BOOST_CHECK_EQUAL( segment.A, VECTOR2I( 20, 30 ) );
241 BOOST_CHECK_EQUAL( segment.B, VECTOR2I( 200, 300 ) );
242}
243
245{
246 std::string m_case_name;
250};
251
252
253// clang-format off
254static const std::vector<SEG_SEG_DISTANCE_CASE> seg_seg_dist_cases = {
255 {
256 "Parallel, 10 apart",
257 { { 0, 0 }, { 10, 0 } },
258 { { 0, 10 }, { 10, 10 } },
259 10,
260 },
261 {
262 "Non-parallel, 10 apart",
263 { { 0, -5 }, { 10, 0 } },
264 { { 0, 10 }, { 10, 10 } },
265 10,
266 },
267 {
268 "Co-incident",
269 { { 0, 0 }, { 30, 0 } },
270 { { 10, 0 }, { 20, 0 } },
271 0,
272 },
273 {
274 "Crossing",
275 { { 0, -10 }, { 0, 10 } },
276 { { -20, 0 }, { 20, 0 } },
277 0,
278 },
279 {
280 "T-junction",
281 { { 0, -10 }, { 0, 10 } },
282 { { -20, 0 }, { 0, 0 } },
283 0,
284 },
285 {
286 "T-junction (no touch)",
287 { { 0, -10 }, { 0, 10 } },
288 { { -20, 0 }, { -2, 0 } },
289 2,
290 },
291};
292// clang-format on
293
294
295BOOST_AUTO_TEST_CASE( SegSegDistance )
296{
297 for( const auto& c : seg_seg_dist_cases )
298 {
299 BOOST_TEST_CONTEXT( c.m_case_name )
300 {
301 BOOST_CHECK_PREDICATE( SegDistanceCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_dist ) );
302 }
303 }
304}
305
306
308{
309 std::string m_case_name;
313};
314
315
316// clang-format off
317static const std::vector<SEG_VECTOR_DISTANCE_CASE> seg_vec_dist_cases = {
318 {
319 "On endpoint",
320 { { 0, 0 }, { 10, 0 } },
321 { 0, 0 },
322 0,
323 },
324 {
325 "On segment",
326 { { 0, 0 }, { 10, 0 } },
327 { 3, 0 },
328 0,
329 },
330 {
331 "At side",
332 { { 0, 0 }, { 10, 0 } },
333 { 3, 2 },
334 2,
335 },
336 {
337 "At end (collinear)",
338 { { 0, 0 }, { 10, 0 } },
339 { 12, 0 },
340 2,
341 },
342 {
343 "At end (not collinear)",
344 { { 0, 0 }, { 1000, 0 } },
345 { 1000 + 200, 200 },
346 282, // sqrt(200^2 + 200^2) = 282.8, rounded to nearest
347 },
348 {
349 "Issue 18473 (inside hit with rounding error)",
350 { { 187360000, 42510000 }, { 105796472, 42510000 } },
351 { 106645000, 42510000 },
352 0,
353 },
354 {
355 "Straight line x distance",
356 { { 187360000, 42510000 }, { 105796472, 42510000 } },
357 { 197360000, 42510000 },
358 10000000,
359 },
360 {
361 "Straight line -x distance",
362 { { 187360000, 42510000 }, { 105796472, 42510000 } },
363 { 104796472, 42510000 },
364 1000000,
365 },
366};
367// clang-format on
368
369
370BOOST_AUTO_TEST_CASE( SegVecDistance )
371{
372 for( const auto& c : seg_vec_dist_cases )
373 {
374 BOOST_TEST_CONTEXT( c.m_case_name )
375 {
376 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( c.m_seg )( c.m_vec )( c.m_exp_dist ) );
377 }
378 }
379}
380
381
387{
388 std::string m_case_name;
393};
394
395
396// clang-format off
397static const std::vector<SEG_SEG_COLLIDE_CASE> seg_seg_coll_cases = {
398 {
399 "Parallel, 10 apart, 5 clear",
400 { { 0, 0 }, { 10, 0 } },
401 { { 0, 10 }, { 10, 10 } },
402 5,
403 false,
404 },
405 {
406 "Parallel, 10 apart, 10 clear",
407 { { 0, 0 }, { 10, 0 } },
408 { { 0, 10 }, { 10, 10 } },
409 10,
410 false,
411 },
412 {
413 "Parallel, 10 apart, 11 clear",
414 { { 0, 0 }, { 10, 0 } },
415 { { 0, 10 }, { 10, 10 } },
416 11,
417 true,
418 },
419 {
420 "T-junction, 2 apart, 2 clear",
421 { { 0, -10 }, { 0, 0 } },
422 { { -20, 0 }, { -2, 0 } },
423 2,
424 false,
425 },
426 {
427 "T-junction, 2 apart, 3 clear",
428 { { 0, -10 }, { 0, 0 } },
429 { { -20, 0 }, { -2, 0 } },
430 3,
431 true,
432 },
433};
434// clang-format on
435
436
437BOOST_AUTO_TEST_CASE( SegSegCollision )
438{
439 for( const auto& c : seg_seg_coll_cases )
440 {
441 BOOST_TEST_CONTEXT( c.m_case_name )
442 {
443 BOOST_CHECK_PREDICATE( SegCollideCorrect,
444 ( c.m_seg_a )( c.m_seg_b )( c.m_clearance )( c.m_exp_coll ) );
445 }
446 }
447}
448
449
454{
455 std::string m_case_name;
459};
460
461// clang-format off
465static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_collinear_cases = {
466 {
467 "coincident",
468 { { 0, 0 }, { 10, 0 } },
469 { { 0, 0 }, { 10, 0 } },
470 true,
471 },
472 {
473 "end-to-end",
474 { { 0, 0 }, { 10, 0 } },
475 { { 10, 0 }, { 20, 0 } },
476 true,
477 },
478 {
479 "In segment",
480 { { 0, 0 }, { 10, 0 } },
481 { { 4, 0 }, { 7, 0 } },
482 true,
483 },
484 {
485 "At side, parallel",
486 { { 0, 0 }, { 10, 0 } },
487 { { 4, 1 }, { 7, 1 } },
488 false,
489 },
490 {
491 "crossing",
492 { { 0, 0 }, { 10, 0 } },
493 { { 5, -5 }, { 5, 5 } },
494 false,
495 },
496};
497// clang-format on
498
499
500BOOST_AUTO_TEST_CASE( SegSegCollinear )
501{
502 for( const auto& c : seg_vec_collinear_cases )
503 {
504 BOOST_TEST_CONTEXT( c.m_case_name )
505 {
506 BOOST_CHECK_PREDICATE( SegCollinearCorrect,
507 ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
508 }
509 }
510}
511
512
513// clang-format off
517static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_parallel_cases = {
518 {
519 "coincident",
520 { { 0, 0 }, { 10, 0 } },
521 { { 0, 0 }, { 10, 0 } },
522 true,
523 },
524 {
525 "end-to-end",
526 { { 0, 0 }, { 10, 0 } },
527 { { 10, 0 }, { 20, 0 } },
528 true,
529 },
530 {
531 "In segment",
532 { { 0, 0 }, { 10, 0 } },
533 { { 4, 0 }, { 7, 0 } },
534 true,
535 },
536 {
537 "At side, parallel",
538 { { 0, 0 }, { 10, 0 } },
539 { { 4, 1 }, { 7, 1 } },
540 true,
541 },
542 {
543 "crossing",
544 { { 0, 0 }, { 10, 0 } },
545 { { 5, -5 }, { 5, 5 } },
546 false,
547 },
548};
549// clang-format on
550
551
552BOOST_AUTO_TEST_CASE( SegSegParallel )
553{
554 for( const auto& c : seg_vec_parallel_cases )
555 {
556 BOOST_TEST_CONTEXT( c.m_case_name )
557 {
558 BOOST_CHECK_PREDICATE( SegParallelCorrect,
559 ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
560 }
561 }
562}
563
564
565// clang-format off
569static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_perpendicular_cases = {
570 {
571 "coincident",
572 { { 0, 0 }, { 10, 0 } },
573 { { 0, 0 }, { 10, 0 } },
574 false,
575 },
576 {
577 "end-to-end",
578 { { 0, 0 }, { 10, 0 } },
579 { { 10, 0 }, { 20, 0 } },
580 false,
581 },
582 {
583 "In segment",
584 { { 0, 0 }, { 10, 0 } },
585 { { 4, 0 }, { 7, 0 } },
586 false,
587 },
588 {
589 "At side, parallel",
590 { { 0, 0 }, { 10, 0 } },
591 { { 4, 1 }, { 7, 1 } },
592 false,
593 },
594 {
595 "crossing 45 deg",
596 { { 0, 0 }, { 10, 0 } },
597 { { 0, 0 }, { 5, 5 } },
598 false,
599 },
600 {
601 "very nearly perpendicular",
602 { { 0, 0 }, { 10, 0 } },
603 { { 0, 0 }, { 1, 10 } },
604 true, //allow error margin of 1 IU
605 },
606 {
607 "not really perpendicular",
608 { { 0, 0 }, { 10, 0 } },
609 { { 0, 0 }, { 3, 10 } },
610 false,
611 },
612 {
613 "perpendicular",
614 { { 0, 0 }, { 10, 0 } },
615 { { 0, 0 }, { 0, 10 } },
616 true,
617 },
618 {
619 "perpendicular not intersecting",
620 { { 0, 0 }, { 10, 0 } },
621 { { 15, 5 }, { 15, 10 } },
622 true,
623 },
624};
625// clang-format on
626
627
628BOOST_AUTO_TEST_CASE( SegSegPerpendicular )
629{
630 for( const auto& c : seg_vec_perpendicular_cases )
631 {
632 BOOST_TEST_CONTEXT( c.m_case_name )
633 {
634 BOOST_CHECK_PREDICATE( SegPerpendicularCorrect,
635 ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
636 }
637 }
638}
639
640
645{
646 std::string m_case_name;
649};
650
651
652// clang-format off
656static const std::vector<SEG_VEC_CASE> segment_and_point_cases = {
657 {
658 "Horizontal: point on edge of seg",
659 { { 0, 0 }, { 10, 0 } },
660 { 0, 0 },
661 },
662 {
663 "Horizontal: point in middle of seg",
664 { { 0, 0 }, { 10, 0 } },
665 { 5, 0 },
666 },
667 {
668 "Horizontal: point outside seg",
669 { { 0, 0 }, { 10, 0 } },
670 { 20, 20 },
671 },
672 {
673 "Vertical: point on edge of seg",
674 { { 0, 0 }, { 0, 10 } },
675 { 0, 0 },
676 },
677 {
678 "Vertical: point in middle of seg",
679 { { 0, 0 }, { 0, 10 } },
680 { 0, 5 },
681 },
682 {
683 "Vertical: point outside seg",
684 { { 0, 0 }, { 0, 10 } },
685 { 20, 20 },
686 },
687};
688// clang-format on
689
690
691BOOST_AUTO_TEST_CASE( SegCreateParallel )
692{
693 for( const auto& c : segment_and_point_cases )
694 {
695 BOOST_TEST_CONTEXT( c.m_case_name )
696 {
697 SEG perpendicular = c.m_seg.ParallelSeg( c.m_vec );
698
699 BOOST_CHECK_PREDICATE( SegParallelCorrect, ( perpendicular )( c.m_seg )( true ) );
700 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( perpendicular )( c.m_vec )( 0 ) );
701 }
702 }
703}
704
705BOOST_AUTO_TEST_CASE( SegCreatePerpendicular )
706{
707 for( const auto& c : segment_and_point_cases )
708 {
709 BOOST_TEST_CONTEXT( c.m_case_name )
710 {
711 SEG perpendicular = c.m_seg.PerpendicularSeg( c.m_vec );
712
713 BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, ( perpendicular )( c.m_seg )( true ) );
714 BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( perpendicular )( c.m_vec )( 0 ) );
715 }
716 }
717}
718
719BOOST_AUTO_TEST_CASE( LineDistance )
720{
721 SEG seg( { 0, 0 }, { 10, 0 } );
722
723 BOOST_TEST( seg.LineDistance( { 5, 0 } ) == 0 );
724 BOOST_TEST( seg.LineDistance( { 5, 8 } ) == 8 );
725}
726
727BOOST_AUTO_TEST_CASE( LineDistanceSided )
728{
729 SEG seg( { 0, 0 }, { 10, 0 } );
730
731 BOOST_TEST( seg.LineDistance( { 5, 8 }, true ) == 8 );
732 BOOST_TEST( seg.LineDistance( { 5, -8 }, true ) == -8 );
733}
734
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
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.
std::string m_case_name
VECTOR2I m_vec
BOOST_TEST(box.ClosestPointTo(VECTOR2D(0, 0))==VECTOR2D(1, 2))
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
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.
static const std::vector< SEG_SEG_DISTANCE_CASE > seg_seg_dist_cases
bool SegDistanceCorrect(const SEG &aSegA, const SEG &aSegB, int aExp)
Predicate to check expected distance between two segments.
static const std::vector< SEG_VECTOR_DISTANCE_CASE > seg_vec_dist_cases
bool SegParallelCorrect(const SEG &aSegA, const SEG &aSegB, bool aExp)
Predicate to check expected parallelism between two segments.
bool SegCollideCorrect(const SEG &aSegA, const SEG &aSegB, int aClearance, bool aExp)
Predicate to check expected collision between two segments.
static const std::vector< SEG_SEG_COLLIDE_CASE > seg_seg_coll_cases
bool SegVecDistanceCorrect(const SEG &aSeg, const VECTOR2I &aVec, int aExp)
Predicate to check expected distance between a segment and a point.
bool SegPerpendicularCorrect(const SEG &aSegA, const SEG &aSegB, bool aExp)
Predicate to check expected perpendicularity between two segments.
static const std::vector< SEG_SEG_BOOLEAN_CASE > seg_vec_parallel_cases
Test cases for parallelism.
bool SegCollinearCorrect(const SEG &aSegA, const SEG &aSegB, bool aExp)
Predicate to check expected collision between two segments.
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691