KiCad PCB EDA Suite
Loading...
Searching...
No Matches
convert_basic_shapes_to_polygon.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <algorithm> // for max, min
26#include <bitset> // for bitset::count
27#include <math.h> // for atan2
28
31#include <geometry/shape_line_chain.h> // for SHAPE_LINE_CHAIN
32#include <geometry/shape_poly_set.h> // for SHAPE_POLY_SET, SHAPE_POLY_SE...
33#include <math/util.h>
34#include <math/vector2d.h> // for VECTOR2I
35#include <trigo.h>
36
37
38void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer, const VECTOR2I& aCenter, int aRadius,
39 int aError, ERROR_LOC aErrorLoc, int aMinSegCount )
40{
41 VECTOR2I corner_position;
42 int numSegs = GetArcToSegmentCount( aRadius, aError, FULL_CIRCLE );
43 numSegs = std::max( aMinSegCount, numSegs );
44
45 // The shape will be built with a even number of segs. Reason: the horizontal
46 // diameter begins and ends to points on the actual circle, or circle
47 // expanded by aError if aErrorLoc == ERROR_OUTSIDE.
48 // This is used by Arc to Polygon shape convert.
49 if( numSegs & 1 )
50 numSegs++;
51
52 EDA_ANGLE delta = ANGLE_360 / numSegs;
53 int radius = aRadius;
54
55 if( aErrorLoc == ERROR_OUTSIDE )
56 {
57 // The outer radius should be radius+aError
58 // Recalculate the actual approx error, as it can be smaller than aError
59 // because numSegs is clamped to a minimal value
60 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
61 radius += GetCircleToPolyCorrection( actual_delta_radius );
62 }
63
64 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
65 {
66 corner_position.x = radius;
67 corner_position.y = 0;
68 RotatePoint( corner_position, angle );
69 corner_position += aCenter;
70 aBuffer.Append( corner_position.x, corner_position.y );
71 }
72
73 aBuffer.SetClosed( true );
74}
75
76
77void TransformCircleToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aCenter, int aRadius,
78 int aError, ERROR_LOC aErrorLoc, int aMinSegCount )
79{
80 VECTOR2I corner_position;
81 int numSegs = GetArcToSegmentCount( aRadius, aError, FULL_CIRCLE );
82 numSegs = std::max( aMinSegCount, numSegs );
83
84 // The shape will be built with a even number of segs. Reason: the horizontal
85 // diameter begins and ends to points on the actual circle, or circle
86 // expanded by aError if aErrorLoc == ERROR_OUTSIDE.
87 // This is used by Arc to Polygon shape convert.
88 if( numSegs & 1 )
89 numSegs++;
90
91 EDA_ANGLE delta = ANGLE_360 / numSegs;
92 int radius = aRadius;
93
94 if( aErrorLoc == ERROR_OUTSIDE )
95 {
96 // The outer radius should be radius+aError
97 // Recalculate the actual approx error, as it can be smaller than aError
98 // because numSegs is clamped to a minimal value
99 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
100 radius += GetCircleToPolyCorrection( actual_delta_radius );
101 }
102
103 aBuffer.NewOutline();
104
105 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
106 {
107 corner_position.x = radius;
108 corner_position.y = 0;
109 RotatePoint( corner_position, angle );
110 corner_position += aCenter;
111 aBuffer.Append( corner_position.x, corner_position.y );
112 }
113
114 // Finish circle
115 corner_position.x = radius;
116 corner_position.y = 0;
117 corner_position += aCenter;
118 aBuffer.Append( corner_position.x, corner_position.y );
119}
120
121
122void TransformOvalToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aStart, const VECTOR2I& aEnd,
123 int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount )
124{
125 // To build the polygonal shape outside the actual shape, we use a bigger
126 // radius to build rounded ends.
127 // However, the width of the segment is too big.
128 // so, later, we will clamp the polygonal shape with the bounding box
129 // of the segment.
130 int radius = aWidth / 2;
131 int numSegs = GetArcToSegmentCount( radius, aError, FULL_CIRCLE );
132 numSegs = std::max( aMinSegCount, numSegs );
133
134 // Round up to 8 to make segment approximations align properly at 45-degrees
135 numSegs = ( numSegs + 7 ) / 8 * 8;
136
137 EDA_ANGLE delta = ANGLE_360 / numSegs;
138
139 if( aErrorLoc == ERROR_OUTSIDE )
140 {
141 // The outer radius should be radius+aError
142 // Recalculate the actual approx error, as it can be smaller than aError
143 // because numSegs is clamped to a minimal value
144 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
145 int correction = GetCircleToPolyCorrection( actual_delta_radius );
146 radius += correction;
147 }
148
149 // end point is the coordinate relative to aStart
150 VECTOR2I endp = aEnd - aStart;
151 VECTOR2I startp = aStart;
152 VECTOR2I corner;
153 SHAPE_POLY_SET polyshape;
154
155 polyshape.NewOutline();
156
157 // normalize the position in order to have endp.x >= 0
158 // it makes calculations more easy to understand
159 if( endp.x < 0 )
160 {
161 endp = aStart - aEnd;
162 startp = aEnd;
163 }
164
165 EDA_ANGLE delta_angle( endp );
166 int seg_len = endp.EuclideanNorm();
167
168 // Compute the outlines of the segment, and creates a polygon
169 // Note: the polygonal shape is built from the equivalent horizontal
170 // segment starting at {0,0}, and ending at {seg_len,0}
171
172 // add right rounded end:
173
174 // Right arc start:
175 corner = VECTOR2I( seg_len, radius );
176 polyshape.Append( corner.x, corner.y );
177
178 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_180; angle += delta )
179 {
180 corner = VECTOR2I( 0, radius );
181 RotatePoint( corner, angle );
182 corner.x += seg_len;
183 polyshape.Append( corner.x, corner.y );
184 }
185
186 // Finish right arc:
187 corner = VECTOR2I( seg_len, -radius );
188 polyshape.Append( corner.x, corner.y );
189
190 // Left arc start:
191 corner = VECTOR2I( 0, -radius );
192 polyshape.Append( corner.x, corner.y );
193
194 // add left rounded end:
195 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_180; angle += delta )
196 {
197 corner = VECTOR2I( 0, -radius );
198 RotatePoint( corner, angle );
199 polyshape.Append( corner.x, corner.y );
200 }
201
202 // Finish left arc:
203 corner = VECTOR2I( 0, radius );
204 polyshape.Append( corner.x, corner.y );
205
206 // Now trim the edges of the polygonal shape which will be slightly outside the
207 // track width.
208 SHAPE_POLY_SET bbox;
209 bbox.NewOutline();
210 // Build the bbox (a horizontal rectangle).
211 int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
212 corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
213 // creating useless corner at segment ends
214 corner.y = halfwidth;
215 bbox.Append( corner.x, corner.y );
216 corner.y = -halfwidth;
217 bbox.Append( corner.x, corner.y );
218 corner.x = radius + seg_len + 2;
219 bbox.Append( corner.x, corner.y );
220 corner.y = halfwidth;
221 bbox.Append( corner.x, corner.y );
222
223 // Now, clamp the shape
225 // Note the final polygon is a simple, convex polygon with no hole
226 // due to the shape of initial polygons
227
228 // Rotate and move the polygon to its right location
229 polyshape.Rotate( -delta_angle );
230 polyshape.Move( startp );
231
232 aBuffer.Append( polyshape);
233}
234
235
237{
240 ROUNDED_CORNER( int x, int y ) : m_position( VECTOR2I( x, y ) ), m_radius( 0 ) {}
241 ROUNDED_CORNER( int x, int y, int radius ) : m_position( VECTOR2I( x, y ) ), m_radius( radius ) {}
242};
243
244
245// Corner List requirements: no concave shape, corners in clockwise order, no duplicate corners
246void CornerListToPolygon( SHAPE_POLY_SET& outline, std::vector<ROUNDED_CORNER>& aCorners,
247 int aInflate, int aError, ERROR_LOC aErrorLoc )
248{
249 assert( aInflate >= 0 );
250 outline.NewOutline();
251 VECTOR2I incoming = aCorners[0].m_position - aCorners.back().m_position;
252
253 for( int n = 0, count = aCorners.size(); n < count; n++ )
254 {
255 ROUNDED_CORNER& cur = aCorners[n];
256 ROUNDED_CORNER& next = aCorners[( n + 1 ) % count];
257 VECTOR2I outgoing = next.m_position - cur.m_position;
258
259 if( !( aInflate || cur.m_radius ) )
260 {
261 outline.Append( cur.m_position );
262 }
263 else
264 {
265 VECTOR2I cornerPosition = cur.m_position;
266 int radius = cur.m_radius;
267 EDA_ANGLE endAngle;
268 double tanAngle2;
269
270 if( ( incoming.x == 0 && outgoing.y == 0 ) || ( incoming.y == 0 && outgoing.x == 0 ) )
271 {
272 endAngle = ANGLE_90;
273 tanAngle2 = 1.0;
274 }
275 else
276 {
277 double cosNum = (double) incoming.x * outgoing.x + (double) incoming.y * outgoing.y;
278 double cosDen = (double) incoming.EuclideanNorm() * outgoing.EuclideanNorm();
279 double angle = acos( cosNum / cosDen );
280 tanAngle2 = tan( ( M_PI - angle ) / 2 );
281 endAngle = EDA_ANGLE( angle, RADIANS_T );
282 }
283
284 if( aInflate && tanAngle2 )
285 {
286 radius += aInflate;
287 cornerPosition += incoming.Resize( aInflate / tanAngle2 )
288 + incoming.Perpendicular().Resize( -aInflate );
289 }
290
291 // Ensure 16+ segments per 360deg and ensure first & last segment are the same size
292 int numSegs = std::max( 16, GetArcToSegmentCount( radius, aError, FULL_CIRCLE ) );
293 EDA_ANGLE angDelta = ANGLE_360 / numSegs;
294 EDA_ANGLE lastSeg = endAngle;
295
296 if( lastSeg > ANGLE_0 )
297 {
298 while( lastSeg > angDelta )
299 lastSeg -= angDelta;
300 }
301 else
302 {
303 while( lastSeg < -angDelta )
304 lastSeg += angDelta;
305 }
306
307 EDA_ANGLE angPos = lastSeg.IsZero() ? angDelta : ( angDelta + lastSeg ) / 2;
308
309 double arcTransitionDistance = ( tanAngle2 > 0 ) ? ( radius / tanAngle2 ) : 0;
310 VECTOR2I arcStart = cornerPosition - incoming.Resize( arcTransitionDistance );
311 VECTOR2I arcCenter = arcStart + incoming.Perpendicular().Resize( radius );
312 VECTOR2I arcEnd, arcStartOrigin;
313
314 if( aErrorLoc == ERROR_INSIDE )
315 {
316 arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( arcStart );
317 arcStartOrigin = arcStart - arcCenter;
318 outline.Append( arcStart );
319 }
320 else
321 {
322 // The outer radius should be radius+aError, recalculate because numSegs is clamped
323 int actualDeltaRadius = CircleToEndSegmentDeltaRadius( radius, numSegs );
324 int radiusExtend = GetCircleToPolyCorrection( actualDeltaRadius );
325 arcStart += incoming.Perpendicular().Resize( -radiusExtend );
326 arcStartOrigin = arcStart - arcCenter;
327
328 // To avoid "ears", we only add segments crossing/within the non-rounded outline
329 // Note: outlineIn is short and must be treated as defining an infinite line
330 SEG outlineIn( cornerPosition - incoming, cornerPosition );
331 VECTOR2I prevPt = arcStart;
332 arcEnd = cornerPosition; // default if no points within the outline are found
333
334 while( angPos < endAngle )
335 {
336 VECTOR2I pt = arcStartOrigin;
337 RotatePoint( pt, -angPos );
338 pt += arcCenter;
339 angPos += angDelta;
340
341 if( outlineIn.Side( pt ) > 0 )
342 {
343 OPT_VECTOR2I intersect = outlineIn.IntersectLines( SEG( prevPt, pt ) );
344
345 wxCHECK_RET( intersect, wxT( "No solutions exist!" ) );
346 outline.Append( *intersect );
347 outline.Append( pt );
348 arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( *intersect );
349 break;
350 }
351
352 endAngle -= angDelta; // if skipping first, also skip last
353 prevPt = pt;
354 }
355 }
356
357 for( ; angPos < endAngle; angPos += angDelta )
358 {
359 VECTOR2I pt = arcStartOrigin;
360 RotatePoint( pt, -angPos );
361 outline.Append( pt + arcCenter );
362 }
363
364 outline.Append( arcEnd );
365 }
366
367 incoming = outgoing;
368 }
369}
370
371
372void CornerListRemoveDuplicates( std::vector<ROUNDED_CORNER>& aCorners )
373{
374 VECTOR2I prev = aCorners[0].m_position;
375
376 for( int pos = aCorners.size() - 1; pos >= 0; pos-- )
377 {
378 if( aCorners[pos].m_position == prev )
379 aCorners.erase( aCorners.begin() + pos );
380 else
381 prev = aCorners[pos].m_position;
382 }
383}
384
385
386void TransformTrapezoidToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aPosition,
387 const VECTOR2I& aSize, const EDA_ANGLE& aRotation, int aDeltaX,
388 int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc )
389{
390 SHAPE_POLY_SET outline;
391 VECTOR2I size( aSize / 2 );
392 std::vector<ROUNDED_CORNER> corners;
393
394 if( aInflate < 0 )
395 {
396 if( !aDeltaX && !aDeltaY ) // rectangle
397 {
398 size.x = std::max( 1, size.x + aInflate );
399 size.y = std::max( 1, size.y + aInflate );
400 }
401 else if( aDeltaX ) // horizontal trapezoid
402 {
403 double slope = (double) aDeltaX / size.x;
404 int yShrink = KiROUND( ( std::hypot( size.x, aDeltaX ) * aInflate ) / size.x );
405 size.y = std::max( 1, size.y + yShrink );
406 size.x = std::max( 1, size.x + aInflate );
407 aDeltaX = KiROUND( size.x * slope );
408
409 if( aDeltaX > size.y ) // shrinking turned the trapezoid into a triangle
410 {
411 corners.reserve( 3 );
412 corners.emplace_back( -size.x, -size.y - aDeltaX );
413 corners.emplace_back( KiROUND( size.y / slope ), 0 );
414 corners.emplace_back( -size.x, size.y + aDeltaX );
415 }
416 }
417 else // vertical trapezoid
418 {
419 double slope = (double) aDeltaY / size.y;
420 int xShrink = KiROUND( ( std::hypot( size.y, aDeltaY ) * aInflate ) / size.y );
421 size.x = std::max( 1, size.x + xShrink );
422 size.y = std::max( 1, size.y + aInflate );
423 aDeltaY = KiROUND( size.y * slope );
424
425 if( aDeltaY > size.x )
426 {
427 corners.reserve( 3 );
428 corners.emplace_back( 0, -KiROUND( size.x / slope ) );
429 corners.emplace_back( size.x + aDeltaY, size.y );
430 corners.emplace_back( -size.x - aDeltaY, size.y );
431 }
432 }
433
434 aInflate = 0;
435 }
436
437 if( corners.empty() )
438 {
439 corners.reserve( 4 );
440 corners.emplace_back( -size.x + aDeltaY, -size.y - aDeltaX );
441 corners.emplace_back( size.x - aDeltaY, -size.y + aDeltaX );
442 corners.emplace_back( size.x + aDeltaY, size.y - aDeltaX );
443 corners.emplace_back( -size.x - aDeltaY, size.y + aDeltaX );
444
445 if( std::abs( aDeltaY ) == std::abs( size.x ) || std::abs( aDeltaX ) == std::abs( size.y ) )
447 }
448
449 CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
450
451 if( !aRotation.IsZero() )
452 outline.Rotate( aRotation );
453
454 outline.Move( VECTOR2I( aPosition ) );
455 aBuffer.Append( outline );
456}
457
458
460 const VECTOR2I& aSize, const EDA_ANGLE& aRotation,
461 int aCornerRadius, double aChamferRatio,
462 int aChamferCorners, int aInflate, int aError,
463 ERROR_LOC aErrorLoc )
464{
465 SHAPE_POLY_SET outline;
466 VECTOR2I size( aSize / 2 );
467 int chamferCnt = std::bitset<8>( aChamferCorners ).count();
468 double chamferDeduct = 0;
469
470 if( aInflate < 0 )
471 {
472 size.x = std::max( 1, size.x + aInflate );
473 size.y = std::max( 1, size.y + aInflate );
474 chamferDeduct = aInflate * ( 2.0 - M_SQRT2 );
475 aCornerRadius = std::max( 0, aCornerRadius + aInflate );
476 aInflate = 0;
477 }
478
479 std::vector<ROUNDED_CORNER> corners;
480 corners.reserve( 4 + chamferCnt );
481 corners.emplace_back( -size.x, -size.y, aCornerRadius );
482 corners.emplace_back( size.x, -size.y, aCornerRadius );
483 corners.emplace_back( size.x, size.y, aCornerRadius );
484 corners.emplace_back( -size.x, size.y, aCornerRadius );
485
486 if( aChamferCorners )
487 {
488 int shorterSide = std::min( aSize.x, aSize.y );
489 int chamfer = std::max( 0, KiROUND( aChamferRatio * shorterSide + chamferDeduct ) );
492 int sign[8] = { 0, 1, -1, 0, 0, -1, 1, 0 };
493
494 for( int cc = 0, pos = 0; cc < 4; cc++, pos++ )
495 {
496 if( !( aChamferCorners & chamId[cc] ) )
497 continue;
498
499 corners[pos].m_radius = 0;
500
501 if( chamfer == 0 )
502 continue;
503
504 corners.insert( corners.begin() + pos + 1, corners[pos] );
505 corners[pos].m_position.x += sign[( 2 * cc ) & 7] * chamfer;
506 corners[pos].m_position.y += sign[( 2 * cc - 2 ) & 7] * chamfer;
507 corners[pos + 1].m_position.x += sign[( 2 * cc + 1 ) & 7] * chamfer;
508 corners[pos + 1].m_position.y += sign[( 2 * cc - 1 ) & 7] * chamfer;
509 pos++;
510 }
511
512 if( chamferCnt > 1 && 2 * chamfer >= shorterSide )
514 }
515
516 CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
517
518 if( !aRotation.IsZero() )
519 outline.Rotate( aRotation );
520
521 outline.Move( aPosition );
522 aBuffer.Append( outline );
523}
524
525
526int ConvertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
527 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aArcAngle,
528 double aAccuracy, ERROR_LOC aErrorLoc )
529{
530 int n = 2;
531
532 if( aRadius >= aAccuracy )
533 n = GetArcToSegmentCount( aRadius, aAccuracy, aArcAngle ) + 1;
534
535 EDA_ANGLE delta = aArcAngle / n;
536
537 if( aErrorLoc == ERROR_INSIDE )
538 {
539 // This is the easy case: with the error on the inside the endpoints of each segment
540 // are error-free.
541
542 EDA_ANGLE rot = aStartAngle;
543
544 for( int i = 0; i <= n; i++, rot += delta )
545 {
546 double x = aCenter.x + aRadius * rot.Cos();
547 double y = aCenter.y + aRadius * rot.Sin();
548
549 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
550 }
551 }
552 else
553 {
554 // This is the hard case: with the error on the outside it's the segment midpoints
555 // that are error-free. So we need to add a half-segment at each end of the arc to get
556 // them correct.
557
558 int seg360 = std::abs( KiROUND( n * 360.0 / aArcAngle.AsDegrees() ) );
559 int actual_delta_radius = CircleToEndSegmentDeltaRadius( aRadius, seg360 );
560 int errorRadius = aRadius + actual_delta_radius;
561
562 double x = aCenter.x + aRadius * aStartAngle.Cos();
563 double y = aCenter.y + aRadius * aStartAngle.Sin();
564
565 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
566
567 EDA_ANGLE rot = aStartAngle + delta / 2;
568
569 for( int i = 0; i < n; i++, rot += delta )
570 {
571 x = aCenter.x + errorRadius * rot.Cos();
572 y = aCenter.y + errorRadius * rot.Sin();
573
574 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
575 }
576
577 x = aCenter.x + aRadius * ( aStartAngle + aArcAngle ).Cos();
578 y = aCenter.y + aRadius * ( aStartAngle + aArcAngle ).Sin();
579
580 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
581 }
582
583 return n;
584}
585
586
587void TransformArcToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aStart, const VECTOR2I& aMid,
588 const VECTOR2I& aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc )
589{
590 SEG startToEnd( aStart, aEnd );
591 int distanceToMid = startToEnd.Distance( aMid );
592
593 if( distanceToMid <= 1 )
594 {
595 // Not an arc but essentially a straight line with a small error
596 TransformOvalToPolygon( aBuffer, aStart, aEnd, aWidth + distanceToMid, aError, aErrorLoc );
597 return;
598 }
599
600 // This appproximation builds a single polygon by starting with a 180 degree arc at one
601 // end, then the outer edge of the arc, then a 180 degree arc at the other end, and finally
602 // the inner edge of the arc.
603 SHAPE_ARC arc( aStart, aMid, aEnd, aWidth );
604 EDA_ANGLE arc_angle_start = arc.GetStartAngle();
605 EDA_ANGLE arc_angle = arc.GetCentralAngle();
606 EDA_ANGLE arc_angle_end = arc_angle_start + arc_angle;
607
608 if( arc_angle < ANGLE_0 )
609 {
610 std::swap( arc_angle_start, arc_angle_end );
611 arc = SHAPE_ARC( aEnd, aMid, aStart, aWidth );
612 arc_angle = -arc_angle;
613 }
614
615 int radial_offset = arc.GetWidth() / 2;
616 int arc_outer_radius = arc.GetRadius() + radial_offset;
617 int arc_inner_radius = arc.GetRadius() - radial_offset;
618 ERROR_LOC errorLocInner = ( aErrorLoc == ERROR_INSIDE ) ? ERROR_OUTSIDE : ERROR_INSIDE;
619 ERROR_LOC errorLocOuter = ( aErrorLoc == ERROR_INSIDE ) ? ERROR_INSIDE : ERROR_OUTSIDE;
620
621 SHAPE_POLY_SET polyshape;
622 polyshape.NewOutline();
623
624 SHAPE_LINE_CHAIN& outline = polyshape.Outline( 0 );
625
626 // Starting end
627 ConvertArcToPolyline( outline, arc.GetP0(), radial_offset, arc_angle_start - ANGLE_180,
628 ANGLE_180, aError, aErrorLoc );
629
630 // Outside edge
631 ConvertArcToPolyline( outline, arc.GetCenter(), arc_outer_radius, arc_angle_start, arc_angle,
632 aError, errorLocOuter );
633
634 // Other end
635 ConvertArcToPolyline( outline, arc.GetP1(), radial_offset, arc_angle_end, ANGLE_180, aError,
636 aErrorLoc );
637
638 // Inside edge
639 if( arc_inner_radius > 0 )
640 {
641 ConvertArcToPolyline( outline, arc.GetCenter(), arc_inner_radius, arc_angle_end,
642 -arc_angle, aError, errorLocInner );
643 }
644
645 aBuffer.Append( polyshape );
646}
647
648
649void TransformRingToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aCentre, int aRadius,
650 int aWidth, int aError, ERROR_LOC aErrorLoc )
651{
652 int inner_radius = aRadius - ( aWidth / 2 );
653 int outer_radius = inner_radius + aWidth;
654
655 if( inner_radius <= 0 )
656 {
657 //In this case, the ring is just a circle (no hole inside)
658 TransformCircleToPolygon( aBuffer, aCentre, aRadius + ( aWidth / 2 ), aError, aErrorLoc );
659 return;
660 }
661
662 SHAPE_POLY_SET buffer;
663
664 TransformCircleToPolygon( buffer, aCentre, outer_radius, aError, aErrorLoc );
665
666 // Build the hole:
667 buffer.NewHole();
668 // The circle is the hole, so the approximation error location is the opposite of aErrorLoc
669 ERROR_LOC inner_err_loc = aErrorLoc == ERROR_OUTSIDE ? ERROR_INSIDE : ERROR_OUTSIDE;
670 TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius,
671 aError, inner_err_loc );
672
674 aBuffer.Append( buffer );
675}
double Sin() const
Definition: eda_angle.h:170
double AsDegrees() const
Definition: eda_angle.h:113
bool IsZero() const
Definition: eda_angle.h:133
double Cos() const
Definition: eda_angle.h:189
Definition: seg.h:42
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition: seg.cpp:350
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition: seg.h:143
EDA_ANGLE GetCentralAngle() const
Definition: shape_arc.cpp:520
int GetWidth() const
Definition: shape_arc.h:158
const VECTOR2I & GetP1() const
Definition: shape_arc.h:114
double GetRadius() const
Definition: shape_arc.cpp:536
EDA_ANGLE GetStartAngle() const
Definition: shape_arc.cpp:489
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:505
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
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.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int NewHole(int aOutline=-1)
Creates a new hole in a given outline.
void Move(const VECTOR2I &aVector) override
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:278
VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition: vector2d.h:309
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:380
void CornerListToPolygon(SHAPE_POLY_SET &outline, std::vector< ROUNDED_CORNER > &aCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
int ConvertArcToPolyline(SHAPE_LINE_CHAIN &aPolyline, VECTOR2I aCenter, int aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aArcAngle, double aAccuracy, ERROR_LOC aErrorLoc)
Generate a polyline to approximate a arc.
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.
void CornerListRemoveDuplicates(std::vector< ROUNDED_CORNER > &aCorners)
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount)
Convert a oblong shape to a polygon, using multiple segments.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount)
Convert a circle to a polygon, using multiple straight lines.
void TransformTrapezoidToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aDeltaX, int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle or trapezoid to a polygon.
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE FULL_CIRCLE
Definition: eda_angle.h:399
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
a few functions useful in geometry calculations.
int GetCircleToPolyCorrection(int aMaxError)
int CircleToEndSegmentDeltaRadius(int aInnerCircleRadius, int aSegCount)
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
static bool intersect(const SEGMENT_WITH_NORMALS &aSeg, const SFVEC2F &aStart, const SFVEC2F &aEnd)
Definition: polygon_2d.cpp:273
CITER next(CITER it)
Definition: ptree.cpp:126
constexpr double correction
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
ROUNDED_CORNER(int x, int y, int radius)
constexpr int delta
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:228
int sign(T val)
Definition: util.h:171
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673