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.

807 lines
28 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file convert_drawsegment_list_to_polygon.cpp
  27. * @brief functions to convert a shape built with DRAWSEGMENTS to a polygon.
  28. * expecting the shape describes shape similar to a polygon
  29. */
  30. #include <trigo.h>
  31. #include <macros.h>
  32. #include <math/vector2d.h>
  33. #include <class_drawsegment.h>
  34. #include <class_module.h>
  35. #include <base_units.h>
  36. #include <convert_basic_shapes_to_polygon.h>
  37. #include <geometry/shape_poly_set.h>
  38. #include <geometry/geometry_utils.h>
  39. /**
  40. * Function close_ness
  41. * is a non-exact distance (also called Manhattan distance) used to approximate
  42. * the distance between two points.
  43. * The distance is very in-exact, but can be helpful when used
  44. * to pick between alternative neighboring points.
  45. * @param aLeft is the first point
  46. * @param aRight is the second point
  47. * @return unsigned - a measure of proximity that the caller knows about, in BIU,
  48. * but remember it is only an approximation.
  49. */
  50. static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
  51. {
  52. // Don't need an accurate distance calculation, just something
  53. // approximating it, for relative ordering.
  54. return unsigned( std::abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
  55. }
  56. /**
  57. * Function close_enough
  58. * is a local and tunable method of qualifying the proximity of two points.
  59. *
  60. * @param aLeft is the first point
  61. * @param aRight is the second point
  62. * @param aLimit is a measure of proximity that the caller knows about.
  63. * @return bool - true if the two points are close enough, else false.
  64. */
  65. inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit )
  66. {
  67. // We don't use an accurate distance calculation, just something
  68. // approximating it, since aLimit is non-exact anyway except when zero.
  69. return close_ness( aLeft, aRight ) <= aLimit;
  70. }
  71. /**
  72. * Function close_st
  73. * is a local method of qualifying if either the start of end point of a segment is closest to a point.
  74. *
  75. * @param aReference is the reference point
  76. * @param aFirst is the first point
  77. * @param aSecond is the second point
  78. * @return bool - true if the the first point is closest to the reference, otherwise false.
  79. */
  80. inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond )
  81. {
  82. // We don't use an accurate distance calculation, just something
  83. // approximating to find the closest to the reference.
  84. return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
  85. }
  86. /**
  87. * Searches for a DRAWSEGMENT matching a given end point or start point in a list, and
  88. * if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
  89. * @param aPoint The starting or ending point to search for.
  90. * @param aList The list to remove from.
  91. * @param aLimit is the distance from \a aPoint that still constitutes a valid find.
  92. * @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
  93. * aPoint, otherwise NULL if none.
  94. */
  95. static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT* >& aList, unsigned aLimit )
  96. {
  97. unsigned min_d = INT_MAX;
  98. int ndx_min = 0;
  99. // find the point closest to aPoint and perhaps exactly matching aPoint.
  100. for( size_t i = 0; i < aList.size(); ++i )
  101. {
  102. DRAWSEGMENT* graphic = aList[i];
  103. unsigned d;
  104. switch( graphic->GetShape() )
  105. {
  106. case S_ARC:
  107. if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
  108. {
  109. aList.erase( aList.begin() + i );
  110. return graphic;
  111. }
  112. d = close_ness( aPoint, graphic->GetArcStart() );
  113. if( d < min_d )
  114. {
  115. min_d = d;
  116. ndx_min = i;
  117. }
  118. d = close_ness( aPoint, graphic->GetArcEnd() );
  119. if( d < min_d )
  120. {
  121. min_d = d;
  122. ndx_min = i;
  123. }
  124. break;
  125. default:
  126. if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
  127. {
  128. aList.erase( aList.begin() + i );
  129. return graphic;
  130. }
  131. d = close_ness( aPoint, graphic->GetStart() );
  132. if( d < min_d )
  133. {
  134. min_d = d;
  135. ndx_min = i;
  136. }
  137. d = close_ness( aPoint, graphic->GetEnd() );
  138. if( d < min_d )
  139. {
  140. min_d = d;
  141. ndx_min = i;
  142. }
  143. }
  144. }
  145. if( min_d <= aLimit )
  146. {
  147. DRAWSEGMENT* graphic = aList[ndx_min];
  148. aList.erase( aList.begin() + ndx_min );
  149. return graphic;
  150. }
  151. return NULL;
  152. }
  153. /**
  154. * Function ConvertOutlineToPolygon
  155. * build a polygon (with holes) from a DRAWSEGMENT list, which is expected to be
  156. * a outline, therefore a closed main outline with perhaps closed inner outlines.
  157. * These closed inner outlines are considered as holes in the main outline
  158. * @param aSegList the initial list of drawsegments (only lines, circles and arcs).
  159. * @param aPolygons will contain the complex polygon.
  160. * @param aTolerance is the max distance between points that is still accepted as connected (internal units)
  161. * @param aErrorText is a wxString to return error message.
  162. * @param aErrorLocation is the optional position of the error in the outline
  163. */
  164. bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
  165. wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
  166. {
  167. if( aSegList.size() == 0 )
  168. return true;
  169. wxString msg;
  170. // Make a working copy of aSegList, because the list is modified during calculations
  171. std::vector< DRAWSEGMENT* > segList = aSegList;
  172. DRAWSEGMENT* graphic;
  173. wxPoint prevPt;
  174. // Find edge point with minimum x, this should be in the outer polygon
  175. // which will define the perimeter polygon polygon.
  176. wxPoint xmin = wxPoint( INT_MAX, 0 );
  177. int xmini = 0;
  178. for( size_t i = 0; i < segList.size(); i++ )
  179. {
  180. graphic = (DRAWSEGMENT*) segList[i];
  181. switch( graphic->GetShape() )
  182. {
  183. case S_SEGMENT:
  184. {
  185. if( graphic->GetStart().x < xmin.x )
  186. {
  187. xmin = graphic->GetStart();
  188. xmini = i;
  189. }
  190. if( graphic->GetEnd().x < xmin.x )
  191. {
  192. xmin = graphic->GetEnd();
  193. xmini = i;
  194. }
  195. }
  196. break;
  197. case S_ARC:
  198. // Freerouter does not yet understand arcs, so approximate
  199. // an arc with a series of short lines and put those
  200. // line segments into the !same! PATH.
  201. {
  202. wxPoint pstart = graphic->GetArcStart();
  203. wxPoint center = graphic->GetCenter();
  204. double angle = -graphic->GetAngle();
  205. double radius = graphic->GetRadius();
  206. int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );
  207. wxPoint pt;
  208. for( int step = 1; step<=steps; ++step )
  209. {
  210. double rotation = ( angle * step ) / steps;
  211. pt = pstart;
  212. RotatePoint( &pt, center, rotation );
  213. if( pt.x < xmin.x )
  214. {
  215. xmin = pt;
  216. xmini = i;
  217. }
  218. }
  219. }
  220. break;
  221. case S_CIRCLE:
  222. {
  223. wxPoint pt = graphic->GetCenter();
  224. // pt has minimum x point
  225. pt.x -= graphic->GetRadius();
  226. // when the radius <= 0, this is a mal-formed circle. Skip it
  227. if( graphic->GetRadius() > 0 && pt.x < xmin.x )
  228. {
  229. xmin = pt;
  230. xmini = i;
  231. }
  232. }
  233. break;
  234. case S_CURVE:
  235. {
  236. graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
  237. for( unsigned int jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
  238. {
  239. wxPoint pt = graphic->GetBezierPoints()[jj];
  240. if( pt.x < xmin.x )
  241. {
  242. xmin = pt;
  243. xmini = i;
  244. }
  245. }
  246. }
  247. break;
  248. case S_POLYGON:
  249. {
  250. const auto poly = graphic->GetPolyShape();
  251. MODULE* module = aSegList[0]->GetParentModule();
  252. double orientation = module ? module->GetOrientation() : 0.0;
  253. VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
  254. for( auto iter = poly.CIterate(); iter; iter++ )
  255. {
  256. auto pt = *iter;
  257. RotatePoint( pt, orientation );
  258. pt += offset;
  259. if( pt.x < xmin.x )
  260. {
  261. xmin.x = pt.x;
  262. xmin.y = pt.y;
  263. xmini = i;
  264. }
  265. }
  266. }
  267. break;
  268. default:
  269. break;
  270. }
  271. }
  272. // Grab the left most point, assume its on the board's perimeter, and see if we
  273. // can put enough graphics together by matching endpoints to formulate a cohesive
  274. // polygon.
  275. graphic = (DRAWSEGMENT*) segList[xmini];
  276. // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
  277. segList.erase( segList.begin() + xmini );
  278. // Output the outline perimeter as polygon.
  279. if( graphic->GetShape() == S_CIRCLE )
  280. {
  281. int steps = std::max<int>( 4, GetArcToSegmentCount( graphic->GetRadius(), aTolerance, 360.0 ) );
  282. TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps );
  283. }
  284. else if( graphic->GetShape() == S_POLYGON )
  285. {
  286. MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
  287. double orientation = module ? module->GetOrientation() : 0.0;
  288. VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
  289. aPolygons.NewOutline();
  290. for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
  291. {
  292. auto pt = *it;
  293. RotatePoint( pt, orientation );
  294. pt += offset;
  295. aPolygons.Append( pt );
  296. }
  297. }
  298. else
  299. {
  300. // Polygon start point. Arbitrarily chosen end of the
  301. // segment and build the poly from here.
  302. wxPoint startPt = wxPoint( graphic->GetEnd() );
  303. prevPt = graphic->GetEnd();
  304. aPolygons.NewOutline();
  305. aPolygons.Append( prevPt );
  306. // Do not append the other end point yet of this 'graphic', this first
  307. // 'graphic' might be an arc or a curve.
  308. for(;;)
  309. {
  310. switch( graphic->GetShape() )
  311. {
  312. case S_SEGMENT:
  313. {
  314. wxPoint nextPt;
  315. // Use the line segment end point furthest away from
  316. // prevPt as we assume the other end to be ON prevPt or
  317. // very close to it.
  318. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
  319. nextPt = graphic->GetEnd();
  320. else
  321. nextPt = graphic->GetStart();
  322. aPolygons.Append( nextPt );
  323. prevPt = nextPt;
  324. }
  325. break;
  326. case S_ARC:
  327. // We do not support arcs in polygons, so approximate
  328. // an arc with a series of short lines and put those
  329. // line segments into the !same! PATH.
  330. {
  331. wxPoint pstart = graphic->GetArcStart();
  332. wxPoint pend = graphic->GetArcEnd();
  333. wxPoint pcenter = graphic->GetCenter();
  334. double angle = -graphic->GetAngle();
  335. double radius = graphic->GetRadius();
  336. int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
  337. if( !close_enough( prevPt, pstart, aTolerance ) )
  338. {
  339. wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
  340. angle = -angle;
  341. std::swap( pstart, pend );
  342. }
  343. wxPoint nextPt;
  344. for( int step = 1; step<=steps; ++step )
  345. {
  346. double rotation = ( angle * step ) / steps;
  347. nextPt = pstart;
  348. RotatePoint( &nextPt, pcenter, rotation );
  349. aPolygons.Append( nextPt );
  350. }
  351. prevPt = nextPt;
  352. }
  353. break;
  354. case S_CURVE:
  355. // We do not support Bezier curves in polygons, so approximate
  356. // with a series of short lines and put those
  357. // line segments into the !same! PATH.
  358. {
  359. wxPoint nextPt;
  360. bool reverse = false;
  361. // Use the end point furthest away from
  362. // prevPt as we assume the other end to be ON prevPt or
  363. // very close to it.
  364. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
  365. nextPt = graphic->GetEnd();
  366. else
  367. {
  368. nextPt = graphic->GetStart();
  369. reverse = true;
  370. }
  371. if( reverse )
  372. {
  373. for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
  374. aPolygons.Append( graphic->GetBezierPoints()[jj] );
  375. }
  376. else
  377. {
  378. for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
  379. aPolygons.Append( graphic->GetBezierPoints()[jj] );
  380. }
  381. prevPt = nextPt;
  382. }
  383. break;
  384. default:
  385. if( aErrorText )
  386. {
  387. msg.Printf( "Unsupported DRAWSEGMENT type %s.",
  388. BOARD_ITEM::ShowShape( graphic->GetShape() ) );
  389. *aErrorText << msg << "\n";
  390. }
  391. if( aErrorLocation )
  392. *aErrorLocation = graphic->GetPosition();
  393. return false;
  394. }
  395. // Get next closest segment.
  396. graphic = findPoint( prevPt, segList, aTolerance );
  397. // If there are no more close segments, check if the board
  398. // outline polygon can be closed.
  399. if( !graphic )
  400. {
  401. if( close_enough( startPt, prevPt, aTolerance ) )
  402. {
  403. // Close the polygon back to start point
  404. // aPolygons.Append( startPt ); // not needed
  405. }
  406. else
  407. {
  408. if( aErrorText )
  409. {
  410. msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
  411. StringFromValue( MILLIMETRES, prevPt.x, true ),
  412. StringFromValue( MILLIMETRES, prevPt.y, true ) );
  413. *aErrorText << msg << "\n";
  414. }
  415. if( aErrorLocation )
  416. *aErrorLocation = prevPt;
  417. return false;
  418. }
  419. break;
  420. }
  421. }
  422. }
  423. while( segList.size() )
  424. {
  425. // emit a signal layers keepout for every interior polygon left...
  426. int hole = aPolygons.NewHole();
  427. graphic = (DRAWSEGMENT*) segList[0];
  428. segList.erase( segList.begin() );
  429. // Both circles and polygons on the edge cuts layer are closed items that
  430. // do not connect to other elements, so we process them independently
  431. if( graphic->GetShape() == S_POLYGON )
  432. {
  433. MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
  434. double orientation = module ? module->GetOrientation() : 0.0;
  435. VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
  436. for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
  437. {
  438. auto val = *it;
  439. RotatePoint( val, orientation );
  440. val += offset;
  441. aPolygons.Append( val, -1, hole );
  442. }
  443. }
  444. else if( graphic->GetShape() == S_CIRCLE )
  445. {
  446. // make a circle by segments;
  447. wxPoint center = graphic->GetCenter();
  448. double angle = 3600.0;
  449. wxPoint start = center;
  450. int radius = graphic->GetRadius();
  451. int steps = std::max<int>( 4, GetArcToSegmentCount( radius, aTolerance, 360.0 ) );
  452. wxPoint nextPt;
  453. start.x += radius;
  454. for( int step = 0; step < steps; ++step )
  455. {
  456. double rotation = ( angle * step ) / steps;
  457. nextPt = start;
  458. RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
  459. aPolygons.Append( nextPt, -1, hole );
  460. }
  461. }
  462. else
  463. {
  464. // Polygon start point. Arbitrarily chosen end of the
  465. // segment and build the poly from here.
  466. wxPoint startPt( graphic->GetEnd() );
  467. prevPt = graphic->GetEnd();
  468. aPolygons.Append( prevPt, -1, hole );
  469. // do not append the other end point yet, this first 'graphic' might be an arc
  470. for(;;)
  471. {
  472. switch( graphic->GetShape() )
  473. {
  474. case S_SEGMENT:
  475. {
  476. wxPoint nextPt;
  477. // Use the line segment end point furthest away from
  478. // prevPt as we assume the other end to be ON prevPt or
  479. // very close to it.
  480. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
  481. {
  482. nextPt = graphic->GetEnd();
  483. }
  484. else
  485. {
  486. nextPt = graphic->GetStart();
  487. }
  488. prevPt = nextPt;
  489. aPolygons.Append( prevPt, -1, hole );
  490. }
  491. break;
  492. case S_ARC:
  493. // Freerouter does not yet understand arcs, so approximate
  494. // an arc with a series of short lines and put those
  495. // line segments into the !same! PATH.
  496. {
  497. wxPoint pstart = graphic->GetArcStart();
  498. wxPoint pend = graphic->GetArcEnd();
  499. wxPoint pcenter = graphic->GetCenter();
  500. double angle = -graphic->GetAngle();
  501. int radius = graphic->GetRadius();
  502. int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
  503. if( !close_enough( prevPt, pstart, aTolerance ) )
  504. {
  505. wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
  506. angle = -angle;
  507. std::swap( pstart, pend );
  508. }
  509. wxPoint nextPt;
  510. for( int step = 1; step <= steps; ++step )
  511. {
  512. double rotation = ( angle * step ) / steps;
  513. nextPt = pstart;
  514. RotatePoint( &nextPt, pcenter, rotation );
  515. aPolygons.Append( nextPt, -1, hole );
  516. }
  517. prevPt = nextPt;
  518. }
  519. break;
  520. case S_CURVE:
  521. // We do not support Bezier curves in polygons, so approximate
  522. // with a series of short lines and put those
  523. // line segments into the !same! PATH.
  524. {
  525. wxPoint nextPt;
  526. bool reverse = false;
  527. // Use the end point furthest away from
  528. // prevPt as we assume the other end to be ON prevPt or
  529. // very close to it.
  530. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
  531. nextPt = graphic->GetEnd();
  532. else
  533. {
  534. nextPt = graphic->GetStart();
  535. reverse = true;
  536. }
  537. if( reverse )
  538. {
  539. for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
  540. aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
  541. }
  542. else
  543. {
  544. for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
  545. aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
  546. }
  547. prevPt = nextPt;
  548. }
  549. break;
  550. default:
  551. if( aErrorText )
  552. {
  553. msg.Printf( "Unsupported DRAWSEGMENT type %s.",
  554. BOARD_ITEM::ShowShape( graphic->GetShape() ) );
  555. *aErrorText << msg << "\n";
  556. }
  557. if( aErrorLocation )
  558. *aErrorLocation = graphic->GetPosition();
  559. return false;
  560. }
  561. // Get next closest segment.
  562. graphic = findPoint( prevPt, segList, aTolerance );
  563. // If there are no more close segments, check if polygon
  564. // can be closed.
  565. if( !graphic )
  566. {
  567. if( close_enough( startPt, prevPt, aTolerance ) )
  568. {
  569. // Close the polygon back to start point
  570. // aPolygons.Append( startPt, -1, hole ); // not needed
  571. }
  572. else
  573. {
  574. if( aErrorText )
  575. {
  576. msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
  577. StringFromValue( MILLIMETRES, prevPt.x, true ),
  578. StringFromValue( MILLIMETRES, prevPt.y, true ) );
  579. *aErrorText << msg << "\n";
  580. }
  581. if( aErrorLocation )
  582. *aErrorLocation = prevPt;
  583. return false;
  584. }
  585. break;
  586. }
  587. }
  588. }
  589. }
  590. // All of the silliness that follows is to work around the segment iterator
  591. // while checking for collisions.
  592. // TODO: Implement proper segment and point iterators that follow std
  593. for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
  594. {
  595. auto seg2 = seg1;
  596. for( ++seg2; seg2; seg2++ )
  597. {
  598. // Check for exact overlapping segments. This is not viewed
  599. // as an intersection below
  600. if( *seg1 == *seg2 ||
  601. ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
  602. {
  603. if( aErrorLocation )
  604. {
  605. aErrorLocation->x = ( *seg1 ).A.x;
  606. aErrorLocation->y = ( *seg1 ).A.y;
  607. }
  608. return false;
  609. }
  610. if( auto pt = seg1.Get().Intersect( seg2.Get(), true ) )
  611. {
  612. if( aErrorLocation )
  613. {
  614. aErrorLocation->x = pt->x;
  615. aErrorLocation->y = pt->y;
  616. }
  617. return false;
  618. }
  619. }
  620. }
  621. return true;
  622. }
  623. #include <class_board.h>
  624. #include <collectors.h>
  625. /* This function is used to extract a board outlines (3D view, automatic zones build ...)
  626. * Any closed outline inside the main outline is a hole
  627. * All contours should be closed, i.e. valid closed polygon vertices
  628. */
  629. bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
  630. wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
  631. {
  632. PCB_TYPE_COLLECTOR items;
  633. // Get all the DRAWSEGMENTS and module graphics into 'items',
  634. // then keep only those on layer == Edge_Cuts.
  635. static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
  636. items.Collect( aBoard, scan_graphics );
  637. // Make a working copy of aSegList, because the list is modified during calculations
  638. std::vector< DRAWSEGMENT* > segList;
  639. for( int ii = 0; ii < items.GetCount(); ii++ )
  640. {
  641. if( items[ii]->GetLayer() == Edge_Cuts )
  642. segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
  643. }
  644. bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation );
  645. if( !success || !aOutlines.OutlineCount() )
  646. {
  647. // Creates a valid polygon outline is not possible.
  648. // So uses the board edge cuts bounding box to create a
  649. // rectangular outline
  650. // When no edge cuts items, build a contour
  651. // from global bounding box
  652. EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
  653. // If null area, uses the global bounding box.
  654. if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
  655. bbbox = aBoard->ComputeBoundingBox();
  656. // Ensure non null area. If happen, gives a minimal size.
  657. if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
  658. bbbox.Inflate( Millimeter2iu( 1.0 ) );
  659. aOutlines.RemoveAllContours();
  660. aOutlines.NewOutline();
  661. wxPoint corner;
  662. aOutlines.Append( bbbox.GetOrigin() );
  663. corner.x = bbbox.GetOrigin().x;
  664. corner.y = bbbox.GetEnd().y;
  665. aOutlines.Append( corner );
  666. aOutlines.Append( bbbox.GetEnd() );
  667. corner.x = bbbox.GetEnd().x;
  668. corner.y = bbbox.GetOrigin().y;
  669. aOutlines.Append( corner );
  670. }
  671. return success;
  672. }