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