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