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 = KiROUND( mid );
60 m_end = KiROUND( end );
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
195SHAPE_ARC::SHAPE_ARC( const SHAPE_ARC& aOther, int aWidth ) :
196 SHAPE_ARC( aOther )
197{
198 m_width = aWidth;
199}
200
201
203 const EDA_ANGLE& aAngle, double aWidth )
204{
205 m_start = aStart;
206 m_mid = aStart;
207 m_end = aEnd;
208 m_width = aWidth;
209
210 VECTOR2I center( CalcArcCenter( aStart, aEnd, aAngle ) );
211
212 RotatePoint( m_mid, center, -aAngle / 2.0 );
213
215
216 return *this;
217}
218
219
221 const VECTOR2I& aCenter, bool aClockwise,
222 double aWidth )
223{
224 VECTOR2I startLine = aStart - aCenter;
225 VECTOR2I endLine = aEnd - aCenter;
226
227 EDA_ANGLE startAngle( startLine );
228 EDA_ANGLE endAngle( endLine );
229
230 startAngle.Normalize();
231 endAngle.Normalize();
232
233 EDA_ANGLE angle = endAngle - startAngle;
234
235 if( aClockwise )
236 angle = angle.Normalize() - ANGLE_360;
237 else
238 angle = angle.Normalize();
239
240 m_start = aStart;
241 m_end = aEnd;
242 m_mid = aStart;
243
244 RotatePoint( m_mid, aCenter, -angle / 2.0 );
245
247
248 return *this;
249}
250
251
253{
254 SEG v1 = SEG( m_start, m_mid );
255 SEG v2 = SEG( m_mid, m_end );
256
257 return v1.ApproxCollinear( v2 ) && (v1.B - v1.A).Dot(v2.B - v2.A) > 0;
258}
259
260
261bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
262{
265
266 // CIRCLE and SHAPE_CIRCLE store radius as int. When the radius exceeds representable
267 // range, fall back to segment-based candidate generation to avoid integer overflow.
268 if( radius >= (double) std::numeric_limits<int>::max() / 2.0 )
269 {
270 SEG arcSeg1( m_start, m_mid );
271 SEG arcSeg2( m_mid, m_end );
272
273 std::vector<VECTOR2I> candidatePts;
274 candidatePts.push_back( aSeg.NearestPoint( m_start ) );
275 candidatePts.push_back( aSeg.NearestPoint( m_mid ) );
276 candidatePts.push_back( aSeg.NearestPoint( m_end ) );
277 candidatePts.push_back( arcSeg1.NearestPoint( aSeg.A ) );
278 candidatePts.push_back( arcSeg1.NearestPoint( aSeg.B ) );
279 candidatePts.push_back( arcSeg2.NearestPoint( aSeg.A ) );
280 candidatePts.push_back( arcSeg2.NearestPoint( aSeg.B ) );
281 candidatePts.push_back( aSeg.A );
282 candidatePts.push_back( aSeg.B );
283
284 bool any_collides = false;
285
286 for( const VECTOR2I& candidate : candidatePts )
287 {
288 bool collides = Collide( candidate, aClearance, aActual, aLocation );
289 any_collides |= collides;
290
291 if( collides && ( !aActual || *aActual == 0 ) )
292 return true;
293 }
294
295 return any_collides;
296 }
297
299 ecoord clearance_sq = SEG::Square( aClearance );
300
301 // Circle or at least an arc with less space remaining than the clearance
302 if( GetCentralAngle().AsDegrees() > 180.0
303 && ( m_start - m_end ).SquaredEuclideanNorm() < clearance_sq )
304 {
305 ecoord a_dist_sq = ( aSeg.A - center ).SquaredEuclideanNorm();
306 ecoord b_dist_sq = ( aSeg.B - center ).SquaredEuclideanNorm();
307 ecoord radius_sq = SEG::Square( radius - aClearance );
308
309 if( a_dist_sq < radius_sq && b_dist_sq < radius_sq )
310 return false;
311
312
313 return circle.Collide( aSeg, aClearance, aActual, aLocation );
314 }
315
316 // Possible points of the collision are:
317 // 1. Intersetion of the segment with the full circle
318 // 2. Closest point on the segment to the center of the circle
319 // 3. Closest point on the segment to the end points of the arc
320 // 4. End points of the segment
321
322 std::vector<VECTOR2I> candidatePts = circle.GetCircle().Intersect( aSeg );
323
324 candidatePts.push_back( aSeg.NearestPoint( center ) );
325 candidatePts.push_back( aSeg.NearestPoint( m_start ) );
326 candidatePts.push_back( aSeg.NearestPoint( m_end ) );
327 candidatePts.push_back( aSeg.A );
328 candidatePts.push_back( aSeg.B );
329
330 bool any_collides = false;
331
332 for( const VECTOR2I& candidate : candidatePts )
333 {
334 bool collides = Collide( candidate, aClearance, aActual, aLocation );
335 any_collides |= collides;
336
337 if( collides && ( !aActual || *aActual == 0 ) )
338 return true;
339 }
340
341 return any_collides;
342}
343
344
345int SHAPE_ARC::IntersectLine( const SEG& aSeg, std::vector<VECTOR2I>* aIpsBuffer ) const
346{
347 if( aSeg.A == aSeg.B ) // One point does not define a line....
348 return 0;
349
350 if( GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 )
351 return 0;
352
353 CIRCLE circ( GetCenter(), GetRadius() );
354
355 std::vector<VECTOR2I> intersections = circ.IntersectLine( aSeg );
356
357 const size_t originalSize = aIpsBuffer->size();
358
359 for( const VECTOR2I& intersection : intersections )
360 {
361 if( sliceContainsPoint( intersection ) )
362 aIpsBuffer->push_back( intersection );
363 }
364
365 return aIpsBuffer->size() - originalSize;
366}
367
368
369int SHAPE_ARC::Intersect( const CIRCLE& aCircle, std::vector<VECTOR2I>* aIpsBuffer ) const
370{
371 if( GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 )
372 return 0;
373
374 CIRCLE thiscirc( GetCenter(), GetRadius() );
375
376 std::vector<VECTOR2I> intersections = thiscirc.Intersect( aCircle );
377
378 const size_t originalSize = aIpsBuffer->size();
379
380 for( const VECTOR2I& intersection : intersections )
381 {
382 if( sliceContainsPoint( intersection ) )
383 aIpsBuffer->push_back( intersection );
384 }
385
386 return aIpsBuffer->size() - originalSize;
387}
388
389
390int SHAPE_ARC::Intersect( const SHAPE_ARC& aArc, std::vector<VECTOR2I>* aIpsBuffer ) const
391{
392 if( GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0
393 || aArc.GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 )
394 {
395 return 0;
396 }
397
398 CIRCLE thiscirc( GetCenter(), GetRadius() );
399 CIRCLE othercirc( aArc.GetCenter(), aArc.GetRadius() );
400
401 std::vector<VECTOR2I> intersections = thiscirc.Intersect( othercirc );
402
403 const size_t originalSize = aIpsBuffer->size();
404
405 for( const VECTOR2I& intersection : intersections )
406 {
407 if( sliceContainsPoint( intersection ) && aArc.sliceContainsPoint( intersection ) )
408 aIpsBuffer->push_back( intersection );
409 }
410
411 return aIpsBuffer->size() - originalSize;
412}
413
414
416{
418 m_radius = std::sqrt( ( VECTOR2D( m_start ) - m_center ).SquaredEuclideanNorm() );
419
420 std::vector<VECTOR2I> points;
421 // Put start, mid, and end points in the point list
422 points.push_back( m_start );
423 points.push_back( m_mid );
424 points.push_back( m_end );
425
426 EDA_ANGLE start_angle = GetStartAngle();
427 EDA_ANGLE end_angle = start_angle + GetCentralAngle();
428
429 // we always count quadrants clockwise (increasing angle)
430 if( start_angle > end_angle )
431 std::swap( start_angle, end_angle );
432
433 int quad_angle_start = std::ceil( start_angle.AsDegrees() / 90.0 );
434 int quad_angle_end = std::floor( end_angle.AsDegrees() / 90.0 );
435
436 // very large radius means the arc is similar to a segment
437 // so do not try to add more points, center cannot be handled
438 // Very large is here > INT_MAX/2
439 if( m_radius < (double)INT_MAX/2.0 )
440 {
441 const int radius = KiROUND( m_radius );
442
443 // count through quadrants included in arc
444 for( int quad_angle = quad_angle_start; quad_angle <= quad_angle_end; ++quad_angle )
445 {
446 VECTOR2I quad_pt = m_center;
447
448 switch( quad_angle % 4 )
449 {
450 case 0: quad_pt += { radius, 0 }; break;
451 case 1: case -3: quad_pt += { 0, radius }; break;
452 case 2: case -2: quad_pt += { -radius, 0 }; break;
453 case 3: case -1: quad_pt += { 0, -radius }; break;
454 default:
455 assert( false );
456 }
457
458 points.push_back( quad_pt );
459 }
460 }
461
462 m_bbox.Compute( points );
463}
464
465
466const BOX2I SHAPE_ARC::BBox( int aClearance ) const
467{
468 BOX2I bbox( m_bbox );
469
470 if( m_width != 0 )
471 bbox.Inflate( KiROUND( m_width / 2.0 ) + 1 );
472
473 if( aClearance != 0 )
474 bbox.Inflate( aClearance );
475
476 return bbox;
477}
478
479
481{
482 const static int s_epsilon = 8;
483
484 CIRCLE fullCircle( GetCenter(), GetRadius() );
485 VECTOR2I nearestPt = fullCircle.NearestPoint( aP );
486
487 if( ( nearestPt - m_start ).SquaredEuclideanNorm() <= s_epsilon )
488 return m_start;
489
490 if( ( nearestPt - m_end ).SquaredEuclideanNorm() <= s_epsilon )
491 return m_end;
492
493 if( sliceContainsPoint( nearestPt ) )
494 return nearestPt;
495
496 if( ( aP - m_start ).SquaredEuclideanNorm() <= ( aP - m_end ).SquaredEuclideanNorm() )
497 return m_start;
498 else
499 return m_end;
500}
501
502
503bool SHAPE_ARC::NearestPoints( const SHAPE_CIRCLE& aCircle, VECTOR2I& aPtA, VECTOR2I& aPtB,
504 int64_t& aDistSq ) const
505{
506 if( GetCenter() == aCircle.GetCenter() && GetRadius() == aCircle.GetRadius() )
507 {
508 aPtA = aPtB = GetP0();
509 aDistSq = 0;
510 return true;
511 }
512
513 aDistSq = std::numeric_limits<int64_t>::max();
514
515 CIRCLE circle1( GetCenter(), GetRadius() );
516 CIRCLE circle2( aCircle.GetCircle() );
517 std::vector<VECTOR2I> intersections = circle1.Intersect( circle2 );
518
519 for( const VECTOR2I& pt : intersections )
520 {
521 if( sliceContainsPoint( pt ) )
522 {
523 aPtA = aPtB = pt;
524 aDistSq = 0;
525 return true;
526 }
527 }
528
529 std::vector<VECTOR2I> pts = { m_start, m_end, circle1.NearestPoint( aCircle.GetCenter() ) };
530
531 for( const VECTOR2I& pt : pts )
532 {
533 if( sliceContainsPoint( pt ) )
534 {
535 VECTOR2I nearestPt2 = circle2.NearestPoint( pt );
536 int64_t distSq = pt.SquaredDistance( nearestPt2 );
537
538 if( distSq < aDistSq )
539 {
540 aDistSq = distSq;
541 aPtA = pt;
542 aPtB = nearestPt2;
543 }
544 }
545 }
546
547 // Adjust point A by half the arc width towards point B
548 VECTOR2I dir = ( aPtB - aPtA ).Resize( GetWidth() / 2 );
549 aPtA += dir;
550
551 if( aDistSq < SEG::Square( GetWidth() / 2 ) )
552 aDistSq = 0;
553 else
554 aDistSq = aPtA.SquaredDistance( aPtB );
555
556 return true;
557}
558
559
560bool SHAPE_ARC::NearestPoints( const SEG& aSeg, VECTOR2I& aPtA, VECTOR2I& aPtB,
561 int64_t& aDistSq ) const
562{
563 aDistSq = std::numeric_limits<int64_t>::max();
565
566 // First check for intersections on the circle
567 std::vector<VECTOR2I> intersections = circle.Intersect( aSeg );
568
569 for( const VECTOR2I& pt : intersections )
570 {
571 if( sliceContainsPoint( pt ) )
572 {
573 aPtA = aPtB = pt;
574 aDistSq = 0;
575 return true;
576 }
577 }
578
579 // Check the endpoints of the segment against the nearest point on the arc
580 for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
581 {
582 if( sliceContainsPoint( pt ) )
583 {
584 VECTOR2I nearestPt = circle.NearestPoint( pt );
585 int64_t distSq = pt.SquaredDistance( nearestPt );
586
587 if( distSq < aDistSq )
588 {
589 aDistSq = distSq;
590 aPtA = nearestPt;
591 aPtB = pt;
592 }
593 }
594 }
595
596 // Check the endpoints of the arc against the nearest point on the segment
597 for( const VECTOR2I& pt : { m_start, m_end } )
598 {
599 VECTOR2I nearestPt = aSeg.NearestPoint( pt );
600 int64_t distSq = pt.SquaredDistance( nearestPt );
601
602 if( distSq < aDistSq )
603 {
604 aDistSq = distSq;
605 aPtA = pt;
606 aPtB = nearestPt;
607 }
608 }
609
610 // Check the closest points on the segment to the circle (for segments outside the arc)
611 VECTOR2I segNearestPt = aSeg.NearestPoint( GetCenter() );
612
613 if( sliceContainsPoint( segNearestPt ) )
614 {
615 VECTOR2I circleNearestPt = circle.NearestPoint( segNearestPt );
616 int64_t distSq = segNearestPt.SquaredDistance( circleNearestPt );
617
618 if( distSq < aDistSq )
619 {
620 aDistSq = distSq;
621 aPtA = segNearestPt;
622 aPtB = circleNearestPt;
623 }
624 }
625
626 // Adjust point A by half the arc width towards point B
627 VECTOR2I dir = ( aPtB - aPtA ).Resize( GetWidth() / 2 );
628 aPtA += dir;
629
630 if( aDistSq < SEG::Square( GetWidth() / 2 ) )
631 aDistSq = 0;
632 else
633 aDistSq = aPtA.SquaredDistance( aPtB );
634
635 return true;
636}
637
638
639bool SHAPE_ARC::NearestPoints( const SHAPE_RECT& aRect, VECTOR2I& aPtA, VECTOR2I& aPtB,
640 int64_t& aDistSq ) const
641{
642 aDistSq = std::numeric_limits<int64_t>::max();
643
644 SHAPE_LINE_CHAIN lineChain( aRect.Outline() );
645
646 // Reverse the output points to match the rect_outline/arc order
647 lineChain.NearestPoints( this, aPtB, aPtA );
648 aDistSq = aPtA.SquaredDistance( aPtB );
649 return true;
650}
651
652
653bool SHAPE_ARC::NearestPoints( const SHAPE_ARC& aArc, VECTOR2I& aPtA, VECTOR2I& aPtB,
654 int64_t& aDistSq ) const
655{
656 auto adjustForArcWidths =
657 [&]()
658 {
659 // Adjust point A by half the arc-width towards point B
660 VECTOR2I dir = ( aPtB - aPtA ).Resize( GetWidth() / 2 );
661 aPtA += dir;
662
663 // Adjust point B by half the other arc-width towards point A
664 dir = ( aPtA - aPtB ).Resize( aArc.GetWidth() / 2 );
665 aPtB += dir;
666
667 if( aDistSq < SEG::Square( GetWidth() / 2 + aArc.GetWidth() / 2 ) )
668 aDistSq = 0;
669 else
670 aDistSq = aPtA.SquaredDistance( aPtB );
671 };
672
673 aDistSq = std::numeric_limits<int64_t>::max();
674
675 VECTOR2I center1 = GetCenter();
676 VECTOR2I center2 = aArc.GetCenter();
677
678 // Centers aren't exact, so center_dist_sq won't be exact either
679 int64_t center_dist_sq = center1.SquaredDistance( center2 );
680 int64_t center_epsilon = KiROUND( std::min( m_radius, aArc.GetRadius() ) / 1000 );
681 bool colocated = center_dist_sq < center_epsilon * center_epsilon;
682
683 // Start by checking endpoints
684 std::vector<VECTOR2I> pts1 = { m_start, m_end };
685 std::vector<VECTOR2I> pts2 = { aArc.GetP0(), aArc.GetP1() };
686
687 for( const VECTOR2I& pt1 : pts1 )
688 {
689 for( const VECTOR2I& pt2 : pts2 )
690 {
691 int64_t distSq = pt1.SquaredDistance( pt2 );
692
693 if( distSq < aDistSq )
694 {
695 aDistSq = distSq;
696 aPtA = pt1;
697 aPtB = pt2;
698
699 if( aDistSq == 0 )
700 return true;
701 }
702 }
703 }
704
705 for( const VECTOR2I& pt : pts1 )
706 {
707 if( aArc.sliceContainsPoint( pt ) )
708 {
709 CIRCLE circle( center2, aArc.GetRadius() );
710 aPtA = pt;
711 aPtB = circle.NearestPoint( pt );
712 aDistSq = aPtA.SquaredDistance( aPtB );
713
714 if( colocated || aDistSq == 0 )
715 {
716 if( aDistSq != 0 )
717 adjustForArcWidths();
718
719 return true;
720 }
721 }
722 }
723
724 for( const VECTOR2I& pt : pts2 )
725 {
726 if( sliceContainsPoint( pt ) )
727 {
728 CIRCLE circle( center1, GetRadius() );
729 aPtA = circle.NearestPoint( pt );
730 aPtB = pt;
731 aDistSq = aPtA.SquaredDistance( aPtB );
732
733 if( colocated || aDistSq == 0 )
734 {
735 if( aDistSq != 0 )
736 adjustForArcWidths();
737
738 return true;
739 }
740 }
741 }
742
743 // The remaining checks are require the arcs to be on non-concentric circles
744 if( colocated )
745 return true;
746
747 CIRCLE circle1( center1, GetRadius() );
748 CIRCLE circle2( center2, aArc.GetRadius() );
749
750 // First check for intersections on the circles
751 std::vector<VECTOR2I> intersections = circle1.Intersect( circle2 );
752
753 for( const VECTOR2I& pt : intersections )
754 {
755 if( sliceContainsPoint( pt ) && aArc.sliceContainsPoint( pt ) )
756 {
757 aPtA = pt;
758 aPtB = pt;
759 aDistSq = 0;
760 return true;
761 }
762 }
763
764 // Check for the closest points on the circles
765 VECTOR2I pt1 = circle1.NearestPoint( center2 );
766 VECTOR2I pt2 = circle2.NearestPoint( center1 );
767 bool pt1InSlice = sliceContainsPoint( pt1 );
768 bool pt2InSlice = aArc.sliceContainsPoint( pt2 );
769
770 if( pt1InSlice && pt2InSlice )
771 {
772 int64_t distSq = pt1.SquaredDistance( pt2 );
773
774 if( distSq < aDistSq )
775 {
776 aDistSq = distSq;
777 aPtA = pt1;
778 aPtB = pt2;
779 }
780
781 adjustForArcWidths();
782 return true;
783 }
784
785 // Check the endpoints of arc 1 against the nearest point on arc 2
786 if( pt2InSlice )
787 {
788 for( const VECTOR2I& pt : pts1 )
789 {
790 int64_t distSq = pt.SquaredDistance( pt2 );
791
792 if( distSq < aDistSq )
793 {
794 aDistSq = distSq;
795 aPtA = pt;
796 aPtB = pt2;
797 }
798 }
799 }
800
801 // Check the endpoints of arc 2 against the nearest point on arc 1
802 if( pt1InSlice )
803 {
804 for( const VECTOR2I& pt : pts2 )
805 {
806 int64_t distSq = pt.SquaredDistance( pt1 );
807
808 if( distSq < aDistSq )
809 {
810 aDistSq = distSq;
811 aPtA = pt1;
812 aPtB = pt;
813 }
814 }
815 }
816
817 adjustForArcWidths();
818 return true;
819}
820
821
822bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
823 VECTOR2I* aLocation ) const
824{
825 int minDist = aClearance + m_width / 2;
826 auto bbox = BBox( minDist );
827
828 // Fast check using bounding box:
829 if( !bbox.Contains( aP ) )
830 return false;
831
834
835 // CIRCLE stores radius as int. When the radius exceeds representable range the arc is
836 // nearly straight, so approximate it as two segments through the midpoint.
837 if( radius >= (double) std::numeric_limits<int>::max() / 2.0 )
838 {
839 SEG seg1( m_start, m_mid );
840 SEG seg2( m_mid, m_end );
841 int dist1 = seg1.Distance( aP );
842 int dist2 = seg2.Distance( aP );
843 int dist = std::min( dist1, dist2 );
844
845 if( dist <= minDist )
846 {
847 if( aActual )
848 *aActual = std::max( 0, dist - m_width / 2 );
849
850 if( aLocation )
851 *aLocation = ( dist1 <= dist2 ) ? seg1.NearestPoint( aP ) : seg2.NearestPoint( aP );
852
853 return true;
854 }
855
856 return false;
857 }
858
859 CIRCLE fullCircle( center, radius );
860 VECTOR2D nearestPt = fullCircle.NearestPoint( VECTOR2D( aP ) );
861 int dist = KiROUND( nearestPt.Distance( aP ) );
862 EDA_ANGLE angleToPt( aP - fullCircle.Center ); // Angle from center to the point
863
864 if( !dist )
865 {
866 // Be sure to keep the sqrt of the squared distance instead of allowing a EuclideanNorm
867 // because this trucates the distance to an integer before subtracting
868 dist = KiROUND( radius - sqrt( ( aP - center ).SquaredEuclideanNorm() ) );
869 nearestPt = center + VECTOR2I( radius, 0 );
870 RotatePoint( nearestPt, center, -angleToPt );
871 }
872
873 // If not a 360 degree arc, need to use arc angles to decide if point collides
874 if( m_start != m_end )
875 {
876 bool ccw = GetCentralAngle() > ANGLE_0;
877 EDA_ANGLE rotatedPtAngle = ( angleToPt.Normalize() - GetStartAngle() ).Normalize();
878 EDA_ANGLE rotatedEndAngle = ( GetEndAngle() - GetStartAngle() ).Normalize();
879
880 if( ( ccw && rotatedPtAngle > rotatedEndAngle )
881 || ( !ccw && rotatedPtAngle < rotatedEndAngle ) )
882 {
883 int distStartpt = ( aP - m_start ).EuclideanNorm();
884 int distEndpt = ( aP - m_end ).EuclideanNorm();
885
886 if( distStartpt < distEndpt )
887 {
888 dist = distStartpt;
889 nearestPt = m_start;
890 }
891 else
892 {
893 dist = distEndpt;
894 nearestPt = m_end;
895 }
896 }
897 }
898
899 if( dist <= minDist )
900 {
901 if( aLocation )
902 *aLocation = nearestPt;
903
904 if( aActual )
905 *aActual = std::max( 0, dist - m_width / 2 );
906
907 return true;
908 }
909
910 return false;
911}
912
913
915{
917 EDA_ANGLE angle( m_start - center );
918 return angle.Normalize();
919}
920
921
923{
925 EDA_ANGLE angle( m_end - center );
926 return angle.Normalize();
927}
928
929
931{
932 return m_center;
933}
934
935
937{
938 double radius = GetRadius();
939 EDA_ANGLE includedAngle = GetCentralAngle();
940
941 return std::abs( radius * includedAngle.AsRadians() );
942}
943
944
946{
947 // Arcs with same start and end points can be 0 deg or 360 deg arcs.
948 // However, they are expected to be circles.
949 // So return 360 degrees as central arc:
950 if( m_start == m_end )
951 return ANGLE_360;
952
955
956 // Using only m_start and m_end arc points to calculate the central arc is not enough
957 // there are 2 arcs having the same center and end points.
958 // Using the middle point is mandatory to know what arc is the right one.
959 // IsCCW() uses m_start, m_middle and m_end arc points to know the arc orientation
960 if( IsCCW() )
961 {
962 if( angle < ANGLE_0 )
963 angle += ANGLE_360;
964 }
965 else
966 {
967 if( angle > ANGLE_0 )
968 angle -= ANGLE_360;
969 }
970
971 return angle;
972}
973
974
976{
977 return m_radius;
978}
979
980
981const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( int aMaxError, int* aActualError ) const
982{
984 double r = GetRadius();
986 VECTOR2I c = GetCenter();
988
989 SEG startToEnd( GetP0(), GetP1() );
990 double halfMaxError = std::max( 1.0, aMaxError / 2.0 );
991
992 int n;
993
994 // To calculate the arc to segment count, use the external radius instead of the radius.
995 // for a arc with small radius and large width, the difference can be significant
996 double external_radius = r + ( m_width / 2.0 );
997 double effectiveError;
998
999 if( external_radius < halfMaxError
1000 || startToEnd.Distance( GetArcMid() ) < halfMaxError ) // Should be a very rare case
1001 {
1002 // In this case, the arc is approximated by one segment, with a effective error
1003 // between -aMaxError/2 and +aMaxError/2, as expected.
1004 n = 0;
1005 effectiveError = external_radius;
1006 }
1007 else
1008 {
1009 n = GetArcToSegmentCount( external_radius, aMaxError, ca );
1010
1011 // Recalculate the effective error of approximation, that can be < aMaxError
1012 int seg360 = n * 360.0 / fabs( ca.AsDegrees() );
1013 effectiveError = CircleToEndSegmentDeltaRadius( external_radius, seg360 );
1014 }
1015
1016 // Split the error on either side of the arc. Since we want the start and end points
1017 // to be exactly on the arc, the first and last segments need to be shorter to stay within
1018 // the error band (since segments normally start 1/2 the error band outside the arc).
1019 r += effectiveError / 2;
1020 n = n * 2;
1021
1022 rv.Append( m_start );
1023
1024 for( int i = 1; i < n ; i += 2 )
1025 {
1026 EDA_ANGLE a = sa;
1027
1028 if( n != 0 )
1029 a += ( ca * i ) / n;
1030
1031 double x = c.x + r * a.Cos();
1032 double y = c.y + r * a.Sin();
1033
1034 rv.Append( KiROUND( x ), KiROUND( y ) );
1035 }
1036
1037 rv.Append( m_end );
1038
1039 if( aActualError )
1040 *aActualError = KiROUND( effectiveError );
1041
1042 return rv;
1043}
1044
1045
1046void SHAPE_ARC::Move( const VECTOR2I& aVector )
1047{
1048 m_start += aVector;
1049 m_end += aVector;
1050 m_mid += aVector;
1051 update_values();
1052}
1053
1054
1055void SHAPE_ARC::Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter )
1056{
1057 RotatePoint( m_start, aCenter, aAngle );
1058 RotatePoint( m_end, aCenter, aAngle );
1059 RotatePoint( m_mid, aCenter, aAngle );
1060
1061 update_values();
1062}
1063
1064
1065void SHAPE_ARC::Mirror( const VECTOR2I& aVector, FLIP_DIRECTION aFlipDirection )
1066{
1067 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
1068 {
1069 m_start.x = -m_start.x + 2 * aVector.x;
1070 m_end.x = -m_end.x + 2 * aVector.x;
1071 m_mid.x = -m_mid.x + 2 * aVector.x;
1072 }
1073 else
1074 {
1075 m_start.y = -m_start.y + 2 * aVector.y;
1076 m_end.y = -m_end.y + 2 * aVector.y;
1077 m_mid.y = -m_mid.y + 2 * aVector.y;
1078 }
1079
1080 update_values();
1081}
1082
1083
1084void SHAPE_ARC::Mirror( const SEG& axis )
1085{
1086 m_start = axis.ReflectPoint( m_start );
1087 m_end = axis.ReflectPoint( m_end );
1088 m_mid = axis.ReflectPoint( m_mid );
1089
1090 update_values();
1091}
1092
1093
1095{
1096 std::swap( m_start, m_end );
1097}
1098
1099
1101{
1102 return SHAPE_ARC( m_end, m_mid, m_start, m_width );
1103}
1104
1105
1107{
1110 EDA_ANGLE ea = sa + ca;
1111
1112 EDA_ANGLE phi( p - GetCenter() ); // Angle from center to the point
1113 phi.Normalize();
1114
1115 if( ca >= ANGLE_0 )
1116 {
1117 while( phi < sa )
1118 phi += ANGLE_360;
1119
1120 return phi >= sa && phi <= ea;
1121 }
1122 else
1123 {
1124 while( phi > sa )
1125 phi -= ANGLE_360;
1126
1127 return phi <= sa && phi >= ea;
1128 }
1129}
1130
1131
1132void SHAPE_ARC::TransformToPolygon( SHAPE_POLY_SET& aBuffer, int aError, ERROR_LOC aErrorLoc ) const
1133{
1134 TransformArcToPolygon( aBuffer, m_start, m_mid, m_end, m_width, aError, aErrorLoc );
1135}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
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
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:229
double Sin() const
Definition eda_angle.h:178
double AsDegrees() const
Definition eda_angle.h:116
double AsRadians() const
Definition eda_angle.h:120
double Cos() const
Definition eda_angle.h:197
Definition seg.h:42
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition seg.cpp:664
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:633
int Length() const
Return the length (this).
Definition seg.h:343
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:446
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:702
EDA_ANGLE GetCentralAngle() const
Get the "central angle" of the arc - this is the angle at the point of the "pie slice".
double m_radius
Definition shape_arc.h:338
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:120
void update_values()
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
void Move(const VECTOR2I &aVector) override
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.
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
EDA_ANGLE GetEndAngle() const
bool IsCCW() const
Definition shape_arc.h:314
double GetLength() const
BOX2I m_bbox
Definition shape_arc.h:336
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter) override
Rotate the arc by a given angle about a point.
bool sliceContainsPoint(const VECTOR2I &p) const
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
VECTOR2I NearestPoint(const VECTOR2I &aP) const
int Intersect(const CIRCLE &aArc, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and a CIRCLE.
VECTOR2I m_mid
Definition shape_arc.h:332
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.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
SHAPE_ARC Reversed() const
VECTOR2I m_center
Definition shape_arc.h:337
int m_width
Definition shape_arc.h:334
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
int IntersectLine(const SEG &aSeg, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and aSeg, treating aSeg as an infinite line.
VECTOR2I m_end
Definition shape_arc.h:333
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,...
double GetRadius() const
EDA_ANGLE GetStartAngle() const
bool IsEffectiveLine() const
void Reverse()
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
VECTOR2I m_start
Definition shape_arc.h:331
const VECTOR2I & GetCenter() const
const CIRCLE GetCircle() const
int GetRadius() const
const VECTOR2I GetCenter() const
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.
Represent a set of closed polygons.
const SHAPE_LINE_CHAIN Outline() const
VECTOR2I::extended_type ecoord
Definition shape.h:301
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
Definition shape.h:136
bool NearestPoints(const SHAPE *aOther, VECTOR2I &aPtThis, VECTOR2I &aPtOther) const
Return the two points that mark the closest distance between this shape and aOther.
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:411
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
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
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:28
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
@ SH_ARC
circular arc
Definition shape.h:54
std::ostream & operator<<(std::ostream &aStream, const SHAPE_ARC &aArc)
Definition shape_arc.cpp:37
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
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
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:696