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 // Round up to 8 to make segment approximations align properly at 45-degrees
46 numSegs = ( numSegs + 7 ) / 8 * 8;
47
48 EDA_ANGLE delta = ANGLE_360 / numSegs;
49 int radius = aRadius;
50
51 if( aErrorLoc == ERROR_OUTSIDE )
52 {
53 // The outer radius should be radius+aError
54 // Recalculate the actual approx error, as it can be smaller than aError
55 // because numSegs is clamped to a minimal value
56 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
57 radius += GetCircleToPolyCorrection( actual_delta_radius );
58 }
59
60 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_360; angle += delta )
61 {
62 corner_position.x = radius;
63 corner_position.y = 0;
64 RotatePoint( corner_position, angle );
65 corner_position += aCenter;
66 aBuffer.Append( corner_position.x, corner_position.y );
67 }
68
69 aBuffer.SetClosed( true );
70}
71
72
73void TransformCircleToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aCenter, int aRadius,
74 int aError, ERROR_LOC aErrorLoc, int aMinSegCount )
75{
76 VECTOR2I corner_position;
77 int numSegs = GetArcToSegmentCount( aRadius, aError, FULL_CIRCLE );
78 numSegs = std::max( aMinSegCount, numSegs );
79
80 // Round up to 8 to make segment approximations align properly at 45-degrees
81 numSegs = ( numSegs + 7 ) / 8 * 8;
82
83 EDA_ANGLE delta = ANGLE_360 / numSegs;
84 int radius = aRadius;
85
86 if( aErrorLoc == ERROR_OUTSIDE )
87 {
88 // The outer radius should be radius+aError
89 // Recalculate the actual approx error, as it can be smaller than aError
90 // because numSegs is clamped to a minimal value
91 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
92 radius += GetCircleToPolyCorrection( actual_delta_radius );
93 }
94
95 aBuffer.NewOutline();
96
97 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_360; angle += delta )
98 {
99 corner_position.x = radius;
100 corner_position.y = 0;
101 RotatePoint( corner_position, angle );
102 corner_position += aCenter;
103 aBuffer.Append( corner_position.x, corner_position.y );
104 }
105
106 // Finish circle
107 corner_position.x = radius;
108 corner_position.y = 0;
109 RotatePoint( corner_position, delta / 2 );
110 corner_position += aCenter;
111 aBuffer.Append( corner_position.x, corner_position.y );
112}
113
114
115void TransformOvalToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aStart, const VECTOR2I& aEnd,
116 int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount )
117{
118 // To build the polygonal shape outside the actual shape, we use a bigger
119 // radius to build rounded ends.
120 // However, the width of the segment is too big.
121 // so, later, we will clamp the polygonal shape with the bounding box
122 // of the segment.
123 int radius = aWidth / 2;
124 int numSegs = GetArcToSegmentCount( radius, aError, FULL_CIRCLE );
125 numSegs = std::max( aMinSegCount, numSegs );
126
127 // Round up to 8 to make segment approximations align properly at 45-degrees
128 numSegs = ( numSegs + 7 ) / 8 * 8;
129
130 EDA_ANGLE delta = ANGLE_360 / numSegs;
131
132 if( aErrorLoc == ERROR_OUTSIDE )
133 {
134 // The outer radius should be radius+aError
135 // Recalculate the actual approx error, as it can be smaller than aError
136 // because numSegs is clamped to a minimal value
137 int actual_delta_radius = CircleToEndSegmentDeltaRadius( radius, numSegs );
138 int correction = GetCircleToPolyCorrection( actual_delta_radius );
139 radius += correction;
140 }
141
142 // end point is the coordinate relative to aStart
143 VECTOR2I endp = aEnd - aStart;
144 VECTOR2I startp = aStart;
145 VECTOR2I corner;
146 SHAPE_POLY_SET polyshape;
147
148 polyshape.NewOutline();
149
150 // normalize the position in order to have endp.x >= 0
151 // it makes calculations more easy to understand
152 if( endp.x < 0 )
153 {
154 endp = aStart - aEnd;
155 startp = aEnd;
156 }
157
158 EDA_ANGLE delta_angle( endp );
159 int seg_len = endp.EuclideanNorm();
160
161 // Compute the outlines of the segment, and creates a polygon
162 // Note: the polygonal shape is built from the equivalent horizontal
163 // segment starting at {0,0}, and ending at {seg_len,0}
164
165 // add right rounded end:
166
167 // Right arc start:
168 corner = VECTOR2I( seg_len, radius );
169 polyshape.Append( corner.x, corner.y );
170
171 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_180; angle += delta )
172 {
173 corner = VECTOR2I( 0, radius );
174 RotatePoint( corner, angle );
175 corner.x += seg_len;
176 polyshape.Append( corner.x, corner.y );
177 }
178
179 // Finish right arc:
180 corner = VECTOR2I( seg_len, -radius );
181 polyshape.Append( corner.x, corner.y );
182
183 // Left arc start:
184 corner = VECTOR2I( 0, -radius );
185 polyshape.Append( corner.x, corner.y );
186
187 // add left rounded end:
188 for( EDA_ANGLE angle = delta / 2; angle < ANGLE_180; angle += delta )
189 {
190 corner = VECTOR2I( 0, -radius );
191 RotatePoint( corner, angle );
192 polyshape.Append( corner.x, corner.y );
193 }
194
195 // Finish left arc:
196 corner = VECTOR2I( 0, radius );
197 polyshape.Append( corner.x, corner.y );
198
199 // Now trim the edges of the polygonal shape which will be slightly outside the
200 // track width.
201 SHAPE_POLY_SET bbox;
202 bbox.NewOutline();
203 // Build the bbox (a horizontal rectangle).
204 int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
205 corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
206 // creating useless corner at segment ends
207 corner.y = halfwidth;
208 bbox.Append( corner.x, corner.y );
209 corner.y = -halfwidth;
210 bbox.Append( corner.x, corner.y );
211 corner.x = radius + seg_len + 2;
212 bbox.Append( corner.x, corner.y );
213 corner.y = halfwidth;
214 bbox.Append( corner.x, corner.y );
215
216 // Now, clamp the shape
218 // Note the final polygon is a simple, convex polygon with no hole
219 // due to the shape of initial polygons
220
221 // Rotate and move the polygon to its right location
222 polyshape.Rotate( -delta_angle );
223 polyshape.Move( startp );
224
225 aBuffer.Append( polyshape);
226}
227
228
230{
233 ROUNDED_CORNER( int x, int y ) : m_position( VECTOR2I( x, y ) ), m_radius( 0 ) {}
234 ROUNDED_CORNER( int x, int y, int radius ) : m_position( VECTOR2I( x, y ) ), m_radius( radius ) {}
235};
236
237
238// Corner List requirements: no concave shape, corners in clockwise order, no duplicate corners
239void CornerListToPolygon( SHAPE_POLY_SET& outline, std::vector<ROUNDED_CORNER>& aCorners,
240 int aInflate, int aError, ERROR_LOC aErrorLoc )
241{
242 assert( aInflate >= 0 );
243 outline.NewOutline();
244 VECTOR2I incoming = aCorners[0].m_position - aCorners.back().m_position;
245
246 for( int n = 0, count = aCorners.size(); n < count; n++ )
247 {
248 ROUNDED_CORNER& cur = aCorners[n];
249 ROUNDED_CORNER& next = aCorners[( n + 1 ) % count];
250 VECTOR2I outgoing = next.m_position - cur.m_position;
251
252 if( !( aInflate || cur.m_radius ) )
253 {
254 outline.Append( cur.m_position );
255 }
256 else
257 {
258 VECTOR2I cornerPosition = cur.m_position;
259 int radius = cur.m_radius;
260 EDA_ANGLE endAngle;
261 double tanAngle2;
262
263 if( ( incoming.x == 0 && outgoing.y == 0 ) || ( incoming.y == 0 && outgoing.x == 0 ) )
264 {
265 endAngle = ANGLE_90;
266 tanAngle2 = 1.0;
267 }
268 else
269 {
270 double cosNum = (double) incoming.x * outgoing.x + (double) incoming.y * outgoing.y;
271 double cosDen = (double) incoming.EuclideanNorm() * outgoing.EuclideanNorm();
272 double angle = acos( cosNum / cosDen );
273 tanAngle2 = tan( ( M_PI - angle ) / 2 );
274 endAngle = EDA_ANGLE( angle, RADIANS_T );
275 }
276
277 if( aInflate && tanAngle2 )
278 {
279 radius += aInflate;
280 cornerPosition += incoming.Resize( aInflate / tanAngle2 )
281 + incoming.Perpendicular().Resize( -aInflate );
282 }
283
284 // Ensure 16+ segments per 360deg and ensure first & last segment are the same size
285 int numSegs = std::max( 16, GetArcToSegmentCount( radius, aError, FULL_CIRCLE ) );
286 EDA_ANGLE angDelta = ANGLE_360 / numSegs;
287 EDA_ANGLE lastSeg = endAngle;
288
289 if( lastSeg > ANGLE_0 )
290 {
291 while( lastSeg > angDelta )
292 lastSeg -= angDelta;
293 }
294 else
295 {
296 while( lastSeg < -angDelta )
297 lastSeg += angDelta;
298 }
299
300 EDA_ANGLE angPos = lastSeg.IsZero() ? angDelta : ( angDelta + lastSeg ) / 2;
301
302 double arcTransitionDistance = ( tanAngle2 > 0 ) ? ( radius / tanAngle2 ) : 0;
303 VECTOR2I arcStart = cornerPosition - incoming.Resize( arcTransitionDistance );
304 VECTOR2I arcCenter = arcStart + incoming.Perpendicular().Resize( radius );
305 VECTOR2I arcEnd, arcStartOrigin;
306
307 if( aErrorLoc == ERROR_INSIDE )
308 {
309 arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( arcStart );
310 arcStartOrigin = arcStart - arcCenter;
311 outline.Append( arcStart );
312 }
313 else
314 {
315 // The outer radius should be radius+aError, recalculate because numSegs is clamped
316 int actualDeltaRadius = CircleToEndSegmentDeltaRadius( radius, numSegs );
317 int radiusExtend = GetCircleToPolyCorrection( actualDeltaRadius );
318 arcStart += incoming.Perpendicular().Resize( -radiusExtend );
319 arcStartOrigin = arcStart - arcCenter;
320
321 // To avoid "ears", we only add segments crossing/within the non-rounded outline
322 // Note: outlineIn is short and must be treated as defining an infinite line
323 SEG outlineIn( cornerPosition - incoming, cornerPosition );
324 VECTOR2I prevPt = arcStart;
325 arcEnd = cornerPosition; // default if no points within the outline are found
326
327 while( angPos < endAngle )
328 {
329 VECTOR2I pt = arcStartOrigin;
330 RotatePoint( pt, -angPos );
331 pt += arcCenter;
332 angPos += angDelta;
333
334 if( outlineIn.Side( pt ) > 0 )
335 {
336 OPT_VECTOR2I intersect = outlineIn.IntersectLines( SEG( prevPt, pt ) );
337
338 wxCHECK_RET( intersect, wxT( "No solutions exist!" ) );
339 outline.Append( *intersect );
340 outline.Append( pt );
341 arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( *intersect );
342 break;
343 }
344
345 endAngle -= angDelta; // if skipping first, also skip last
346 prevPt = pt;
347 }
348 }
349
350 for( ; angPos < endAngle; angPos += angDelta )
351 {
352 VECTOR2I pt = arcStartOrigin;
353 RotatePoint( pt, -angPos );
354 outline.Append( pt + arcCenter );
355 }
356
357 outline.Append( arcEnd );
358 }
359
360 incoming = outgoing;
361 }
362}
363
364
365void CornerListRemoveDuplicates( std::vector<ROUNDED_CORNER>& aCorners )
366{
367 VECTOR2I prev = aCorners[0].m_position;
368
369 for( int pos = aCorners.size() - 1; pos >= 0; pos-- )
370 {
371 if( aCorners[pos].m_position == prev )
372 aCorners.erase( aCorners.begin() + pos );
373 else
374 prev = aCorners[pos].m_position;
375 }
376}
377
378
379void TransformTrapezoidToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aPosition,
380 const VECTOR2I& aSize, const EDA_ANGLE& aRotation, int aDeltaX,
381 int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc )
382{
383 SHAPE_POLY_SET outline;
384 VECTOR2I size( aSize / 2 );
385 std::vector<ROUNDED_CORNER> corners;
386
387 if( aInflate < 0 )
388 {
389 if( !aDeltaX && !aDeltaY ) // rectangle
390 {
391 size.x = std::max( 1, size.x + aInflate );
392 size.y = std::max( 1, size.y + aInflate );
393 }
394 else if( aDeltaX ) // horizontal trapezoid
395 {
396 double slope = (double) aDeltaX / size.x;
397 int yShrink = KiROUND( ( std::hypot( size.x, aDeltaX ) * aInflate ) / size.x );
398 size.y = std::max( 1, size.y + yShrink );
399 size.x = std::max( 1, size.x + aInflate );
400 aDeltaX = KiROUND( size.x * slope );
401
402 if( aDeltaX > size.y ) // shrinking turned the trapezoid into a triangle
403 {
404 corners.reserve( 3 );
405 corners.emplace_back( -size.x, -size.y - aDeltaX );
406 corners.emplace_back( KiROUND( size.y / slope ), 0 );
407 corners.emplace_back( -size.x, size.y + aDeltaX );
408 }
409 }
410 else // vertical trapezoid
411 {
412 double slope = (double) aDeltaY / size.y;
413 int xShrink = KiROUND( ( std::hypot( size.y, aDeltaY ) * aInflate ) / size.y );
414 size.x = std::max( 1, size.x + xShrink );
415 size.y = std::max( 1, size.y + aInflate );
416 aDeltaY = KiROUND( size.y * slope );
417
418 if( aDeltaY > size.x )
419 {
420 corners.reserve( 3 );
421 corners.emplace_back( 0, -KiROUND( size.x / slope ) );
422 corners.emplace_back( size.x + aDeltaY, size.y );
423 corners.emplace_back( -size.x - aDeltaY, size.y );
424 }
425 }
426
427 aInflate = 0;
428 }
429
430 if( corners.empty() )
431 {
432 corners.reserve( 4 );
433 corners.emplace_back( -size.x + aDeltaY, -size.y - aDeltaX );
434 corners.emplace_back( size.x - aDeltaY, -size.y + aDeltaX );
435 corners.emplace_back( size.x + aDeltaY, size.y - aDeltaX );
436 corners.emplace_back( -size.x - aDeltaY, size.y + aDeltaX );
437
438 if( std::abs( aDeltaY ) == std::abs( size.x ) || std::abs( aDeltaX ) == std::abs( size.y ) )
440 }
441
442 CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
443
444 if( !aRotation.IsZero() )
445 outline.Rotate( aRotation );
446
447 outline.Move( VECTOR2I( aPosition ) );
448 aBuffer.Append( outline );
449}
450
451
453 const VECTOR2I& aSize, const EDA_ANGLE& aRotation,
454 int aCornerRadius, double aChamferRatio,
455 int aChamferCorners, int aInflate, int aError,
456 ERROR_LOC aErrorLoc )
457{
458 SHAPE_POLY_SET outline;
459 VECTOR2I size( aSize / 2 );
460 int chamferCnt = std::bitset<8>( aChamferCorners ).count();
461 double chamferDeduct = 0;
462
463 if( aInflate < 0 )
464 {
465 size.x = std::max( 1, size.x + aInflate );
466 size.y = std::max( 1, size.y + aInflate );
467 chamferDeduct = aInflate * ( 2.0 - M_SQRT2 );
468 aCornerRadius = std::max( 0, aCornerRadius + aInflate );
469 aInflate = 0;
470 }
471
472 std::vector<ROUNDED_CORNER> corners;
473 corners.reserve( 4 + chamferCnt );
474 corners.emplace_back( -size.x, -size.y, aCornerRadius );
475 corners.emplace_back( size.x, -size.y, aCornerRadius );
476 corners.emplace_back( size.x, size.y, aCornerRadius );
477 corners.emplace_back( -size.x, size.y, aCornerRadius );
478
479 if( aChamferCorners )
480 {
481 int shorterSide = std::min( aSize.x, aSize.y );
482 int chamfer = std::max( 0, KiROUND( aChamferRatio * shorterSide + chamferDeduct ) );
485 int sign[8] = { 0, 1, -1, 0, 0, -1, 1, 0 };
486
487 for( int cc = 0, pos = 0; cc < 4; cc++, pos++ )
488 {
489 if( !( aChamferCorners & chamId[cc] ) )
490 continue;
491
492 corners[pos].m_radius = 0;
493
494 if( chamfer == 0 )
495 continue;
496
497 corners.insert( corners.begin() + pos + 1, corners[pos] );
498 corners[pos].m_position.x += sign[( 2 * cc ) & 7] * chamfer;
499 corners[pos].m_position.y += sign[( 2 * cc - 2 ) & 7] * chamfer;
500 corners[pos + 1].m_position.x += sign[( 2 * cc + 1 ) & 7] * chamfer;
501 corners[pos + 1].m_position.y += sign[( 2 * cc - 1 ) & 7] * chamfer;
502 pos++;
503 }
504
505 if( chamferCnt > 1 && 2 * chamfer >= shorterSide )
507 }
508
509 CornerListToPolygon( outline, corners, aInflate, aError, aErrorLoc );
510
511 if( !aRotation.IsZero() )
512 outline.Rotate( aRotation );
513
514 outline.Move( aPosition );
515 aBuffer.Append( outline );
516}
517
518
519int ConvertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
520 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aArcAngle,
521 double aAccuracy, ERROR_LOC aErrorLoc )
522{
523 int n = 2;
524
525 if( aRadius >= aAccuracy )
526 n = GetArcToSegmentCount( aRadius, aAccuracy, aArcAngle ) + 1;
527
528 EDA_ANGLE delta = aArcAngle / n;
529
530 if( aErrorLoc == ERROR_INSIDE )
531 {
532 // This is the easy case: with the error on the inside the endpoints of each segment
533 // are error-free.
534
535 EDA_ANGLE rot = aStartAngle;
536
537 for( int i = 0; i <= n; i++, rot += delta )
538 {
539 double x = aCenter.x + aRadius * rot.Cos();
540 double y = aCenter.y + aRadius * rot.Sin();
541
542 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
543 }
544 }
545 else
546 {
547 // This is the hard case: with the error on the outside it's the segment midpoints
548 // that are error-free. So we need to add a half-segment at each end of the arc to get
549 // them correct.
550
551 int seg360 = std::abs( KiROUND( n * 360.0 / aArcAngle.AsDegrees() ) );
552 int actual_delta_radius = CircleToEndSegmentDeltaRadius( aRadius, seg360 );
553 int errorRadius = aRadius + actual_delta_radius;
554
555 double x = aCenter.x + aRadius * aStartAngle.Cos();
556 double y = aCenter.y + aRadius * aStartAngle.Sin();
557
558 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
559
560 EDA_ANGLE rot = aStartAngle + delta / 2;
561
562 for( int i = 0; i < n; i++, rot += delta )
563 {
564 x = aCenter.x + errorRadius * rot.Cos();
565 y = aCenter.y + errorRadius * rot.Sin();
566
567 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
568 }
569
570 x = aCenter.x + aRadius * ( aStartAngle + aArcAngle ).Cos();
571 y = aCenter.y + aRadius * ( aStartAngle + aArcAngle ).Sin();
572
573 aPolyline.Append( KiROUND( x ), KiROUND( y ) );
574 }
575
576 return n;
577}
578
579
580void TransformArcToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aStart, const VECTOR2I& aMid,
581 const VECTOR2I& aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc )
582{
583 SEG startToEnd( aStart, aEnd );
584 int distanceToMid = startToEnd.Distance( aMid );
585
586 if( distanceToMid <= 1 )
587 {
588 // Not an arc but essentially a straight line with a small error
589 TransformOvalToPolygon( aBuffer, aStart, aEnd, aWidth + distanceToMid, aError, aErrorLoc );
590 return;
591 }
592
593 // This appproximation builds a single polygon by starting with a 180 degree arc at one
594 // end, then the outer edge of the arc, then a 180 degree arc at the other end, and finally
595 // the inner edge of the arc.
596 SHAPE_ARC arc( aStart, aMid, aEnd, aWidth );
597 EDA_ANGLE arc_angle_start = arc.GetStartAngle();
598 EDA_ANGLE arc_angle = arc.GetCentralAngle();
599 EDA_ANGLE arc_angle_end = arc_angle_start + arc_angle;
600
601 if( arc_angle < ANGLE_0 )
602 {
603 std::swap( arc_angle_start, arc_angle_end );
604 arc = SHAPE_ARC( aEnd, aMid, aStart, aWidth );
605 arc_angle = -arc_angle;
606 }
607
608 int radial_offset = arc.GetWidth() / 2;
609 int arc_outer_radius = arc.GetRadius() + radial_offset;
610 int arc_inner_radius = arc.GetRadius() - radial_offset;
611 ERROR_LOC errorLocInner = ( aErrorLoc == ERROR_INSIDE ) ? ERROR_OUTSIDE : ERROR_INSIDE;
612 ERROR_LOC errorLocOuter = ( aErrorLoc == ERROR_INSIDE ) ? ERROR_INSIDE : ERROR_OUTSIDE;
613
614 SHAPE_POLY_SET polyshape;
615 polyshape.NewOutline();
616
617 SHAPE_LINE_CHAIN& outline = polyshape.Outline( 0 );
618
619 // Starting end
620 ConvertArcToPolyline( outline, arc.GetP0(), radial_offset, arc_angle_start - ANGLE_180,
621 ANGLE_180, aError, aErrorLoc );
622
623 // Outside edge
624 ConvertArcToPolyline( outline, arc.GetCenter(), arc_outer_radius, arc_angle_start, arc_angle,
625 aError, errorLocOuter );
626
627 // Other end
628 ConvertArcToPolyline( outline, arc.GetP1(), radial_offset, arc_angle_end, ANGLE_180, aError,
629 aErrorLoc );
630
631 // Inside edge
632 if( arc_inner_radius > 0 )
633 {
634 ConvertArcToPolyline( outline, arc.GetCenter(), arc_inner_radius, arc_angle_end,
635 -arc_angle, aError, errorLocInner );
636 }
637
638 aBuffer.Append( polyshape );
639}
640
641
642void TransformRingToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aCentre, int aRadius,
643 int aWidth, int aError, ERROR_LOC aErrorLoc )
644{
645 int inner_radius = aRadius - ( aWidth / 2 );
646 int outer_radius = inner_radius + aWidth;
647
648 if( inner_radius <= 0 )
649 {
650 //In this case, the ring is just a circle (no hole inside)
651 TransformCircleToPolygon( aBuffer, aCentre, aRadius + ( aWidth / 2 ), aError, aErrorLoc );
652 return;
653 }
654
655 SHAPE_POLY_SET buffer;
656
657 TransformCircleToPolygon( buffer, aCentre, outer_radius, aError, aErrorLoc );
658
659 // Build the hole:
660 buffer.NewHole();
661 // The circle is the hole, so the approximation error location is the opposite of aErrorLoc
662 ERROR_LOC inner_err_loc = aErrorLoc == ERROR_OUTSIDE ? ERROR_INSIDE : ERROR_OUTSIDE;
663 TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius,
664 aError, inner_err_loc );
665
667 aBuffer.Append( buffer );
668}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
Definition: approximation.h:32
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
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:538
int GetWidth() const
Definition: shape_arc.h:168
const VECTOR2I & GetP1() const
Definition: shape_arc.h:115
double GetRadius() const
Definition: shape_arc.cpp:554
EDA_ANGLE GetStartAngle() const
Definition: shape_arc.cpp:507
const VECTOR2I & GetP0() const
Definition: shape_arc.h:114
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:523
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:283
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition: vector2d.h:314
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
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)
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:229
constexpr int sign(T val)
Definition: util.h:159
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691