KiCad PCB EDA Suite
Loading...
Searching...
No Matches
shape_arc.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 * 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 <core/kicad_algo.h>
28#include <geometry/seg.h> // for SEG
29#include <geometry/shape_arc.h>
32#include <geometry/shape_rect.h>
34#include <trigo.h>
35
36
37std::ostream& operator<<( std::ostream& aStream, const SHAPE_ARC& aArc )
38{
39 aStream << "Arc( P0=" << aArc.GetP0() << " P1=" << aArc.GetP1() << " Mid=" << aArc.GetArcMid()
40 << " Width=" << aArc.GetWidth() << " )";
41 return aStream;
42}
43
44
45SHAPE_ARC::SHAPE_ARC( const VECTOR2I& aArcCenter, const VECTOR2I& aArcStartPoint,
46 const EDA_ANGLE& aCenterAngle, int aWidth ) :
47 SHAPE( SH_ARC ),
48 m_width( aWidth )
49{
50 m_start = aArcStartPoint;
51
52 VECTOR2D mid = aArcStartPoint;
53 VECTOR2D end = aArcStartPoint;
54 VECTOR2D center = aArcCenter;
55
56 RotatePoint( mid, center, -aCenterAngle / 2.0 );
57 RotatePoint( end, center, -aCenterAngle );
58
59 m_mid = VECTOR2I( KiROUND( mid.x ), KiROUND( mid.y ) );
60 m_end = VECTOR2I( KiROUND( end.x ), KiROUND( end.y ) );
61
63}
64
65
66SHAPE_ARC::SHAPE_ARC( const VECTOR2I& aArcStart, const VECTOR2I& aArcMid,
67 const VECTOR2I& aArcEnd, int aWidth ) :
68 SHAPE( SH_ARC ),
69 m_start( aArcStart ),
70 m_mid( aArcMid ),
71 m_end( aArcEnd ),
72 m_width( aWidth )
73{
75}
76
77
78SHAPE_ARC::SHAPE_ARC( const SEG& aSegmentA, const SEG& aSegmentB, int aRadius, int aWidth ) :
79 SHAPE( SH_ARC )
80{
81 m_width = aWidth;
82
83 /*
84 * Construct an arc that is tangent to two segments with a given radius.
85 *
86 * p
87 * A
88 * A \
89 * / \
90 * / . . \ segB
91 * /. .\
92 * segA / c \
93 * / B
94 * /
95 * /
96 * B
97 *
98 *
99 * segA is the fist segment (with its points A and B)
100 * segB is the second segment (with its points A and B)
101 * p is the point at which segA and segB would intersect if they were projected
102 * c is the centre of the arc to be constructed
103 * rad is the radius of the arc to be constructed
104 *
105 * We can create two vectors, between point p and segA /segB
106 * pToA = p - segA.B //< note that segA.A would also be valid as it is colinear
107 * pToB = p - segB.B //< note that segB.A would also be valid as it is colinear
108 *
109 * Let the angle formed by segA and segB be called 'alpha':
110 * alpha = angle( pToA ) - angle( pToB )
111 *
112 * The distance PC can be computed as
113 * distPC = rad / abs( sin( alpha / 2 ) )
114 *
115 * The polar angle of the vector PC can be computed as:
116 * anglePC = angle( pToA ) + alpha / 2
117 *
118 * Therefore:
119 * C.x = P.x + distPC*cos( anglePC )
120 * C.y = P.y + distPC*sin( anglePC )
121 */
122
123 OPT_VECTOR2I p = aSegmentA.Intersect( aSegmentB, true, true );
124
125 if( !p || aSegmentA.Length() == 0 || aSegmentB.Length() == 0 )
126 {
127 // Catch bugs in debug
128 wxASSERT_MSG( false, "The input segments do not intersect or one is zero length." );
129
130 // Make a 180 degree arc around aSegmentA in case we end up here in release
131 m_start = aSegmentA.A;
132 m_end = aSegmentA.B;
133 m_mid = m_start;
134
135 VECTOR2I arcCenter = aSegmentA.Center();
136 RotatePoint( m_mid, arcCenter, ANGLE_90 ); // mid point at 90 degrees
137 }
138 else
139 {
140 VECTOR2I pToA = aSegmentA.B - *p;
141 VECTOR2I pToB = aSegmentB.B - *p;
142
143 if( pToA.EuclideanNorm() == 0 )
144 pToA = aSegmentA.A - *p;
145
146 if( pToB.EuclideanNorm() == 0 )
147 pToB = aSegmentB.A - *p;
148
149 EDA_ANGLE pToAangle( pToA );
150 EDA_ANGLE pToBangle( pToB );
151
152 EDA_ANGLE alpha = ( pToAangle - pToBangle ).Normalize180();
153
154 double distPC = (double) aRadius / abs( sin( alpha.AsRadians() / 2 ) );
155 EDA_ANGLE angPC = pToAangle - alpha / 2;
156 VECTOR2I arcCenter;
157
158 arcCenter.x = p->x + KiROUND( distPC * angPC.Cos() );
159 arcCenter.y = p->y + KiROUND( distPC * angPC.Sin() );
160
161 // The end points of the arc are the orthogonal projected lines from the line segments
162 // to the center of the arc
163 m_start = aSegmentA.LineProject( arcCenter );
164 m_end = aSegmentB.LineProject( arcCenter );
165
166 //The mid point is rotated start point around center, half the angle of the arc.
167 VECTOR2I startVector = m_start - arcCenter;
168 VECTOR2I endVector = m_end - arcCenter;
169
170 EDA_ANGLE startAngle( startVector );
171 EDA_ANGLE endAngle( endVector );
172 EDA_ANGLE midPointRotAngle = ( startAngle - endAngle ).Normalize180() / 2;
173
174 m_mid = m_start;
175 RotatePoint( m_mid, arcCenter, midPointRotAngle );
176 }
177
179}
180
181
183 : SHAPE( SH_ARC )
184{
185 m_start = aOther.m_start;
186 m_end = aOther.m_end;
187 m_mid = aOther.m_mid;
188 m_width = aOther.m_width;
189 m_bbox = aOther.m_bbox;
190 m_center = aOther.m_center;
191 m_radius = aOther.m_radius;
192}
193
194
196 const EDA_ANGLE& aAngle, double aWidth )
197{
198 m_start = aStart;
199 m_mid = aStart;
200 m_end = aEnd;
201 m_width = aWidth;
202
203 VECTOR2I center( CalcArcCenter( aStart, aEnd, aAngle ) );
204
205 RotatePoint( m_mid, center, -aAngle / 2.0 );
206
208
209 return *this;
210}
211
212
214 const VECTOR2I& aCenter, bool aClockwise,
215 double aWidth )
216{
217 VECTOR2I startLine = aStart - aCenter;
218 VECTOR2I endLine = aEnd - aCenter;
219
220 EDA_ANGLE startAngle( startLine );
221 EDA_ANGLE endAngle( endLine );
222
223 startAngle.Normalize();
224 endAngle.Normalize();
225
226 EDA_ANGLE angle = endAngle - startAngle;
227
228 if( aClockwise )
229 angle = angle.Normalize() - ANGLE_360;
230 else
231 angle = angle.Normalize();
232
233 m_start = aStart;
234 m_end = aEnd;
235 m_mid = aStart;
236
237 RotatePoint( m_mid, aCenter, -angle / 2.0 );
238
240
241 return *this;
242}
243
244
246{
247 SEG v1 = SEG( m_start, m_mid );
248 SEG v2 = SEG( m_mid, m_end );
249
250 return v1.ApproxCollinear( v2 );
251}
252
253
254bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
255{
256 VECTOR2I center = GetCenter();
257 double radius = VECTOR2D( center - m_start ).EuclideanNorm();
258 SHAPE_CIRCLE circle( center, radius );
259 ecoord clearance_sq = SEG::Square( aClearance );
260
261 // Circle or at least an arc with less space remaining than the clearance
262 if( GetCentralAngle().AsDegrees() > 180.0
263 && ( m_start - m_end ).SquaredEuclideanNorm() < clearance_sq )
264 {
265 ecoord a_dist_sq = ( aSeg.A - center ).SquaredEuclideanNorm();
266 ecoord b_dist_sq = ( aSeg.B - center ).SquaredEuclideanNorm();
267 ecoord radius_sq = SEG::Square( radius - aClearance );
268
269 if( a_dist_sq < radius_sq && b_dist_sq < radius_sq )
270 return false;
271
272
273 return circle.Collide( aSeg, aClearance, aActual, aLocation );
274 }
275
276 // Possible points of the collision are:
277 // 1. Intersetion of the segment with the full circle
278 // 2. Closest point on the segment to the center of the circle
279 // 3. Closest point on the segment to the end points of the arc
280 // 4. End points of the segment
281
282 std::vector<VECTOR2I> candidatePts = circle.GetCircle().Intersect( aSeg );
283
284 candidatePts.push_back( aSeg.NearestPoint( center ) );
285 candidatePts.push_back( aSeg.NearestPoint( m_start ) );
286 candidatePts.push_back( aSeg.NearestPoint( m_end ) );
287 candidatePts.push_back( aSeg.A );
288 candidatePts.push_back( aSeg.B );
289
290 bool any_collides = false;
291
292 for( const VECTOR2I& candidate : candidatePts )
293 {
294 bool collides = Collide( candidate, aClearance, aActual, aLocation );
295 any_collides |= collides;
296
297 if( collides && ( !aActual || *aActual == 0 ) )
298 return true;
299 }
300
301 return any_collides;
302}
303
304
305int SHAPE_ARC::IntersectLine( const SEG& aSeg, std::vector<VECTOR2I>* aIpsBuffer ) const
306{
307 if( aSeg.A == aSeg.B ) // One point does not define a line....
308 return 0;
309
310 CIRCLE circ( GetCenter(), GetRadius() );
311
312 std::vector<VECTOR2I> intersections = circ.IntersectLine( aSeg );
313
314 const size_t originalSize = aIpsBuffer->size();
315
316 for( const VECTOR2I& intersection : intersections )
317 {
318 if( sliceContainsPoint( intersection ) )
319 aIpsBuffer->push_back( intersection );
320 }
321
322 return aIpsBuffer->size() - originalSize;
323}
324
325
326int SHAPE_ARC::Intersect( const CIRCLE& aCircle, std::vector<VECTOR2I>* aIpsBuffer ) const
327{
328 CIRCLE thiscirc( GetCenter(), GetRadius() );
329
330 std::vector<VECTOR2I> intersections = thiscirc.Intersect( aCircle );
331
332 const size_t originalSize = aIpsBuffer->size();
333
334 for( const VECTOR2I& intersection : intersections )
335 {
336 if( sliceContainsPoint( intersection ) )
337 aIpsBuffer->push_back( intersection );
338 }
339
340 return aIpsBuffer->size() - originalSize;
341}
342
343
344int SHAPE_ARC::Intersect( const SHAPE_ARC& aArc, std::vector<VECTOR2I>* aIpsBuffer ) const
345{
346 CIRCLE thiscirc( GetCenter(), GetRadius() );
347 CIRCLE othercirc( aArc.GetCenter(), aArc.GetRadius() );
348
349 std::vector<VECTOR2I> intersections = thiscirc.Intersect( othercirc );
350
351 const size_t originalSize = aIpsBuffer->size();
352
353 for( const VECTOR2I& intersection : intersections )
354 {
355 if( sliceContainsPoint( intersection ) && aArc.sliceContainsPoint( intersection ) )
356 aIpsBuffer->push_back( intersection );
357 }
358
359 return aIpsBuffer->size() - originalSize;
360}
361
362
364{
366 m_radius = std::sqrt( ( VECTOR2D( m_start ) - m_center ).SquaredEuclideanNorm() );
367
368 std::vector<VECTOR2I> points;
369 // Put start and end points in the point list
370 points.push_back( m_start );
371 points.push_back( m_end );
372
373 EDA_ANGLE start_angle = GetStartAngle();
374 EDA_ANGLE end_angle = start_angle + GetCentralAngle();
375
376 // we always count quadrants clockwise (increasing angle)
377 if( start_angle > end_angle )
378 std::swap( start_angle, end_angle );
379
380 int quad_angle_start = std::ceil( start_angle.AsDegrees() / 90.0 );
381 int quad_angle_end = std::floor( end_angle.AsDegrees() / 90.0 );
382
383 // very large radius means the arc is similar to a segment
384 // so do not try to add more points, center cannot be handled
385 // Very large is here > INT_MAX/2
386 if( m_radius < (double)INT_MAX/2.0 )
387 {
388 const int radius = KiROUND( m_radius );
389
390 // count through quadrants included in arc
391 for( int quad_angle = quad_angle_start; quad_angle <= quad_angle_end; ++quad_angle )
392 {
393 VECTOR2I quad_pt = m_center;
394
395 switch( quad_angle % 4 )
396 {
397 case 0: quad_pt += { radius, 0 }; break;
398 case 1: case -3: quad_pt += { 0, radius }; break;
399 case 2: case -2: quad_pt += { -radius, 0 }; break;
400 case 3: case -1: quad_pt += { 0, -radius }; break;
401 default:
402 assert( false );
403 }
404
405 points.push_back( quad_pt );
406 }
407 }
408
409 m_bbox.Compute( points );
410}
411
412
413const BOX2I SHAPE_ARC::BBox( int aClearance ) const
414{
415 BOX2I bbox( m_bbox );
416
417 if( m_width != 0 )
418 bbox.Inflate( KiROUND( m_width / 2.0 ) + 1 );
419
420 if( aClearance != 0 )
421 bbox.Inflate( aClearance );
422
423 return bbox;
424}
425
426
428{
429 const static int s_epsilon = 8;
430
431 CIRCLE fullCircle( GetCenter(), GetRadius() );
432 VECTOR2I nearestPt = fullCircle.NearestPoint( aP );
433
434 if( ( nearestPt - m_start ).SquaredEuclideanNorm() <= s_epsilon )
435 return m_start;
436
437 if( ( nearestPt - m_end ).SquaredEuclideanNorm() <= s_epsilon )
438 return m_end;
439
440 if( sliceContainsPoint( nearestPt ) )
441 return nearestPt;
442
443 if( ( aP - m_start ).SquaredEuclideanNorm() <= ( aP - m_end ).SquaredEuclideanNorm() )
444 return m_start;
445 else
446 return m_end;
447}
448
449
450bool SHAPE_ARC::NearestPoints( const SHAPE_CIRCLE& aCircle, VECTOR2I& aPtA, VECTOR2I& aPtB,
451 int64_t& aDistSq ) const
452{
453 if( GetCenter() == aCircle.GetCenter() && GetRadius() == aCircle.GetRadius() )
454 {
455 aPtA = aPtB = GetP0();
456 aDistSq = 0;
457 return true;
458 }
459
460 aDistSq = std::numeric_limits<int64_t>::max();
461
462 CIRCLE circle1( GetCenter(), GetRadius() );
463 CIRCLE circle2( aCircle.GetCircle() );
464 std::vector<VECTOR2I> intersections = circle1.Intersect( circle2 );
465
466 for( const VECTOR2I& pt : intersections )
467 {
468 if( sliceContainsPoint( pt ) )
469 {
470 aPtA = aPtB = pt;
471 aDistSq = 0;
472 return true;
473 }
474 }
475
476 std::vector<VECTOR2I> pts = { m_start, m_end, circle1.NearestPoint( GetCenter() ) };
477
478 for( const VECTOR2I& pt : pts )
479 {
480 if( sliceContainsPoint( pt ) )
481 {
482 VECTOR2I nearestPt2 = circle2.NearestPoint( pt );
483 int64_t distSq = pt.SquaredDistance( nearestPt2 );
484
485 if( distSq < aDistSq )
486 {
487 aDistSq = distSq;
488 aPtA = pt;
489 aPtB = nearestPt2;
490 }
491 }
492 }
493
494 return true;
495}
496
497
498bool SHAPE_ARC::NearestPoints( const SEG& aSeg, VECTOR2I& aPtA, VECTOR2I& aPtB,
499 int64_t& aDistSq ) const
500{
501 aDistSq = std::numeric_limits<int64_t>::max();
502 CIRCLE circle( GetCenter(), GetRadius() );
503
504 // First check for intersections on the circle
505 std::vector<VECTOR2I> intersections = circle.Intersect( aSeg );
506
507 for( const VECTOR2I& pt : intersections )
508 {
509 if( sliceContainsPoint( pt ) )
510 {
511 aPtA = aPtB = pt;
512 aDistSq = 0;
513 return true;
514 }
515 }
516
517 // Check the endpoints of the segment against the nearest point on the arc
518 for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
519 {
520 if( sliceContainsPoint( pt ) )
521 {
522 VECTOR2I nearestPt = circle.NearestPoint( pt );
523 int64_t distSq = pt.SquaredDistance( nearestPt );
524
525 if( distSq < aDistSq )
526 {
527 aDistSq = distSq;
528 aPtA = nearestPt;
529 aPtB = pt;
530 }
531 }
532 }
533
534 // Check the endpoints of the arc against the nearest point on the segment
535 for( const VECTOR2I& pt : { m_start, m_end } )
536 {
537 VECTOR2I nearestPt = aSeg.NearestPoint( pt );
538 int64_t distSq = pt.SquaredDistance( nearestPt );
539
540 if( distSq < aDistSq )
541 {
542 aDistSq = distSq;
543 aPtA = pt;
544 aPtB = nearestPt;
545 }
546 }
547
548 // Check the closest points on the segment to the circle
549 VECTOR2I segNearestPt = aSeg.NearestPoint( GetCenter() );
550
551 if( sliceContainsPoint( segNearestPt ) )
552 {
553 VECTOR2I circleNearestPt = circle.NearestPoint( segNearestPt );
554 int64_t distSq = segNearestPt.SquaredDistance( circleNearestPt );
555
556 if( distSq < aDistSq )
557 {
558 aDistSq = distSq;
559 aPtA = segNearestPt;
560 aPtB = circleNearestPt;
561 }
562 }
563
564 return true;
565}
566
567
568bool SHAPE_ARC::NearestPoints( const SHAPE_RECT& aRect, VECTOR2I& aPtA, VECTOR2I& aPtB,
569 int64_t& aDistSq ) const
570{
571 BOX2I bbox = aRect.BBox();
572 CIRCLE circle( GetCenter(), GetRadius() );
573 aDistSq = std::numeric_limits<int64_t>::max();
574
575 // First check for intersections
576 SHAPE_LINE_CHAIN lineChain( aRect.Outline() );
577
578 for( int i = 0; i < 4; ++i )
579 {
580 SEG seg( lineChain.CPoint( i ), lineChain.CPoint( i + 1 ) );
581
582 std::vector<VECTOR2I> intersections = circle.Intersect( seg );
583
584 for( const VECTOR2I& pt : intersections )
585 {
586 if( sliceContainsPoint( pt ) )
587 {
588 aPtA = aPtB = pt;
589 aDistSq = 0;
590 return true;
591 }
592 }
593 }
594
595 // Check the endpoints of the arc against the nearest point on the rectangle
596 for( const VECTOR2I& pt : { m_start, m_end } )
597 {
598 VECTOR2I nearestPt = bbox.NearestPoint( pt );
599 int64_t distSq = pt.SquaredDistance( nearestPt );
600
601 if( distSq < aDistSq )
602 {
603 aDistSq = distSq;
604 aPtA = pt;
605 aPtB = nearestPt;
606 }
607 }
608
609 // Check the closest points on the rectangle to the circle
610 VECTOR2I rectNearestPt = bbox.NearestPoint( GetCenter() );
611
612 if( sliceContainsPoint( rectNearestPt ) )
613 {
614 VECTOR2I circleNearestPt = circle.NearestPoint( rectNearestPt );
615 int64_t distSq = rectNearestPt.SquaredDistance( circleNearestPt );
616
617 if( distSq < aDistSq )
618 {
619 aDistSq = distSq;
620 aPtA = rectNearestPt;
621 aPtB = circleNearestPt;
622 }
623 }
624
625 return true;
626}
627
628
629bool SHAPE_ARC::NearestPoints( const SHAPE_ARC& aArc, VECTOR2I& aPtA, VECTOR2I& aPtB,
630 int64_t& aDistSq ) const
631{
632 aDistSq = std::numeric_limits<int64_t>::max();
633
634 VECTOR2I center1 = GetCenter();
635 VECTOR2I center2 = aArc.GetCenter();
636
637 int64_t center_dist_sq = center1.SquaredDistance( center2 );
638
639 // Start by checking endpoints
640 std::vector<VECTOR2I> pts1 = { m_start, m_end };
641 std::vector<VECTOR2I> pts2 = { aArc.GetP0(), aArc.GetP1() };
642
643 for( const VECTOR2I& pt1 : pts1 )
644 {
645 for( const VECTOR2I& pt2 : pts2 )
646 {
647 int64_t distSq = pt1.SquaredDistance( pt2 );
648
649 if( distSq < aDistSq )
650 {
651 aDistSq = distSq;
652 aPtA = pt1;
653 aPtB = pt2;
654
655 if( aDistSq == 0 )
656 return true;
657 }
658 }
659 }
660
661 for( const VECTOR2I& pt : pts1 )
662 {
663 if( aArc.sliceContainsPoint( pt ) )
664 {
665 CIRCLE circle( center2, aArc.GetRadius() );
666 aPtA = circle.NearestPoint( pt );
667 aPtB = pt;
668 aDistSq = aPtA.SquaredDistance( aPtB );
669
670 if( center_dist_sq == 0 || aDistSq == 0 )
671 return true;
672 }
673 }
674
675 for( const VECTOR2I& pt : pts2 )
676 {
677 if( sliceContainsPoint( pt ) )
678 {
679 CIRCLE circle( center1, GetRadius() );
680 aPtA = pt;
681 aPtB = circle.NearestPoint( pt );
682 aDistSq = aPtA.SquaredDistance( aPtB );
683
684 if( center_dist_sq == 0 || aDistSq == 0 )
685 return true;
686 }
687 }
688
689 // The remaining checks are require the arcs to be on non-concentric circles
690 if( center_dist_sq == 0 )
691 return true;
692
693 CIRCLE circle1( center1, GetRadius() );
694 CIRCLE circle2( center2, aArc.GetRadius() );
695
696 // First check for intersections on the circles
697 std::vector<VECTOR2I> intersections = circle1.Intersect( circle2 );
698
699 for( const VECTOR2I& pt : intersections )
700 {
701 if( sliceContainsPoint( pt ) && aArc.sliceContainsPoint( pt ) )
702 {
703 aPtA = pt;
704 aPtB = pt;
705 aDistSq = 0;
706 return true;
707 }
708 }
709
710 // Check for the closest points on the circles
711 VECTOR2I pt1 = circle1.NearestPoint( center2 );
712 VECTOR2I pt2 = circle2.NearestPoint( center1 );
713 bool pt1InSlice = sliceContainsPoint( pt1 );
714 bool pt2InSlice = aArc.sliceContainsPoint( pt2 );
715
716 if( pt1InSlice && pt2InSlice )
717 {
718 int64_t distSq = pt1.SquaredDistance( pt2 );
719
720 if( distSq < aDistSq )
721 {
722 aDistSq = distSq;
723 aPtA = pt1;
724 aPtB = pt2;
725 }
726
727 return true;
728 }
729
730 // Check the endpoints of arc 1 against the nearest point on arc 2
731 if( pt2InSlice )
732 {
733 for( const VECTOR2I& pt : pts1 )
734 {
735 int64_t distSq = pt.SquaredDistance( pt2 );
736
737 if( distSq < aDistSq )
738 {
739 aDistSq = distSq;
740 aPtA = pt;
741 aPtB = pt2;
742 }
743 }
744 }
745
746 // Check the endpoints of arc 2 against the nearest point on arc 1
747 if( pt1InSlice )
748 {
749 for( const VECTOR2I& pt : pts2 )
750 {
751 int64_t distSq = pt.SquaredDistance( pt1 );
752
753 if( distSq < aDistSq )
754 {
755 aDistSq = distSq;
756 aPtA = pt1;
757 aPtB = pt;
758 }
759 }
760 }
761
762 return true;
763}
764
765
766bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
767 VECTOR2I* aLocation ) const
768{
769 int minDist = aClearance + m_width / 2;
770 auto bbox = BBox( minDist );
771
772 // Fast check using bounding box:
773 if( !bbox.Contains( aP ) )
774 return false;
775
776 VECTOR2L center = GetCenter();
777 double radius = VECTOR2D( center - m_start ).EuclideanNorm();
778 CIRCLE fullCircle( center, radius );
779 VECTOR2D nearestPt = fullCircle.NearestPoint( VECTOR2D( aP ) );
780 int dist = KiROUND( nearestPt.Distance( aP ) );
781 EDA_ANGLE angleToPt( aP - fullCircle.Center ); // Angle from center to the point
782
783 if( !dist )
784 {
785 // Be sure to keep the sqrt of the squared distance instead of allowing a EuclideanNorm
786 // because this trucates the distance to an integer before subtracting
787 dist = KiROUND( radius - sqrt( ( aP - center ).SquaredEuclideanNorm() ) );
788 nearestPt = center + VECTOR2I( radius, 0 );
789 RotatePoint( nearestPt, center, angleToPt );
790 }
791
792 // If not a 360 degree arc, need to use arc angles to decide if point collides
793 if( m_start != m_end )
794 {
795 bool ccw = GetCentralAngle() > ANGLE_0;
796 EDA_ANGLE rotatedPtAngle = ( angleToPt.Normalize() - GetStartAngle() ).Normalize();
797 EDA_ANGLE rotatedEndAngle = ( GetEndAngle() - GetStartAngle() ).Normalize();
798
799 if( ( ccw && rotatedPtAngle > rotatedEndAngle )
800 || ( !ccw && rotatedPtAngle < rotatedEndAngle ) )
801 {
802 int distStartpt = ( aP - m_start ).EuclideanNorm();
803 int distEndpt = ( aP - m_end ).EuclideanNorm();
804
805 if( distStartpt < distEndpt )
806 {
807 dist = distStartpt;
808 nearestPt = m_start;
809 }
810 else
811 {
812 dist = distEndpt;
813 nearestPt = m_end;
814 }
815 }
816 }
817
818 if( dist <= minDist )
819 {
820 if( aLocation )
821 *aLocation = nearestPt;
822
823 if( aActual )
824 *aActual = std::max( 0, dist - m_width / 2 );
825
826 return true;
827 }
828
829 return false;
830}
831
832
834{
835 VECTOR2L center = GetCenter();
836 EDA_ANGLE angle( m_start - center );
837 return angle.Normalize();
838}
839
840
842{
843 VECTOR2L center = GetCenter();
844 EDA_ANGLE angle( m_end - center );
845 return angle.Normalize();
846}
847
848
850{
851 return m_center;
852}
853
854
856{
857 double radius = GetRadius();
858 EDA_ANGLE includedAngle = GetCentralAngle();
859
860 return std::abs( radius * includedAngle.AsRadians() );
861}
862
863
865{
866 // Arcs with same start and end points can be 0 deg or 360 deg arcs.
867 // However, they are expected to be circles.
868 // So return 360 degrees as central arc:
869 if( m_start == m_end )
870 return ANGLE_360;
871
872 VECTOR2L center = GetCenter();
873 EDA_ANGLE angle1 = EDA_ANGLE( m_mid - center ) - EDA_ANGLE( m_start - center );
874 EDA_ANGLE angle2 = EDA_ANGLE( m_end - center ) - EDA_ANGLE( m_mid - center );
875
876 return angle1.Normalize180() + angle2.Normalize180();
877}
878
879
881{
882 return m_radius;
883}
884
885
887 double* aEffectiveAccuracy ) const
888{
890 double r = GetRadius();
892 VECTOR2I c = GetCenter();
894
895 SEG startToEnd( GetP0(), GetP1() );
896 double halfAccuracy = std::max( 1.0, aAccuracy / 2 );
897
898 int n;
899
900 // To calculate the arc to segment count, use the external radius instead of the radius.
901 // for a arc with small radius and large width, the difference can be significant
902 double external_radius = r+(m_width/2);
903 double effectiveAccuracy;
904
905 if( external_radius < halfAccuracy
906 || startToEnd.Distance( GetArcMid() ) < halfAccuracy ) // Should be a very rare case
907 {
908 // In this case, the arc is approximated by one segment, with a effective error
909 // between -aAccuracy/2 and +aAccuracy/2, as expected.
910 n = 0;
911 effectiveAccuracy = external_radius;
912 }
913 else
914 {
915 n = GetArcToSegmentCount( external_radius, aAccuracy, ca );
916
917 // Recalculate the effective error of approximation, that can be < aAccuracy
918 int seg360 = n * 360.0 / fabs( ca.AsDegrees() );
919 effectiveAccuracy = CircleToEndSegmentDeltaRadius( external_radius, seg360 );
920 }
921
922 // Split the error on either side of the arc. Since we want the start and end points
923 // to be exactly on the arc, the first and last segments need to be shorter to stay within
924 // the error band (since segments normally start 1/2 the error band outside the arc).
925 r += effectiveAccuracy / 2;
926 n = n * 2;
927
928 rv.Append( m_start );
929
930 for( int i = 1; i < n ; i += 2 )
931 {
932 EDA_ANGLE a = sa;
933
934 if( n != 0 )
935 a += ( ca * i ) / n;
936
937 double x = c.x + r * a.Cos();
938 double y = c.y + r * a.Sin();
939
940 rv.Append( KiROUND( x ), KiROUND( y ) );
941 }
942
943 rv.Append( m_end );
944
945 if( aEffectiveAccuracy )
946 *aEffectiveAccuracy = effectiveAccuracy;
947
948 return rv;
949}
950
951
952void SHAPE_ARC::Move( const VECTOR2I& aVector )
953{
954 m_start += aVector;
955 m_end += aVector;
956 m_mid += aVector;
958}
959
960
961void SHAPE_ARC::Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter )
962{
963 RotatePoint( m_start, aCenter, aAngle );
964 RotatePoint( m_end, aCenter, aAngle );
965 RotatePoint( m_mid, aCenter, aAngle );
966
968}
969
970
971void SHAPE_ARC::Mirror( const VECTOR2I& aVector, FLIP_DIRECTION aFlipDirection )
972{
973 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
974 {
975 m_start.x = -m_start.x + 2 * aVector.x;
976 m_end.x = -m_end.x + 2 * aVector.x;
977 m_mid.x = -m_mid.x + 2 * aVector.x;
978 }
979 else
980 {
981 m_start.y = -m_start.y + 2 * aVector.y;
982 m_end.y = -m_end.y + 2 * aVector.y;
983 m_mid.y = -m_mid.y + 2 * aVector.y;
984 }
985
987}
988
989
990void SHAPE_ARC::Mirror( const SEG& axis )
991{
992 m_start = axis.ReflectPoint( m_start );
993 m_end = axis.ReflectPoint( m_end );
994 m_mid = axis.ReflectPoint( m_mid );
995
997}
998
999
1001{
1002 std::swap( m_start, m_end );
1003}
1004
1005
1007{
1008 return SHAPE_ARC( m_end, m_mid, m_start, m_width );
1009}
1010
1011
1013{
1016 EDA_ANGLE ea = sa + ca;
1017
1018 EDA_ANGLE phi( p - GetCenter() ); // Angle from center to the point
1019 phi.Normalize();
1020
1021 if( ca >= ANGLE_0 )
1022 {
1023 while( phi < sa )
1024 phi += ANGLE_360;
1025
1026 return phi >= sa && phi <= ea;
1027 }
1028 else
1029 {
1030 while( phi > sa )
1031 phi -= ANGLE_360;
1032
1033 return phi <= sa && phi >= ea;
1034 }
1035}
1036
1037
1038void SHAPE_ARC::TransformToPolygon( SHAPE_POLY_SET& aBuffer, int aError, ERROR_LOC aErrorLoc ) const
1039{
1040 TransformArcToPolygon( aBuffer, m_start, m_mid, m_end, m_width, aError, aErrorLoc );
1041}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
Definition: approximation.h:32
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr Vec NearestPoint(const Vec &aPoint) const
Return the point in this rect that is closest to the provided point.
Definition: box2.h:851
void Compute(const Container &aPointList)
Compute the bounding box from a given list of points.
Definition: box2.h:109
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:130
std::vector< VECTOR2I > Intersect(const CIRCLE &aCircle) const
Compute the intersection points between this circle and aCircle.
Definition: circle.cpp:221
std::vector< VECTOR2I > IntersectLine(const SEG &aLine) const
Compute the intersection points between this circle and aLine.
Definition: circle.cpp:300
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute the point on the circumference of the circle that is the closest to aP.
Definition: circle.cpp:197
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
double Sin() const
Definition: eda_angle.h:170
double AsDegrees() const
Definition: eda_angle.h:113
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
double AsRadians() const
Definition: eda_angle.h:117
double Cos() const
Definition: eda_angle.h:189
Definition: seg.h:42
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition: seg.cpp:350
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
int Length() const
Return the length (this).
Definition: seg.h:333
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:254
static SEG::ecoord Square(int a)
Definition: seg.h:123
VECTOR2I Center() const
Definition: seg.h:369
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
EDA_ANGLE GetCentralAngle() const
Definition: shape_arc.cpp:864
double m_radius
Definition: shape_arc.h:332
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:118
void update_values()
Definition: shape_arc.cpp:363
void Move(const VECTOR2I &aVector) override
Definition: shape_arc.cpp:952
SHAPE_ARC & ConstructFromStartEndAngle(const VECTOR2I &aStart, const VECTOR2I &aEnd, const EDA_ANGLE &aAngle, double aWidth=0)
Construct this arc from the given start, end and angle.
Definition: shape_arc.cpp:195
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Definition: shape_arc.cpp:413
EDA_ANGLE GetEndAngle() const
Definition: shape_arc.cpp:841
double GetLength() const
Definition: shape_arc.cpp:855
BOX2I m_bbox
Definition: shape_arc.h:330
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter) override
Rotate the arc by a given angle about a point.
Definition: shape_arc.cpp:961
bool sliceContainsPoint(const VECTOR2I &p) const
Definition: shape_arc.cpp:1012
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Definition: shape_arc.cpp:427
SHAPE_ARC()
Definition: shape_arc.h:43
int Intersect(const CIRCLE &aArc, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and a CIRCLE.
Definition: shape_arc.cpp:326
int GetWidth() const
Definition: shape_arc.h:210
VECTOR2I m_mid
Definition: shape_arc.h:326
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
Definition: shape_arc.cpp:213
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Definition: shape_arc.cpp:971
SHAPE_ARC Reversed() const
Definition: shape_arc.cpp:1006
VECTOR2I m_center
Definition: shape_arc.h:331
int m_width
Definition: shape_arc.h:328
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
int IntersectLine(const SEG &aSeg, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and aSeg, treating aSeg as an infinite line.
Definition: shape_arc.cpp:305
VECTOR2I m_end
Definition: shape_arc.h:327
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,...
Definition: shape_arc.cpp:254
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=DefaultAccuracyForPCB(), double *aEffectiveAccuracy=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:886
double GetRadius() const
Definition: shape_arc.cpp:880
EDA_ANGLE GetStartAngle() const
Definition: shape_arc.cpp:833
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
Definition: shape_arc.cpp:1038
bool IsEffectiveLine() const
Definition: shape_arc.cpp:245
void Reverse()
Definition: shape_arc.cpp:1000
bool NearestPoints(const SHAPE_ARC &aArc, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this arc and aArc.
Definition: shape_arc.cpp:629
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
VECTOR2I m_start
Definition: shape_arc.h:325
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:849
const CIRCLE GetCircle() const
Definition: shape_circle.h:128
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,...
Definition: shape_circle.h:77
int GetRadius() const
Definition: shape_circle.h:118
const VECTOR2I GetCenter() const
Definition: shape_circle.h:123
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN Outline() const
Definition: shape_rect.h:212
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:102
An abstract shape on 2D plane.
Definition: shape.h:126
VECTOR2I::extended_type ecoord
Definition: shape.h:284
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:561
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition: vector2d.h:569
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
std::ostream & operator<<(std::ostream &aStream, const EDA_TEXT &aText)
Definition: eda_text.cpp:1311
a few functions useful in geometry calculations.
int CircleToEndSegmentDeltaRadius(int aInnerCircleRadius, int aSegCount)
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
FLIP_DIRECTION
Definition: mirror.h:27
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
@ SH_ARC
circular arc
Definition: shape.h:54
VECTOR3I v1(5, 5, 5)
VECTOR2I v2(1, 0)
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694