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.

1676 lines
52 KiB

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