KiCad PCB EDA Suite
Loading...
Searching...
No Matches
bitmap2component.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) 1992-2019 jean-pierre.charras
5 * Copyright (C) 1992-2023 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> // std::max
26#include <cerrno>
27#include <cmath>
28#include <cstdio>
29#include <cstdlib>
30#include <cstring>
31#include <string>
32#include <vector>
33#include <kiid.h>
34
35#include <layer_ids.h>
36
37#include <locale_io.h>
38#include <potracelib.h>
39
40#include "bitmap2component.h"
41
42
43/* free a potrace bitmap */
44static void bm_free( potrace_bitmap_t* bm )
45{
46 if( bm != nullptr )
47 {
48 free( bm->map );
49 }
50
51 free( bm );
52}
53
54
55static void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
56 potrace_dpoint_t p1,
57 potrace_dpoint_t p2,
58 potrace_dpoint_t p3,
59 potrace_dpoint_t p4 );
60
62 m_Data( aData )
63{
65 m_PixmapWidth = 0;
67 m_ScaleX = 1.0;
68 m_ScaleY = 1.0;
69 m_Paths = nullptr;
70 m_CmpName = "LOGO";
71}
72
73
74int BITMAPCONV_INFO::ConvertBitmap( potrace_bitmap_t* aPotrace_bitmap, OUTPUT_FMT_ID aFormat,
75 int aDpi_X, int aDpi_Y, BMP2CMP_MOD_LAYER aModLayer )
76{
77 potrace_param_t* param;
78 potrace_state_t* st;
79
80 // set tracing parameters, starting from defaults
81 param = potrace_param_default();
82
83 if( !param )
84 {
85 char msg[256];
86 snprintf( msg, sizeof( msg ), "Error allocating parameters: %s\n", strerror( errno ) );
87 m_errors += msg;
88 return 1;
89 }
90
91 // For parameters: see http://potrace.sourceforge.net/potracelib.pdf
92 param->turdsize = 0; // area (in pixels) of largest path to be ignored.
93 // Potrace default is 2
94 param->opttolerance = 0.2; // curve optimization tolerance. Potrace default is 0.2
95
96 /* convert the bitmap to curves */
97 st = potrace_trace( param, aPotrace_bitmap );
98
99 if( !st || st->status != POTRACE_STATUS_OK )
100 {
101 if( st )
102 {
103 potrace_state_free( st );
104 }
105
106 potrace_param_free( param );
107
108 char msg[256];
109 sprintf( msg, "Error tracing bitmap: %s\n", strerror( errno ) );
110 m_errors += msg;
111 return 1;
112 }
113
114 m_PixmapWidth = aPotrace_bitmap->w;
115 m_PixmapHeight = aPotrace_bitmap->h; // the bitmap size in pixels
116 m_Paths = st->plist;
117
118 switch( aFormat )
119 {
120 case KICAD_WKS_LOGO:
122 m_ScaleX = PL_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to micron
123 m_ScaleY = PL_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom
125 break;
126
127 case POSTSCRIPT_FMT:
129 m_ScaleX = 1.0; // the conversion scale
131
132 // output vector data, e.g. as a rudimentary EPS file (mainly for tests)
134 break;
135
136 case EESCHEMA_FMT:
138 m_ScaleX = SCH_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to eeschema iu
139 m_ScaleY = -SCH_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is bottom to Top for components in libs
141 break;
142
143 case PCBNEW_KICAD_MOD:
145 m_ScaleX = PCB_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to IU
146 m_ScaleY = PCB_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom in Footprint Editor
147 createOutputData( aModLayer );
148 break;
149
150 default:
151 break;
152 }
153
154
155 bm_free( aPotrace_bitmap );
156 potrace_state_free( st );
157 potrace_param_free( param );
158
159 return 0;
160}
161
162
164{
165 const char* layerName = "F.SilkS";
166
167 switch( aChoice )
168 {
170 layerName = "F.Mask";
171 break;
172
173 case MOD_LYR_FAB:
174 layerName = "F.Fab";
175 break;
176
177 case MOD_LYR_DRAWINGS:
178 layerName = "Dwgs.User";
179 break;
180
181 case MOD_LYR_COMMENTS:
182 layerName = "Cmts.User";
183 break;
184
185 case MOD_LYR_ECO1:
186 layerName = "Eco1.User";
187 break;
188
189 case MOD_LYR_ECO2:
190 layerName = "Eco2.User";
191 break;
192
193 case MOD_LYR_FSILKS:
194 break;
195 }
196
197 return layerName;
198}
199
200
201void BITMAPCONV_INFO::outputDataHeader( const char * aBrdLayerName )
202{
203 double Ypos = ( m_PixmapHeight / 2 * m_ScaleY ); // fields Y position in mm
204 double fieldSize; // fields text size in mm
205 char strbuf[1024];
206
207 switch( m_Format )
208 {
209 case POSTSCRIPT_FMT:
210 /* output vector data, e.g. as a rudimentary EPS file */
211 m_Data += "%%!PS-Adobe-3.0 EPSF-3.0\n";
212 snprintf( strbuf, sizeof( strbuf ), "%%%%BoundingBox: 0 0 %d %d\n", m_PixmapWidth,
214 m_Data += strbuf;
215 m_Data += "gsave\n";
216 break;
217
218 case PCBNEW_KICAD_MOD:
219 // fields text size = 1.5 mm
220 // fields text thickness = 1.5 / 5 = 0.3mm
221 snprintf( strbuf, sizeof( strbuf ),
222 "(footprint \"%s\" (version 20221018) (generator bitmap2component)\n"
223 " (layer \"F.Cu\")\n",
224 m_CmpName.c_str() );
225
226 m_Data += strbuf;
227 snprintf( strbuf, sizeof( strbuf ),
228 " (attr board_only exclude_from_pos_files exclude_from_bom)\n" );
229 m_Data += strbuf;
230 snprintf( strbuf, sizeof( strbuf ),
231 " (fp_text reference \"G***\" (at 0 0) (layer \"%s\")\n"
232 " (effects (font (size 1.5 1.5) (thickness 0.3)))\n"
233 " (tstamp %s)\n )\n",
234 aBrdLayerName, KIID().AsString().ToStdString().c_str() );
235
236 m_Data += strbuf;
237 snprintf( strbuf, sizeof( strbuf ),
238 " (fp_text value \"%s\" (at 0.75 0) (layer \"%s\") hide\n"
239 " (effects (font (size 1.5 1.5) (thickness 0.3)))\n"
240 " (tstamp %s)\n )\n",
241 m_CmpName.c_str(), aBrdLayerName, KIID().AsString().ToStdString().c_str() );
242
243 m_Data += strbuf;
244 break;
245
246 case KICAD_WKS_LOGO:
247 m_Data += "(kicad_wks (version 20220228) (generator bitmap2component)\n";
248 m_Data += " (setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)\n";
249 m_Data += " (left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))\n";
250 m_Data += " (polygon (name \"\") (pos 0 0) (linewidth 0.01)\n";
251 break;
252
253 case EESCHEMA_FMT:
254 fieldSize = 1.27; // fields text size in mm (= 50 mils)
255 Ypos /= SCH_IU_PER_MM;
256 Ypos += fieldSize / 2;
257 // snprintf( strbuf, sizeof(strbuf), "# pixmap size w = %d, h = %d\n#\n", m_PixmapWidth, m_PixmapHeight );
258 snprintf( strbuf, sizeof( strbuf ),
259 "(kicad_symbol_lib (version 20220914) (generator bitmap2component)\n"
260 " (symbol \"%s\" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)\n",
261 m_CmpName.c_str() );
262 m_Data += strbuf;
263
264 snprintf( strbuf, sizeof( strbuf ),
265 " (property \"Reference\" \"#G\" (at 0 %g 0)\n"
266 " (effects (font (size %g %g)) hide)\n )\n",
267 -Ypos, fieldSize, fieldSize );
268 m_Data += strbuf;
269
270 snprintf( strbuf, sizeof( strbuf ),
271 " (property \"Value\" \"%s\" (at 0 %g 0)\n"
272 " (effects (font (size %g %g)) hide)\n )\n",
273 m_CmpName.c_str(), Ypos, fieldSize, fieldSize );
274 m_Data += strbuf;
275
276 snprintf( strbuf, sizeof( strbuf ),
277 " (property \"Footprint\" \"\" (at 0 0 0)\n"
278 " (effects (font (size %g %g)) hide)\n )\n",
279 fieldSize, fieldSize );
280 m_Data += strbuf;
281
282 snprintf( strbuf, sizeof( strbuf ),
283 " (property \"Datasheet\" \"\" (at 0 0 0)\n"
284 " (effects (font (size %g %g)) hide)\n )\n",
285 fieldSize, fieldSize );
286 m_Data += strbuf;
287
288 snprintf( strbuf, sizeof( strbuf ), " (symbol \"%s_0_0\"\n", m_CmpName.c_str() );
289 m_Data += strbuf;
290 break;
291 }
292}
293
294
296{
297 switch( m_Format )
298 {
299 case POSTSCRIPT_FMT:
300 m_Data += "grestore\n";
301 m_Data += "%%EOF\n";
302 break;
303
304 case PCBNEW_KICAD_MOD:
305 m_Data += ")\n";
306 break;
307
308 case KICAD_WKS_LOGO:
309 m_Data += " )\n)\n";
310 break;
311
312 case EESCHEMA_FMT:
313 m_Data += " )\n"; // end symbol_0_0
314 m_Data += " )\n"; // end symbol
315 m_Data += ")\n"; // end lib
316 break;
317 }
318}
319
320
321void BITMAPCONV_INFO::outputOnePolygon( SHAPE_LINE_CHAIN& aPolygon, const char* aBrdLayerName )
322{
323 // write one polygon to output file.
324 // coordinates are expected in target unit.
325 int ii, jj;
326 VECTOR2I currpoint;
327 char strbuf[1024];
328
329 int offsetX = (int)( m_PixmapWidth / 2 * m_ScaleX );
330 int offsetY = (int)( m_PixmapHeight / 2 * m_ScaleY );
331
332 const VECTOR2I startpoint = aPolygon.CPoint( 0 );
333
334 switch( m_Format )
335 {
336 case POSTSCRIPT_FMT:
337 offsetY = (int)( m_PixmapHeight * m_ScaleY );
338 snprintf( strbuf, sizeof( strbuf ), "newpath\n%d %d moveto\n", startpoint.x,
339 offsetY - startpoint.y );
340 m_Data += strbuf;
341 jj = 0;
342
343 for( ii = 1; ii < aPolygon.PointCount(); ii++ )
344 {
345 currpoint = aPolygon.CPoint( ii );
346 snprintf( strbuf, sizeof( strbuf ), " %d %d lineto", currpoint.x,
347 offsetY - currpoint.y );
348 m_Data += strbuf;
349
350 if( jj++ > 6 )
351 {
352 jj = 0;
353 m_Data += "\n";
354 }
355 }
356
357 m_Data += "\nclosepath fill\n";
358 break;
359
360 case PCBNEW_KICAD_MOD:
361 {
362 double width = 0.0; // outline thickness in mm: no thickness
363 m_Data += " (fp_poly\n (pts\n";
364
365 jj = 0;
366
367 for( ii = 0; ii < aPolygon.PointCount(); ii++ )
368 {
369 currpoint = aPolygon.CPoint( ii );
370 snprintf( strbuf, sizeof( strbuf ), " (xy %f %f)\n",
371 ( currpoint.x - offsetX ) / PCB_IU_PER_MM,
372 ( currpoint.y - offsetY ) / PCB_IU_PER_MM );
373 m_Data += strbuf;
374 }
375 // No need to close polygon
376
377 m_Data += " )\n\n";
378 snprintf( strbuf, sizeof( strbuf ),
379 " (stroke (width %f) (type solid)) (fill solid) (layer \"%s\") (tstamp %s))\n",
380 width, aBrdLayerName, KIID().AsString().ToStdString().c_str() );
381
382 m_Data += strbuf;
383 }
384 break;
385
386 case KICAD_WKS_LOGO:
387 m_Data += " (pts";
388
389 // Internal units = micron, file unit = mm
390 jj = 1;
391
392 for( ii = 0; ii < aPolygon.PointCount(); ii++ )
393 {
394 currpoint = aPolygon.CPoint( ii );
395 snprintf( strbuf, sizeof( strbuf ), " (xy %.3f %.3f)",
396 ( currpoint.x - offsetX ) / PL_IU_PER_MM,
397 ( currpoint.y - offsetY ) / PL_IU_PER_MM );
398 m_Data += strbuf;
399
400 if( jj++ > 4 )
401 {
402 jj = 0;
403 m_Data += "\n ";
404 }
405 }
406
407 // Close polygon
408 snprintf( strbuf, sizeof( strbuf ), " (xy %.3f %.3f) )\n",
409 ( startpoint.x - offsetX ) / PL_IU_PER_MM,
410 ( startpoint.y - offsetY ) / PL_IU_PER_MM );
411 m_Data += strbuf;
412 break;
413
414 case EESCHEMA_FMT:
415 // The polygon outline thickness is fixed here to 0.01 ( 0.0 is the default thickness)
416#define SCH_LINE_THICKNESS_MM 0.01
417 //snprintf( strbuf, sizeof(strbuf), "P %d 0 0 %d", (int) aPolygon.PointCount() + 1, EE_LINE_THICKNESS );
418 m_Data += " (polyline\n (pts\n";
419
420 for( ii = 0; ii < aPolygon.PointCount(); ii++ )
421 {
422 currpoint = aPolygon.CPoint( ii );
423 snprintf( strbuf, sizeof( strbuf ), " (xy %f %f)\n",
424 ( currpoint.x - offsetX ) / SCH_IU_PER_MM,
425 ( currpoint.y - offsetY ) / SCH_IU_PER_MM );
426 m_Data += strbuf;
427 }
428
429 // Close polygon
430 snprintf( strbuf, sizeof( strbuf ), " (xy %f %f)\n",
431 ( startpoint.x - offsetX ) / SCH_IU_PER_MM,
432 ( startpoint.y - offsetY ) / SCH_IU_PER_MM );
433 m_Data += strbuf;
434 m_Data += " )\n"; // end pts
435
436 snprintf( strbuf, sizeof( strbuf ),
437 " (stroke (width %g) (type default))\n (fill (type outline))\n",
439
440 m_Data += strbuf;
441 m_Data += " )\n"; // end polyline
442 break;
443 }
444}
445
446
448{
449 std::vector <potrace_dpoint_t> cornersBuffer;
450
451 // polyset_areas is a set of polygon to draw
452 SHAPE_POLY_SET polyset_areas;
453
454 // polyset_holes is the set of holes inside polyset_areas outlines
455 SHAPE_POLY_SET polyset_holes;
456
457 potrace_dpoint_t( *c )[3];
458
459 LOCALE_IO toggle; // Temporary switch the locale to standard C to r/w floats
460
461 // The layer name has meaning only for .kicad_mod files.
462 // For these files the header creates 2 invisible texts: value and ref
463 // (needed but not useful) on silk screen layer
465
466 bool main_outline = true;
467
468 /* draw each as a polygon with no hole.
469 * Bezier curves are approximated by a polyline
470 */
471 potrace_path_t* paths = m_Paths; // the list of paths
472
473 if(!m_Paths)
474 {
475 m_errors += "No shape in black and white image to convert: no outline created\n";
476 }
477
478 while( paths != nullptr )
479 {
480 int cnt = paths->curve.n;
481 int* tag = paths->curve.tag;
482 c = paths->curve.c;
483 potrace_dpoint_t startpoint = c[cnt - 1][2];
484
485 for( int i = 0; i < cnt; i++ )
486 {
487 switch( tag[i] )
488 {
489 case POTRACE_CORNER:
490 cornersBuffer.push_back( c[i][1] );
491 cornersBuffer.push_back( c[i][2] );
492 startpoint = c[i][2];
493 break;
494
495 case POTRACE_CURVETO:
496 BezierToPolyline( cornersBuffer, startpoint, c[i][0], c[i][1], c[i][2] );
497 startpoint = c[i][2];
498 break;
499 }
500 }
501
502 // Store current path
503 if( main_outline )
504 {
505 main_outline = false;
506
507 // build the current main polygon
508 polyset_areas.NewOutline();
509 for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
510 {
511 polyset_areas.Append( int( cornersBuffer[i].x * m_ScaleX ),
512 int( cornersBuffer[i].y * m_ScaleY ) );
513 }
514 }
515 else
516 {
517 // Add current hole in polyset_holes
518 polyset_holes.NewOutline();
519
520 for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
521 {
522 polyset_holes.Append( int( cornersBuffer[i].x * m_ScaleX ),
523 int( cornersBuffer[i].y * m_ScaleY ) );
524 }
525 }
526
527 cornersBuffer.clear();
528
529 // at the end of a group of a positive path and its negative children, fill.
530 if( paths->next == nullptr || paths->next->sign == '+' )
531 {
534 polyset_areas.BooleanSubtract( polyset_holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
535
536 // Ensure there are no self intersecting polygons
537 if( polyset_areas.NormalizeAreaOutlines() )
538 {
539 // Convert polygon with holes to a unique polygon
541
542 // Output current resulting polygon(s)
543 for( int ii = 0; ii < polyset_areas.OutlineCount(); ii++ )
544 {
545 SHAPE_LINE_CHAIN& poly = polyset_areas.Outline( ii );
546 outputOnePolygon( poly, getBoardLayerName( aModLayer ));
547 }
548
549 polyset_areas.RemoveAllContours();
550 polyset_holes.RemoveAllContours();
551 main_outline = true;
552 }
553 }
554
555 paths = paths->next;
556 }
557
559}
560
561// a helper function to calculate a square value
562inline double square( double x )
563{
564 return x * x;
565}
566
567
568// a helper function to calculate a cube value
569inline double cube( double x )
570{
571 return x * x * x;
572}
573
574
575/* render a Bezier curve. */
576void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
577 potrace_dpoint_t p1,
578 potrace_dpoint_t p2,
579 potrace_dpoint_t p3,
580 potrace_dpoint_t p4 )
581{
582 double dd0, dd1, dd, delta, e2, epsilon, t;
583
584 // p1 = starting point
585
586 /* we approximate the curve by small line segments. The interval
587 * size, epsilon, is determined on the fly so that the distance
588 * between the true curve and its approximation does not exceed the
589 * desired accuracy delta. */
590
591 delta = 0.25; /* desired accuracy, in pixels */
592
593 /* let dd = maximal value of 2nd derivative over curve - this must
594 * occur at an endpoint. */
595 dd0 = square( p1.x - 2 * p2.x + p3.x ) + square( p1.y - 2 * p2.y + p3.y );
596 dd1 = square( p2.x - 2 * p3.x + p4.x ) + square( p2.y - 2 * p3.y + p4.y );
597 dd = 6 * sqrt( std::max( dd0, dd1 ) );
598 e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
599 epsilon = sqrt( e2 ); /* necessary interval size */
600
601 for( t = epsilon; t<1; t += epsilon )
602 {
603 potrace_dpoint_t intermediate_point;
604 intermediate_point.x = p1.x * cube( 1 - t ) +
605 3* p2.x* square( 1 - t ) * t +
606 3 * p3.x * (1 - t) * square( t ) +
607 p4.x* cube( t );
608
609 intermediate_point.y = p1.y * cube( 1 - t ) +
610 3* p2.y* square( 1 - t ) * t +
611 3 * p3.y * (1 - t) * square( t ) + p4.y* cube( t );
612
613 aCornersBuffer.push_back( intermediate_point );
614 }
615
616 aCornersBuffer.push_back( p4 );
617}
constexpr double SCH_IU_PER_MM
Definition: base_units.h:73
constexpr double PCB_IU_PER_MM
Definition: base_units.h:71
constexpr double PL_IU_PER_MM
Definition: base_units.h:72
double square(double x)
static void BezierToPolyline(std::vector< potrace_dpoint_t > &aCornersBuffer, potrace_dpoint_t p1, potrace_dpoint_t p2, potrace_dpoint_t p3, potrace_dpoint_t p4)
double cube(double x)
static void bm_free(potrace_bitmap_t *bm)
#define SCH_LINE_THICKNESS_MM
BMP2CMP_MOD_LAYER
@ MOD_LYR_ECO1
@ MOD_LYR_FSILKS
@ MOD_LYR_ECO2
@ MOD_LYR_DRAWINGS
@ MOD_LYR_FSOLDERMASK
@ MOD_LYR_COMMENTS
@ MOD_LYR_FAB
OUTPUT_FMT_ID
@ KICAD_WKS_LOGO
@ PCBNEW_KICAD_MOD
@ POSTSCRIPT_FMT
@ EESCHEMA_FMT
void outputDataEnd()
Function outputDataEnd write to file the last strings depending on file format.
std::string & m_Data
BITMAPCONV_INFO(std::string &aData)
potrace_path_t * m_Paths
std::string m_CmpName
void outputDataHeader(const char *aBrdLayerName)
Function outputDataHeader write to file the header depending on file format.
void outputOnePolygon(SHAPE_LINE_CHAIN &aPolygon, const char *aBrdLayerName)
Function outputOnePolygon write one polygon to output file.
enum OUTPUT_FMT_ID m_Format
void createOutputData(BMP2CMP_MOD_LAYER aModLayer=(BMP2CMP_MOD_LAYER) 0)
Creates the data specified by m_Format.
int ConvertBitmap(potrace_bitmap_t *aPotrace_bitmap, OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y, BMP2CMP_MOD_LAYER aModLayer)
Run the conversion of the bitmap.
std::string m_errors
const char * getBoardLayerName(BMP2CMP_MOD_LAYER aChoice)
Definition: kiid.h:48
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int NormalizeAreaOutlines()
Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s).
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)
void Simplify(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
constexpr int delta