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