KiCad PCB EDA Suite
convert_basic_shapes_to_polygon.cpp
Go to the documentation of this file.
1 
4 /*
5  * This program source code file is part of KiCad, a free EDA CAD application.
6  *
7  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
8  * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23  * or you may search the http://www.gnu.org website for the version 2 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26  */
27 #include <vector>
28 
29 #include <fctsys.h>
30 #include <trigo.h>
31 #include <macros.h>
32 #include <common.h>
34 
46  wxPoint aCenter, int aRadius,
47  int aCircleToSegmentsCount )
48 {
49  wxPoint corner_position;
50  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
51  double halfstep = delta/2; // the starting value for rot angles
52 
53  for( int ii = 0; ii < aCircleToSegmentsCount; ii++ )
54  {
55  corner_position.x = aRadius;
56  corner_position.y = 0;
57  double angle = (ii * delta) + halfstep;
58  RotatePoint( &corner_position, angle );
59  corner_position += aCenter;
60  aBuffer.Append( corner_position.x, corner_position.y );
61  }
62 
63  aBuffer.SetClosed( true );
64 }
65 
66 
78  wxPoint aCenter, int aRadius,
79  int aCircleToSegmentsCount )
80 {
81  wxPoint corner_position;
82  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
83  double halfstep = delta/2; // the starting value for rot angles
84 
85  aCornerBuffer.NewOutline();
86 
87  for( int ii = 0; ii < aCircleToSegmentsCount; ii++ )
88  {
89  corner_position.x = aRadius;
90  corner_position.y = 0;
91  double angle = (ii * delta) + halfstep;
92  RotatePoint( &corner_position, angle );
93  corner_position += aCenter;
94  aCornerBuffer.Append( corner_position.x, corner_position.y );
95  }
96 }
97 
99  wxPoint aStart, wxPoint aEnd, int aWidth,
100  int aCircleToSegmentsCount, double aCorrectionFactor )
101 {
102  // To build the polygonal shape outside the actual shape, we use a bigger
103  // radius to build rounded ends.
104  // However, the width of the segment is too big.
105  // so, later, we will clamp the polygonal shape with the bounding box
106  // of the segment.
107  int radius = aWidth / 2;
108 
109  // Note if we want to compensate the radius reduction of a circle due to
110  // the segment approx, aCorrectionFactor must be calculated like this:
111  // For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
112  // aCorrectionFactor is 1 /cos( PI/s_CircleToSegmentsCount )
113 
114  radius = radius * aCorrectionFactor; // make segments outside the circles
115 
116  // end point is the coordinate relative to aStart
117  wxPoint endp = aEnd - aStart;
118  wxPoint startp = aStart;
119  wxPoint corner;
120  SHAPE_POLY_SET polyshape;
121 
122  polyshape.NewOutline();
123 
124  // normalize the position in order to have endp.x >= 0
125  // it makes calculations more easy to understand
126  if( endp.x < 0 )
127  {
128  endp = aStart - aEnd;
129  startp = aEnd;
130  }
131 
132  // delta_angle is in radian
133  double delta_angle = atan2( (double)endp.y, (double)endp.x );
134  int seg_len = KiROUND( EuclideanNorm( endp ) );
135 
136  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
137 
138  // Compute the outlines of the segment, and creates a polygon
139  // Note: the polygonal shape is built from the equivalent horizontal
140  // segment starting ar 0,0, and ending at seg_len,0
141 
142  // add right rounded end:
143  for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
144  {
145  corner = wxPoint( 0, radius );
146  RotatePoint( &corner, delta*ii );
147  corner.x += seg_len;
148  polyshape.Append( corner.x, corner.y );
149  }
150 
151  // Finish arc:
152  corner = wxPoint( seg_len, -radius );
153  polyshape.Append( corner.x, corner.y );
154 
155  // add left rounded end:
156  for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
157  {
158  corner = wxPoint( 0, -radius );
159  RotatePoint( &corner, delta*ii );
160  polyshape.Append( corner.x, corner.y );
161  }
162 
163  // Finish arc:
164  corner = wxPoint( 0, radius );
165  polyshape.Append( corner.x, corner.y );
166 
167  // Now, clamp the polygonal shape (too big) with the segment bounding box
168  // the polygonal shape bbox equivalent to the segment has a too big height,
169  // and the right width
170  if( aCorrectionFactor > 1.0 )
171  {
172  SHAPE_POLY_SET bbox;
173  bbox.NewOutline();
174  // Build the bbox (a horizontal rectangle).
175  int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
176  corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
177  // creating useless corner at segment ends
178  corner.y = halfwidth;
179  bbox.Append( corner.x, corner.y );
180  corner.y = -halfwidth;
181  bbox.Append( corner.x, corner.y );
182  corner.x = radius + seg_len + 2;
183  bbox.Append( corner.x, corner.y );
184  corner.y = halfwidth;
185  bbox.Append( corner.x, corner.y );
186 
187  // Now, clamp the shape
189  // Note the final polygon is a simple, convex polygon with no hole
190  // due to the shape of initial polygons
191  }
192 
193  // Rotate and move the polygon to its right location
194  polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) );
195  polyshape.Move( startp );
196 
197  aCornerBuffer.Append( polyshape);
198 }
199 
200 /* Returns the centers of the rounded corners of a rect.
201  */
202 void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
203  const wxPoint& aPosition, const wxSize& aSize, double aRotation )
204 {
205  wxSize size( aSize/2 );
206 
207  size.x -= aRadius;
208  size.y -= aRadius;
209 
210  // Ensure size is > 0, to avoid generating unusable shapes
211  // which can crash kicad.
212  if( size.x <= 1 )
213  size.x = 1;
214  if( size.y <= 1 )
215  size.y = 1;
216 
217  aCenters[0].x = -size.x;
218  aCenters[0].y = size.y;
219 
220  aCenters[1].x = size.x;
221  aCenters[1].y = size.y;
222 
223  aCenters[2].x = size.x;
224  aCenters[2].y = -size.y;
225 
226  aCenters[3].x = -size.x;
227  aCenters[3].y = -size.y;
228 
229  // Rotate the polygon
230  if( aRotation )
231  {
232  for( int ii = 0; ii < 4; ii++ )
233  RotatePoint( &aCenters[ii], aRotation );
234  }
235 
236  // move the polygon to the position
237  for( int ii = 0; ii < 4; ii++ )
238  aCenters[ii] += aPosition;
239 }
240 
241 
243  const wxPoint& aPosition, const wxSize& aSize,
244  double aRotation, int aCornerRadius,
245  double aChamferRatio, int aChamferCorners,
246  int aCircleToSegmentsCount )
247 {
248  // Build the basic shape in orientation 0.0, position 0,0 for chamfered corners
249  // or in actual position/orientation for round rect only
250  wxPoint corners[4];
251  GetRoundRectCornerCenters( corners, aCornerRadius,
252  aChamferCorners ? wxPoint( 0, 0 ) : aPosition,
253  aSize, aChamferCorners ? 0.0 : aRotation );
254 
255  SHAPE_POLY_SET outline;
256  outline.NewOutline();
257 
258  for( int ii = 0; ii < 4; ++ii )
259  outline.Append( corners[ii].x, corners[ii].y );
260 
261  outline.Inflate( aCornerRadius, aCircleToSegmentsCount );
262 
263  if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer
264  {
265  // Add the outline:
266  aCornerBuffer.Append( outline );
267  return;
268  }
269 
270  // Now we have the round rect outline, in position 0,0 orientation 0.0.
271  // Chamfer the corner(s).
272  int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y );
273 
274  SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer
275 
276  int corner_id[4] =
277  {
280  };
281  // Depending on the corner position, signX[] and signY[] give the sign of chamfer
282  // coordinates relative to the corner position
283  // The first corner is the top left corner, then top right, bottom left and bottom right
284  int signX[4] = {1, -1, 1,-1 };
285  int signY[4] = {1, 1, -1,-1 };
286 
287  for( int ii = 0; ii < 4; ii++ )
288  {
289  if( (corner_id[ii] & aChamferCorners) == 0 )
290  continue;
291 
292  VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 );
293 
294  if( aCornerRadius )
295  {
296  // We recreate a rectangular area covering the full rounded corner (max size = aSize/2)
297  // to rebuild the corner before chamfering, to be sure the rounded corner shape does not
298  // overlap the chamfered corner shape:
299  chamfered_corner.RemoveAllContours();
300  chamfered_corner.NewOutline();
301  chamfered_corner.Append( 0, 0 );
302  chamfered_corner.Append( 0, signY[ii]*aSize.y/2 );
303  chamfered_corner.Append( signX[ii]*aSize.x/2, signY[ii]*aSize.y/2 );
304  chamfered_corner.Append( signX[ii]*aSize.x/2, 0 );
305  chamfered_corner.Move( corner_pos );
306  outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
307  }
308 
309  // Now chamfer this corner
310  chamfered_corner.RemoveAllContours();
311  chamfered_corner.NewOutline();
312  chamfered_corner.Append( 0, 0 );
313  chamfered_corner.Append( 0, signY[ii]*chamfer_value );
314  chamfered_corner.Append( signX[ii]*chamfer_value, 0 );
315  chamfered_corner.Move( corner_pos );
316  outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
317  }
318 
319  // Rotate and move the outline:
320  if( aRotation != 0.0 )
321  outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) );
322 
323  outline.Move( VECTOR2I( aPosition ) );
324 
325  // Add the outline:
326  aCornerBuffer.Append( outline );
327 }
328 
329 
343  wxPoint aStart, wxPoint aEnd,
344  int aCircleToSegmentsCount,
345  int aWidth )
346 {
347  int radius = aWidth / 2;
348  wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
349  wxPoint startp = aStart;
350  wxPoint corner;
351  VECTOR2I polypoint;
352 
353  aCornerBuffer.NewOutline();
354 
355  // normalize the position in order to have endp.x >= 0;
356  if( endp.x < 0 )
357  {
358  endp = aStart - aEnd;
359  startp = aEnd;
360  }
361 
362  double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees
363  int seg_len = KiROUND( EuclideanNorm( endp ) );
364 
365  int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
366 
367  // Compute the outlines of the segment, and creates a polygon
368  // add right rounded end:
369  for( int ii = 0; ii < 1800; ii += delta )
370  {
371  corner = wxPoint( 0, radius );
372  RotatePoint( &corner, ii );
373  corner.x += seg_len;
374  RotatePoint( &corner, -delta_angle );
375  corner += startp;
376  polypoint.x = corner.x;
377  polypoint.y = corner.y;
378  aCornerBuffer.Append( polypoint.x, polypoint.y );
379  }
380 
381  // Finish arc:
382  corner = wxPoint( seg_len, -radius );
383  RotatePoint( &corner, -delta_angle );
384  corner += startp;
385  polypoint.x = corner.x;
386  polypoint.y = corner.y;
387  aCornerBuffer.Append( polypoint.x, polypoint.y );
388 
389  // add left rounded end:
390  for( int ii = 0; ii < 1800; ii += delta )
391  {
392  corner = wxPoint( 0, -radius );
393  RotatePoint( &corner, ii );
394  RotatePoint( &corner, -delta_angle );
395  corner += startp;
396  polypoint.x = corner.x;
397  polypoint.y = corner.y;
398  aCornerBuffer.Append( polypoint.x, polypoint.y );
399  }
400 
401  // Finish arc:
402  corner = wxPoint( 0, radius );
403  RotatePoint( &corner, -delta_angle );
404  corner += startp;
405  polypoint.x = corner.x;
406  polypoint.y = corner.y;
407  aCornerBuffer.Append( polypoint.x, polypoint.y );
408 }
409 
410 
423  wxPoint aCentre, wxPoint aStart, double aArcAngle,
424  int aCircleToSegmentsCount, int aWidth )
425 {
426  wxPoint arc_start, arc_end;
427  int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
428 
429  arc_end = arc_start = aStart;
430 
431  if( aArcAngle != 3600 )
432  {
433  RotatePoint( &arc_end, aCentre, -aArcAngle );
434  }
435 
436  if( aArcAngle < 0 )
437  {
438  std::swap( arc_start, arc_end );
439  aArcAngle = -aArcAngle;
440  }
441 
442  // Compute the ends of segments and creates poly
443  wxPoint curr_end = arc_start;
444  wxPoint curr_start = arc_start;
445 
446  for( int ii = delta; ii < aArcAngle; ii += delta )
447  {
448  curr_end = arc_start;
449  RotatePoint( &curr_end, aCentre, -ii );
450  TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end,
451  aCircleToSegmentsCount, aWidth );
452  curr_start = curr_end;
453  }
454 
455  if( curr_end != arc_end )
457  curr_end, arc_end,
458  aCircleToSegmentsCount, aWidth );
459 }
460 
461 
473  wxPoint aCentre, int aRadius,
474  int aCircleToSegmentsCount, int aWidth )
475 {
476  // Compute the corners positions and creates the poly
477  wxPoint curr_point;
478  int inner_radius = aRadius - ( aWidth / 2 );
479  int outer_radius = inner_radius + aWidth;
480 
481  if( inner_radius <= 0 )
482  { //In this case, the ring is just a circle (no hole inside)
483  TransformCircleToPolygon( aCornerBuffer, aCentre, aRadius + ( aWidth / 2 ),
484  aCircleToSegmentsCount );
485  return;
486  }
487 
488  SHAPE_POLY_SET buffer;
489 
490  TransformCircleToPolygon( buffer, aCentre, outer_radius, aCircleToSegmentsCount );
491 
492  // Build the hole:
493  buffer.NewHole();
494  TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius, aCircleToSegmentsCount );
495 
497  aCornerBuffer.Append( buffer );
498 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:121
void TransformRoundedEndsSegmentToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth)
Function TransformRoundedEndsSegmentToPolygon convert a segment with rounded ends to a polygon Conver...
int NewHole(int aOutline=-1)
Creates a new hole in a given outline
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:118
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset union For aFastMode meaning, see function booleanOp
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aCircleToSegmentsCount)
convert a rectangle with rounded corners and/or chamfered corners to a polygon Convert rounded corner...
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
void TransformOvalClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aWidth, int aCircleToSegmentsCount, double aCorrectionFactor)
convert a oblong shape to a polygon, using multiple segments It is similar to TransformRoundedEndsSeg...
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
static const int delta[8][2]
Definition: solve.cpp:112
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
This file contains miscellaneous commonly used macros and functions.
void SetClosed(bool aClosed)
Function SetClosed()
void Inflate(int aFactor, int aCircleSegmentsCount)
Performs outline inflation/deflation, using round corners.
void Move(const VECTOR2I &aVector) override
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:170
Class SHAPE_POLY_SET.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, wxPoint aCenter, int aRadius, int aCircleToSegmentsCount)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
int NewOutline()
Creates a new empty polygon in the set and returns its index
void Fracture(POLYGON_MODE aFastMode)
Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the oute...
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCentre, int aRadius, int aCircleToSegmentsCount, int aWidth)
Function TransformRingToPolygon Creates a polygon from a ring Convert arcs to multiple straight segme...
void TransformArcToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCentre, wxPoint aStart, double aArcAngle, int aCircleToSegmentsCount, int aWidth)
Function TransformArcToPolygon Creates a polygon from an Arc Convert arcs to multiple straight segmen...
Class SHAPE_LINE_CHAIN.
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
double DECIDEG2RAD(double deg)
Definition: trigo.h:212
void GetRoundRectCornerCenters(wxPoint aCenters[4], int aRadius, const wxPoint &aPosition, const wxSize &aSize, double aRotation)
Helper function GetRoundRectCornerCenters Has meaning only for rounded rect Returns the centers of th...
The common library.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp
void Rotate(double aAngle, const VECTOR2I &aCenter)
Function Rotate rotates all vertices by a given angle.
#define min(a, b)
Definition: auxiliary.h:85
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)