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-2019 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>
35 
36 
38  wxPoint aCenter, int aRadius,
39  int aError )
40 {
41  wxPoint corner_position;
42  int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 );
43  int delta = 3600 / numSegs; // rotate angle in 0.1 degree
44  double correction = GetCircletoPolyCorrectionFactor( numSegs );
45  int radius = aRadius * correction; // make segments outside the circles
46  double halfstep = delta/2; // the starting value for rot angles
47 
48  for( int ii = 0; ii < numSegs; ii++ )
49  {
50  corner_position.x = radius;
51  corner_position.y = 0;
52  double angle = (ii * delta) + halfstep;
53  RotatePoint( &corner_position, angle );
54  corner_position += aCenter;
55  aBuffer.Append( corner_position.x, corner_position.y );
56  }
57 
58  aBuffer.SetClosed( true );
59 }
60 
61 
63  wxPoint aCenter, int aRadius,
64  int aError )
65 {
66  wxPoint corner_position;
67  int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 );
68  int delta = 3600 / numSegs; // rotate angle in 0.1 degree
69  double correction = GetCircletoPolyCorrectionFactor( numSegs );
70  int radius = aRadius * correction; // make segments outside the circles
71  double halfstep = delta/2; // the starting value for rot angles
72 
73  aCornerBuffer.NewOutline();
74 
75  for( int ii = 0; ii < numSegs; ii++ )
76  {
77  corner_position.x = radius;
78  corner_position.y = 0;
79  double angle = (ii * delta) + halfstep;
80  RotatePoint( &corner_position, angle );
81  corner_position += aCenter;
82  aCornerBuffer.Append( corner_position.x, corner_position.y );
83  }
84 }
85 
86 
88  wxPoint aStart, wxPoint aEnd, int aWidth,
89  int aError )
90 {
91  // To build the polygonal shape outside the actual shape, we use a bigger
92  // radius to build rounded ends.
93  // However, the width of the segment is too big.
94  // so, later, we will clamp the polygonal shape with the bounding box
95  // of the segment.
96  int radius = aWidth / 2;
97  int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 );
98  int delta = 3600 / numSegs; // rotate angle in 0.1 degree
99  double correction = GetCircletoPolyCorrectionFactor( numSegs );
100 
101  radius = radius * correction; // make segments outside the circles
102 
103  // end point is the coordinate relative to aStart
104  wxPoint endp = aEnd - aStart;
105  wxPoint startp = aStart;
106  wxPoint corner;
107  SHAPE_POLY_SET polyshape;
108 
109  polyshape.NewOutline();
110 
111  // normalize the position in order to have endp.x >= 0
112  // it makes calculations more easy to understand
113  if( endp.x < 0 )
114  {
115  endp = aStart - aEnd;
116  startp = aEnd;
117  }
118 
119  // delta_angle is in radian
120  double delta_angle = atan2( (double)endp.y, (double)endp.x );
121  int seg_len = KiROUND( EuclideanNorm( endp ) );
122 
123 
124  // Compute the outlines of the segment, and creates a polygon
125  // Note: the polygonal shape is built from the equivalent horizontal
126  // segment starting ar 0,0, and ending at seg_len,0
127 
128  // add right rounded end:
129  for( int ii = 0; ii < numSegs / 2; ii++ )
130  {
131  corner = wxPoint( 0, radius );
132  RotatePoint( &corner, delta * ii );
133  corner.x += seg_len;
134  polyshape.Append( corner.x, corner.y );
135  }
136 
137  // Finish arc:
138  corner = wxPoint( seg_len, -radius );
139  polyshape.Append( corner.x, corner.y );
140 
141  // add left rounded end:
142  for( int ii = 0; ii < numSegs / 2; ii++ )
143  {
144  corner = wxPoint( 0, -radius );
145  RotatePoint( &corner, delta * ii );
146  polyshape.Append( corner.x, corner.y );
147  }
148 
149  // Finish arc:
150  corner = wxPoint( 0, radius );
151  polyshape.Append( corner.x, corner.y );
152 
153  // Now, clamp the polygonal shape (too big) with the segment bounding box
154  // the polygonal shape bbox equivalent to the segment has a too big height,
155  // and the right width
156  if( correction > 1.0 )
157  {
158  SHAPE_POLY_SET bbox;
159  bbox.NewOutline();
160  // Build the bbox (a horizontal rectangle).
161  int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
162  corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
163  // creating useless corner at segment ends
164  corner.y = halfwidth;
165  bbox.Append( corner.x, corner.y );
166  corner.y = -halfwidth;
167  bbox.Append( corner.x, corner.y );
168  corner.x = radius + seg_len + 2;
169  bbox.Append( corner.x, corner.y );
170  corner.y = halfwidth;
171  bbox.Append( corner.x, corner.y );
172 
173  // Now, clamp the shape
175  // Note the final polygon is a simple, convex polygon with no hole
176  // due to the shape of initial polygons
177  }
178 
179  // Rotate and move the polygon to its right location
180  polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) );
181  polyshape.Move( startp );
182 
183  aCornerBuffer.Append( polyshape);
184 }
185 
186 
187 void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
188  const wxPoint& aPosition, const wxSize& aSize, double aRotation )
189 {
190  wxSize size( aSize/2 );
191 
192  size.x -= aRadius;
193  size.y -= aRadius;
194 
195  // Ensure size is > 0, to avoid generating unusable shapes
196  // which can crash kicad.
197  if( size.x <= 1 )
198  size.x = 1;
199  if( size.y <= 1 )
200  size.y = 1;
201 
202  aCenters[0].x = -size.x;
203  aCenters[0].y = size.y;
204 
205  aCenters[1].x = size.x;
206  aCenters[1].y = size.y;
207 
208  aCenters[2].x = size.x;
209  aCenters[2].y = -size.y;
210 
211  aCenters[3].x = -size.x;
212  aCenters[3].y = -size.y;
213 
214  // Rotate the polygon
215  if( aRotation )
216  {
217  for( int ii = 0; ii < 4; ii++ )
218  RotatePoint( &aCenters[ii], aRotation );
219  }
220 
221  // move the polygon to the position
222  for( int ii = 0; ii < 4; ii++ )
223  aCenters[ii] += aPosition;
224 }
225 
226 
228  const wxPoint& aPosition, const wxSize& aSize,
229  double aRotation, int aCornerRadius,
230  double aChamferRatio, int aChamferCorners,
231  int aApproxErrorMax, int aMinSegPerCircleCount )
232 {
233  // Build the basic shape in orientation 0.0, position 0,0 for chamfered corners
234  // or in actual position/orientation for round rect only
235  wxPoint corners[4];
236  GetRoundRectCornerCenters( corners, aCornerRadius,
237  aChamferCorners ? wxPoint( 0, 0 ) : aPosition,
238  aSize, aChamferCorners ? 0.0 : aRotation );
239 
240  SHAPE_POLY_SET outline;
241  outline.NewOutline();
242 
243  for( int ii = 0; ii < 4; ++ii )
244  outline.Append( corners[ii].x, corners[ii].y );
245 
246  int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aApproxErrorMax, 360.0 ),
247  aMinSegPerCircleCount );
248  outline.Inflate( aCornerRadius, numSegs );
249 
250  if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer
251  {
252  // Add the outline:
253  aCornerBuffer.Append( outline );
254  return;
255  }
256 
257  // Now we have the round rect outline, in position 0,0 orientation 0.0.
258  // Chamfer the corner(s).
259  int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y );
260 
261  SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer
262 
263  int corner_id[4] =
264  {
267  };
268  // Depending on the corner position, signX[] and signY[] give the sign of chamfer
269  // coordinates relative to the corner position
270  // The first corner is the top left corner, then top right, bottom left and bottom right
271  int signX[4] = {1, -1, 1,-1 };
272  int signY[4] = {1, 1, -1,-1 };
273 
274  for( int ii = 0; ii < 4; ii++ )
275  {
276  if( (corner_id[ii] & aChamferCorners) == 0 )
277  continue;
278 
279  VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 );
280 
281  if( aCornerRadius )
282  {
283  // We recreate a rectangular area covering the full rounded corner (max size = aSize/2)
284  // to rebuild the corner before chamfering, to be sure the rounded corner shape does not
285  // overlap the chamfered corner shape:
286  chamfered_corner.RemoveAllContours();
287  chamfered_corner.NewOutline();
288  chamfered_corner.Append( 0, 0 );
289  chamfered_corner.Append( 0, signY[ii] * aSize.y / 2 );
290  chamfered_corner.Append( signX[ii] * aSize.x / 2, signY[ii] * aSize.y / 2 );
291  chamfered_corner.Append( signX[ii] * aSize.x / 2, 0 );
292  chamfered_corner.Move( corner_pos );
293  outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
294  }
295 
296  // Now chamfer this corner
297  chamfered_corner.RemoveAllContours();
298  chamfered_corner.NewOutline();
299  chamfered_corner.Append( 0, 0 );
300  chamfered_corner.Append( 0, signY[ii] * chamfer_value );
301  chamfered_corner.Append( signX[ii] * chamfer_value, 0 );
302  chamfered_corner.Move( corner_pos );
303  outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
304  }
305 
306  // Rotate and move the outline:
307  if( aRotation != 0.0 )
308  outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) );
309 
310  outline.Move( VECTOR2I( aPosition ) );
311 
312  // Add the outline:
313  aCornerBuffer.Append( outline );
314 }
315 
316 
318  wxPoint aStart, wxPoint aEnd,
319  int aError, int aWidth )
320 {
321  int radius = aWidth / 2;
322  wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
323  wxPoint startp = aStart;
324  wxPoint corner;
325  VECTOR2I polypoint;
326  int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 );
327  double correction = GetCircletoPolyCorrectionFactor( numSegs );
328  int delta = 3600 / numSegs; // rotate angle in 0.1 degree
329 
330  radius = KiROUND( radius * correction );
331  aCornerBuffer.NewOutline();
332 
333  // normalize the position in order to have endp.x >= 0;
334  if( endp.x < 0 )
335  {
336  endp = aStart - aEnd;
337  startp = aEnd;
338  }
339 
340  double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees
341  int seg_len = KiROUND( EuclideanNorm( endp ) );
342 
343  // Compute the outlines of the segment, and creates a polygon
344  // add right rounded end:
345  for( int ii = 0; ii < 1800; ii += delta )
346  {
347  corner = wxPoint( 0, radius );
348  RotatePoint( &corner, ii );
349  corner.x += seg_len;
350  RotatePoint( &corner, -delta_angle );
351  corner += startp;
352  polypoint.x = corner.x;
353  polypoint.y = corner.y;
354  aCornerBuffer.Append( polypoint.x, polypoint.y );
355  }
356 
357  // Finish arc:
358  corner = wxPoint( seg_len, -radius );
359  RotatePoint( &corner, -delta_angle );
360  corner += startp;
361  polypoint.x = corner.x;
362  polypoint.y = corner.y;
363  aCornerBuffer.Append( polypoint.x, polypoint.y );
364 
365  // add left rounded end:
366  for( int ii = 0; ii < 1800; ii += delta )
367  {
368  corner = wxPoint( 0, -radius );
369  RotatePoint( &corner, ii );
370  RotatePoint( &corner, -delta_angle );
371  corner += startp;
372  polypoint.x = corner.x;
373  polypoint.y = corner.y;
374  aCornerBuffer.Append( polypoint.x, polypoint.y );
375  }
376 
377  // Finish arc:
378  corner = wxPoint( 0, radius );
379  RotatePoint( &corner, -delta_angle );
380  corner += startp;
381  polypoint.x = corner.x;
382  polypoint.y = corner.y;
383  aCornerBuffer.Append( polypoint.x, polypoint.y );
384 }
385 
386 
388  wxPoint aCentre, wxPoint aStart, double aArcAngle,
389  int aError, int aWidth )
390 {
391  wxPoint arc_start, arc_end;
392  int dist = EuclideanNorm( aCentre - aStart );
393  int numSegs = std::max( GetArcToSegmentCount( dist, aError, 360.0 ), 6 );
394  int delta = 3600 / numSegs; // rotate angle in 0.1 degree
395 
396  arc_end = arc_start = aStart;
397 
398  if( aArcAngle != 3600 )
399  {
400  RotatePoint( &arc_end, aCentre, -aArcAngle );
401  }
402 
403  if( aArcAngle < 0 )
404  {
405  std::swap( arc_start, arc_end );
406  aArcAngle = -aArcAngle;
407  }
408 
409  // Compute the ends of segments and creates poly
410  wxPoint curr_end = arc_start;
411  wxPoint curr_start = arc_start;
412 
413  for( int ii = delta; ii < aArcAngle; ii += delta )
414  {
415  curr_end = arc_start;
416  RotatePoint( &curr_end, aCentre, -ii );
417  TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError,
418  aWidth );
419  curr_start = curr_end;
420  }
421 
422  if( curr_end != arc_end )
423  TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_end, arc_end, aError, aWidth );
424 }
425 
426 
428  wxPoint aCentre, int aRadius,
429  int aError, int aWidth )
430 {
431  // Compute the corners positions and creates the poly
432  wxPoint curr_point;
433  int inner_radius = aRadius - ( aWidth / 2 );
434  int outer_radius = inner_radius + aWidth;
435 
436  if( inner_radius <= 0 )
437  { //In this case, the ring is just a circle (no hole inside)
438  TransformCircleToPolygon( aCornerBuffer, aCentre, aRadius + ( aWidth / 2 ), aError );
439  return;
440  }
441 
442  SHAPE_POLY_SET buffer;
443 
444  TransformCircleToPolygon( buffer, aCentre, outer_radius, aError );
445 
446  // Build the hole:
447  buffer.NewHole();
448  TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius, aError );
449 
451  aCornerBuffer.Append( buffer );
452 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:123
int NewHole(int aOutline=-1)
Creates a new hole in a given outline
void TransformArcToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCentre, wxPoint aStart, double aArcAngle, int aError, int aWidth)
Function TransformArcToPolygon Creates a polygon from an Arc Convert arcs to multiple straight segmen...
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, wxPoint aCenter, int aRadius, int aError)
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
static const int dist[10][10]
Definition: ar_matrix.cpp:320
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
void TransformOvalClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aWidth, int aError)
convert a oblong shape to a polygon, using multiple segments It is similar to TransformRoundedEndsSeg...
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aApproxErrorMax, int aMinSegPerCircleCount)
convert a rectangle with rounded corners and/or chamfered corners to a polygon Convert rounded corner...
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCentre, int aRadius, int aError, int aWidth)
Function TransformRingToPolygon Creates a polygon from a ring Convert arcs to multiple straight segme...
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
void Inflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Performs outline inflation/deflation.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
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 TransformRoundedEndsSegmentToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aError, int aWidth)
Function TransformRoundedEndsSegmentToPolygon convert a segment with rounded ends to a polygon Conver...
void Move(const VECTOR2I &aVector) override
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:183
Class SHAPE_POLY_SET.
a few functions useful in geometry calculations.
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...
#define max(a, b)
Definition: auxiliary.h:86
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:214
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)
double GetCircletoPolyCorrectionFactor(int aSegCountforCircle)