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