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, 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
26#include <cmath>
27#include <limits>
28
29#include <geometry/seg.h> // for SEG
30#include <geometry/shape.h>
31#include <geometry/shape_arc.h>
34#include <geometry/shape_rect.h>
38#include <math/vector2d.h>
39
41
42
43static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance,
44 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
45{
46 ecoord min_dist = aClearance + aA.GetRadius() + aB.GetRadius();
47 ecoord min_dist_sq = min_dist * min_dist;
48
49 const VECTOR2I delta = aB.GetCenter() - aA.GetCenter();
50 ecoord dist_sq = delta.SquaredEuclideanNorm();
51
52 if( dist_sq == 0 || dist_sq < min_dist_sq )
53 {
54 if( aActual )
55 *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() - aB.GetRadius() );
56
57 if( aLocation )
58 *aLocation = ( aA.GetCenter() + aB.GetCenter() ) / 2;
59
60 if( aMTV )
61 *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error
62
63 return true;
64 }
65
66 return false;
67}
68
69
70static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance,
71 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
72{
73 if( aA.GetRadius() > 0 )
74 {
75 wxASSERT_MSG( !aMTV, wxT( "MTV not implemented for SHAPE_RECT to SHAPE_CIRCLE collisions when rect "
76 "has rounded corners" ) );
77
78 const SHAPE_LINE_CHAIN& outline = aA.Outline();
79 return outline.SHAPE::Collide( &aB, aClearance, aActual, aLocation );
80 }
81
82 const VECTOR2I c = aB.GetCenter();
83 const VECTOR2I p0 = aA.GetPosition();
84 const VECTOR2I size = aA.GetSize();
85 const int r = aB.GetRadius();
86 const int min_dist = aClearance + r;
87 const ecoord min_dist_sq = SEG::Square( min_dist );
88
89 const VECTOR2I vts[] =
90 {
91 VECTOR2I( p0.x, p0.y ),
92 VECTOR2I( p0.x, p0.y + size.y ),
93 VECTOR2I( p0.x + size.x, p0.y + size.y ),
94 VECTOR2I( p0.x + size.x, p0.y ),
95 VECTOR2I( p0.x, p0.y )
96 };
97
98 ecoord nearest_side_dist_sq = VECTOR2I::ECOORD_MAX;
99 VECTOR2I nearest;
100
101 bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x )
102 && c.y >= p0.y && c.y <= ( p0.y + size.y );
103
104 // If we're not looking for MTV or actual, short-circuit once we find a hard collision
105 if( inside && !aActual && !aLocation && !aMTV )
106 return true;
107
108 for( int i = 0; i < 4; i++ )
109 {
110 const SEG side( vts[i], vts[ i + 1] );
111
112 VECTOR2I pn = side.NearestPoint( c );
113 ecoord side_dist_sq = ( pn - c ).SquaredEuclideanNorm();
114
115 if( side_dist_sq < nearest_side_dist_sq )
116 {
117 nearest = pn;
118 nearest_side_dist_sq = side_dist_sq;
119
120 if( aMTV )
121 continue;
122
123 if( nearest_side_dist_sq == 0 )
124 break;
125
126 // If we're not looking for aActual then any collision will do
127 if( nearest_side_dist_sq < min_dist_sq && !aActual )
128 break;
129 }
130 }
131
132 if( inside || nearest_side_dist_sq == 0 || nearest_side_dist_sq < min_dist_sq )
133 {
134 if( aLocation )
135 *aLocation = nearest;
136
137 if( aActual )
138 *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r );
139
140 if( aMTV )
141 {
142 VECTOR2I delta = c - nearest;
143
144 if( inside )
145 *aMTV = -delta.Resize( abs( min_dist + 1 + sqrt( nearest_side_dist_sq ) ) + 1 );
146 else
147 *aMTV = delta.Resize( abs( min_dist + 1 - sqrt( nearest_side_dist_sq ) ) + 1 );
148 }
149
150 return true;
151 }
152
153 return false;
154}
155
156
157static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aClearance )
158{
159 VECTOR2I f( 0, 0 );
160
161 const VECTOR2I c = aA.GetCenter();
162 const VECTOR2I nearest = aB.NearestPoint( c );
163
164 const int r = aA.GetRadius();
165
166 int dist = ( nearest - c ).EuclideanNorm();
167 int min_dist = aClearance + r;
168
169 if( dist < min_dist )
170 {
171 for( int corr = 0; corr < 5; corr++ )
172 {
173 f = ( aA.GetCenter() - nearest ).Resize( min_dist - dist + corr );
174
175 if( aB.Distance( c + f ) >= min_dist )
176 break;
177 }
178 }
179
180 return f;
181}
182
183
184static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB,
185 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
186{
187 int closest_dist = std::numeric_limits<int>::max();
188 int closest_mtv_dist = std::numeric_limits<int>::max();
189 VECTOR2I nearest;
190 int closest_mtv_seg = -1;
191
192 if( aB.IsClosed() && aB.PointInside( aA.GetCenter() ) )
193 {
194 nearest = aA.GetCenter();
195 closest_dist = 0;
196
197 if( aMTV )
198 {
199 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
200 {
201 int dist = aB.GetSegment(s).Distance( aA.GetCenter() );
202
203 if( dist < closest_mtv_dist )
204 {
205 closest_mtv_dist = dist;
206 closest_mtv_seg = s;
207 }
208 }
209 }
210
211 }
212 else
213 {
214 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
215 {
216 int collision_dist = 0;
217 VECTOR2I pn;
218
219 if( aA.Collide( aB.GetSegment( s ), aClearance,
220 aActual || aLocation ? &collision_dist : nullptr,
221 aLocation ? &pn : nullptr ) )
222 {
223 if( collision_dist < closest_dist )
224 {
225 nearest = pn;
226 closest_dist = collision_dist;
227 }
228
229 if( closest_dist == 0 )
230 break;
231
232 // If we're not looking for aActual then any collision will do
233 if( !aActual )
234 break;
235 }
236 }
237 }
238
239 if( closest_dist == 0 || closest_dist < aClearance )
240 {
241 if( aLocation )
242 *aLocation = nearest;
243
244 if( aActual )
245 *aActual = closest_dist;
246
247 if( aMTV )
248 {
249 SHAPE_CIRCLE cmoved( aA );
250 VECTOR2I f_total( 0, 0 );
251
252 VECTOR2I f;
253
254 if (closest_mtv_seg >= 0)
255 {
256 SEG cs = aB.GetSegment( closest_mtv_seg );
257 VECTOR2I np = cs.NearestPoint( aA.GetCenter() );
258 f = ( np - aA.GetCenter() ) + ( np - aA.GetCenter() ).Resize( aA.GetRadius() );
259 }
260
261 cmoved.SetCenter( cmoved.GetCenter() + f );
262 f_total += f;
263
264 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
265 {
266 f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance );
267 cmoved.SetCenter( cmoved.GetCenter() + f );
268 f_total += f;
269 }
270
271 *aMTV = f_total;
272 }
273
274 return true;
275 }
276
277 return false;
278}
279
280
281static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, int aClearance,
282 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
283{
284 if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual, aLocation ) )
285 {
286 if( aMTV )
287 *aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2);
288
289 if( aActual )
290 *aActual = std::max( 0, *aActual - aSeg.GetWidth() / 2 );
291
292 return true;
293 }
294
295 return false;
296}
297
298
299static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CHAIN_BASE& aB,
300 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
301{
302 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
303 aA.TypeName(),
304 aB.TypeName() ) );
305
306 int closest_dist = std::numeric_limits<int>::max();
307 VECTOR2I nearest;
308
309 if( aB.IsClosed() && aA.GetPointCount() > 0 && aB.PointInside( aA.GetPoint( 0 ) ) )
310 {
311 closest_dist = 0;
312 nearest = aA.GetPoint( 0 );
313 }
314 else if( aA.IsClosed() && aB.GetPointCount() > 0 && aA.PointInside( aB.GetPoint( 0 ) ) )
315 {
316 closest_dist = 0;
317 nearest = aB.GetPoint( 0 );
318 }
319 else
320 {
321 std::vector<SEG> a_segs;
322 std::vector<SEG> b_segs;
323
324 for( size_t ii = 0; ii < aA.GetSegmentCount(); ii++ )
325 {
326 if( aA.Type() != SH_LINE_CHAIN
327 || !static_cast<const SHAPE_LINE_CHAIN*>( &aA )->IsArcSegment( ii ) )
328 {
329 a_segs.push_back( aA.GetSegment( ii ) );
330 }
331 }
332
333 for( size_t ii = 0; ii < aB.GetSegmentCount(); ii++ )
334 {
335 if( aB.Type() != SH_LINE_CHAIN
336 || !static_cast<const SHAPE_LINE_CHAIN*>( &aB )->IsArcSegment( ii ) )
337 {
338 b_segs.push_back( aB.GetSegment( ii ) );
339 }
340 }
341
342 auto seg_sort = []( const SEG& a, const SEG& b )
343 {
344 return a.A.x < b.A.x || ( a.A.x == b.A.x && a.A.y < b.A.y );
345 };
346
347 std::sort( a_segs.begin(), a_segs.end(), seg_sort );
348 std::sort( b_segs.begin(), b_segs.end(), seg_sort );
349
350 for( const SEG& a_seg : a_segs )
351 {
352 for( const SEG& b_seg : b_segs )
353 {
354 int dist = 0;
355
356 if( a_seg.Collide( b_seg, aClearance, aActual || aLocation ? &dist : nullptr ) )
357 {
358 if( dist < closest_dist )
359 {
360 nearest = a_seg.NearestPoint( b_seg );
361 closest_dist = dist;
362 }
363
364 if( closest_dist == 0 )
365 break;
366
367 // If we're not looking for aActual then any collision will do
368 if( !aActual )
369 break;
370 }
371 }
372 }
373 }
374
375 if( (!aActual && !aLocation ) || closest_dist > 0 )
376 {
377 std::vector<const SHAPE_LINE_CHAIN*> chains = {
378 dynamic_cast<const SHAPE_LINE_CHAIN*>( &aA ),
379 dynamic_cast<const SHAPE_LINE_CHAIN*>( &aB )
380 };
381
382 std::vector<const SHAPE*> shapes = { &aA, &aB };
383
384 for( int ii = 0; ii < 2; ii++ )
385 {
386 const SHAPE_LINE_CHAIN* chain = chains[ii];
387 const SHAPE* other = shapes[( ii + 1 ) % 2];
388
389 if( !chain )
390 continue;
391
392 for( size_t jj = 0; jj < chain->ArcCount(); jj++ )
393 {
394 const SHAPE_ARC& arc = chain->Arc( jj );
395
396 if( arc.Collide( other, aClearance, aActual, aLocation ) )
397 return true;
398 }
399 }
400 }
401
402 if( closest_dist == 0 || closest_dist < aClearance )
403 {
404 if( aLocation )
405 *aLocation = nearest;
406
407 if( aActual )
408 *aActual = closest_dist;
409
410 return true;
411 }
412
413 return false;
414}
415
416
417static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
418 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
419{
420 if( aA.GetRadius() > 0 )
421 return Collide( aA.Outline(), aB, aClearance, aActual, aLocation, aMTV );
422
423 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
424 aA.TypeName(),
425 aB.TypeName() ) );
426
427 int closest_dist = std::numeric_limits<int>::max();
428 VECTOR2I nearest;
429
430 if( aB.IsClosed() && aB.PointInside( aA.Centre() ) )
431 {
432 nearest = aA.Centre();
433 closest_dist = 0;
434 }
435 else
436 {
437 for( size_t s = 0; s < aB.GetSegmentCount(); s++ )
438 {
439 int collision_dist = 0;
440 VECTOR2I pn;
441
442 if( aA.Collide( aB.GetSegment( s ), aClearance,
443 aActual || aLocation ? &collision_dist : nullptr,
444 aLocation ? &pn : nullptr ) )
445 {
446 if( collision_dist < closest_dist )
447 {
448 nearest = pn;
449 closest_dist = collision_dist;
450 }
451
452 if( closest_dist == 0 )
453 break;
454
455 // If we're not looking for aActual then any collision will do
456 if( !aActual )
457 break;
458 }
459 }
460 }
461
462 if( closest_dist == 0 || closest_dist < aClearance )
463 {
464 if( aLocation )
465 *aLocation = nearest;
466
467 if( aActual )
468 *aActual = closest_dist;
469
470 return true;
471 }
472
473 return false;
474}
475
476
477static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, int aClearance,
478 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
479{
480 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
481 aA.TypeName(),
482 aB.TypeName() ) );
483
484 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
485
486 if( rv && aActual )
487 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
488
489 return rv;
490}
491
492
493static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT& aB,
494 int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
495{
496 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
497 aA.TypeName(),
498 aB.TypeName() ) );
499
500 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
501
502 if( rv && aActual )
503 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
504
505 return rv;
506}
507
508
509static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aB, int aClearance,
510 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
511{
512 if( aA.GetRadius() > 0 )
513 return Collide( aA.Outline(), aB, aClearance, aActual, aLocation, aMTV );
514
515 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
516 aA.TypeName(),
517 aB.TypeName() ) );
518
519 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
520
521 if( rv && aActual )
522 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
523
524 return rv;
525}
526
527
528static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
529 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
530{
531 if( aClearance || aActual || aLocation || aMTV || aA.GetRadius() > 0 || aB.GetRadius() > 0 )
532 {
533 return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
534 }
535 else
536 {
537 BOX2I bboxa = aA.BBox();
538 BOX2I bboxb = aB.BBox();
539
540 return bboxa.Intersects( bboxb );
541 }
542}
543
544
545static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aClearance,
546 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
547{
548 if( aA.IsEffectiveLine() )
549 {
550 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
551 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
552
553 if( retval && aMTV )
554 *aMTV = - *aMTV;
555
556 return retval;
557 }
558
559 VECTOR2I ptA, ptB;
560 int64_t dist_sq = std::numeric_limits<int64_t>::max();
561 aA.NearestPoints( aB, ptA, ptB, dist_sq );
562
563 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
564 {
565 if( aLocation )
566 *aLocation = ( ptA + ptB ) / 2;
567
568 if( aActual )
569 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
570
571 if( aMTV )
572 {
573 const VECTOR2I delta = ptB - ptA;
574 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
575 }
576
577 return true;
578 }
579
580 return false;
581}
582
583
584static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int aClearance,
585 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
586{
587 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
588 aA.TypeName(),
589 aB.TypeName() ) );
590
591 int closest_dist = std::numeric_limits<int>::max();
592 VECTOR2I nearest;
593
594 if( aB.IsClosed() && aB.PointInside( aA.GetP0() ) )
595 {
596 closest_dist = 0;
597 nearest = aA.GetP0();
598 }
599 else
600 {
601 int collision_dist = 0;
602 VECTOR2I pn;
603
604 for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
605 {
606 // ignore arcs - we will collide these separately
607 if( aB.IsArcSegment( i ) )
608 continue;
609
610 if( aA.Collide( aB.GetSegment( i ), aClearance,
611 aActual || aLocation ? &collision_dist : nullptr,
612 aLocation ? &pn : nullptr ) )
613 {
614 if( collision_dist < closest_dist )
615 {
616 nearest = pn;
617 closest_dist = collision_dist;
618 }
619
620 if( closest_dist == 0 )
621 break;
622
623 // If we're not looking for aActual then any collision will do
624 if( !aActual )
625 break;
626 }
627 }
628
629 for( size_t i = 0; i < aB.ArcCount(); i++ )
630 {
631 const SHAPE_ARC& arc = aB.Arc( i );
632
633 // The arcs in the chain should have zero width
634 wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
635
636 if( aA.Collide( &arc, aClearance, aActual || aLocation ? &collision_dist : nullptr,
637 aLocation ? &pn : nullptr ) )
638 {
639 if( collision_dist < closest_dist )
640 {
641 nearest = pn;
642 closest_dist = collision_dist;
643 }
644
645 if( closest_dist == 0 )
646 break;
647
648 if( !aActual )
649 break;
650 }
651 }
652 }
653
654 if( closest_dist == 0 || closest_dist < aClearance )
655 {
656 if( aLocation )
657 *aLocation = nearest;
658
659 if( aActual )
660 *aActual = closest_dist;
661
662 return true;
663 }
664
665 return false;
666}
667
668
669static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_RECT& aB, int aClearance,
670 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
671{
672 if( aB.GetRadius() > 0 )
673 return Collide( aA, aB.Outline(), aClearance, aActual, aLocation, aMTV );
674
675 if( aA.IsEffectiveLine() )
676 {
677 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
678 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
679
680 if( retval && aMTV )
681 *aMTV = - *aMTV;
682
683 return retval;
684 }
685
686 VECTOR2I ptA, ptB;
687 int64_t dist_sq = std::numeric_limits<int64_t>::max();
688 aA.NearestPoints( aB, ptA, ptB, dist_sq );
689
690 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
691 {
692 if( aLocation )
693 *aLocation = ( ptA + ptB ) / 2;
694
695 if( aActual )
696 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
697
698 if( aMTV )
699 {
700 const VECTOR2I delta = ptB - ptA;
701 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
702 }
703
704 return true;
705 }
706
707 return false;
708}
709
710
711static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aClearance,
712 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
713{
714 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
715 aA.TypeName(),
716 aB.TypeName() ) );
717
718 // If the arc radius is too large, it is effectively a line segment
719 if( aA.IsEffectiveLine() )
720 {
721 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
722 return Collide( tmp, aB, aClearance, aActual, aLocation, aMTV );
723 }
724
725 bool rv = aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation );
726
727 if( rv && aActual )
728 *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 );
729
730 return rv;
731}
732
733
734static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
735 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
736{
737 // If the arc radius is too large, it is effectively a line segment
738 if( aA.IsEffectiveLine() )
739 {
740 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
741 return Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
742 }
743
744 wxASSERT_MSG( !aMTV, wxString::Format( wxT( "MTV not implemented for %s : %s collisions" ),
745 aA.TypeName(),
746 aB.TypeName() ) );
747
748 int closest_dist = std::numeric_limits<int>::max();
749 VECTOR2I nearest;
750
751 if( aB.IsClosed() && aB.PointInside( aA.GetP0() ) )
752 {
753 closest_dist = 0;
754 nearest = aA.GetP0();
755 }
756 else
757 {
758 for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
759 {
760 int collision_dist = 0;
761 VECTOR2I pn;
762
763 if( aA.Collide( aB.GetSegment( i ), aClearance,
764 aActual || aLocation ? &collision_dist : nullptr,
765 aLocation ? &pn : nullptr ) )
766 {
767 if( collision_dist < closest_dist )
768 {
769 nearest = pn;
770 closest_dist = collision_dist;
771 }
772
773 if( closest_dist == 0 )
774 break;
775
776 // If we're not looking for aActual then any collision will do
777 if( !aActual )
778 break;
779 }
780 }
781 }
782
783 if( closest_dist == 0 || closest_dist < aClearance )
784 {
785 if( aLocation )
786 *aLocation = nearest;
787
788 if( aActual )
789 *aActual = closest_dist;
790
791 return true;
792 }
793
794 return false;
795}
796
797
798static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_ARC& aB, int aClearance,
799 int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
800{
801 if( aA.IsEffectiveLine() )
802 {
803 SHAPE_SEGMENT tmp( aA.GetP0(), aA.GetP1(), aA.GetWidth() );
804 bool retval = Collide( aB, tmp, aClearance, aActual, aLocation, aMTV );
805
806 if( retval && aMTV )
807 *aMTV = - *aMTV;
808
809 return retval;
810 }
811
812 if( aB.IsEffectiveLine() )
813 {
814 SHAPE_SEGMENT tmp( aB.GetP0(), aB.GetP1(), aB.GetWidth() );
815 return Collide( aA, tmp, aClearance, aActual, aLocation, aMTV );
816 }
817
818 VECTOR2I ptA, ptB;
819 int64_t dist_sq = std::numeric_limits<int64_t>::max();
820 aA.NearestPoints( aB, ptA, ptB, dist_sq );
821
822 if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) )
823 {
824 if( aLocation )
825 *aLocation = ( ptA + ptB ) / 2;
826
827 if( aActual )
828 *aActual = std::max( 0, KiROUND( std::sqrt( dist_sq ) ) );
829
830 if( aMTV )
831 {
832 const VECTOR2I delta = ptB - ptA;
833 *aMTV = delta.Resize( aClearance - std::sqrt( dist_sq ) + 3 );
834 }
835
836 return true;
837 }
838
839 return false;
840}
841
842
843template<class T_a, class T_b>
844inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
845 VECTOR2I* aLocation, VECTOR2I* aMTV )
846
847{
848 return Collide( *static_cast<const T_a*>( aA ), *static_cast<const T_b*>( aB ),
849 aClearance, aActual, aLocation, aMTV);
850}
851
852
853template<class T_a, class T_b>
854inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
855 VECTOR2I* aLocation, VECTOR2I* aMTV )
856{
857 bool rv = Collide( *static_cast<const T_b*>( aB ), *static_cast<const T_a*>( aA ),
858 aClearance, aActual, aLocation, aMTV);
859
860 if( rv && aMTV)
861 *aMTV = - *aMTV;
862
863 return rv;
864}
865
866
867static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
868 VECTOR2I* aLocation, VECTOR2I* aMTV )
869{
870 if( aA->Type() == SH_POLY_SET )
871 {
872 const SHAPE_POLY_SET* polySetA = static_cast<const SHAPE_POLY_SET*>( aA );
873
874 wxASSERT( !aMTV );
875 return polySetA->Collide( aB, aClearance, aActual, aLocation );
876 }
877 else if( aB->Type() == SH_POLY_SET )
878 {
879 const SHAPE_POLY_SET* polySetB = static_cast<const SHAPE_POLY_SET*>( aB );
880
881 wxASSERT( !aMTV );
882 return polySetB->Collide( aA, aClearance, aActual, aLocation );
883 }
884
885 switch( aA->Type() )
886 {
887 case SH_NULL:
888 return false;
889
890 case SH_RECT:
891 switch( aB->Type() )
892 {
893 case SH_RECT:
894 return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
895
896 case SH_CIRCLE:
897 return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
898
899 case SH_LINE_CHAIN:
900 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
901
902 case SH_SEGMENT:
903 return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
904
905 case SH_SIMPLE:
907 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
908
909 case SH_ARC:
910 return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
911
912 case SH_NULL:
913 return false;
914
915 default:
916 break;
917 }
918 break;
919
920 case SH_CIRCLE:
921 switch( aB->Type() )
922 {
923 case SH_RECT:
924 return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
925
926 case SH_CIRCLE:
927 return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
928
929 case SH_LINE_CHAIN:
930 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
931
932 case SH_SEGMENT:
933 return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
934
935 case SH_SIMPLE:
937 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
938
939 case SH_ARC:
940 return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
941
942 case SH_NULL:
943 return false;
944
945 default:
946 break;
947 }
948 break;
949
950 case SH_LINE_CHAIN:
951 switch( aB->Type() )
952 {
953 case SH_RECT:
954 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
955
956 case SH_CIRCLE:
957 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
958
959 case SH_LINE_CHAIN:
960 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
961
962 case SH_SEGMENT:
963 return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
964
965 case SH_SIMPLE:
967 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
968
969 case SH_ARC:
970 return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
971
972 case SH_NULL:
973 return false;
974
975 default:
976 break;
977 }
978 break;
979
980 case SH_SEGMENT:
981 switch( aB->Type() )
982 {
983 case SH_RECT:
984 return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
985
986 case SH_CIRCLE:
987 return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
988
989 case SH_LINE_CHAIN:
990 return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
991
992 case SH_SEGMENT:
993 return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
994
995 case SH_SIMPLE:
997 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
998
999 case SH_ARC:
1000 return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1001
1002 case SH_NULL:
1003 return false;
1004
1005 default:
1006 break;
1007 }
1008 break;
1009
1010 case SH_SIMPLE:
1012 switch( aB->Type() )
1013 {
1014 case SH_RECT:
1015 return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1016
1017 case SH_CIRCLE:
1018 return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1019
1020 case SH_LINE_CHAIN:
1021 return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
1022
1023 case SH_SEGMENT:
1024 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1025
1026 case SH_SIMPLE:
1028 return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1029
1030 case SH_ARC:
1031 return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1032
1033 case SH_NULL:
1034 return false;
1035
1036 default:
1037 break;
1038 }
1039 break;
1040
1041 case SH_ARC:
1042 switch( aB->Type() )
1043 {
1044 case SH_RECT:
1045 return CollCase<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1046
1047 case SH_CIRCLE:
1048 return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1049
1050 case SH_LINE_CHAIN:
1051 return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
1052
1053 case SH_SEGMENT:
1054 return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
1055
1056 case SH_SIMPLE:
1058 return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
1059
1060 case SH_ARC:
1061 return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aLocation, aMTV );
1062
1063 case SH_NULL:
1064 return false;
1065
1066 default:
1067 break;
1068 }
1069 break;
1070
1071 default:
1072 break;
1073 }
1074
1075 wxFAIL_MSG( wxString::Format( wxT( "Unsupported collision: %s with %s" ),
1076 SHAPE_TYPE_asString( aA->Type() ),
1077 SHAPE_TYPE_asString( aB->Type() ) ) );
1078
1079 return false;
1080}
1081
1082static bool collideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
1083 VECTOR2I* aLocation, VECTOR2I* aMTV )
1084{
1085 int currentActual = std::numeric_limits<int>::max();
1086 VECTOR2I currentLocation;
1087 VECTOR2I currentMTV(0, 0);
1088 bool colliding = false;
1089
1090 auto canExit =
1091 [&]()
1092 {
1093 if( !colliding )
1094 return false;
1095
1096 if( aActual && currentActual > 0 )
1097 return false;
1098
1099 if( aMTV )
1100 return false;
1101
1102 return true;
1103 };
1104
1105 auto collideCompoundSubshapes =
1106 [&]( const SHAPE* elemA, const SHAPE* elemB, int clearance ) -> bool
1107 {
1108 int actual = 0;
1110 VECTOR2I mtv;
1111
1112 if( collideSingleShapes( elemA, elemB, clearance,
1113 aActual || aLocation ? &actual : nullptr,
1114 aLocation ? &location : nullptr,
1115 aMTV ? &mtv : nullptr ) )
1116 {
1117 if( actual < currentActual )
1118 {
1119 currentActual = actual;
1120 currentLocation = location;
1121 }
1122
1123 if( aMTV && mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() )
1124 {
1125 currentMTV = mtv;
1126 }
1127
1128 return true;
1129 }
1130
1131 return false;
1132 };
1133
1134 if( aA->Type() == SH_COMPOUND && aB->Type() == SH_COMPOUND )
1135 {
1136 const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
1137 const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
1138
1139 for( const SHAPE* elemA : cmpA->Shapes() )
1140 {
1141 for( const SHAPE* elemB : cmpB->Shapes() )
1142 {
1143 if( collideCompoundSubshapes( elemA, elemB, aClearance ) )
1144 {
1145 colliding = true;
1146
1147 if( canExit() )
1148 break;
1149 }
1150 }
1151
1152 if( canExit() )
1153 break;
1154 }
1155 }
1156 else if( aA->Type() == SH_COMPOUND )
1157 {
1158 const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
1159
1160 for( const SHAPE* elemA : cmpA->Shapes() )
1161 {
1162 if( collideCompoundSubshapes( elemA, aB, aClearance ) )
1163 {
1164 colliding = true;
1165
1166 if( canExit() )
1167 break;
1168 }
1169 }
1170 }
1171 else if( aB->Type() == SH_COMPOUND )
1172 {
1173 const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
1174
1175 for( const SHAPE* elemB : cmpB->Shapes() )
1176 {
1177 if( collideCompoundSubshapes( aA, elemB, aClearance ) )
1178 {
1179 colliding = true;
1180
1181 if( canExit() )
1182 break;
1183 }
1184 }
1185 }
1186 else
1187 {
1188 return collideSingleShapes( aA, aB, aClearance, aActual, aLocation, aMTV );
1189 }
1190
1191 if( colliding )
1192 {
1193 if( aLocation )
1194 *aLocation = currentLocation;
1195
1196 if( aActual )
1197 *aActual = currentActual;
1198
1199 if( aMTV )
1200 *aMTV = currentMTV;
1201 }
1202
1203 return colliding;
1204}
1205
1206
1207bool SHAPE::Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const
1208{
1209 return collideShapes( this, aShape, aClearance, nullptr, nullptr, aMTV );
1210}
1211
1212
1213bool SHAPE::Collide( const SHAPE* aShape, int aClearance, int* aActual, VECTOR2I* aLocation ) const
1214{
1215 return collideShapes( this, aShape, aClearance, aActual, aLocation, nullptr );
1216}
1217
1218
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:604
static SEG::ecoord Square(int a)
Definition seg.h:123
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:673
int GetWidth() const override
Definition shape_arc.h:215
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
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:118
wxString TypeName() const
Definition shape.h:103
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:98
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
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
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,...
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,...
Definition shape_rect.h:151
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:109
const VECTOR2I & GetPosition() const
Definition shape_rect.h:169
const VECTOR2I GetSize() const
Definition shape_rect.h:177
int GetRadius() const
Definition shape_rect.h:201
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:126
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:232
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
Definition shape.h:136
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:307
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:76
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:73
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
@ SH_POLY_SET
set of polygons (with holes, etc.)
Definition shape.h:52
@ SH_RECT
axis-aligned rectangle
Definition shape.h:47
@ SH_CIRCLE
circle
Definition shape.h:50
@ SH_SIMPLE
simple polygon
Definition shape.h:51
@ SH_NULL
empty shape (no shape...),
Definition shape.h:55
@ SH_SEGMENT
line segment
Definition shape.h:48
@ SH_ARC
circular arc
Definition shape.h:54
@ SH_POLY_SET_TRIANGLE
a single triangle belonging to a POLY_SET triangulation
Definition shape.h:56
@ SH_LINE_CHAIN
line chain (polyline)
Definition shape.h:49
@ SH_COMPOUND
compound shape, consisting of multiple simple shapes
Definition shape.h:53
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition shape.h:59
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 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
const SHAPE_LINE_CHAIN chain
int clearance
VECTOR2I location
int actual
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695