You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1495 lines
45 KiB

18 years ago
17 years ago
16 years ago
16 years ago
16 years ago
17 years ago
  1. // PolyLine.cpp ... implementation of CPolyLine class from FreePCB.
  2. //
  3. // implementation for kicad and kbool polygon clipping library
  4. //
  5. #include <math.h>
  6. #include <vector>
  7. #include "fctsys.h"
  8. #include "PolyLine.h"
  9. #include "gr_basic.h"
  10. #include "bezier_curves.h"
  11. #include "polygon_test_point_inside.h"
  12. using namespace std;
  13. #define pi M_PI
  14. CPolyLine::CPolyLine()
  15. {
  16. m_HatchStyle = 0;
  17. m_Width = 0;
  18. utility = 0;
  19. m_Kbool_Poly_Engine = NULL;
  20. }
  21. // destructor, removes display elements
  22. //
  23. CPolyLine::~CPolyLine()
  24. {
  25. Undraw();
  26. if( m_Kbool_Poly_Engine )
  27. delete m_Kbool_Poly_Engine;
  28. }
  29. /**
  30. * Function NormalizeWithKbool
  31. * Use the Kbool Library to clip contours: if outlines are crossing, the self-crossing polygon
  32. * is converted to non self-crossing polygon by adding extra points at the crossing locations
  33. * and reordering corners
  34. * if more than one outside contour are found, extra CPolyLines will be created
  35. * because copper areas have only one outside contour
  36. * Therefore, if this results in new CPolyLines, return them as std::vector pa
  37. * @param aExtraPolys: pointer on a std::vector<CPolyLine*> to store extra CPolyLines
  38. * @param bRetainArcs == TRUE, try to retain arcs in polys
  39. * @return number of external contours, or -1 if error
  40. */
  41. int CPolyLine::NormalizeWithKbool( std::vector<CPolyLine*> * aExtraPolyList, bool bRetainArcs )
  42. {
  43. std::vector<CArc> arc_array;
  44. std::vector <void*> hole_array; // list of holes
  45. std::vector<int> * hole; // used to store corners for a given hole
  46. CPolyLine* polyline;
  47. int n_ext_cont = 0; // CPolyLine count
  48. /* Creates a bool engine from this CPolyLine.
  49. * Normalized outlines and holes will be in m_Kbool_Poly_Engine
  50. * If some polygons are self crossing, after running the Kbool Engine, self crossing polygons
  51. * will be converted in non self crossing polygons by inserting extra points at the crossing locations
  52. * True holes are combined if possible
  53. */
  54. if( bRetainArcs )
  55. MakeKboolPoly( -1, -1, &arc_array );
  56. else
  57. MakeKboolPoly( -1, -1, NULL );
  58. Undraw();
  59. /* now, recreate polys
  60. * if more than one outside contour are found, extra CPolyLines will be created
  61. * because copper areas have only one outside contour
  62. * the first outside contour found is the new "this" outside contour
  63. * if others outside contours are found we create new CPolyLines
  64. * Note: if there are holes in polygons, we must store them
  65. * and when all outside contours are found, search the corresponding outside contour for each hole
  66. */
  67. while( m_Kbool_Poly_Engine->StartPolygonGet() )
  68. {
  69. // See if the current polygon is flagged as a hole
  70. if( m_Kbool_Poly_Engine->GetPolygonPointEdgeType() == KB_INSIDE_EDGE )
  71. {
  72. hole = new std::vector<int>;
  73. hole_array.push_back( hole );
  74. while( m_Kbool_Poly_Engine->PolygonHasMorePoints() ) // store hole
  75. {
  76. int x = (int) m_Kbool_Poly_Engine->GetPolygonXPoint();
  77. int y = (int) m_Kbool_Poly_Engine->GetPolygonYPoint();
  78. hole->push_back( x );
  79. hole->push_back( y );
  80. }
  81. m_Kbool_Poly_Engine->EndPolygonGet();
  82. }
  83. else if( n_ext_cont == 0 )
  84. {
  85. // first external contour, replace this poly
  86. corner.clear();
  87. side_style.clear();
  88. bool first = true;
  89. while( m_Kbool_Poly_Engine->PolygonHasMorePoints() )
  90. { // foreach point in the polygon
  91. int x = (int) m_Kbool_Poly_Engine->GetPolygonXPoint();
  92. int y = (int) m_Kbool_Poly_Engine->GetPolygonYPoint();
  93. if( first )
  94. {
  95. first = false;
  96. Start( GetLayer(), x, y, GetHatchStyle() );
  97. }
  98. else
  99. AppendCorner( x, y );
  100. }
  101. m_Kbool_Poly_Engine->EndPolygonGet();
  102. Close();
  103. n_ext_cont++;
  104. }
  105. else if( aExtraPolyList ) // a new outside contour is found: create a new CPolyLine
  106. {
  107. polyline = new CPolyLine; // create new poly
  108. aExtraPolyList->push_back( polyline ); // put it in array
  109. bool first = true;
  110. while( m_Kbool_Poly_Engine->PolygonHasMorePoints() ) // read next external contour
  111. {
  112. int x = (int) m_Kbool_Poly_Engine->GetPolygonXPoint();
  113. int y = (int) m_Kbool_Poly_Engine->GetPolygonYPoint();
  114. if( first )
  115. {
  116. first = false;
  117. polyline->Start( GetLayer(), x, y, GetHatchStyle() );
  118. }
  119. else
  120. polyline->AppendCorner( x, y );
  121. }
  122. m_Kbool_Poly_Engine->EndPolygonGet();
  123. polyline->Close( STRAIGHT, FALSE );
  124. n_ext_cont++;
  125. }
  126. }
  127. // now add cutouts to the corresponding CPolyLine(s)
  128. for( unsigned ii = 0; ii < hole_array.size(); ii++ )
  129. {
  130. hole = (std::vector<int> *)hole_array[ii];
  131. polyline = NULL;
  132. if( n_ext_cont == 1 )
  133. {
  134. polyline = this;
  135. }
  136. else
  137. {
  138. // find the polygon that contains this hole
  139. // testing one corner inside is enought because a hole is entirely inside the polygon
  140. // so we test only the first corner
  141. int x = (*hole)[0];
  142. int y = (*hole)[1];
  143. if( TestPointInside( x, y ) )
  144. polyline = this;
  145. else if( aExtraPolyList )
  146. {
  147. for( int ext_ic = 0; ext_ic<n_ext_cont - 1; ext_ic++ )
  148. {
  149. if( (*aExtraPolyList)[ext_ic]->TestPointInside( x, y ) )
  150. {
  151. polyline = (*aExtraPolyList)[ext_ic];
  152. break;
  153. }
  154. }
  155. }
  156. }
  157. if( !polyline )
  158. wxASSERT( 0 );
  159. else
  160. {
  161. for( unsigned ii = 0; ii< (*hole).size(); ii++ )
  162. {
  163. int x = (*hole)[ii]; ii++;
  164. int y = (*hole)[ii];
  165. polyline->AppendCorner( x, y, STRAIGHT, FALSE );
  166. }
  167. polyline->Close( STRAIGHT, FALSE );
  168. }
  169. }
  170. if( bRetainArcs )
  171. RestoreArcs( &arc_array, aExtraPolyList );
  172. delete m_Kbool_Poly_Engine;
  173. m_Kbool_Poly_Engine = NULL;
  174. // free hole list
  175. for( unsigned ii = 0; ii < hole_array.size(); ii++ )
  176. delete (std::vector<int> *)hole_array[ii];
  177. return n_ext_cont;
  178. }
  179. /**
  180. * Function AddPolygonsToBoolEng
  181. * Add a CPolyLine to a kbool engine, preparing a boolean op between polygons
  182. * @param aStart_contour: starting contour number (-1 = all, 0 is the outlines of zone, > 1 = holes in zone
  183. * @param aEnd_contour: ending contour number (-1 = all after aStart_contour)
  184. * @param arc_array: arc converted to poly segments (NULL if not exists)
  185. * @param aBooleng : pointer on a bool engine (handle a set of polygons)
  186. * @param aGroup : group to fill (aGroup = GROUP_A or GROUP_B) operations are made between GROUP_A and GROUP_B
  187. */
  188. int CPolyLine::AddPolygonsToBoolEng( Bool_Engine* aBooleng,
  189. GroupType aGroup,
  190. int aStart_contour,
  191. int aEnd_contour,
  192. std::vector<CArc> * arc_array )
  193. {
  194. int count = 0;
  195. if( (aGroup != GROUP_A) && (aGroup != GROUP_B ) )
  196. return 0; //Error !
  197. /* Convert the current polyline contour to a kbool polygon: */
  198. MakeKboolPoly( aStart_contour, aEnd_contour, arc_array );
  199. /* add the resulting kbool set of polygons to the current kcool engine */
  200. while( m_Kbool_Poly_Engine->StartPolygonGet() )
  201. {
  202. if( aBooleng->StartPolygonAdd( GROUP_A ) )
  203. {
  204. while( m_Kbool_Poly_Engine->PolygonHasMorePoints() )
  205. {
  206. int x = (int) m_Kbool_Poly_Engine->GetPolygonXPoint();
  207. int y = (int) m_Kbool_Poly_Engine->GetPolygonYPoint();
  208. aBooleng->AddPoint( x, y );
  209. count++;
  210. }
  211. aBooleng->EndPolygonAdd();
  212. }
  213. m_Kbool_Poly_Engine->EndPolygonGet();
  214. }
  215. delete m_Kbool_Poly_Engine;
  216. m_Kbool_Poly_Engine = NULL;
  217. return count;
  218. }
  219. /**
  220. * Function MakeKboolPoly
  221. * fill a kbool engine with a closed polyline contour
  222. * approximates arcs with multiple straight-line segments
  223. * @param aStart_contour: starting contour number (-1 = all, 0 is the outlines of zone, > 1 = holes in zone
  224. * @param aEnd_contour: ending contour number (-1 = all after aStart_contour)
  225. * combining intersecting contours if possible
  226. * @param arc_array : return corners computed from arcs approximations in arc_array
  227. * @param aConvertHoles = mode for holes when a boolean operation is made
  228. * true: holes are linked into outer contours by double overlapping segments
  229. * false: holes are not linked: in this mode contours are added clockwise
  230. * and polygons added counter clockwise are holes (default)
  231. * @return error: 0 if Ok, 1 if error
  232. */
  233. int CPolyLine::MakeKboolPoly( int aStart_contour, int aEnd_contour, std::vector<CArc> * arc_array,
  234. bool aConvertHoles )
  235. {
  236. if( m_Kbool_Poly_Engine )
  237. {
  238. delete m_Kbool_Poly_Engine;
  239. m_Kbool_Poly_Engine = NULL;
  240. }
  241. if( !GetClosed() && (aStart_contour == (GetNumContours() - 1) || aStart_contour == -1) )
  242. return 1; // error
  243. int n_arcs = 0;
  244. int first_contour = aStart_contour;
  245. int last_contour = aEnd_contour;
  246. if( aStart_contour == -1 )
  247. {
  248. first_contour = 0;
  249. last_contour = GetNumContours() - 1;
  250. }
  251. if( aEnd_contour == -1 )
  252. {
  253. last_contour = GetNumContours() - 1;
  254. }
  255. if( arc_array )
  256. arc_array->clear();
  257. int iarc = 0;
  258. for( int icont = first_contour; icont<=last_contour; icont++ )
  259. {
  260. // Fill a kbool engine for this contour,
  261. // and combine it with previous contours
  262. Bool_Engine* booleng = new Bool_Engine();
  263. ArmBoolEng( booleng, aConvertHoles );
  264. if( m_Kbool_Poly_Engine ) // a previous contour exists. Put it in new engine
  265. {
  266. while( m_Kbool_Poly_Engine->StartPolygonGet() )
  267. {
  268. if( booleng->StartPolygonAdd( GROUP_A ) )
  269. {
  270. while( m_Kbool_Poly_Engine->PolygonHasMorePoints() )
  271. {
  272. int x = (int) m_Kbool_Poly_Engine->GetPolygonXPoint();
  273. int y = (int) m_Kbool_Poly_Engine->GetPolygonYPoint();
  274. booleng->AddPoint( x, y );
  275. }
  276. booleng->EndPolygonAdd();
  277. }
  278. m_Kbool_Poly_Engine->EndPolygonGet();
  279. }
  280. }
  281. // first, calculate number of vertices in contour
  282. int n_vertices = 0;
  283. int ic_st = GetContourStart( icont );
  284. int ic_end = GetContourEnd( icont );
  285. if( !booleng->StartPolygonAdd( GROUP_B ) )
  286. {
  287. wxASSERT( 0 );
  288. return 1; //error
  289. }
  290. for( int ic = ic_st; ic<=ic_end; ic++ )
  291. {
  292. int style = side_style[ic];
  293. int x1 = corner[ic].x;
  294. int y1 = corner[ic].y;
  295. int x2, y2;
  296. if( ic < ic_end )
  297. {
  298. x2 = corner[ic + 1].x;
  299. y2 = corner[ic + 1].y;
  300. }
  301. else
  302. {
  303. x2 = corner[ic_st].x;
  304. y2 = corner[ic_st].y;
  305. }
  306. if( style == STRAIGHT )
  307. n_vertices++;
  308. else
  309. {
  310. // style is ARC_CW or ARC_CCW
  311. int n; // number of steps for arcs
  312. n = ( abs( x2 - x1 ) + abs( y2 - y1 ) ) / (CArc::MAX_STEP);
  313. n = MAX( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
  314. n_vertices += n;
  315. n_arcs++;
  316. }
  317. }
  318. // now enter this contour to booleng
  319. int ivtx = 0;
  320. for( int ic = ic_st; ic<=ic_end; ic++ )
  321. {
  322. int style = side_style[ic];
  323. int x1 = corner[ic].x;
  324. int y1 = corner[ic].y;
  325. int x2, y2;
  326. if( ic < ic_end )
  327. {
  328. x2 = corner[ic + 1].x;
  329. y2 = corner[ic + 1].y;
  330. }
  331. else
  332. {
  333. x2 = corner[ic_st].x;
  334. y2 = corner[ic_st].y;
  335. }
  336. if( style == STRAIGHT )
  337. {
  338. booleng->AddPoint( x1, y1 );
  339. ivtx++;
  340. }
  341. else
  342. {
  343. // style is arc_cw or arc_ccw
  344. int n; // number of steps for arcs
  345. n = ( abs( x2 - x1 ) + abs( y2 - y1 ) ) / (CArc::MAX_STEP);
  346. n = MAX( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
  347. double xo, yo, theta1, theta2, a, b;
  348. a = fabs( (double) (x1 - x2) );
  349. b = fabs( (double) (y1 - y2) );
  350. if( style == CPolyLine::ARC_CW )
  351. {
  352. // clockwise arc (ie.quadrant of ellipse)
  353. if( x2 > x1 && y2 > y1 )
  354. {
  355. // first quadrant, draw second quadrant of ellipse
  356. xo = x2;
  357. yo = y1;
  358. theta1 = pi;
  359. theta2 = pi / 2.0;
  360. }
  361. else if( x2 < x1 && y2 > y1 )
  362. {
  363. // second quadrant, draw third quadrant of ellipse
  364. xo = x1;
  365. yo = y2;
  366. theta1 = 3.0 * pi / 2.0;
  367. theta2 = pi;
  368. }
  369. else if( x2 < x1 && y2 < y1 )
  370. {
  371. // third quadrant, draw fourth quadrant of ellipse
  372. xo = x2;
  373. yo = y1;
  374. theta1 = 2.0 * pi;
  375. theta2 = 3.0 * pi / 2.0;
  376. }
  377. else
  378. {
  379. xo = x1; // fourth quadrant, draw first quadrant of ellipse
  380. yo = y2;
  381. theta1 = pi / 2.0;
  382. theta2 = 0.0;
  383. }
  384. }
  385. else
  386. {
  387. // counter-clockwise arc
  388. if( x2 > x1 && y2 > y1 )
  389. {
  390. xo = x1; // first quadrant, draw fourth quadrant of ellipse
  391. yo = y2;
  392. theta1 = 3.0 * pi / 2.0;
  393. theta2 = 2.0 * pi;
  394. }
  395. else if( x2 < x1 && y2 > y1 )
  396. {
  397. xo = x2; // second quadrant
  398. yo = y1;
  399. theta1 = 0.0;
  400. theta2 = pi / 2.0;
  401. }
  402. else if( x2 < x1 && y2 < y1 )
  403. {
  404. xo = x1; // third quadrant
  405. yo = y2;
  406. theta1 = pi / 2.0;
  407. theta2 = pi;
  408. }
  409. else
  410. {
  411. xo = x2; // fourth quadrant
  412. yo = y1;
  413. theta1 = pi;
  414. theta2 = 3.0 * pi / 2.0;
  415. }
  416. }
  417. // now write steps for arc
  418. if( arc_array )
  419. {
  420. CArc new_arc;
  421. new_arc.style = style;
  422. new_arc.n_steps = n;
  423. new_arc.xi = x1;
  424. new_arc.yi = y1;
  425. new_arc.xf = x2;
  426. new_arc.yf = y2;
  427. arc_array->push_back( new_arc );
  428. iarc++;
  429. }
  430. for( int is = 0; is<n; is++ )
  431. {
  432. double theta = theta1 + ( (theta2 - theta1) * (double) is ) / n;
  433. double x = xo + a* cos( theta );
  434. double y = yo + b* sin( theta );
  435. if( is == 0 )
  436. {
  437. x = x1;
  438. y = y1;
  439. }
  440. booleng->AddPoint( x1, y1 );
  441. ivtx++;
  442. }
  443. }
  444. }
  445. if( n_vertices != ivtx )
  446. {
  447. wxASSERT( 0 );
  448. }
  449. // close list added to the bool engine
  450. booleng->EndPolygonAdd();
  451. /* now combine polygon to the previous polygons.
  452. * note: the first polygon is the outline contour, and others are holes inside the first polygon
  453. * The first polygon is ORed with nothing, but is is a trick to sort corners (vertex)
  454. * clockwise with the kbool engine.
  455. * Others polygons are substract to the outline and corners will be ordered counter clockwise
  456. * by the kbool engine
  457. */
  458. if( aStart_contour <= 0 && icont != 0 ) // substract hole to outside ( if the outline contour is take in account)
  459. {
  460. booleng->Do_Operation( BOOL_A_SUB_B );
  461. }
  462. else // add outside or add holes if we do not use the outline contour
  463. {
  464. booleng->Do_Operation( BOOL_OR );
  465. }
  466. // now use result as new polygon (delete the old one if exists)
  467. if( m_Kbool_Poly_Engine )
  468. delete m_Kbool_Poly_Engine;
  469. m_Kbool_Poly_Engine = booleng;
  470. }
  471. return 0;
  472. }
  473. /**
  474. * Function ArmBoolEng
  475. * Initialise parameters used in kbool
  476. * @param aBooleng = pointer to the Bool_Engine to initialise
  477. * @param aConvertHoles = mode for holes when a boolean operation is made
  478. * true: in resulting polygon, holes are linked into outer contours by double overlapping segments
  479. * false: in resulting polygons, holes are not linked: they are separate polygons
  480. */
  481. void ArmBoolEng( Bool_Engine* aBooleng, bool aConvertHoles )
  482. {
  483. // set some global vals to arm the boolean engine
  484. // input points are scaled up with GetDGrid() * GetGrid()
  485. // DGRID is only meant to make fractional parts of input data which
  486. /*
  487. The input data scaled up with DGrid is related to the accuracy the user has in his input data.
  488. User data with a minimum accuracy of 0.001, means set the DGrid to 1000.
  489. The input data may contain data with a minimum accuracy much smaller, but by setting the DGrid
  490. everything smaller than 1/DGrid is rounded.
  491. DGRID is only meant to make fractional parts of input data which can be
  492. doubles, part of the integers used in vertexes within the boolean algorithm.
  493. And therefore DGRID bigger than 1 is not usefull, you would only loose accuracy.
  494. Within the algorithm all input data is multiplied with DGRID, and the result
  495. is rounded to an integer.
  496. */
  497. double DGRID = 1000.0; // round coordinate X or Y value in calculations to this (initial value = 1000.0 in kbool example)
  498. // kbool uses DGRID to convert float user units to integer
  499. // kbool unit = (int)(user unit * DGRID)
  500. // Note: in kicad, coordinates are already integer so DGRID could be set to 1
  501. // we can choose 1.0,
  502. // but choose DGRID = 1000.0 solves some filling problems
  503. // (perhaps because this allows a better precision in kbool internal calculations
  504. double MARGE = 1.0/DGRID; // snap with in this range points to lines in the intersection routines
  505. // should always be >= 1/DGRID a MARGE >= 10/DGRID is ok
  506. // this is also used to remove small segments and to decide when
  507. // two segments are in line. ( initial value = 0.001 )
  508. // For kicad we choose MARGE = 1/DGRID
  509. double CORRECTIONFACTOR = 0.0; // correct the polygons by this number: used in BOOL_CORRECTION operation
  510. // this operation shrinks a polygon if CORRECTIONFACTOR < 0
  511. // or stretch it if CORRECTIONFACTOR > 0
  512. // the size change is CORRECTIONFACTOR (holes are correctly handled)
  513. double CORRECTIONABER = 1.0; // the accuracy for the rounded shapes used in correction
  514. double ROUNDFACTOR = 1.5; // when will we round the correction shape to a circle
  515. double SMOOTHABER = 10.0; // accuracy when smoothing a polygon
  516. double MAXLINEMERGE = 1000.0; // leave as is, segments of this length in smoothen
  517. /*
  518. Grid makes sure that the integer data used within the algorithm has room for extra intersections
  519. smaller than the smallest number within the input data.
  520. The input data scaled up with DGrid is related to the accuracy the user has in his input data.
  521. Another scaling with Grid is applied on top of it to create space in the integer number for
  522. even smaller numbers.
  523. */
  524. int GRID = (int) 10000/DGRID; // initial value = 10000 in kbool example
  525. // But we use 10000/DGRID because the scalling is made
  526. // by DGRID on integer pcbnew units and
  527. // the global scalling ( GRID*DGRID) must be < 30000 to avoid
  528. // overflow in calculations (made in long long in kbool)
  529. if ( GRID <= 1 ) // Cannot be null!
  530. GRID = 1;
  531. aBooleng->SetMarge( MARGE );
  532. aBooleng->SetGrid( GRID );
  533. aBooleng->SetDGrid( DGRID );
  534. aBooleng->SetCorrectionFactor( CORRECTIONFACTOR );
  535. aBooleng->SetCorrectionAber( CORRECTIONABER );
  536. aBooleng->SetSmoothAber( SMOOTHABER );
  537. aBooleng->SetMaxlinemerge( MAXLINEMERGE );
  538. aBooleng->SetRoundfactor( ROUNDFACTOR );
  539. aBooleng->SetWindingRule( TRUE ); // This is the default kbool value
  540. if( aConvertHoles )
  541. {
  542. #if 1 // Can be set to 1 for kbool version >= 2.1, must be set to 0 for previous versions
  543. // SetAllowNonTopHoleLinking() exists only in kbool >= 2.1
  544. aBooleng->SetAllowNonTopHoleLinking( false ); // Default = true, but i have problems (filling errors) when true
  545. #endif
  546. aBooleng->SetLinkHoles( true ); // holes will be connected by double overlapping segments
  547. aBooleng->SetOrientationEntryMode( false ); // all polygons are contours, not holes
  548. }
  549. else
  550. {
  551. aBooleng->SetLinkHoles( false ); // holes will not be connected by double overlapping segments
  552. aBooleng->SetOrientationEntryMode( true ); // holes are entered counter clockwise
  553. }
  554. }
  555. int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*> * pa, bool bRetainArcs )
  556. {
  557. return NormalizeWithKbool( pa, bRetainArcs );
  558. }
  559. // Restore arcs to a polygon where they were replaced with steps
  560. // If pa != NULL, also use polygons in pa array
  561. //
  562. int CPolyLine::RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine*> * pa )
  563. {
  564. // get poly info
  565. int n_polys = 1;
  566. if( pa )
  567. n_polys += pa->size();
  568. CPolyLine* poly;
  569. // undraw polys and clear utility flag for all corners
  570. for( int ip = 0; ip<n_polys; ip++ )
  571. {
  572. if( ip == 0 )
  573. poly = this;
  574. else
  575. poly = (*pa)[ip - 1];
  576. poly->Undraw();
  577. for( int ic = 0; ic<poly->GetNumCorners(); ic++ )
  578. poly->SetUtility( ic, 0 );
  579. // clear utility flag
  580. }
  581. // find arcs and replace them
  582. bool bFound;
  583. int arc_start = 0;
  584. int arc_end = 0;
  585. for( unsigned iarc = 0; iarc<arc_array->size(); iarc++ )
  586. {
  587. int arc_xi = (*arc_array)[iarc].xi;
  588. int arc_yi = (*arc_array)[iarc].yi;
  589. int arc_xf = (*arc_array)[iarc].xf;
  590. int arc_yf = (*arc_array)[iarc].yf;
  591. int n_steps = (*arc_array)[iarc].n_steps;
  592. int style = (*arc_array)[iarc].style;
  593. bFound = FALSE;
  594. // loop through polys
  595. for( int ip = 0; ip<n_polys; ip++ )
  596. {
  597. if( ip == 0 )
  598. poly = this;
  599. else
  600. poly = (*pa)[ip - 1];
  601. for( int icont = 0; icont<poly->GetNumContours(); icont++ )
  602. {
  603. int ic_start = poly->GetContourStart( icont );
  604. int ic_end = poly->GetContourEnd( icont );
  605. if( (ic_end - ic_start) > n_steps )
  606. {
  607. for( int ic = ic_start; ic<=ic_end; ic++ )
  608. {
  609. int ic_next = ic + 1;
  610. if( ic_next > ic_end )
  611. ic_next = ic_start;
  612. int xi = poly->GetX( ic );
  613. int yi = poly->GetY( ic );
  614. if( xi == arc_xi && yi == arc_yi )
  615. {
  616. // test for forward arc
  617. int ic2 = ic + n_steps;
  618. if( ic2 > ic_end )
  619. ic2 = ic2 - ic_end + ic_start - 1;
  620. int xf = poly->GetX( ic2 );
  621. int yf = poly->GetY( ic2 );
  622. if( xf == arc_xf && yf == arc_yf )
  623. {
  624. // arc from ic to ic2
  625. bFound = TRUE;
  626. arc_start = ic;
  627. arc_end = ic2;
  628. }
  629. else
  630. {
  631. // try reverse arc
  632. ic2 = ic - n_steps;
  633. if( ic2 < ic_start )
  634. ic2 = ic2 - ic_start + ic_end + 1;
  635. xf = poly->GetX( ic2 );
  636. yf = poly->GetY( ic2 );
  637. if( xf == arc_xf && yf == arc_yf )
  638. {
  639. // arc from ic2 to ic
  640. bFound = TRUE;
  641. arc_start = ic2;
  642. arc_end = ic;
  643. style = 3 - style;
  644. }
  645. }
  646. if( bFound )
  647. {
  648. poly->side_style[arc_start] = style;
  649. // mark corners for deletion from arc_start+1 to arc_end-1
  650. for( int i = arc_start + 1; i!=arc_end; )
  651. {
  652. if( i > ic_end )
  653. i = ic_start;
  654. poly->SetUtility( i, 1 );
  655. if( i == ic_end )
  656. i = ic_start;
  657. else
  658. i++;
  659. }
  660. break;
  661. }
  662. }
  663. if( bFound )
  664. break;
  665. }
  666. }
  667. if( bFound )
  668. break;
  669. }
  670. }
  671. if( bFound )
  672. (*arc_array)[iarc].bFound = TRUE;
  673. }
  674. // now delete all marked corners
  675. for( int ip = 0; ip<n_polys; ip++ )
  676. {
  677. if( ip == 0 )
  678. poly = this;
  679. else
  680. poly = (*pa)[ip - 1];
  681. for( int ic = poly->GetNumCorners() - 1; ic>=0; ic-- )
  682. {
  683. if( poly->GetUtility( ic ) )
  684. poly->DeleteCorner( ic, FALSE );
  685. }
  686. }
  687. return 0;
  688. }
  689. // initialize new polyline
  690. // set layer, width, selection box size, starting point, id and pointer
  691. //
  692. // if sel_box = 0, don't create selection elements at all
  693. //
  694. // if polyline is board outline, enter with:
  695. // id.type = ID_BOARD
  696. // id.st = ID_BOARD_OUTLINE
  697. // id.i = 0
  698. // ptr = NULL
  699. //
  700. // if polyline is copper area, enter with:
  701. // id.type = ID_NET;
  702. // id.st = ID_AREA
  703. // id.i = index to area
  704. // ptr = pointer to net
  705. //
  706. void CPolyLine::Start( int layer, int x, int y, int hatch )
  707. {
  708. m_layer = layer;
  709. m_HatchStyle = hatch;
  710. CPolyPt poly_pt( x, y );
  711. poly_pt.end_contour = FALSE;
  712. corner.push_back( poly_pt );
  713. side_style.push_back( 0 );
  714. }
  715. // add a corner to unclosed polyline
  716. //
  717. void CPolyLine::AppendCorner( int x, int y, int style, bool bDraw )
  718. {
  719. Undraw();
  720. CPolyPt poly_pt( x, y );
  721. poly_pt.end_contour = FALSE;
  722. // add entries for new corner and side
  723. corner.push_back( poly_pt );
  724. side_style.push_back( style );
  725. if( corner.size() > 0 && !corner[corner.size() - 1].end_contour )
  726. side_style[corner.size() - 1] = style;
  727. if( bDraw )
  728. Draw();
  729. }
  730. // close last polyline contour
  731. //
  732. void CPolyLine::Close( int style, bool bDraw )
  733. {
  734. if( GetClosed() )
  735. {
  736. wxASSERT( 0 );
  737. }
  738. Undraw();
  739. side_style[corner.size() - 1] = style;
  740. corner[corner.size() - 1].end_contour = TRUE;
  741. if( bDraw )
  742. Draw();
  743. }
  744. // move corner of polyline
  745. //
  746. void CPolyLine::MoveCorner( int ic, int x, int y )
  747. {
  748. Undraw();
  749. corner[ic].x = x;
  750. corner[ic].y = y;
  751. Draw();
  752. }
  753. // delete corner and adjust arrays
  754. //
  755. void CPolyLine::DeleteCorner( int ic, bool bDraw )
  756. {
  757. Undraw();
  758. int icont = GetContour( ic );
  759. int istart = GetContourStart( icont );
  760. int iend = GetContourEnd( icont );
  761. bool bClosed = icont < GetNumContours() - 1 || GetClosed();
  762. if( !bClosed )
  763. {
  764. // open contour, must be last contour
  765. corner.erase( corner.begin() + ic );
  766. if( ic != istart )
  767. side_style.erase( side_style.begin() + ic - 1 );
  768. }
  769. else
  770. {
  771. // closed contour
  772. corner.erase( corner.begin() + ic );
  773. side_style.erase( side_style.begin() + ic );
  774. if( ic == iend )
  775. corner[ic - 1].end_contour = TRUE;
  776. }
  777. if( bClosed && GetContourSize( icont ) < 3 )
  778. {
  779. // delete the entire contour
  780. RemoveContour( icont );
  781. }
  782. if( bDraw )
  783. Draw();
  784. }
  785. /******************************************/
  786. void CPolyLine::RemoveContour( int icont )
  787. /******************************************/
  788. /**
  789. * Function RemoveContour
  790. * @param icont = contour number to remove
  791. * remove a contour only if there is more than 1 contour
  792. */
  793. {
  794. Undraw();
  795. int istart = GetContourStart( icont );
  796. int iend = GetContourEnd( icont );
  797. if( icont == 0 && GetNumContours() == 1 )
  798. {
  799. // remove the only contour
  800. wxASSERT( 0 );
  801. }
  802. else if( icont == GetNumContours() - 1 )
  803. {
  804. // remove last contour
  805. corner.erase( corner.begin() + istart, corner.end() );
  806. side_style.erase( side_style.begin() + istart, side_style.end() );
  807. }
  808. else
  809. {
  810. // remove closed contour
  811. for( int ic = iend; ic>=istart; ic-- )
  812. {
  813. corner.erase( corner.begin() + ic );
  814. side_style.erase( side_style.begin() + ic );
  815. }
  816. }
  817. Draw();
  818. }
  819. /******************************************/
  820. void CPolyLine::RemoveAllContours( void )
  821. /******************************************/
  822. /**
  823. * function RemoveAllContours
  824. * removes all corners from the lists.
  825. * Others params are not chnaged
  826. */
  827. {
  828. corner.clear();
  829. side_style.clear();
  830. }
  831. /**
  832. * Function InsertCorner
  833. * insert a new corner between two existing corners
  834. * @param ic = index for the insertion point: the corner is inserted AFTER ic
  835. * @param x, y = coordinates corner to insert
  836. */
  837. void CPolyLine::InsertCorner( int ic, int x, int y )
  838. {
  839. Undraw();
  840. if( (unsigned) (ic) >= corner.size() )
  841. {
  842. corner.push_back( CPolyPt( x, y ) );
  843. side_style.push_back( STRAIGHT );
  844. }
  845. else
  846. {
  847. corner.insert( corner.begin() + ic + 1, CPolyPt( x, y ) );
  848. side_style.insert( side_style.begin() + ic + 1, STRAIGHT );
  849. }
  850. if( (unsigned) (ic + 1) < corner.size() )
  851. {
  852. if( corner[ic].end_contour )
  853. {
  854. corner[ic + 1].end_contour = TRUE;
  855. corner[ic].end_contour = FALSE;
  856. }
  857. }
  858. Draw();
  859. }
  860. // undraw polyline by removing all graphic elements from display list
  861. //
  862. void CPolyLine::Undraw()
  863. {
  864. m_HatchLines.clear();
  865. bDrawn = FALSE;
  866. }
  867. // draw polyline by adding all graphics to display list
  868. // if side style is ARC_CW or ARC_CCW but endpoints are not angled,
  869. // convert to STRAIGHT
  870. //
  871. void CPolyLine::Draw()
  872. {
  873. // first, undraw if necessary
  874. if( bDrawn )
  875. Undraw();
  876. Hatch();
  877. bDrawn = TRUE;
  878. }
  879. int CPolyLine::GetX( int ic )
  880. {
  881. return corner[ic].x;
  882. }
  883. int CPolyLine::GetY( int ic )
  884. {
  885. return corner[ic].y;
  886. }
  887. int CPolyLine::GetEndContour( int ic )
  888. {
  889. return corner[ic].end_contour;
  890. }
  891. CRect CPolyLine::GetBounds()
  892. {
  893. CRect r = GetCornerBounds();
  894. r.left -= m_Width / 2;
  895. r.right += m_Width / 2;
  896. r.bottom -= m_Width / 2;
  897. r.top += m_Width / 2;
  898. return r;
  899. }
  900. CRect CPolyLine::GetCornerBounds()
  901. {
  902. CRect r;
  903. r.left = r.bottom = INT_MAX;
  904. r.right = r.top = INT_MIN;
  905. for( unsigned i = 0; i<corner.size(); i++ )
  906. {
  907. r.left = MIN( r.left, corner[i].x );
  908. r.right = MAX( r.right, corner[i].x );
  909. r.bottom = MIN( r.bottom, corner[i].y );
  910. r.top = MAX( r.top, corner[i].y );
  911. }
  912. return r;
  913. }
  914. CRect CPolyLine::GetCornerBounds( int icont )
  915. {
  916. CRect r;
  917. r.left = r.bottom = INT_MAX;
  918. r.right = r.top = INT_MIN;
  919. int istart = GetContourStart( icont );
  920. int iend = GetContourEnd( icont );
  921. for( int i = istart; i<=iend; i++ )
  922. {
  923. r.left = MIN( r.left, corner[i].x );
  924. r.right = MAX( r.right, corner[i].x );
  925. r.bottom = MIN( r.bottom, corner[i].y );
  926. r.top = MAX( r.top, corner[i].y );
  927. }
  928. return r;
  929. }
  930. int CPolyLine::GetNumCorners()
  931. {
  932. return corner.size();
  933. }
  934. int CPolyLine::GetNumSides()
  935. {
  936. if( GetClosed() )
  937. return corner.size();
  938. else
  939. return corner.size() - 1;
  940. }
  941. int CPolyLine::GetNumContours()
  942. {
  943. int ncont = 0;
  944. if( !corner.size() )
  945. return 0;
  946. for( unsigned ic = 0; ic<corner.size(); ic++ )
  947. if( corner[ic].end_contour )
  948. ncont++;
  949. if( !corner[corner.size() - 1].end_contour )
  950. ncont++;
  951. return ncont;
  952. }
  953. int CPolyLine::GetContour( int ic )
  954. {
  955. int ncont = 0;
  956. for( int i = 0; i<ic; i++ )
  957. {
  958. if( corner[i].end_contour )
  959. ncont++;
  960. }
  961. return ncont;
  962. }
  963. int CPolyLine::GetContourStart( int icont )
  964. {
  965. if( icont == 0 )
  966. return 0;
  967. int ncont = 0;
  968. for( unsigned i = 0; i<corner.size(); i++ )
  969. {
  970. if( corner[i].end_contour )
  971. {
  972. ncont++;
  973. if( ncont == icont )
  974. return i + 1;
  975. }
  976. }
  977. wxASSERT( 0 );
  978. return 0;
  979. }
  980. int CPolyLine::GetContourEnd( int icont )
  981. {
  982. if( icont < 0 )
  983. return 0;
  984. if( icont == GetNumContours() - 1 )
  985. return corner.size() - 1;
  986. int ncont = 0;
  987. for( unsigned i = 0; i<corner.size(); i++ )
  988. {
  989. if( corner[i].end_contour )
  990. {
  991. if( ncont == icont )
  992. return i;
  993. ncont++;
  994. }
  995. }
  996. wxASSERT( 0 );
  997. return 0;
  998. }
  999. int CPolyLine::GetContourSize( int icont )
  1000. {
  1001. return GetContourEnd( icont ) - GetContourStart( icont ) + 1;
  1002. }
  1003. void CPolyLine::SetSideStyle( int is, int style )
  1004. {
  1005. Undraw();
  1006. CPoint p1, p2;
  1007. if( is == (int) (corner.size() - 1) )
  1008. {
  1009. p1.x = corner[corner.size() - 1].x;
  1010. p1.y = corner[corner.size() - 1].y;
  1011. p2.x = corner[0].x;
  1012. p2.y = corner[0].y;
  1013. }
  1014. else
  1015. {
  1016. p1.x = corner[is].x;
  1017. p1.y = corner[is].y;
  1018. p2.x = corner[is + 1].x;
  1019. p2.y = corner[is + 1].y;
  1020. }
  1021. if( p1.x == p2.x || p1.y == p2.y )
  1022. side_style[is] = STRAIGHT;
  1023. else
  1024. side_style[is] = style;
  1025. Draw();
  1026. }
  1027. int CPolyLine::GetSideStyle( int is )
  1028. {
  1029. return side_style[is];
  1030. }
  1031. int CPolyLine::GetClosed()
  1032. {
  1033. if( corner.size() == 0 )
  1034. return 0;
  1035. else
  1036. return corner[corner.size() - 1].end_contour;
  1037. }
  1038. // draw hatch lines
  1039. //
  1040. void CPolyLine::Hatch()
  1041. {
  1042. m_HatchLines.clear();
  1043. if( m_HatchStyle == NO_HATCH )
  1044. {
  1045. return;
  1046. }
  1047. int layer = GetLayer();
  1048. if( !GetClosed() ) // If not closed, the poly is beeing created and not finalised. Not not hatch
  1049. return;
  1050. enum {
  1051. MAXPTS = 100
  1052. };
  1053. int xx[MAXPTS], yy[MAXPTS];
  1054. // define range for hatch lines
  1055. int min_x = corner[0].x;
  1056. int max_x = corner[0].x;
  1057. int min_y = corner[0].y;
  1058. int max_y = corner[0].y;
  1059. for( unsigned ic = 1; ic < corner.size(); ic++ )
  1060. {
  1061. if( corner[ic].x < min_x )
  1062. min_x = corner[ic].x;
  1063. if( corner[ic].x > max_x )
  1064. max_x = corner[ic].x;
  1065. if( corner[ic].y < min_y )
  1066. min_y = corner[ic].y;
  1067. if( corner[ic].y > max_y )
  1068. max_y = corner[ic].y;
  1069. }
  1070. int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
  1071. double slope = 0.707106 * slope_flag;
  1072. int spacing;
  1073. if( m_HatchStyle == DIAGONAL_EDGE )
  1074. spacing = 10 * PCBU_PER_MIL;
  1075. else
  1076. spacing = 50 * PCBU_PER_MIL;
  1077. int max_a, min_a;
  1078. if( slope_flag == 1 )
  1079. {
  1080. max_a = (int) (max_y - slope * min_x);
  1081. min_a = (int) (min_y - slope * max_x);
  1082. }
  1083. else
  1084. {
  1085. max_a = (int) (max_y - slope * max_x);
  1086. min_a = (int) (min_y - slope * min_x);
  1087. }
  1088. min_a = (min_a / spacing) * spacing;
  1089. // calculate an offset depending on layer number, for a better display of hatches on a multilayer board
  1090. int offset = (layer * 7) / 8;
  1091. min_a += offset;
  1092. // now calculate and draw hatch lines
  1093. int nc = corner.size();
  1094. // loop through hatch lines
  1095. for( int a = min_a; a<max_a; a += spacing )
  1096. {
  1097. // get intersection points for this hatch line
  1098. int nloops = 0;
  1099. int npts;
  1100. // make this a loop in case my homebrew hatching algorithm screws up
  1101. do
  1102. {
  1103. npts = 0;
  1104. int i_start_contour = 0;
  1105. for( int ic = 0; ic<nc; ic++ )
  1106. {
  1107. double x, y, x2, y2;
  1108. int ok;
  1109. if( corner[ic].end_contour || ( ic == (int) (corner.size() - 1) ) )
  1110. {
  1111. ok = FindLineSegmentIntersection( a, slope,
  1112. corner[ic].x, corner[ic].y,
  1113. corner[i_start_contour].x,
  1114. corner[i_start_contour].y,
  1115. side_style[ic],
  1116. &x, &y, &x2, &y2 );
  1117. i_start_contour = ic + 1;
  1118. }
  1119. else
  1120. {
  1121. ok = FindLineSegmentIntersection( a, slope,
  1122. corner[ic].x, corner[ic].y,
  1123. corner[ic + 1].x, corner[ic + 1].y,
  1124. side_style[ic],
  1125. &x, &y, &x2, &y2 );
  1126. }
  1127. if( ok )
  1128. {
  1129. xx[npts] = (int) x;
  1130. yy[npts] = (int) y;
  1131. npts++;
  1132. wxASSERT( npts<MAXPTS ); // overflow
  1133. }
  1134. if( ok == 2 )
  1135. {
  1136. xx[npts] = (int) x2;
  1137. yy[npts] = (int) y2;
  1138. npts++;
  1139. wxASSERT( npts<MAXPTS ); // overflow
  1140. }
  1141. }
  1142. nloops++;
  1143. a += PCBU_PER_MIL / 100;
  1144. } while( npts % 2 != 0 && nloops < 3 );
  1145. /* DICK 1/22/08: this was firing repeatedly on me, needed to comment out to get
  1146. * my work done:
  1147. * wxASSERT( npts%2==0 ); // odd number of intersection points, error
  1148. */
  1149. // sort points in order of descending x (if more than 2)
  1150. if( npts>2 )
  1151. {
  1152. for( int istart = 0; istart<(npts - 1); istart++ )
  1153. {
  1154. int max_x = INT_MIN;
  1155. int imax = INT_MIN;
  1156. for( int i = istart; i<npts; i++ )
  1157. {
  1158. if( xx[i] > max_x )
  1159. {
  1160. max_x = xx[i];
  1161. imax = i;
  1162. }
  1163. }
  1164. int temp = xx[istart];
  1165. xx[istart] = xx[imax];
  1166. xx[imax] = temp;
  1167. temp = yy[istart];
  1168. yy[istart] = yy[imax];
  1169. yy[imax] = temp;
  1170. }
  1171. }
  1172. // draw lines
  1173. for( int ip = 0; ip<npts; ip += 2 )
  1174. {
  1175. double dx = xx[ip + 1] - xx[ip];
  1176. if( m_HatchStyle == DIAGONAL_FULL || fabs( dx ) < 40 * NM_PER_MIL )
  1177. {
  1178. m_HatchLines.push_back( CSegment( xx[ip], yy[ip], xx[ip + 1], yy[ip + 1] ) );
  1179. }
  1180. else
  1181. {
  1182. double dy = yy[ip + 1] - yy[ip];
  1183. double slope = dy / dx;
  1184. if( dx > 0 )
  1185. dx = 20 * NM_PER_MIL;
  1186. else
  1187. dx = -20 * NM_PER_MIL;
  1188. double x1 = xx[ip] + dx;
  1189. double x2 = xx[ip + 1] - dx;
  1190. double y1 = yy[ip] + dx * slope;
  1191. double y2 = yy[ip + 1] - dx * slope;
  1192. m_HatchLines.push_back( CSegment( xx[ip], yy[ip], to_int( x1 ), to_int( y1 ) ) );
  1193. m_HatchLines.push_back( CSegment( xx[ip + 1], yy[ip + 1], to_int( x2 ),
  1194. to_int( y2 ) ) );
  1195. }
  1196. }
  1197. }
  1198. }
  1199. // test to see if a point is inside polyline
  1200. //
  1201. bool CPolyLine::TestPointInside( int px, int py )
  1202. {
  1203. if( !GetClosed() )
  1204. {
  1205. wxASSERT( 0 );
  1206. }
  1207. // Test all polygons.
  1208. // Since the first is the main outline, and other are hole,
  1209. // if the tested point is inside only one contour, it is inside the whole polygon
  1210. // (in fact inside the main outline, and outside all holes).
  1211. // if inside 2 contours (the main outline + an hole), it is outside the poly.
  1212. int polycount = GetNumContours();
  1213. bool inside = false;
  1214. for( int icont = 0; icont < polycount; icont++ )
  1215. {
  1216. int istart = GetContourStart( icont );
  1217. int iend = GetContourEnd( icont );
  1218. // Test this polygon:
  1219. if( TestPointInsidePolygon( corner, istart, iend, px, py) ) // test point inside the current polygon
  1220. inside = not inside;
  1221. }
  1222. return inside;
  1223. }
  1224. // copy data from another poly, but don't draw it
  1225. //
  1226. void CPolyLine::Copy( CPolyLine* src )
  1227. {
  1228. Undraw();
  1229. m_HatchStyle = src->m_HatchStyle;
  1230. // copy corners, using vector copy
  1231. corner = src->corner;
  1232. // copy side styles, using vector copy
  1233. side_style = src->side_style;
  1234. }
  1235. /*******************************************/
  1236. bool CPolyLine::IsCutoutContour( int icont )
  1237. /*******************************************/
  1238. /*
  1239. * return true if the corner icont is inside the outline (i.e it is a hole)
  1240. */
  1241. {
  1242. int ncont = GetContour( icont );
  1243. if( ncont == 0 ) // the first contour is the main outline, not an hole
  1244. return false;
  1245. return true;
  1246. }
  1247. void CPolyLine::MoveOrigin( int x_off, int y_off )
  1248. {
  1249. Undraw();
  1250. for( int ic = 0; ic < GetNumCorners(); ic++ )
  1251. {
  1252. SetX( ic, GetX( ic ) + x_off );
  1253. SetY( ic, GetY( ic ) + y_off );
  1254. }
  1255. Draw();
  1256. }
  1257. // Set various parameters:
  1258. // the calling function should Undraw() before calling them,
  1259. // and Draw() after
  1260. //
  1261. void CPolyLine::SetX( int ic, int x )
  1262. {
  1263. corner[ic].x = x;
  1264. }
  1265. void CPolyLine::SetY( int ic, int y )
  1266. {
  1267. corner[ic].y = y;
  1268. }
  1269. void CPolyLine::SetEndContour( int ic, bool end_contour )
  1270. {
  1271. corner[ic].end_contour = end_contour;
  1272. }
  1273. void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num )
  1274. {
  1275. // get radius
  1276. double r = sqrt( (double) (xi - xc) * (xi - xc) + (double) (yi - yc) * (yi - yc) );
  1277. // get angles of start and finish
  1278. double th_i = atan2( (double) (yi - yc), (double) (xi - xc) );
  1279. double th_f = atan2( (double) (yf - yc), (double) (xf - xc) );
  1280. double th_d = (th_f - th_i) / (num - 1);
  1281. double theta = th_i;
  1282. // generate arc
  1283. for( int ic = 0; ic<num; ic++ )
  1284. {
  1285. int x = to_int( xc + r * cos( theta ) );
  1286. int y = to_int( yc + r * sin( theta ) );
  1287. AppendCorner( x, y, STRAIGHT, 0 );
  1288. theta += th_d;
  1289. }
  1290. Close( STRAIGHT );
  1291. }
  1292. // Bezier Support
  1293. void CPolyLine::AppendBezier(int x1, int y1, int x2, int y2, int x3, int y3) {
  1294. std::vector<wxPoint> bezier_points;
  1295. bezier_points = Bezier2Poly(x1,y1,x2,y2,x3,y3);
  1296. for( unsigned int i = 0; i < bezier_points.size() ; i++)
  1297. AppendCorner( bezier_points[i].x, bezier_points[i].y);
  1298. }
  1299. void CPolyLine::AppendBezier(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4){
  1300. std::vector<wxPoint> bezier_points;
  1301. bezier_points = Bezier2Poly(x1,y1,x2,y2,x3,y3,x4,y4);
  1302. for( unsigned int i = 0; i < bezier_points.size() ; i++)
  1303. AppendCorner( bezier_points[i].x, bezier_points[i].y);
  1304. }