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 
35 
47  wxPoint aCenter, int aRadius,
48  int aCircleToSegmentsCount )
49 {
50  wxPoint corner_position;
51  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
52  double halfstep = delta/2; // the starting value for rot angles
53 
54  aCornerBuffer.NewOutline();
55 
56  for( int ii = 0; ii < aCircleToSegmentsCount; ii++ )
57  {
58  corner_position.x = aRadius;
59  corner_position.y = 0;
60  double angle = (ii * delta) + halfstep;
61  RotatePoint( &corner_position, angle );
62  corner_position += aCenter;
63  aCornerBuffer.Append( corner_position.x, corner_position.y );
64  }
65 }
66 
68  wxPoint aStart, wxPoint aEnd, int aWidth,
69  int aCircleToSegmentsCount, double aCorrectionFactor )
70 {
71  // To build the polygonal shape outside the actual shape, we use a bigger
72  // radius to build rounded ends.
73  // However, the width of the segment is too big.
74  // so, later, we will clamp the polygonal shape with the bounding box
75  // of the segment.
76  int radius = aWidth / 2;
77 
78  // Note if we want to compensate the radius reduction of a circle due to
79  // the segment approx, aCorrectionFactor must be calculated like this:
80  // For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
81  // aCorrectionFactor is 1 /cos( PI/s_CircleToSegmentsCount )
82 
83  radius = radius * aCorrectionFactor; // make segments outside the circles
84 
85  // end point is the coordinate relative to aStart
86  wxPoint endp = aEnd - aStart;
87  wxPoint startp = aStart;
88  wxPoint corner;
89  SHAPE_POLY_SET polyshape;
90 
91  polyshape.NewOutline();
92 
93  // normalize the position in order to have endp.x >= 0
94  // it makes calculations more easy to understand
95  if( endp.x < 0 )
96  {
97  endp = aStart - aEnd;
98  startp = aEnd;
99  }
100 
101  // delta_angle is in radian
102  double delta_angle = atan2( (double)endp.y, (double)endp.x );
103  int seg_len = KiROUND( EuclideanNorm( endp ) );
104 
105  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
106 
107  // Compute the outlines of the segment, and creates a polygon
108  // Note: the polygonal shape is built from the equivalent horizontal
109  // segment starting ar 0,0, and ending at seg_len,0
110 
111  // add right rounded end:
112  for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
113  {
114  corner = wxPoint( 0, radius );
115  RotatePoint( &corner, delta*ii );
116  corner.x += seg_len;
117  polyshape.Append( corner.x, corner.y );
118  }
119 
120  // Finish arc:
121  corner = wxPoint( seg_len, -radius );
122  polyshape.Append( corner.x, corner.y );
123 
124  // add left rounded end:
125  for( int ii = 0; ii < aCircleToSegmentsCount/2; ii++ )
126  {
127  corner = wxPoint( 0, -radius );
128  RotatePoint( &corner, delta*ii );
129  polyshape.Append( corner.x, corner.y );
130  }
131 
132  // Finish arc:
133  corner = wxPoint( 0, radius );
134  polyshape.Append( corner.x, corner.y );
135 
136  // Now, clamp the polygonal shape (too big) with the segment bounding box
137  // the polygonal shape bbox equivalent to the segment has a too big height,
138  // and the right width
139  if( aCorrectionFactor > 1.0 )
140  {
141  SHAPE_POLY_SET bbox;
142  bbox.NewOutline();
143  // Build the bbox (a horizontal rectangle).
144  int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
145  corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
146  // creating useless corner at segment ends
147  corner.y = halfwidth;
148  bbox.Append( corner.x, corner.y );
149  corner.y = -halfwidth;
150  bbox.Append( corner.x, corner.y );
151  corner.x = radius + seg_len + 2;
152  bbox.Append( corner.x, corner.y );
153  corner.y = halfwidth;
154  bbox.Append( corner.x, corner.y );
155 
156  // Now, clamp the shape
158  // Note the final polygon is a simple, convex polygon with no hole
159  // due to the shape of initial polygons
160  }
161 
162  // Rotate and move the polygon to its right location
163  polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) );
164  polyshape.Move( startp );
165 
166  aCornerBuffer.Append( polyshape);
167 }
168 
169 /* Returns the centers of the rounded corners of a rect.
170  */
171 void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
172  const wxPoint& aPosition, const wxSize& aSize, double aRotation )
173 {
174  wxSize size( aSize/2 );
175 
176  size.x -= aRadius;
177  size.y -= aRadius;
178 
179  // Ensure size is > 0, to avoid generating unusable shapes
180  // which can crash kicad.
181  if( size.x <= 1 )
182  size.x = 1;
183  if( size.y <= 1 )
184  size.y = 1;
185 
186  aCenters[0].x = -size.x;
187  aCenters[0].y = size.y;
188 
189  aCenters[1].x = size.x;
190  aCenters[1].y = size.y;
191 
192  aCenters[2].x = size.x;
193  aCenters[2].y = -size.y;
194 
195  aCenters[3].x = -size.x;
196  aCenters[3].y = -size.y;
197 
198  // Rotate the polygon
199  if( aRotation )
200  {
201  for( int ii = 0; ii < 4; ii++ )
202  RotatePoint( &aCenters[ii], aRotation );
203  }
204 
205  // move the polygon to the position
206  for( int ii = 0; ii < 4; ii++ )
207  aCenters[ii] += aPosition;
208 }
209 
222  const wxPoint& aPosition, const wxSize& aSize,
223  double aRotation, int aCornerRadius,
224  int aCircleToSegmentsCount )
225 {
226  wxPoint corners[4];
227  GetRoundRectCornerCenters( corners, aCornerRadius, aPosition, aSize, aRotation );
228 
229  SHAPE_POLY_SET outline;
230  outline.NewOutline();
231 
232  for( int ii = 0; ii < 4; ++ii )
233  outline.Append( corners[ii].x, corners[ii].y );
234 
235  outline.Inflate( aCornerRadius, aCircleToSegmentsCount );
236 
237  // Add the outline:
238  aCornerBuffer.Append( outline );
239 }
240 
241 
255  wxPoint aStart, wxPoint aEnd,
256  int aCircleToSegmentsCount,
257  int aWidth )
258 {
259  int radius = aWidth / 2;
260  wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
261  wxPoint startp = aStart;
262  wxPoint corner;
263  VECTOR2I polypoint;
264 
265  aCornerBuffer.NewOutline();
266 
267  // normalize the position in order to have endp.x >= 0;
268  if( endp.x < 0 )
269  {
270  endp = aStart - aEnd;
271  startp = aEnd;
272  }
273 
274  double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees
275  int seg_len = KiROUND( EuclideanNorm( endp ) );
276 
277  int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
278 
279  // Compute the outlines of the segment, and creates a polygon
280  // add right rounded end:
281  for( int ii = 0; ii < 1800; ii += delta )
282  {
283  corner = wxPoint( 0, radius );
284  RotatePoint( &corner, ii );
285  corner.x += seg_len;
286  RotatePoint( &corner, -delta_angle );
287  corner += startp;
288  polypoint.x = corner.x;
289  polypoint.y = corner.y;
290  aCornerBuffer.Append( polypoint.x, polypoint.y );
291  }
292 
293  // Finish arc:
294  corner = wxPoint( seg_len, -radius );
295  RotatePoint( &corner, -delta_angle );
296  corner += startp;
297  polypoint.x = corner.x;
298  polypoint.y = corner.y;
299  aCornerBuffer.Append( polypoint.x, polypoint.y );
300 
301  // add left rounded end:
302  for( int ii = 0; ii < 1800; ii += delta )
303  {
304  corner = wxPoint( 0, -radius );
305  RotatePoint( &corner, ii );
306  RotatePoint( &corner, -delta_angle );
307  corner += startp;
308  polypoint.x = corner.x;
309  polypoint.y = corner.y;
310  aCornerBuffer.Append( polypoint.x, polypoint.y );
311  }
312 
313  // Finish arc:
314  corner = wxPoint( 0, radius );
315  RotatePoint( &corner, -delta_angle );
316  corner += startp;
317  polypoint.x = corner.x;
318  polypoint.y = corner.y;
319  aCornerBuffer.Append( polypoint.x, polypoint.y );
320 }
321 
322 
335  wxPoint aCentre, wxPoint aStart, double aArcAngle,
336  int aCircleToSegmentsCount, int aWidth )
337 {
338  wxPoint arc_start, arc_end;
339  int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
340 
341  arc_end = arc_start = aStart;
342 
343  if( aArcAngle != 3600 )
344  {
345  RotatePoint( &arc_end, aCentre, -aArcAngle );
346  }
347 
348  if( aArcAngle < 0 )
349  {
350  std::swap( arc_start, arc_end );
351  aArcAngle = -aArcAngle;
352  }
353 
354  // Compute the ends of segments and creates poly
355  wxPoint curr_end = arc_start;
356  wxPoint curr_start = arc_start;
357 
358  for( int ii = delta; ii < aArcAngle; ii += delta )
359  {
360  curr_end = arc_start;
361  RotatePoint( &curr_end, aCentre, -ii );
362  TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end,
363  aCircleToSegmentsCount, aWidth );
364  curr_start = curr_end;
365  }
366 
367  if( curr_end != arc_end )
369  curr_end, arc_end,
370  aCircleToSegmentsCount, aWidth );
371 }
372 
373 
385  wxPoint aCentre, int aRadius,
386  int aCircleToSegmentsCount, int aWidth )
387 {
388  // Compute the corners positions and creates the poly
389  wxPoint curr_point;
390  int inner_radius = aRadius - ( aWidth / 2 );
391  int outer_radius = inner_radius + aWidth;
392 
393  if( inner_radius <= 0 )
394  { //In this case, the ring is just a circle (no hole inside)
395  TransformCircleToPolygon( aCornerBuffer, aCentre, aRadius + ( aWidth / 2 ),
396  aCircleToSegmentsCount );
397  return;
398  }
399 
400  aCornerBuffer.NewOutline();
401 
402  // Draw the inner circle of the ring
403  int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
404 
405  for( int ii = 0; ii < 3600; ii += delta )
406  {
407  curr_point.x = inner_radius;
408  curr_point.y = 0;
409  RotatePoint( &curr_point, ii );
410  curr_point += aCentre;
411  aCornerBuffer.Append( curr_point.x, curr_point.y );
412  }
413 
414  // Draw the last point of inner circle
415  aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y );
416 
417  // Draw the outer circle of the ring
418  // the first point creates also a segment from the inner to the outer polygon
419  for( int ii = 0; ii < 3600; ii += delta )
420  {
421  curr_point.x = outer_radius;
422  curr_point.y = 0;
423  RotatePoint( &curr_point, -ii );
424  curr_point += aCentre;
425  aCornerBuffer.Append( curr_point.x, curr_point.y );
426  }
427 
428  // Draw the last point of outer circle
429  aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y );
430 
431  // And connect the outer polygon to the inner polygon,.
432  // because a segment from inner to the outer polygon was already created,
433  // the final polygon is the inner and the outer outlines connected by
434  // 2 overlapping segments
435  aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y );
436 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
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...
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
void TransformCircleToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCenter, int aRadius, int aCircleToSegmentsCount)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines...
void TransformRoundRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, int aCircleToSegmentsCount)
Function TransformRoundRectToPolygon convert a rectangle with rounded corners to a polygon Convert ar...
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
This file contains miscellaneous commonly used macros and functions.
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 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 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...
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
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 Rotate(double aAngle, const VECTOR2I &aCenter)
Function Rotate rotates all vertices by a given angle.
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) ...