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