KiCad PCB EDA Suite
Loading...
Searching...
No Matches
shape_collisions.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) 2013 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <algorithm>
23#include <cmath>
24#include <limits>
25
26#include <geometry/seg.h> // for SEG
28#include <geometry/shape.h>
29#include <geometry/shape_arc.h>
32#include <geometry/shape_rect.h>
37#include <math/vector2d.h>
38
40
41
42static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance,
43 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
44{
45 ecoord min_dist = aClearance + aA.GetRadius() + aB.GetRadius();
46 ecoord min_dist_sq = min_dist * min_dist;
47
48 const VECTOR2I delta = aB.GetCenter() - aA.GetCenter();
49 ecoord dist_sq = delta.SquaredEuclideanNorm();
50
51 if( dist_sq == 0 || dist_sq < min_dist_sq )
52 {
53 if( aActual )
54 *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() - aB.GetRadius() );
55
56 if( aLocation )
57 *aLocation = ( aA.GetCenter() + aB.GetCenter() ) / 2;
58
59 if( aMTV )
60 *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error
61
62 return true;
63 }
64
65 return false;
66}
67
68
69static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance,
70 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
71{
72 if( aA.GetRadius() > 0 )
73 {
74 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_RECT to SHAPE_CIRCLE collisions when rect "
75 "has rounded corners" ) );
76
77 const SHAPE_LINE_CHAIN& outline = aA.Outline();
78 return outline.SHAPE::Collide( &aB, aClearance, aActual, aLocation );
79 }
80
81 const VECTOR2I c = aB.GetCenter();
82 const VECTOR2I p0 = aA.GetPosition();
83 const VECTOR2I size = aA.GetSize();
84 const int r = aB.GetRadius();
85 const int min_dist = aClearance + r;
86 const ecoord min_dist_sq = SEG::Square( min_dist );
87
88 const VECTOR2I vts[] =
89 {
90 VECTOR2I( p0.x, p0.y ),
91 VECTOR2I( p0.x, p0.y + size.y ),
92 VECTOR2I( p0.x + size.x, p0.y + size.y ),
93 VECTOR2I( p0.x + size.x, p0.y ),
94 VECTOR2I( p0.x, p0.y )
95 };
96
97 ecoord nearest_side_dist_sq = VECTOR2I::ECOORD_MAX;
98 VECTOR2I nearest;
99
100 bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x )
101 && c.y >= p0.y && c.y <= ( p0.y + size.y );
102
103 // If we're not looking for MTV or actual, short-circuit once we find a hard collision
104 if( inside && !aActual && !aLocation && !aMTV )
105 return true;
106
107 for( int i = 0; i < 4; i++ )
108 {
109 const SEG side( vts[i], vts[ i + 1] );
110
111 VECTOR2I pn = side.NearestPoint( c );
112 ecoord side_dist_sq = ( pn - c ).SquaredEuclideanNorm();
113
114 if( side_dist_sq < nearest_side_dist_sq )
115 {
116 nearest = pn;
117 nearest_side_dist_sq = side_dist_sq;
118
119 if( aMTV )
120 continue;
121
122 if( nearest_side_dist_sq == 0 )
123 break;
124
125 // If we're not looking for aActual then any collision will do
126 if( nearest_side_dist_sq < min_dist_sq && !aActual )
127 break;
128 }
129 }
130
131 if( inside || nearest_side_dist_sq == 0 || nearest_side_dist_sq < min_dist_sq )
132 {
133 if( aLocation )
134 *aLocation = nearest;
135
136 if( aActual )
137 *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r );
138
139 if( aMTV )
140 {
141 VECTOR2I delta = c - nearest;
142
143 if( inside )
144 *aMTV = -delta.Resize( abs( min_dist + 1 + sqrt( nearest_side_dist_sq ) ) + 1 );
145 else
146 *aMTV = delta.Resize( abs( min_dist + 1 - sqrt( nearest_side_dist_sq ) ) + 1 );
147 }
148
149 return true;
150 }
151
152 return false;
153}
154
155
156static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aClearance )
157{
158 VECTOR2I f( 0, 0 );
159
160 const VECTOR2I c = aA.GetCenter();
161 const VECTOR2I nearest = aB.NearestPoint( c );
162
163 const int r = aA.GetRadius();
164
165 int dist = ( nearest - c ).EuclideanNorm();
166 int min_dist = aClearance + r;
167
168 if( dist < min_dist )
169 {
170 for( int corr = 0; corr < 5; corr++ )
171 {
172 f = ( aA.GetCenter() - nearest ).Resize( min_dist - dist + corr );
173
174 if( aB.Distance( c + f ) >= min_dist )
175 break;
176 }
177 }
178
179 return f;
180}
181
182
183template <typename SegmentSource, typename Containment>
184static inline bool collideEllipseVsSegments( const SHAPE_ELLIPSE& aA, const SegmentSource& aSegSource, int aClearance,
185 int aHalfWidth, Containment aContainment, int* aActual,
186 VECTOR2I* aLocation )
187{
188 const int effClear = aClearance + aHalfWidth;
189
190 bool found = false;
191 int bestActual = std::numeric_limits<int>::max();
192 VECTOR2I bestLocation;
193
194 const size_t nSegs = aSegSource.GetSegmentCount();
195
196 for( size_t i = 0; i < nSegs; ++i )
197 {
198 const SEG seg = aSegSource.GetSegment( static_cast<int>( i ) );
199 int localActual = 0;
200 VECTOR2I localLoc;
201
202 if( aA.Collide( seg, effClear, aActual ? &localActual : nullptr, aLocation ? &localLoc : nullptr ) )
203 {
204 if( !aActual && !aLocation )
205 return true;
206
207 if( !found || localActual < bestActual )
208 {
209 bestActual = localActual;
210 bestLocation = localLoc;
211 found = true;
212 }
213 }
214 }
215
216 if( found )
217 {
218 if( aActual )
219 *aActual = std::max( 0, bestActual - aHalfWidth );
220 if( aLocation )
221 *aLocation = bestLocation;
222 return true;
223 }
224
225 if( aContainment() )
226 {
227 if( aActual )
228 *aActual = 0;
229 if( aLocation )
230 *aLocation = aA.GetCenter();
231 return true;
232 }
233
234 return false;
235}
236
237
238static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB,
239 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
240{
241 int closest_dist = std::numeric_limits<int>::max();
242 int closest_mtv_dist = std::numeric_limits<int>::max();
243 VECTOR2I nearest;
244 int closest_mtv_seg = -1;
245
246 if( aB.IsClosed() && aB.PointInside( aA.GetCenter() ) )
247 {
248 nearest = aA.GetCenter();
249 closest_dist = 0;
250
251 if( aMTV )
252 {
253 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
254 {
255 int dist = aB.GetSegment(s).Distance( aA.GetCenter() );
256
257 if( dist < closest_mtv_dist )
258 {
259 closest_mtv_dist = dist;
260 closest_mtv_seg = s;
261 }
262 }
263 }
264
265 }
266 else
267 {
268 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
269 {
270 int collision_dist = 0;
271 VECTOR2I pn;
272
273 if( aA.Collide( aB.GetSegment( s ), aClearance,
274 aActual || aLocation ? &collision_dist : nullptr,
275 aLocation ? &pn : nullptr ) )
276 {
277 if( collision_dist < closest_dist )
278 {
279 nearest = pn;
280 closest_dist = collision_dist;
281 }
282
283 if( closest_dist == 0 )
284 break;
285
286 // If we're not looking for aActual then any collision will do
287 if( !aActual )
288 break;
289 }
290 }
291 }
292
293 if( closest_dist == 0 || closest_dist < aClearance )
294 {
295 if( aLocation )
296 *aLocation = nearest;
297
298 if( aActual )
299 *aActual = closest_dist;
300
301 if( aMTV )
302 {
303 SHAPE_CIRCLE cmoved( aA );
304 VECTOR2I f_total( 0, 0 );
305
306 VECTOR2I f;
307
308 if (closest_mtv_seg >= 0)
309 {
310 SEG cs = aB.GetSegment( closest_mtv_seg );
311 VECTOR2I np = cs.NearestPoint( aA.GetCenter() );
312 f = ( np - aA.GetCenter() ) + ( np - aA.GetCenter() ).Resize( aA.GetRadius() );
313 }
314
315 cmoved.SetCenter( cmoved.GetCenter() + f );
316 f_total += f;
317
318 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
319 {
320 f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance );
321 cmoved.SetCenter( cmoved.GetCenter() + f );
322 f_total += f;
323 }
324
325 *aMTV = f_total;
326 }
327
328 return true;
329 }
330
331 return false;
332}
333
334
335static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, int aClearance,
336 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
337{
338 if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual, aLocation ) )
339 {
340 if( aMTV )
341 *aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2);
342
343 if( aActual )
344 *aActual = std::max( 0, *aActual - aSeg.GetWidth() / 2 );
345
346 return true;
347 }
348
349 return false;
350}
351
352
353static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CHAIN_BASE& aB,
354 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
355{
356 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
357 aA.TypeName(),
358 aB.TypeName() ) );
359
360 int closest_dist = std::numeric_limits<int>::max();
361 VECTOR2I nearest;
362
363 if( aB.IsClosed() && aA.GetPointCount() > 0 && aB.PointInside( aA.GetPoint( 0 ) ) )
364 {
365 closest_dist = 0;
366 nearest = aA.GetPoint( 0 );
367 }
368 else if( aA.IsClosed() && aB.GetPointCount() > 0 && aA.PointInside( aB.GetPoint( 0 ) ) )
369 {
370 closest_dist = 0;
371 nearest = aB.GetPoint( 0 );
372 }
373 else
374 {
375 std::vector<SEG> a_segs;
376 std::vector<SEG> b_segs;
377
378 for( size_t ii = 0; ii < aA.GetSegmentCount(); ii++ )
379 {
380 if( aA.Type() != SH_LINE_CHAIN
381 || !static_cast<const SHAPE_LINE_CHAIN*>( &aA )->IsArcSegment( ii ) )
382 {
383 a_segs.push_back( aA.GetSegment( ii ) );
384 }
385 }
386
387 for( size_t ii = 0; ii < aB.GetSegmentCount(); ii++ )
388 {
389 if( aB.Type() != SH_LINE_CHAIN
390 || !static_cast<const SHAPE_LINE_CHAIN*>( &aB )->IsArcSegment( ii ) )
391 {
392 b_segs.push_back( aB.GetSegment( ii ) );
393 }
394 }
395
396 // Index gates below are measured crossovers rather than correctness requirements, and the
397 // dense checks bound index build cost where nearly every pair is a candidate anyway
398 const bool need_output = aActual || aLocation;
399 const int64_t pair_count = static_cast<int64_t>( a_segs.size() ) * b_segs.size();
400 auto dense_candidates = [&]( const std::vector<SEG>& aQueries,
401 const std::vector<SEG>& aIndexed )
402 {
403 int indexed_min_x = std::numeric_limits<int>::max();
404 int indexed_min_y = std::numeric_limits<int>::max();
405 int indexed_max_x = std::numeric_limits<int>::min();
406 int indexed_max_y = std::numeric_limits<int>::min();
407
408 for( const SEG& segment : aIndexed )
409 {
410 indexed_min_x = std::min( { indexed_min_x, segment.A.x, segment.B.x } );
411 indexed_min_y = std::min( { indexed_min_y, segment.A.y, segment.B.y } );
412 indexed_max_x = std::max( { indexed_max_x, segment.A.x, segment.B.x } );
413 indexed_max_y = std::max( { indexed_max_y, segment.A.y, segment.B.y } );
414 }
415
416 const SEG& query = aQueries.front();
417 const int64_t min_x = static_cast<int64_t>( std::min( query.A.x, query.B.x ) ) - aClearance;
418 const int64_t min_y = static_cast<int64_t>( std::min( query.A.y, query.B.y ) ) - aClearance;
419 const int64_t max_x = static_cast<int64_t>( std::max( query.A.x, query.B.x ) ) + aClearance;
420 const int64_t max_y = static_cast<int64_t>( std::max( query.A.y, query.B.y ) ) + aClearance;
421 return min_x <= indexed_min_x && min_y <= indexed_min_y && max_x >= indexed_max_x
422 && max_y >= indexed_max_y;
423 };
424
425 if( !need_output )
426 {
427 bool use_index = aClearance >= 0 && std::min( a_segs.size(), b_segs.size() ) >= 64
428 && pair_count >= 4096;
429
430 if( use_index )
431 {
432 const std::vector<SEG>& queries = b_segs.size() >= a_segs.size() ? a_segs : b_segs;
433 const std::vector<SEG>& indexed = b_segs.size() >= a_segs.size() ? b_segs : a_segs;
434 use_index = !dense_candidates( queries, indexed );
435 }
436
437 if( use_index )
438 {
439 // Bounded probe answers the common early hit without paying for index construction
440 const size_t probe_a_count = std::min<size_t>( a_segs.size(), 4 );
441 const size_t probe_b_count = std::min<size_t>( b_segs.size(), 8 );
442 bool early_collision = false;
443
444 for( size_t i = 0; i < probe_a_count && !early_collision; ++i )
445 {
446 for( size_t j = 0; j < probe_b_count; ++j )
447 {
448 if( a_segs[i].Collide( b_segs[j], aClearance ) )
449 {
450 early_collision = true;
451 break;
452 }
453 }
454 }
455
456 if( early_collision )
457 {
458 return true;
459 }
460 else if( b_segs.size() >= a_segs.size() )
461 {
462 SEGMENT_INDEX index( std::move( b_segs ) );
463
464 for( const SEG& a_seg : a_segs )
465 {
466 bool found = false;
467 auto visitor = [&]( int aIndex )
468 {
469 if( a_seg.Collide( index.Segment( aIndex ), aClearance ) )
470 {
471 found = true;
472 return false;
473 }
474
475 return true;
476 };
477 index.VisitCandidates( a_seg, aClearance, visitor );
478
479 if( found )
480 return true;
481 }
482 }
483 else
484 {
485 SEGMENT_INDEX index( std::move( a_segs ) );
486
487 for( const SEG& b_seg : b_segs )
488 {
489 bool found = false;
490 auto visitor = [&]( int aIndex )
491 {
492 if( index.Segment( aIndex ).Collide( b_seg, aClearance ) )
493 {
494 found = true;
495 return false;
496 }
497
498 return true;
499 };
500 index.VisitCandidates( b_seg, aClearance, visitor );
501
502 if( found )
503 return true;
504 }
505 }
506 }
507 else if( aClearance >= 0 )
508 {
509 for( const SEG& a_seg : a_segs )
510 {
511 for( const SEG& b_seg : b_segs )
512 {
513 if( a_seg.Collide( b_seg, aClearance ) )
514 return true;
515 }
516 }
517 }
518 }
519 else
520 {
521 auto seg_sort = []( const SEG& a, const SEG& b )
522 {
523 return a.A.x < b.A.x || ( a.A.x == b.A.x && a.A.y < b.A.y );
524 };
525
526 std::sort( a_segs.begin(), a_segs.end(), seg_sort );
527 std::sort( b_segs.begin(), b_segs.end(), seg_sort );
528
529 const bool use_index = aClearance >= 0 && a_segs.size() >= 32 && b_segs.size() >= 64
530 && pair_count >= 4096 && !dense_candidates( a_segs, b_segs );
531
532 if( use_index )
533 {
534 SEGMENT_INDEX index( std::move( b_segs ) );
535 std::vector<int> candidates;
536 bool scan_direct = false;
537
538 enum class COLLISION_RESULT
539 {
540 NONE,
541 FOUND,
542 EXACT
543 };
544
545 auto collide_pair = [&]( const SEG& aASeg, const SEG& aBSeg )
546 {
547 int dist = 0;
548
549 if( !aASeg.Collide( aBSeg, aClearance, &dist ) )
550 return COLLISION_RESULT::NONE;
551
552 if( dist < closest_dist )
553 {
554 nearest = aASeg.NearestPoint( aBSeg );
555 closest_dist = dist;
556 }
557
558 return closest_dist == 0 ? COLLISION_RESULT::EXACT : COLLISION_RESULT::FOUND;
559 };
560
561 for( const SEG& a_seg : a_segs )
562 {
563 if( !scan_direct )
564 {
565 candidates.clear();
566 auto visitor = [&]( int aIndex )
567 {
568 candidates.push_back( aIndex );
569 return true;
570 };
571 index.VisitCandidates( a_seg, aClearance, visitor );
572 // Candidates pruning less than half the index cost more than a direct scan
573 scan_direct = candidates.size() * 2 >= index.size();
574 }
575
576 // Candidate IDs restore the legacy order that determines tied witness locations
577 if( !scan_direct )
578 std::sort( candidates.begin(), candidates.end() );
579
580 const size_t candidate_count = scan_direct ? index.size() : candidates.size();
581
582 for( size_t i = 0; i < candidate_count; ++i )
583 {
584 const int b_index = scan_direct ? static_cast<int>( i ) : candidates[i];
585 const COLLISION_RESULT result = collide_pair( a_seg, index.Segment( b_index ) );
586
587 if( result == COLLISION_RESULT::EXACT )
588 {
589 if( aLocation )
590 *aLocation = nearest;
591
592 if( aActual )
593 *aActual = closest_dist;
594
595 return true;
596 }
597
598 if( result == COLLISION_RESULT::FOUND && !aActual )
599 break;
600 }
601 }
602 }
603 else if( aClearance >= 0 )
604 {
605 for( const SEG& a_seg : a_segs )
606 {
607 for( const SEG& b_seg : b_segs )
608 {
609 int dist = 0;
610
611 if( a_seg.Collide( b_seg, aClearance, &dist ) )
612 {
613 if( dist < closest_dist )
614 {
615 nearest = a_seg.NearestPoint( b_seg );
616 closest_dist = dist;
617 }
618
619 if( closest_dist == 0 )
620 break;
621
622 if( !aActual )
623 break;
624 }
625 }
626 }
627 }
628 }
629 }
630
631 if( (!aActual && !aLocation ) || closest_dist > 0 )
632 {
633 std::vector<const SHAPE_LINE_CHAIN*> chains = {
634 dynamic_cast<const SHAPE_LINE_CHAIN*>( &aA ),
635 dynamic_cast<const SHAPE_LINE_CHAIN*>( &aB )
636 };
637
638 std::vector<const SHAPE*> shapes = { &aA, &aB };
639
640 for( int ii = 0; ii < 2; ii++ )
641 {
642 const SHAPE_LINE_CHAIN* chain = chains[ii];
643 const SHAPE* other = shapes[( ii + 1 ) % 2];
644
645 if( !chain )
646 continue;
647
648 for( size_t jj = 0; jj < chain->ArcCount(); jj++ )
649 {
650 const SHAPE_ARC& arc = chain->Arc( jj );
651
652 if( arc.Collide( other, aClearance, aActual, aLocation ) )
653 return true;
654 }
655 }
656 }
657
658 if( closest_dist == 0 || closest_dist < aClearance )
659 {
660 if( aLocation )
661 *aLocation = nearest;
662
663 if( aActual )
664 *aActual = closest_dist;
665
666 return true;
667 }
668
669 return false;
670}
671
672
673static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
674 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
675{
676 if( aA.GetRadius() > 0 )
677 return Collide( aA.Outline(), aB, aClearance, aActual, aLocation, aMTV );
678
679 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
680 aA.TypeName(),
681 aB.TypeName() ) );
682
683 int closest_dist = std::numeric_limits<int>::max();
684 VECTOR2I nearest;
685
686 if( aB.IsClosed() && aB.PointInside( aA.Centre() ) )
687 {
688 nearest = aA.Centre();
689 closest_dist = 0;
690 }
691 else
692 {
693 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
694 {
695 int collision_dist = 0;
696 VECTOR2I pn;
697
698 if( aA.Collide( aB.GetSegment( s ), aClearance,
699 aActual || aLocation ? &collision_dist : nullptr,
700 aLocation ? &pn : nullptr ) )
701 {
702 if( collision_dist < closest_dist )
703 {
704 nearest = pn;
705 closest_dist = collision_dist;
706 }
707
708 if( closest_dist == 0 )
709 break;
710
711 // If we're not looking for aActual then any collision will do
712 if( !aActual )
713 break;
714 }
715 }
716 }
717
718 if( closest_dist == 0 || closest_dist < aClearance )
719 {
720 if( aLocation )
721 *aLocation = nearest;
722
723 if( aActual )
724 *aActual = closest_dist;
725
726 return true;
727 }
728
729 return false;
730}
731
732
733static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, int aClearance,
734 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
735{
736 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
737 aA.TypeName(),
738 aB.TypeName() ) );
739
740 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
741
742 if( rv && aActual )
743 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
744
745 return rv;
746}
747
748
749static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT& aB,
750 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
751{
752 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
753 aA.TypeName(),
754 aB.TypeName() ) );
755
756 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
757
758 if( rv && aActual )
759 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
760
761 return rv;
762}
763
764
765static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aB, int aClearance,
766 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
767{
768 if( aA.GetRadius() > 0 )
769 return Collide( aA.Outline(), aB, aClearance, aActual, aLocation, aMTV );
770
771 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
772 aA.TypeName(),
773 aB.TypeName() ) );
774
775 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
776
777 if( rv && aActual )
778 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
779
780 return rv;
781}
782
783
784static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
785 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
786{
787 if( aClearance || aActual || aLocation || aMTV || aA.GetRadius() > 0 || aB.GetRadius() > 0 )
788 {
789 return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
790 }
791 else
792 {
793 BOX2I bboxa = aA.BBox();
794 BOX2I bboxb = aB.BBox();
795
796 return bboxa.Intersects( bboxb );
797 }
798}
799
800
801static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aClearance,
802 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
803{
804 if( aA.IsEffectiveLine() )
805 {
806 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
807 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
808
809 if( retval && aMTV )
810 *aMTV = - *aMTV;
811
812 return retval;
813 }
814
815 VECTOR2I ptA, ptB;
816 int64_t dist_sq = std::numeric_limits<int64_t>::max();
817 aA.NearestPoints( aB, ptA, ptB, dist_sq );
818
819 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
820 {
821 if( aLocation )
822 *aLocation = ( ptA + ptB ) / 2;
823
824 if( aActual )
825 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
826
827 if( aMTV )
828 {
829 const VECTOR2I delta = ptB - ptA;
830 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
831 }
832
833 return true;
834 }
835
836 return false;
837}
838
839
840static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int aClearance,
841 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
842{
843 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
844 aA.TypeName(),
845 aB.TypeName() ) );
846
847 int closest_dist = std::numeric_limits<int>::max();
848 VECTOR2I nearest;
849
850 if( aB.IsClosed() && aB.PointInside( aA.GetP0() ) )
851 {
852 closest_dist = 0;
853 nearest = aA.GetP0();
854 }
855 else
856 {
857 int collision_dist = 0;
858 VECTOR2I pn;
859
860 for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
861 {
862 // ignore arcs - we will collide these separately
863 if( aB.IsArcSegment( i ) )
864 continue;
865
866 if( aA.Collide( aB.GetSegment( i ), aClearance,
867 aActual || aLocation ? &collision_dist : nullptr,
868 aLocation ? &pn : nullptr ) )
869 {
870 if( collision_dist < closest_dist )
871 {
872 nearest = pn;
873 closest_dist = collision_dist;
874 }
875
876 if( closest_dist == 0 )
877 break;
878
879 // If we're not looking for aActual then any collision will do
880 if( !aActual )
881 break;
882 }
883 }
884
885 for( size_t i = 0; i < aB.ArcCount(); i++ )
886 {
887 const SHAPE_ARC& arc = aB.Arc( i );
888
889 // The arcs in the chain should have zero width
890 wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
891
892 if( aA.Collide( &arc, aClearance, aActual || aLocation ? &collision_dist : nullptr,
893 aLocation ? &pn : nullptr ) )
894 {
895 if( collision_dist < closest_dist )
896 {
897 nearest = pn;
898 closest_dist = collision_dist;
899 }
900
901 if( closest_dist == 0 )
902 break;
903
904 if( !aActual )
905 break;
906 }
907 }
908 }
909
910 if( closest_dist == 0 || closest_dist < aClearance )
911 {
912 if( aLocation )
913 *aLocation = nearest;
914
915 if( aActual )
916 *aActual = closest_dist;
917
918 return true;
919 }
920
921 return false;
922}
923
924
925static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_RECT& aB, int aClearance,
926 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
927{
928 if( aB.GetRadius() > 0 )
929 return Collide( aA, aB.Outline(), aClearance, aActual, aLocation, aMTV );
930
931 if( aA.IsEffectiveLine() )
932 {
933 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
934 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
935
936 if( retval && aMTV )
937 *aMTV = - *aMTV;
938
939 return retval;
940 }
941
942 VECTOR2I ptA, ptB;
943 int64_t dist_sq = std::numeric_limits<int64_t>::max();
944 aA.NearestPoints( aB, ptA, ptB, dist_sq );
945
946 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
947 {
948 if( aLocation )
949 *aLocation = ( ptA + ptB ) / 2;
950
951 if( aActual )
952 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
953
954 if( aMTV )
955 {
956 const VECTOR2I delta = ptB - ptA;
957 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
958 }
959
960 return true;
961 }
962
963 return false;
964}
965
966
967static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aClearance,
968 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
969{
970 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
971 aA.TypeName(),
972 aB.TypeName() ) );
973
974 // If the arc radius is too large, it is effectively a line segment
975 if( aA.IsEffectiveLine() )
976 {
977 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
978 return Collide( tmp, aB, aClearance, aActual, aLocation, aMTV );
979 }
980
981 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
982
983 if( rv && aActual )
984 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
985
986 return rv;
987}
988
989
990static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
991 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
992{
993 // If the arc radius is too large, it is effectively a line segment
994 if( aA.IsEffectiveLine() )
995 {
996 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
997 return Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
998 }
999
1000 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
1001 aA.TypeName(),
1002 aB.TypeName() ) );
1003
1004 int closest_dist = std::numeric_limits<int>::max();
1005 VECTOR2I nearest;
1006
1007 if( aB.IsClosed() && aB.PointInside( aA.GetP0() ) )
1008 {
1009 closest_dist = 0;
1010 nearest = aA.GetP0();
1011 }
1012 else
1013 {
1014 const BOX2I arc_bbox = aA.BBox( aClearance );
1015 const bool near_full_circle = ( aA.GetP0() - aA.GetP1() ).SquaredEuclideanNorm() < SEG::Square( aClearance )
1016 && aA.GetCentralAngle().AsDegrees() > 180.0;
1017
1018 for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
1019 {
1020 const SEG segment = aB.GetSegment( i );
1021
1022 // SHAPE_ARC::Collide( SEG ) uses the full circle for near-full arcs
1023 if( !near_full_circle
1024 && ( std::max( segment.A.x, segment.B.x ) < arc_bbox.GetLeft()
1025 || std::min( segment.A.x, segment.B.x ) > arc_bbox.GetRight()
1026 || std::max( segment.A.y, segment.B.y ) < arc_bbox.GetTop()
1027 || std::min( segment.A.y, segment.B.y ) > arc_bbox.GetBottom() ) )
1028 {
1029 continue;
1030 }
1031
1032 int collision_dist = 0;
1033 VECTOR2I pn;
1034
1035 if( aA.Collide( segment, aClearance,
1036 aActual || aLocation ? &collision_dist : nullptr,
1037 aLocation ? &pn : nullptr ) )
1038 {
1039 if( collision_dist < closest_dist )
1040 {
1041 nearest = pn;
1042 closest_dist = collision_dist;
1043 }
1044
1045 if( closest_dist == 0 )
1046 break;
1047
1048 // If we're not looking for aActual then any collision will do
1049 if( !aActual )
1050 break;
1051 }
1052 }
1053 }
1054
1055 if( closest_dist == 0 || closest_dist < aClearance )
1056 {
1057 if( aLocation )
1058 *aLocation = nearest;
1059
1060 if( aActual )
1061 *aActual = closest_dist;
1062
1063 return true;
1064 }
1065
1066 return false;
1067}
1068
1069
1070static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_ARC& aB, int aClearance,
1071 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
1072{
1073 if( aA.IsEffectiveLine() )
1074 {
1075 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
1076 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
1077
1078 if( retval && aMTV )
1079 *aMTV = - *aMTV;
1080
1081 return retval;
1082 }
1083
1084 if( aB.IsEffectiveLine() )
1085 {
1086 SHAPE_SEGMENT tmp( aB.GetP0(), aB.GetP1(), aB.GetWidth() );
1087 return Collide( aA, tmp, aClearance, aActual, aLocation, aMTV );
1088 }
1089
1090 VECTOR2I ptA, ptB;
1091 int64_t dist_sq = std::numeric_limits<int64_t>::max();
1092 aA.NearestPoints( aB, ptA, ptB, dist_sq );
1093
1094 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
1095 {
1096 if( aLocation )
1097 *aLocation = ( ptA + ptB ) / 2;
1098
1099 if( aActual )
1100 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
1101
1102 if( aMTV )
1103 {
1104 const VECTOR2I delta = ptB - ptA;
1105 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
1106 }
1107
1108 return true;
1109 }
1110
1111 return false;
1112}
1113
1114
1115template<class T_a, class T_b>
1116inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
1117 VECTOR2I* aLocation, VECTOR2I* aMTV )
1118
1119{
1120 return Collide( *static_cast<const T_a*>( aA ), *static_cast<const T_b*>( aB ),
1121 aClearance, aActual, aLocation, aMTV);
1122}
1123
1124
1125template<class T_a, class T_b>
1126inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
1127 VECTOR2I* aLocation, VECTOR2I* aMTV )
1128{
1129 bool rv = Collide( *static_cast<const T_b*>( aB ), *static_cast<const T_a*>( aA ),
1130 aClearance, aActual, aLocation, aMTV);
1131
1132 if( rv && aMTV)
1133 *aMTV = - *aMTV;
1134
1135 return rv;
1136}
1137
1138
1139static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_SEGMENT& aSeg, int aClearance, int* aActual,
1140 VECTOR2I* aLocation, VECTOR2I* aMTV )
1141{
1142 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1143
1144 const int halfWidth = aSeg.GetWidth() / 2;
1145 const int effClear = aClearance + halfWidth;
1146
1147 int localActual = 0;
1148 VECTOR2I localLoc;
1149
1150 if( aA.Collide( aSeg.GetSeg(), effClear, aActual ? &localActual : nullptr, aLocation ? &localLoc : nullptr ) )
1151 {
1152 if( aActual )
1153 *aActual = std::max( 0, localActual - halfWidth );
1154 if( aLocation )
1155 *aLocation = localLoc;
1156 return true;
1157 }
1158
1159 return false;
1160}
1161
1162
1163static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_CIRCLE& aB, int aClearance, int* aActual,
1164 VECTOR2I* aLocation, VECTOR2I* aMTV )
1165{
1166 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1167
1168 const VECTOR2I c = aB.GetCenter();
1169 const int r = aB.GetRadius();
1170 const int effClr = aClearance + r;
1171 const SEG::ecoord dSq = aA.SquaredDistance( c, false );
1172 const SEG::ecoord effSq = static_cast<SEG::ecoord>( effClr ) * static_cast<SEG::ecoord>( effClr );
1173
1174 if( dSq == 0 || dSq < effSq )
1175 {
1176 if( aActual )
1177 {
1178 const int d = static_cast<int>( std::round( std::sqrt( static_cast<double>( dSq ) ) ) );
1179 *aActual = std::max( 0, d - r );
1180 }
1181 if( aLocation )
1182 *aLocation = c;
1183 return true;
1184 }
1185
1186 return false;
1187}
1188
1189
1190static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_RECT& aB, int aClearance, int* aActual,
1191 VECTOR2I* aLocation, VECTOR2I* aMTV )
1192{
1193 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1195 aA, aB.Outline(), aClearance, /*halfWidth*/ 0,
1196 [&]
1197 {
1198 return aB.BBox().Contains( aA.GetCenter() );
1199 },
1200 aActual, aLocation );
1201}
1202
1203
1204static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, int* aActual,
1205 VECTOR2I* aLocation, VECTOR2I* aMTV )
1206{
1207 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1209 aA, aB, aClearance, /*halfWidth*/ 0,
1210 [&]
1211 {
1212 return aB.IsClosed() && aB.GetSegmentCount() > 0 && aB.PointInside( aA.GetCenter() );
1213 },
1214 aActual, aLocation );
1215}
1216
1217
1218static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_ARC& aB, int aClearance, int* aActual,
1219 VECTOR2I* aLocation, VECTOR2I* aMTV )
1220{
1221 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1222
1223 const int halfWidth = aB.GetWidth() / 2;
1224 const int effClear = aClearance + halfWidth;
1225 const int tessError = std::max( 1, effClear / 4 );
1226 const SHAPE_LINE_CHAIN chain = aB.ConvertToPolyline( tessError );
1227
1229 aA, chain, aClearance, halfWidth,
1230 []
1231 {
1232 return false;
1233 },
1234 aActual, aLocation );
1235}
1236
1237
1238static inline bool Collide( const SHAPE_ELLIPSE& aA, const SHAPE_ELLIPSE& aB, int aClearance, int* aActual,
1239 VECTOR2I* aLocation, VECTOR2I* aMTV )
1240{
1241 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_ELLIPSE collisions" ) );
1242
1243 if( !aA.IsArc() && aA.PointInside( aB.GetCenter() ) )
1244 {
1245 if( aActual )
1246 *aActual = 0;
1247 if( aLocation )
1248 *aLocation = aB.GetCenter();
1249 return true;
1250 }
1251 if( !aB.IsArc() && aB.PointInside( aA.GetCenter() ) )
1252 {
1253 if( aActual )
1254 *aActual = 0;
1255 if( aLocation )
1256 *aLocation = aA.GetCenter();
1257 return true;
1258 }
1259
1260 const int tessError = std::max( 1, aClearance / 4 );
1261 const SHAPE_LINE_CHAIN chainB = aB.ConvertToPolyline( tessError );
1262
1263 return Collide( aA, chainB, aClearance, aActual, aLocation, aMTV );
1264}
1265
1266
1267static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
1268 VECTOR2I* aLocation, VECTOR2I* aMTV )
1269{
1270 if( aA->Type() == SH_POLY_SET )
1271 {
1272 const SHAPE_POLY_SET* polySetA = static_cast<const SHAPE_POLY_SET*>( aA );
1273
1274 wxASSERT( !aMTV );
1275 return polySetA->Collide( aB, aClearance, aActual, aLocation );
1276 }
1277 else if( aB->Type() == SH_POLY_SET )
1278 {
1279 const SHAPE_POLY_SET* polySetB = static_cast<const SHAPE_POLY_SET*>( aB );
1280
1281 wxASSERT( !aMTV );
1282 return polySetB->Collide( aA, aClearance, aActual, aLocation );
1283 }
1284
1285 switch( aA->Type() )
1286 {
1287 case SH_NULL:
1288 return false;
1289
1290 case SH_RECT:
1291 switch( aB->Type() )
1292 {
1293 case SH_RECT:
1294 return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1295
1296 case SH_CIRCLE:
1297 return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1298
1299 case SH_LINE_CHAIN:
1300 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
1301
1302 case SH_SEGMENT:
1303 return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1304
1305 case SH_SIMPLE:
1307 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1308
1309 case SH_ARC:
1310 return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1311
1312 case SH_ELLIPSE:
1313 return CollCaseReversed<SHAPE_RECT, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1314
1315 case SH_NULL:
1316 return false;
1317
1318 default:
1319 break;
1320 }
1321 break;
1322
1323 case SH_CIRCLE:
1324 switch( aB->Type() )
1325 {
1326 case SH_RECT:
1327 return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1328
1329 case SH_CIRCLE:
1330 return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1331
1332 case SH_LINE_CHAIN:
1333 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
1334
1335 case SH_SEGMENT:
1336 return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1337
1338 case SH_SIMPLE:
1340 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1341
1342 case SH_ARC:
1343 return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1344
1345 case SH_ELLIPSE:
1346 return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1347
1348 case SH_NULL:
1349 return false;
1350
1351 default:
1352 break;
1353 }
1354 break;
1355
1356 case SH_LINE_CHAIN:
1357 switch( aB->Type() )
1358 {
1359 case SH_RECT:
1360 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
1361
1362 case SH_CIRCLE:
1363 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
1364
1365 case SH_LINE_CHAIN:
1366 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
1367
1368 case SH_SEGMENT:
1369 return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1370
1371 case SH_SIMPLE:
1373 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1374
1375 case SH_ARC:
1376 return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1377
1378 case SH_ELLIPSE:
1379 return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1380
1381 case SH_NULL:
1382 return false;
1383
1384 default:
1385 break;
1386 }
1387 break;
1388
1389 case SH_SEGMENT:
1390 switch( aB->Type() )
1391 {
1392 case SH_RECT:
1393 return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
1394
1395 case SH_CIRCLE:
1396 return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1397
1398 case SH_LINE_CHAIN:
1399 return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
1400
1401 case SH_SEGMENT:
1402 return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1403
1404 case SH_SIMPLE:
1406 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
1407
1408 case SH_ARC:
1409 return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1410
1411 case SH_ELLIPSE:
1412 return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1413
1414 case SH_NULL:
1415 return false;
1416
1417 default:
1418 break;
1419 }
1420 break;
1421
1422 case SH_SIMPLE:
1424 switch( aB->Type() )
1425 {
1426 case SH_RECT:
1427 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1428
1429 case SH_CIRCLE:
1430 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1431
1432 case SH_LINE_CHAIN:
1433 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1434
1435 case SH_SEGMENT:
1436 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1437
1438 case SH_SIMPLE:
1440 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1441
1442 case SH_ARC:
1443 return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1444
1445 case SH_ELLIPSE:
1446 return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation,
1447 aMTV );
1448
1449 case SH_NULL:
1450 return false;
1451
1452 default:
1453 break;
1454 }
1455 break;
1456
1457 case SH_ARC:
1458 switch( aB->Type() )
1459 {
1460 case SH_RECT:
1461 return CollCase<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1462
1463 case SH_CIRCLE:
1464 return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1465
1466 case SH_LINE_CHAIN:
1467 return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
1468
1469 case SH_SEGMENT:
1470 return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1471
1472 case SH_SIMPLE:
1474 return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1475
1476 case SH_ARC:
1477 return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1478
1479 case SH_ELLIPSE:
1480 return CollCaseReversed<SHAPE_ARC, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1481
1482 case SH_NULL: return false;
1483
1484 default: break;
1485 }
1486 break;
1487
1488 case SH_ELLIPSE:
1489 switch( aB->Type() )
1490 {
1491 case SH_RECT: return CollCase<SHAPE_ELLIPSE, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1492
1493 case SH_CIRCLE: return CollCase<SHAPE_ELLIPSE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1494
1495 case SH_LINE_CHAIN:
1496 return CollCase<SHAPE_ELLIPSE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1497
1498 case SH_SEGMENT: return CollCase<SHAPE_ELLIPSE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1499
1500 case SH_SIMPLE:
1502 return CollCase<SHAPE_ELLIPSE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1503
1504 case SH_ARC: return CollCase<SHAPE_ELLIPSE, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1505
1506 case SH_ELLIPSE: return CollCase<SHAPE_ELLIPSE, SHAPE_ELLIPSE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1507
1508 case SH_NULL:
1509 return false;
1510
1511 default:
1512 break;
1513 }
1514 break;
1515
1516 default:
1517 break;
1518 }
1519
1520 wxFAIL_MSG( wxString::Format( wxT( "Unsupported collision: %s with %s" ),
1521 SHAPE_TYPE_asString( aA->Type() ),
1522 SHAPE_TYPE_asString( aB->Type() ) ) );
1523
1524 return false;
1525}
1526
1527static bool collideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
1528 VECTOR2I* aLocation, VECTOR2I* aMTV )
1529{
1530 int currentActual = std::numeric_limits<int>::max();
1531 VECTOR2I currentLocation;
1532 VECTOR2I currentMTV(0, 0);
1533 bool colliding = false;
1534
1535 auto canExit =
1536 [&]()
1537 {
1538 if( !colliding )
1539 return false;
1540
1541 if( aActual && currentActual > 0 )
1542 return false;
1543
1544 if( aMTV )
1545 return false;
1546
1547 return true;
1548 };
1549
1550 auto collideCompoundSubshapes =
1551 [&]( const SHAPE* elemA, const SHAPE* elemB, int clearance ) -> bool
1552 {
1553 int actual = 0;
1555 VECTOR2I mtv;
1556
1557 if( collideSingleShapes( elemA, elemB, clearance,
1558 aActual || aLocation ? &actual : nullptr,
1559 aLocation ? &location : nullptr,
1560 aMTV ? &mtv : nullptr ) )
1561 {
1562 if( actual < currentActual )
1563 {
1564 currentActual = actual;
1565 currentLocation = location;
1566 }
1567
1568 if( aMTV && mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() )
1569 {
1570 currentMTV = mtv;
1571 }
1572
1573 return true;
1574 }
1575
1576 return false;
1577 };
1578
1579 if( aA->Type() == SH_COMPOUND && aB->Type() == SH_COMPOUND )
1580 {
1581 const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
1582 const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
1583
1584 for( const SHAPE* elemA : cmpA->Shapes() )
1585 {
1586 for( const SHAPE* elemB : cmpB->Shapes() )
1587 {
1588 if( collideCompoundSubshapes( elemA, elemB, aClearance ) )
1589 {
1590 colliding = true;
1591
1592 if( canExit() )
1593 break;
1594 }
1595 }
1596
1597 if( canExit() )
1598 break;
1599 }
1600 }
1601 else if( aA->Type() == SH_COMPOUND )
1602 {
1603 const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
1604
1605 for( const SHAPE* elemA : cmpA->Shapes() )
1606 {
1607 if( collideCompoundSubshapes( elemA, aB, aClearance ) )
1608 {
1609 colliding = true;
1610
1611 if( canExit() )
1612 break;
1613 }
1614 }
1615 }
1616 else if( aB->Type() == SH_COMPOUND )
1617 {
1618 const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
1619
1620 for( const SHAPE* elemB : cmpB->Shapes() )
1621 {
1622 if( collideCompoundSubshapes( aA, elemB, aClearance ) )
1623 {
1624 colliding = true;
1625
1626 if( canExit() )
1627 break;
1628 }
1629 }
1630 }
1631 else
1632 {
1633 return collideSingleShapes( aA, aB, aClearance, aActual, aLocation, aMTV );
1634 }
1635
1636 if( colliding )
1637 {
1638 if( aLocation )
1639 *aLocation = currentLocation;
1640
1641 if( aActual )
1642 *aActual = currentActual;
1643
1644 if( aMTV )
1645 *aMTV = currentMTV;
1646 }
1647
1648 return colliding;
1649}
1650
1651
1652bool SHAPE::Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const
1653{
1654 return collideShapes( this, aShape, aClearance, nullptr, nullptr, aMTV );
1655}
1656
1657
1658bool SHAPE::Collide( const SHAPE* aShape, int aClearance, int* aActual, VECTOR2I* aLocation ) const
1659{
1660 return collideShapes( this, aShape, aClearance, aActual, aLocation, nullptr );
1661}
1662
1663
int index
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
constexpr coord_type GetBottom() const
Definition box2.h:219
double AsDegrees() const
Definition eda_angle.h:115
Immutable owning spatial snapshot of straight segments.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
static SEG::ecoord Square(int a)
Definition seg.h:119
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition seg.cpp:497
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
EDA_ANGLE GetCentralAngle() const
Get the "central angle" of the arc - this is the angle at the point of the "pie slice".
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
int GetWidth() const override
Definition shape_arc.h:215
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
bool IsEffectiveLine() const
bool NearestPoints(const SHAPE_ARC &aArc, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this arc and aArc.
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
wxString TypeName() const
Definition shape.h:101
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:96
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
int GetRadius() const
const VECTOR2I GetCenter() const
void SetCenter(const VECTOR2I &aCenter)
const std::vector< SHAPE * > & Shapes() const
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
const VECTOR2I & GetCenter() const
SEG::ecoord SquaredDistance(const VECTOR2I &aP, bool aOutlineOnly=false) const override
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
bool IsArc() const
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
virtual size_t GetPointCount() const =0
virtual size_t GetSegmentCount() const =0
virtual const VECTOR2I GetPoint(int aIndex) const =0
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
virtual bool IsClosed() const =0
virtual const SEG GetSegment(int aIndex) const =0
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
virtual const SEG GetSegment(int aIndex) const override
virtual size_t GetSegmentCount() const override
size_t ArcCount() const
bool IsArcSegment(size_t aSegment) const
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
const SHAPE_LINE_CHAIN Outline() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Definition shape_rect.h:105
const VECTOR2I & GetPosition() const
Definition shape_rect.h:164
const VECTOR2I GetSize() const
Definition shape_rect.h:172
int GetRadius() const
Definition shape_rect.h:196
bool Collide(const SHAPE *aShape, int aClearance, VECTOR2I *aActual) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
Definition shape_rect.h:147
const SEG & GetSeg() const
int GetWidth() const override
bool Collide(const SHAPE *aShape, int aClearance, VECTOR2I *aMTV) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
An abstract shape on 2D plane.
Definition shape.h:124
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:181
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Definition shape.h:242
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
Definition shape.h:134
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:312
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:69
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
@ NONE
Definition eda_fill.h:42
@ SH_POLY_SET
set of polygons (with holes, etc.)
Definition shape.h:48
@ SH_RECT
axis-aligned rectangle
Definition shape.h:43
@ SH_CIRCLE
circle
Definition shape.h:46
@ SH_SIMPLE
simple polygon
Definition shape.h:47
@ SH_ELLIPSE
ellipse or elliptical arc
Definition shape.h:53
@ SH_NULL
empty shape (no shape...),
Definition shape.h:51
@ SH_SEGMENT
line segment
Definition shape.h:44
@ SH_ARC
circular arc
Definition shape.h:50
@ SH_POLY_SET_TRIANGLE
a single triangle belonging to a POLY_SET triangulation
Definition shape.h:52
@ SH_LINE_CHAIN
line chain (polyline)
Definition shape.h:45
@ SH_COMPOUND
compound shape, consisting of multiple simple shapes
Definition shape.h:49
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition shape.h:56
static bool collideSingleShapes(const SHAPE *aA, const SHAPE *aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
static bool Collide(const SHAPE_CIRCLE &aA, const SHAPE_CIRCLE &aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
bool CollCaseReversed(const SHAPE *aA, const SHAPE *aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
static bool collideEllipseVsSegments(const SHAPE_ELLIPSE &aA, const SegmentSource &aSegSource, int aClearance, int aHalfWidth, Containment aContainment, int *aActual, VECTOR2I *aLocation)
static VECTOR2I pushoutForce(const SHAPE_CIRCLE &aA, const SEG &aB, int aClearance)
bool CollCase(const SHAPE *aA, const SHAPE *aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
static bool collideShapes(const SHAPE *aA, const SHAPE *aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
VECTOR2I::extended_type ecoord
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
const SHAPE_LINE_CHAIN chain
int clearance
VECTOR2I location
int actual
wxString result
Test unit parsing edge cases and error handling.
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708