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.

5970 lines
156 KiB

  1. /*******************************************************************************
  2. * *
  3. * Author : Angus Johnson *
  4. * Version : 6.4.2 *
  5. * Date : 27 February 2017 *
  6. * Website : http://www.angusj.com *
  7. * Copyright : Angus Johnson 2010-2017 *
  8. * *
  9. * License: *
  10. * Use, modification & distribution is subject to Boost Software License Ver 1. *
  11. * http://www.boost.org/LICENSE_1_0.txt *
  12. * *
  13. * Attributions: *
  14. * The code in this library is an extension of Bala Vatti's clipping algorithm: *
  15. * "A generic solution to polygon clipping" *
  16. * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
  17. * http://portal.acm.org/citation.cfm?id=129906 *
  18. * *
  19. * Computer graphics and geometric modeling: implementation and algorithms *
  20. * By Max K. Agoston *
  21. * Springer; 1 edition (January 4, 2005) *
  22. * http://books.google.com/books?q=vatti+clipping+agoston *
  23. * *
  24. * See also: *
  25. * "Polygon Offsetting by Computing Winding Numbers" *
  26. * Paper no. DETC2005-85513 pp. 565-575 *
  27. * ASME 2005 International Design Engineering Technical Conferences *
  28. * and Computers and Information in Engineering Conference (IDETC/CIE2005) *
  29. * September 24-28, 2005 , Long Beach, California, USA *
  30. * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
  31. * *
  32. *******************************************************************************/
  33. /*******************************************************************************
  34. * *
  35. * This is a translation of the Delphi Clipper library and the naming style *
  36. * used has retained a Delphi flavour. *
  37. * *
  38. *******************************************************************************/
  39. #include "clipper.hpp"
  40. #include <cmath>
  41. #include <vector>
  42. #include <algorithm>
  43. #include <stdexcept>
  44. #include <cstring>
  45. #include <cstdlib>
  46. #include <ostream>
  47. #include <functional>
  48. namespace ClipperLib {
  49. static double const pi = 3.141592653589793238;
  50. static double const two_pi = pi * 2;
  51. static double const def_arc_tolerance = 0.25;
  52. enum Direction
  53. {
  54. dRightToLeft, dLeftToRight
  55. };
  56. static int const Unassigned = -1; // edge not currently 'owning' a solution
  57. static int const Skip = -2; // edge that would otherwise close a path
  58. #define HORIZONTAL (-1.0E+40)
  59. #define TOLERANCE (1.0e-20)
  60. #define NEAR_ZERO( val ) ( ( (val) > -TOLERANCE ) && ( (val) < TOLERANCE ) )
  61. struct TEdge
  62. {
  63. IntPoint Bot;
  64. IntPoint Curr; // current (updated for every new scanbeam)
  65. IntPoint Top;
  66. double Dx;
  67. PolyType PolyTyp;
  68. EdgeSide Side; // side only refers to current side of solution poly
  69. int WindDelta; // 1 or -1 depending on winding direction
  70. int WindCnt;
  71. int WindCnt2; // winding count of the opposite polytype
  72. int OutIdx;
  73. TEdge* Next;
  74. TEdge* Prev;
  75. TEdge* NextInLML;
  76. TEdge* NextInAEL;
  77. TEdge* PrevInAEL;
  78. TEdge* NextInSEL;
  79. TEdge* PrevInSEL;
  80. };
  81. struct IntersectNode
  82. {
  83. TEdge* Edge1;
  84. TEdge* Edge2;
  85. IntPoint Pt;
  86. };
  87. struct LocalMinimum
  88. {
  89. cInt Y;
  90. TEdge* LeftBound;
  91. TEdge* RightBound;
  92. };
  93. struct OutPt;
  94. // OutRec: contains a path in the clipping solution. Edges in the AEL will
  95. // carry a pointer to an OutRec when they are part of the clipping solution.
  96. struct OutRec
  97. {
  98. int Idx;
  99. bool IsHole;
  100. bool IsOpen;
  101. OutRec* FirstLeft; // see comments in clipper.pas
  102. PolyNode* PolyNd;
  103. OutPt* Pts;
  104. OutPt* BottomPt;
  105. };
  106. struct OutPt
  107. {
  108. int Idx;
  109. IntPoint Pt;
  110. OutPt* Next;
  111. OutPt* Prev;
  112. };
  113. struct Join
  114. {
  115. OutPt* OutPt1;
  116. OutPt* OutPt2;
  117. IntPoint OffPt;
  118. };
  119. struct LocMinSorter
  120. {
  121. inline bool operator()( const LocalMinimum& locMin1, const LocalMinimum& locMin2 )
  122. {
  123. return locMin2.Y < locMin1.Y;
  124. }
  125. };
  126. // ------------------------------------------------------------------------------
  127. // ------------------------------------------------------------------------------
  128. inline cInt Round( double val )
  129. {
  130. if( (val < 0) )
  131. return static_cast<cInt>(val - 0.5);
  132. else
  133. return static_cast<cInt>(val + 0.5);
  134. }
  135. // ------------------------------------------------------------------------------
  136. inline cInt Abs( cInt val )
  137. {
  138. return val < 0 ? -val : val;
  139. }
  140. // ------------------------------------------------------------------------------
  141. // PolyTree methods ...
  142. // ------------------------------------------------------------------------------
  143. void PolyTree::Clear()
  144. {
  145. for( PolyNodes::size_type i = 0; i < AllNodes.size(); ++i )
  146. delete AllNodes[i];
  147. AllNodes.resize( 0 );
  148. Childs.resize( 0 );
  149. }
  150. // ------------------------------------------------------------------------------
  151. PolyNode* PolyTree::GetFirst() const
  152. {
  153. if( !Childs.empty() )
  154. return Childs[0];
  155. else
  156. return 0;
  157. }
  158. // ------------------------------------------------------------------------------
  159. int PolyTree::Total() const
  160. {
  161. int result = (int) AllNodes.size();
  162. // with negative offsets, ignore the hidden outer polygon ...
  163. if( result > 0 && Childs[0] != AllNodes[0] )
  164. result--;
  165. return result;
  166. }
  167. // ------------------------------------------------------------------------------
  168. // PolyNode methods ...
  169. // ------------------------------------------------------------------------------
  170. PolyNode::PolyNode() : Parent( 0 ), Index( 0 ), m_IsOpen( false )
  171. {
  172. }
  173. // ------------------------------------------------------------------------------
  174. int PolyNode::ChildCount() const
  175. {
  176. return (int) Childs.size();
  177. }
  178. // ------------------------------------------------------------------------------
  179. void PolyNode::AddChild( PolyNode& child )
  180. {
  181. unsigned cnt = (unsigned) Childs.size();
  182. Childs.push_back( &child );
  183. child.Parent = this;
  184. child.Index = cnt;
  185. }
  186. // ------------------------------------------------------------------------------
  187. PolyNode* PolyNode::GetNext() const
  188. {
  189. if( !Childs.empty() )
  190. return Childs[0];
  191. else
  192. return GetNextSiblingUp();
  193. }
  194. // ------------------------------------------------------------------------------
  195. PolyNode* PolyNode::GetNextSiblingUp() const
  196. {
  197. if( !Parent ) // protects against PolyTree.GetNextSiblingUp()
  198. return 0;
  199. else if( Index == Parent->Childs.size() - 1 )
  200. return Parent->GetNextSiblingUp();
  201. else
  202. return Parent->Childs[Index + 1];
  203. }
  204. // ------------------------------------------------------------------------------
  205. bool PolyNode::IsHole() const
  206. {
  207. bool result = true;
  208. PolyNode* node = Parent;
  209. while( node )
  210. {
  211. result = !result;
  212. node = node->Parent;
  213. }
  214. return result;
  215. }
  216. // ------------------------------------------------------------------------------
  217. bool PolyNode::IsOpen() const
  218. {
  219. return m_IsOpen;
  220. }
  221. // ------------------------------------------------------------------------------
  222. #ifndef use_int32
  223. // ------------------------------------------------------------------------------
  224. // Int128 class (enables safe math on signed 64bit integers)
  225. // eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
  226. // Int128 val2((long64)9223372036854775807);
  227. // Int128 val3 = val1 * val2;
  228. // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
  229. // ------------------------------------------------------------------------------
  230. class Int128
  231. {
  232. public:
  233. ulong64 lo;
  234. long64 hi;
  235. Int128( long64 _lo = 0 )
  236. {
  237. lo = (ulong64) _lo;
  238. if( _lo < 0 ) hi = -1; else hi = 0;
  239. }
  240. Int128( const Int128& val ) : lo( val.lo ), hi( val.hi ) {}
  241. Int128( const long64& _hi, const ulong64& _lo ) : lo( _lo ), hi( _hi ) {}
  242. Int128& operator =( const long64& val )
  243. {
  244. lo = (ulong64) val;
  245. if( val < 0 ) hi = -1; else hi = 0;
  246. return *this;
  247. }
  248. bool operator ==( const Int128& val ) const
  249. { return hi == val.hi && lo == val.lo; }
  250. bool operator !=( const Int128& val ) const
  251. { return !(*this == val); }
  252. bool operator >( const Int128& val ) const
  253. {
  254. if( hi != val.hi )
  255. return hi > val.hi;
  256. else
  257. return lo > val.lo;
  258. }
  259. bool operator <( const Int128& val ) const
  260. {
  261. if( hi != val.hi )
  262. return hi < val.hi;
  263. else
  264. return lo < val.lo;
  265. }
  266. bool operator >=( const Int128& val ) const
  267. { return !(*this < val); }
  268. bool operator <=( const Int128& val ) const
  269. { return !(*this > val); }
  270. Int128& operator +=( const Int128& rhs )
  271. {
  272. hi += rhs.hi;
  273. lo += rhs.lo;
  274. if( lo < rhs.lo ) hi++;
  275. return *this;
  276. }
  277. Int128 operator +( const Int128& rhs ) const
  278. {
  279. Int128 result( *this );
  280. result += rhs;
  281. return result;
  282. }
  283. Int128& operator -=( const Int128& rhs )
  284. {
  285. *this += -rhs;
  286. return *this;
  287. }
  288. Int128 operator -( const Int128& rhs ) const
  289. {
  290. Int128 result( *this );
  291. result -= rhs;
  292. return result;
  293. }
  294. Int128 operator-() const // unary negation
  295. {
  296. if( lo == 0 )
  297. return Int128( -hi, 0 );
  298. else
  299. return Int128( ~hi, ~lo + 1 );
  300. }
  301. operator double() const
  302. {
  303. const double shift64 = 18446744073709551616.0; // 2^64
  304. if( hi < 0 )
  305. {
  306. if( lo == 0 ) return (double) hi * shift64;
  307. else return -(double) (~lo + ~hi * shift64);
  308. }
  309. else
  310. return (double) (lo + hi * shift64);
  311. }
  312. };
  313. // ------------------------------------------------------------------------------
  314. Int128 Int128Mul( long64 lhs, long64 rhs )
  315. {
  316. bool negate = (lhs < 0) != (rhs < 0);
  317. if( lhs < 0 )
  318. lhs = -lhs;
  319. ulong64 int1Hi = ulong64( lhs ) >> 32;
  320. ulong64 int1Lo = ulong64( lhs & 0xFFFFFFFF );
  321. if( rhs < 0 )
  322. rhs = -rhs;
  323. ulong64 int2Hi = ulong64( rhs ) >> 32;
  324. ulong64 int2Lo = ulong64( rhs & 0xFFFFFFFF );
  325. // nb: see comments in clipper.pas
  326. ulong64 a = int1Hi * int2Hi;
  327. ulong64 b = int1Lo * int2Lo;
  328. ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
  329. Int128 tmp;
  330. tmp.hi = long64( a + (c >> 32) );
  331. tmp.lo = long64( c << 32 );
  332. tmp.lo += long64( b );
  333. if( tmp.lo < b )
  334. tmp.hi++;
  335. if( negate )
  336. tmp = -tmp;
  337. return tmp;
  338. }
  339. #endif
  340. // ------------------------------------------------------------------------------
  341. // Miscellaneous global functions
  342. // ------------------------------------------------------------------------------
  343. bool Orientation( const Path& poly )
  344. {
  345. return Area( poly ) >= 0;
  346. }
  347. // ------------------------------------------------------------------------------
  348. double Area( const Path& poly )
  349. {
  350. int size = (int) poly.size();
  351. if( size < 3 )
  352. return 0;
  353. double a = 0;
  354. for( int i = 0, j = size - 1; i < size; ++i )
  355. {
  356. a += ( (double) poly[j].X + poly[i].X ) * ( (double) poly[j].Y - poly[i].Y );
  357. j = i;
  358. }
  359. return -a * 0.5;
  360. }
  361. // ------------------------------------------------------------------------------
  362. double Area( const OutPt* op )
  363. {
  364. const OutPt* startOp = op;
  365. if( !op )
  366. return 0;
  367. double a = 0;
  368. do {
  369. a += (double) (op->Prev->Pt.X + op->Pt.X) * (double) (op->Prev->Pt.Y - op->Pt.Y);
  370. op = op->Next;
  371. } while( op != startOp );
  372. return a * 0.5;
  373. }
  374. // ------------------------------------------------------------------------------
  375. double Area( const OutRec& outRec )
  376. {
  377. return Area( outRec.Pts );
  378. }
  379. // ------------------------------------------------------------------------------
  380. bool PointIsVertex( const IntPoint& Pt, OutPt* pp )
  381. {
  382. OutPt* pp2 = pp;
  383. do
  384. {
  385. if( pp2->Pt == Pt )
  386. return true;
  387. pp2 = pp2->Next;
  388. } while( pp2 != pp );
  389. return false;
  390. }
  391. // ------------------------------------------------------------------------------
  392. // See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
  393. // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
  394. int PointInPolygon( const IntPoint& pt, const Path& path )
  395. {
  396. // returns 0 if false, +1 if true, -1 if pt ON polygon boundary
  397. int result = 0;
  398. size_t cnt = path.size();
  399. if( cnt < 3 )
  400. return 0;
  401. IntPoint ip = path[0];
  402. for( size_t i = 1; i <= cnt; ++i )
  403. {
  404. IntPoint ipNext = (i == cnt ? path[0] : path[i]);
  405. if( ipNext.Y == pt.Y )
  406. {
  407. if( (ipNext.X == pt.X) || ( ip.Y == pt.Y
  408. && ( (ipNext.X > pt.X) == (ip.X < pt.X) ) ) )
  409. return -1;
  410. }
  411. if( (ip.Y < pt.Y) != (ipNext.Y < pt.Y) )
  412. {
  413. if( ip.X >= pt.X )
  414. {
  415. if( ipNext.X > pt.X )
  416. result = 1 - result;
  417. else
  418. {
  419. double d = (double) (ip.X - pt.X) * (ipNext.Y - pt.Y) -
  420. (double) (ipNext.X - pt.X) * (ip.Y - pt.Y);
  421. if( !d )
  422. return -1;
  423. if( (d > 0) == (ipNext.Y > ip.Y) )
  424. result = 1 - result;
  425. }
  426. }
  427. else
  428. {
  429. if( ipNext.X > pt.X )
  430. {
  431. double d = (double) (ip.X - pt.X) * (ipNext.Y - pt.Y) -
  432. (double) (ipNext.X - pt.X) * (ip.Y - pt.Y);
  433. if( !d )
  434. return -1;
  435. if( (d > 0) == (ipNext.Y > ip.Y) )
  436. result = 1 - result;
  437. }
  438. }
  439. }
  440. ip = ipNext;
  441. }
  442. return result;
  443. }
  444. // ------------------------------------------------------------------------------
  445. int PointInPolygon( const IntPoint& pt, OutPt* op )
  446. {
  447. // returns 0 if false, +1 if true, -1 if pt ON polygon boundary
  448. int result = 0;
  449. OutPt* startOp = op;
  450. for( ; ; )
  451. {
  452. if( op->Next->Pt.Y == pt.Y )
  453. {
  454. if( (op->Next->Pt.X == pt.X) || ( op->Pt.Y == pt.Y
  455. && ( (op->Next->Pt.X > pt.X) ==
  456. (op->Pt.X < pt.X) ) ) )
  457. return -1;
  458. }
  459. if( (op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y) )
  460. {
  461. if( op->Pt.X >= pt.X )
  462. {
  463. if( op->Next->Pt.X > pt.X )
  464. result = 1 - result;
  465. else
  466. {
  467. double d = (double) (op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
  468. (double) (op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
  469. if( !d )
  470. return -1;
  471. if( (d > 0) == (op->Next->Pt.Y > op->Pt.Y) )
  472. result = 1 - result;
  473. }
  474. }
  475. else
  476. {
  477. if( op->Next->Pt.X > pt.X )
  478. {
  479. double d = (double) (op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
  480. (double) (op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
  481. if( !d )
  482. return -1;
  483. if( (d > 0) == (op->Next->Pt.Y > op->Pt.Y) )
  484. result = 1 - result;
  485. }
  486. }
  487. }
  488. op = op->Next;
  489. if( startOp == op )
  490. break;
  491. }
  492. return result;
  493. }
  494. // ------------------------------------------------------------------------------
  495. bool Poly2ContainsPoly1( OutPt* OutPt1, OutPt* OutPt2 )
  496. {
  497. OutPt* op = OutPt1;
  498. do
  499. {
  500. // nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
  501. int res = PointInPolygon( op->Pt, OutPt2 );
  502. if( res >= 0 )
  503. return res > 0;
  504. op = op->Next;
  505. } while( op != OutPt1 );
  506. return true;
  507. }
  508. // ----------------------------------------------------------------------
  509. bool SlopesEqual( const TEdge& e1, const TEdge& e2, bool UseFullInt64Range )
  510. {
  511. #ifndef use_int32
  512. if( UseFullInt64Range )
  513. return Int128Mul( e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X ) ==
  514. Int128Mul( e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y );
  515. else
  516. #endif
  517. return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) ==
  518. (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y);
  519. }
  520. // ------------------------------------------------------------------------------
  521. bool SlopesEqual( const IntPoint pt1, const IntPoint pt2,
  522. const IntPoint pt3, bool UseFullInt64Range )
  523. {
  524. #ifndef use_int32
  525. if( UseFullInt64Range )
  526. return Int128Mul( pt1.Y - pt2.Y,
  527. pt2.X - pt3.X ) == Int128Mul( pt1.X - pt2.X, pt2.Y - pt3.Y );
  528. else
  529. #endif
  530. return (pt1.Y - pt2.Y) * (pt2.X - pt3.X) == (pt1.X - pt2.X) * (pt2.Y - pt3.Y);
  531. }
  532. // ------------------------------------------------------------------------------
  533. bool SlopesEqual( const IntPoint pt1, const IntPoint pt2,
  534. const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range )
  535. {
  536. #ifndef use_int32
  537. if( UseFullInt64Range )
  538. return Int128Mul( pt1.Y - pt2.Y,
  539. pt3.X - pt4.X ) == Int128Mul( pt1.X - pt2.X, pt3.Y - pt4.Y );
  540. else
  541. #endif
  542. return (pt1.Y - pt2.Y) * (pt3.X - pt4.X) == (pt1.X - pt2.X) * (pt3.Y - pt4.Y);
  543. }
  544. // ------------------------------------------------------------------------------
  545. inline bool IsHorizontal( TEdge& e )
  546. {
  547. return e.Dx == HORIZONTAL;
  548. }
  549. // ------------------------------------------------------------------------------
  550. inline double GetDx( const IntPoint pt1, const IntPoint pt2 )
  551. {
  552. return (pt1.Y == pt2.Y) ?
  553. HORIZONTAL : (double) (pt2.X - pt1.X) / (pt2.Y - pt1.Y);
  554. }
  555. // ---------------------------------------------------------------------------
  556. inline void SetDx( TEdge& e )
  557. {
  558. cInt dy = (e.Top.Y - e.Bot.Y);
  559. if( dy == 0 )
  560. e.Dx = HORIZONTAL;
  561. else
  562. e.Dx = (double) (e.Top.X - e.Bot.X) / dy;
  563. }
  564. // ---------------------------------------------------------------------------
  565. inline void SwapSides( TEdge& Edge1, TEdge& Edge2 )
  566. {
  567. EdgeSide Side = Edge1.Side;
  568. Edge1.Side = Edge2.Side;
  569. Edge2.Side = Side;
  570. }
  571. // ------------------------------------------------------------------------------
  572. inline void SwapPolyIndexes( TEdge& Edge1, TEdge& Edge2 )
  573. {
  574. int OutIdx = Edge1.OutIdx;
  575. Edge1.OutIdx = Edge2.OutIdx;
  576. Edge2.OutIdx = OutIdx;
  577. }
  578. // ------------------------------------------------------------------------------
  579. inline cInt TopX( TEdge& edge, const cInt currentY )
  580. {
  581. return ( currentY == edge.Top.Y ) ?
  582. edge.Top.X : edge.Bot.X + Round( edge.Dx * (currentY - edge.Bot.Y) );
  583. }
  584. // ------------------------------------------------------------------------------
  585. void IntersectPoint( TEdge& Edge1, TEdge& Edge2, IntPoint& ip )
  586. {
  587. #ifdef use_xyz
  588. ip.Z = 0;
  589. #endif
  590. double b1, b2;
  591. if( Edge1.Dx == Edge2.Dx )
  592. {
  593. ip.Y = Edge1.Curr.Y;
  594. ip.X = TopX( Edge1, ip.Y );
  595. return;
  596. }
  597. else if( Edge1.Dx == 0 )
  598. {
  599. ip.X = Edge1.Bot.X;
  600. if( IsHorizontal( Edge2 ) )
  601. ip.Y = Edge2.Bot.Y;
  602. else
  603. {
  604. b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
  605. ip.Y = Round( ip.X / Edge2.Dx + b2 );
  606. }
  607. }
  608. else if( Edge2.Dx == 0 )
  609. {
  610. ip.X = Edge2.Bot.X;
  611. if( IsHorizontal( Edge1 ) )
  612. ip.Y = Edge1.Bot.Y;
  613. else
  614. {
  615. b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
  616. ip.Y = Round( ip.X / Edge1.Dx + b1 );
  617. }
  618. }
  619. else
  620. {
  621. b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;
  622. b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;
  623. double q = (b2 - b1) / (Edge1.Dx - Edge2.Dx);
  624. ip.Y = Round( q );
  625. if( std::fabs( Edge1.Dx ) < std::fabs( Edge2.Dx ) )
  626. ip.X = Round( Edge1.Dx * q + b1 );
  627. else
  628. ip.X = Round( Edge2.Dx * q + b2 );
  629. }
  630. if( ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y )
  631. {
  632. if( Edge1.Top.Y > Edge2.Top.Y )
  633. ip.Y = Edge1.Top.Y;
  634. else
  635. ip.Y = Edge2.Top.Y;
  636. if( std::fabs( Edge1.Dx ) < std::fabs( Edge2.Dx ) )
  637. ip.X = TopX( Edge1, ip.Y );
  638. else
  639. ip.X = TopX( Edge2, ip.Y );
  640. }
  641. // finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
  642. if( ip.Y > Edge1.Curr.Y )
  643. {
  644. ip.Y = Edge1.Curr.Y;
  645. // use the more vertical edge to derive X ...
  646. if( std::fabs( Edge1.Dx ) > std::fabs( Edge2.Dx ) )
  647. ip.X = TopX( Edge2, ip.Y );
  648. else
  649. ip.X = TopX( Edge1, ip.Y );
  650. }
  651. }
  652. // ------------------------------------------------------------------------------
  653. void ReversePolyPtLinks( OutPt* pp )
  654. {
  655. if( !pp )
  656. return;
  657. OutPt* pp1, * pp2;
  658. pp1 = pp;
  659. do {
  660. pp2 = pp1->Next;
  661. pp1->Next = pp1->Prev;
  662. pp1->Prev = pp2;
  663. pp1 = pp2;
  664. } while( pp1 != pp );
  665. }
  666. // ------------------------------------------------------------------------------
  667. void DisposeOutPts( OutPt*& pp )
  668. {
  669. if( pp == 0 )
  670. return;
  671. pp->Prev->Next = 0;
  672. while( pp )
  673. {
  674. OutPt* tmpPp = pp;
  675. pp = pp->Next;
  676. delete tmpPp;
  677. }
  678. }
  679. // ------------------------------------------------------------------------------
  680. inline void InitEdge( TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt )
  681. {
  682. // This clears the C++ way
  683. *e = TEdge( { 0 } );
  684. e->Next = eNext;
  685. e->Prev = ePrev;
  686. e->Curr = Pt;
  687. e->OutIdx = Unassigned;
  688. }
  689. // ------------------------------------------------------------------------------
  690. void InitEdge2( TEdge& e, PolyType Pt )
  691. {
  692. if( e.Curr.Y >= e.Next->Curr.Y )
  693. {
  694. e.Bot = e.Curr;
  695. e.Top = e.Next->Curr;
  696. }
  697. else
  698. {
  699. e.Top = e.Curr;
  700. e.Bot = e.Next->Curr;
  701. }
  702. SetDx( e );
  703. e.PolyTyp = Pt;
  704. }
  705. // ------------------------------------------------------------------------------
  706. TEdge* RemoveEdge( TEdge* e )
  707. {
  708. // removes e from double_linked_list (but without removing from memory)
  709. e->Prev->Next = e->Next;
  710. e->Next->Prev = e->Prev;
  711. TEdge* result = e->Next;
  712. e->Prev = 0; // flag as removed (see ClipperBase.Clear)
  713. return result;
  714. }
  715. // ------------------------------------------------------------------------------
  716. inline void ReverseHorizontal( TEdge& e )
  717. {
  718. // swap horizontal edges' Top and Bottom x's so they follow the natural
  719. // progression of the bounds - ie so their xbots will align with the
  720. // adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
  721. std::swap( e.Top.X, e.Bot.X );
  722. #ifdef use_xyz
  723. std::swap( e.Top.Z, e.Bot.Z );
  724. #endif
  725. }
  726. // ------------------------------------------------------------------------------
  727. void SwapPoints( IntPoint& pt1, IntPoint& pt2 )
  728. {
  729. IntPoint tmp = pt1;
  730. pt1 = pt2;
  731. pt2 = tmp;
  732. }
  733. // ------------------------------------------------------------------------------
  734. bool GetOverlapSegment( IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
  735. IntPoint pt2b, IntPoint& pt1, IntPoint& pt2 )
  736. {
  737. // precondition: segments are Collinear.
  738. if( Abs( pt1a.X - pt1b.X ) > Abs( pt1a.Y - pt1b.Y ) )
  739. {
  740. if( pt1a.X > pt1b.X )
  741. SwapPoints( pt1a, pt1b );
  742. if( pt2a.X > pt2b.X )
  743. SwapPoints( pt2a, pt2b );
  744. if( pt1a.X > pt2a.X )
  745. pt1 = pt1a;
  746. else
  747. pt1 = pt2a;
  748. if( pt1b.X < pt2b.X )
  749. pt2 = pt1b;
  750. else
  751. pt2 = pt2b;
  752. return pt1.X < pt2.X;
  753. }
  754. else
  755. {
  756. if( pt1a.Y < pt1b.Y )
  757. SwapPoints( pt1a, pt1b );
  758. if( pt2a.Y < pt2b.Y )
  759. SwapPoints( pt2a, pt2b );
  760. if( pt1a.Y < pt2a.Y )
  761. pt1 = pt1a;
  762. else
  763. pt1 = pt2a;
  764. if( pt1b.Y > pt2b.Y )
  765. pt2 = pt1b;
  766. else
  767. pt2 = pt2b;
  768. return pt1.Y > pt2.Y;
  769. }
  770. }
  771. // ------------------------------------------------------------------------------
  772. bool FirstIsBottomPt( const OutPt* btmPt1, const OutPt* btmPt2 )
  773. {
  774. OutPt* p = btmPt1->Prev;
  775. while( (p->Pt == btmPt1->Pt) && (p != btmPt1) )
  776. p = p->Prev;
  777. double dx1p = std::fabs( GetDx( btmPt1->Pt, p->Pt ) );
  778. p = btmPt1->Next;
  779. while( (p->Pt == btmPt1->Pt) && (p != btmPt1) )
  780. p = p->Next;
  781. double dx1n = std::fabs( GetDx( btmPt1->Pt, p->Pt ) );
  782. p = btmPt2->Prev;
  783. while( (p->Pt == btmPt2->Pt) && (p != btmPt2) )
  784. p = p->Prev;
  785. double dx2p = std::fabs( GetDx( btmPt2->Pt, p->Pt ) );
  786. p = btmPt2->Next;
  787. while( (p->Pt == btmPt2->Pt) && (p != btmPt2) )
  788. p = p->Next;
  789. double dx2n = std::fabs( GetDx( btmPt2->Pt, p->Pt ) );
  790. if( std::max( dx1p, dx1n ) == std::max( dx2p, dx2n )
  791. && std::min( dx1p, dx1n ) == std::min( dx2p, dx2n ) )
  792. return Area( btmPt1 ) > 0; // if otherwise identical use orientation
  793. else
  794. return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
  795. }
  796. // ------------------------------------------------------------------------------
  797. OutPt* GetBottomPt( OutPt* pp )
  798. {
  799. OutPt* dups = 0;
  800. OutPt* p = pp->Next;
  801. while( p != pp )
  802. {
  803. if( p->Pt.Y > pp->Pt.Y )
  804. {
  805. pp = p;
  806. dups = 0;
  807. }
  808. else if( p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X )
  809. {
  810. if( p->Pt.X < pp->Pt.X )
  811. {
  812. dups = 0;
  813. pp = p;
  814. }
  815. else
  816. {
  817. if( p->Next != pp && p->Prev != pp )
  818. dups = p;
  819. }
  820. }
  821. p = p->Next;
  822. }
  823. if( dups )
  824. {
  825. // there appears to be at least 2 vertices at BottomPt so ...
  826. while( dups != p )
  827. {
  828. if( !FirstIsBottomPt( p, dups ) )
  829. pp = dups;
  830. dups = dups->Next;
  831. while( dups->Pt != pp->Pt )
  832. dups = dups->Next;
  833. }
  834. }
  835. return pp;
  836. }
  837. // ------------------------------------------------------------------------------
  838. bool Pt2IsBetweenPt1AndPt3( const IntPoint pt1,
  839. const IntPoint pt2, const IntPoint pt3 )
  840. {
  841. if( (pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2) )
  842. return false;
  843. else if( pt1.X != pt3.X )
  844. return (pt2.X > pt1.X) == (pt2.X < pt3.X);
  845. else
  846. return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
  847. }
  848. // ------------------------------------------------------------------------------
  849. bool HorzSegmentsOverlap( cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b )
  850. {
  851. if( seg1a > seg1b )
  852. std::swap( seg1a, seg1b );
  853. if( seg2a > seg2b )
  854. std::swap( seg2a, seg2b );
  855. return (seg1a < seg2b) && (seg2a < seg1b);
  856. }
  857. // ------------------------------------------------------------------------------
  858. // ClipperBase class methods ...
  859. // ------------------------------------------------------------------------------
  860. ClipperBase::ClipperBase() // constructor
  861. {
  862. m_CurrentLM = m_MinimaList.begin(); // begin() == end() here
  863. m_UseFullRange = false;
  864. }
  865. // ------------------------------------------------------------------------------
  866. ClipperBase::~ClipperBase() // destructor
  867. {
  868. Clear();
  869. }
  870. // ------------------------------------------------------------------------------
  871. void RangeTest( const IntPoint& Pt, bool& useFullRange )
  872. {
  873. if( useFullRange )
  874. {
  875. if( Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange )
  876. throw clipperException( "Coordinate outside allowed range" );
  877. }
  878. else if( Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange )
  879. {
  880. useFullRange = true;
  881. RangeTest( Pt, useFullRange );
  882. }
  883. }
  884. // ------------------------------------------------------------------------------
  885. TEdge* FindNextLocMin( TEdge* E )
  886. {
  887. for( ; ; )
  888. {
  889. while( E->Bot != E->Prev->Bot || E->Curr == E->Top )
  890. E = E->Next;
  891. if( !IsHorizontal( *E ) && !IsHorizontal( *E->Prev ) )
  892. break;
  893. while( IsHorizontal( *E->Prev ) )
  894. E = E->Prev;
  895. TEdge* E2 = E;
  896. while( IsHorizontal( *E ) )
  897. E = E->Next;
  898. if( E->Top.Y == E->Prev->Bot.Y )
  899. continue; // ie just an intermediate horz.
  900. if( E2->Prev->Bot.X < E->Bot.X )
  901. E = E2;
  902. break;
  903. }
  904. return E;
  905. }
  906. // ------------------------------------------------------------------------------
  907. TEdge* ClipperBase::ProcessBound( TEdge* E, bool NextIsForward )
  908. {
  909. TEdge* Result = E;
  910. TEdge* Horz = 0;
  911. if( E->OutIdx == Skip )
  912. {
  913. // if edges still remain in the current bound beyond the skip edge then
  914. // create another LocMin and call ProcessBound once more
  915. if( NextIsForward )
  916. {
  917. while( E->Top.Y == E->Next->Bot.Y )
  918. E = E->Next;
  919. // don't include top horizontals when parsing a bound a second time,
  920. // they will be contained in the opposite bound ...
  921. while( E != Result && IsHorizontal( *E ) )
  922. E = E->Prev;
  923. }
  924. else
  925. {
  926. while( E->Top.Y == E->Prev->Bot.Y )
  927. E = E->Prev;
  928. while( E != Result && IsHorizontal( *E ) )
  929. E = E->Next;
  930. }
  931. if( E == Result )
  932. {
  933. if( NextIsForward )
  934. Result = E->Next;
  935. else
  936. Result = E->Prev;
  937. }
  938. else
  939. {
  940. // there are more edges in the bound beyond result starting with E
  941. if( NextIsForward )
  942. E = Result->Next;
  943. else
  944. E = Result->Prev;
  945. MinimaList::value_type locMin;
  946. locMin.Y = E->Bot.Y;
  947. locMin.LeftBound = 0;
  948. locMin.RightBound = E;
  949. E->WindDelta = 0;
  950. Result = ProcessBound( E, NextIsForward );
  951. m_MinimaList.push_back( locMin );
  952. }
  953. return Result;
  954. }
  955. TEdge* EStart;
  956. if( IsHorizontal( *E ) )
  957. {
  958. // We need to be careful with open paths because this may not be a
  959. // true local minima (ie E may be following a skip edge).
  960. // Also, consecutive horz. edges may start heading left before going right.
  961. if( NextIsForward )
  962. EStart = E->Prev;
  963. else
  964. EStart = E->Next;
  965. if( IsHorizontal( *EStart ) ) // ie an adjoining horizontal skip edge
  966. {
  967. if( EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X )
  968. ReverseHorizontal( *E );
  969. }
  970. else if( EStart->Bot.X != E->Bot.X )
  971. ReverseHorizontal( *E );
  972. }
  973. EStart = E;
  974. if( NextIsForward )
  975. {
  976. while( Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip )
  977. Result = Result->Next;
  978. if( IsHorizontal( *Result ) && Result->Next->OutIdx != Skip )
  979. {
  980. // nb: at the top of a bound, horizontals are added to the bound
  981. // only when the preceding edge attaches to the horizontal's left vertex
  982. // unless a Skip edge is encountered when that becomes the top divide
  983. Horz = Result;
  984. while( IsHorizontal( *Horz->Prev ) )
  985. Horz = Horz->Prev;
  986. if( Horz->Prev->Top.X > Result->Next->Top.X )
  987. Result = Horz->Prev;
  988. }
  989. while( E != Result )
  990. {
  991. E->NextInLML = E->Next;
  992. if( IsHorizontal( *E ) && E != EStart
  993. && E->Bot.X != E->Prev->Top.X )
  994. ReverseHorizontal( *E );
  995. E = E->Next;
  996. }
  997. if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Prev->Top.X )
  998. ReverseHorizontal( *E );
  999. Result = Result->Next; // move to the edge just beyond current bound
  1000. }
  1001. else
  1002. {
  1003. while( Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip )
  1004. Result = Result->Prev;
  1005. if( IsHorizontal( *Result ) && Result->Prev->OutIdx != Skip )
  1006. {
  1007. Horz = Result;
  1008. while( IsHorizontal( *Horz->Next ) )
  1009. Horz = Horz->Next;
  1010. if( Horz->Next->Top.X == Result->Prev->Top.X
  1011. || Horz->Next->Top.X > Result->Prev->Top.X )
  1012. Result = Horz->Next;
  1013. }
  1014. while( E != Result )
  1015. {
  1016. E->NextInLML = E->Prev;
  1017. if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Next->Top.X )
  1018. ReverseHorizontal( *E );
  1019. E = E->Prev;
  1020. }
  1021. if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Next->Top.X )
  1022. ReverseHorizontal( *E );
  1023. Result = Result->Prev; // move to the edge just beyond current bound
  1024. }
  1025. return Result;
  1026. }
  1027. // ------------------------------------------------------------------------------
  1028. bool ClipperBase::AddPath( const Path& pg, PolyType PolyTyp, bool Closed )
  1029. {
  1030. #ifdef use_lines
  1031. if( !Closed && PolyTyp == ptClip )
  1032. throw clipperException( "AddPath: Open paths must be subject." );
  1033. #else
  1034. if( !Closed )
  1035. throw clipperException( "AddPath: Open paths have been disabled." );
  1036. #endif
  1037. int highI = (int) pg.size() - 1;
  1038. if( Closed )
  1039. while( highI > 0 && (pg[highI] == pg[0]) )
  1040. --highI;
  1041. while( highI > 0 && (pg[highI] == pg[highI - 1]) )
  1042. --highI;
  1043. if( (Closed && highI < 2) || (!Closed && highI < 1) )
  1044. return false;
  1045. // create a new edge array ...
  1046. TEdge* edges = new TEdge[highI + 1];
  1047. bool IsFlat = true;
  1048. // 1. Basic (first) edge initialization ...
  1049. try
  1050. {
  1051. edges[1].Curr = pg[1];
  1052. RangeTest( pg[0], m_UseFullRange );
  1053. RangeTest( pg[highI], m_UseFullRange );
  1054. InitEdge( &edges[0], &edges[1], &edges[highI], pg[0] );
  1055. InitEdge( &edges[highI], &edges[0], &edges[highI - 1], pg[highI] );
  1056. for( int i = highI - 1; i >= 1; --i )
  1057. {
  1058. RangeTest( pg[i], m_UseFullRange );
  1059. InitEdge( &edges[i], &edges[i + 1], &edges[i - 1], pg[i] );
  1060. }
  1061. }
  1062. catch( ... )
  1063. {
  1064. delete [] edges;
  1065. throw; // range test fails
  1066. }
  1067. TEdge* eStart = &edges[0];
  1068. // 2. Remove duplicate vertices, and (when closed) collinear edges ...
  1069. TEdge* E = eStart, * eLoopStop = eStart;
  1070. for( ; ; )
  1071. {
  1072. // nb: allows matching start and end points when not Closed ...
  1073. if( E->Curr == E->Next->Curr && (Closed || E->Next != eStart) )
  1074. {
  1075. if( E == E->Next )
  1076. break;
  1077. if( E == eStart )
  1078. eStart = E->Next;
  1079. E = RemoveEdge( E );
  1080. eLoopStop = E;
  1081. continue;
  1082. }
  1083. if( E->Prev == E->Next )
  1084. break; // only two vertices
  1085. else if( Closed
  1086. && SlopesEqual( E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange )
  1087. && ( !m_PreserveCollinear
  1088. || !Pt2IsBetweenPt1AndPt3( E->Prev->Curr, E->Curr, E->Next->Curr ) ) )
  1089. {
  1090. // Collinear edges are allowed for open paths but in closed paths
  1091. // the default is to merge adjacent collinear edges into a single edge.
  1092. // However, if the PreserveCollinear property is enabled, only overlapping
  1093. // collinear edges (ie spikes) will be removed from closed paths.
  1094. if( E == eStart )
  1095. eStart = E->Next;
  1096. E = RemoveEdge( E );
  1097. E = E->Prev;
  1098. eLoopStop = E;
  1099. continue;
  1100. }
  1101. E = E->Next;
  1102. if( (E == eLoopStop) || (!Closed && E->Next == eStart) )
  1103. break;
  1104. }
  1105. if( ( !Closed && (E == E->Next) ) || ( Closed && (E->Prev == E->Next) ) )
  1106. {
  1107. delete [] edges;
  1108. return false;
  1109. }
  1110. if( !Closed )
  1111. {
  1112. m_HasOpenPaths = true;
  1113. eStart->Prev->OutIdx = Skip;
  1114. }
  1115. // 3. Do second stage of edge initialization ...
  1116. E = eStart;
  1117. do
  1118. {
  1119. InitEdge2( *E, PolyTyp );
  1120. E = E->Next;
  1121. if( IsFlat && E->Curr.Y != eStart->Curr.Y )
  1122. IsFlat = false;
  1123. } while( E != eStart );
  1124. // 4. Finally, add edge bounds to LocalMinima list ...
  1125. // Totally flat paths must be handled differently when adding them
  1126. // to LocalMinima list to avoid endless loops etc ...
  1127. if( IsFlat )
  1128. {
  1129. if( Closed )
  1130. {
  1131. delete [] edges;
  1132. return false;
  1133. }
  1134. E->Prev->OutIdx = Skip;
  1135. MinimaList::value_type locMin;
  1136. locMin.Y = E->Bot.Y;
  1137. locMin.LeftBound = 0;
  1138. locMin.RightBound = E;
  1139. locMin.RightBound->Side = esRight;
  1140. locMin.RightBound->WindDelta = 0;
  1141. for( ; ; )
  1142. {
  1143. if( E->Bot.X != E->Prev->Top.X )
  1144. ReverseHorizontal( *E );
  1145. if( E->Next->OutIdx == Skip )
  1146. break;
  1147. E->NextInLML = E->Next;
  1148. E = E->Next;
  1149. }
  1150. m_MinimaList.push_back( locMin );
  1151. m_edges.push_back( edges );
  1152. return true;
  1153. }
  1154. m_edges.push_back( edges );
  1155. bool leftBoundIsForward;
  1156. TEdge* EMin = 0;
  1157. // workaround to avoid an endless loop in the while loop below when
  1158. // open paths have matching start and end points ...
  1159. if( E->Prev->Bot == E->Prev->Top )
  1160. E = E->Next;
  1161. for( ; ; )
  1162. {
  1163. E = FindNextLocMin( E );
  1164. if( E == EMin )
  1165. break;
  1166. else if( !EMin )
  1167. EMin = E;
  1168. // E and E.Prev now share a local minima (left aligned if horizontal).
  1169. // Compare their slopes to find which starts which bound ...
  1170. MinimaList::value_type locMin;
  1171. locMin.Y = E->Bot.Y;
  1172. if( E->Dx < E->Prev->Dx )
  1173. {
  1174. locMin.LeftBound = E->Prev;
  1175. locMin.RightBound = E;
  1176. leftBoundIsForward = false; // Q.nextInLML = Q.prev
  1177. }
  1178. else
  1179. {
  1180. locMin.LeftBound = E;
  1181. locMin.RightBound = E->Prev;
  1182. leftBoundIsForward = true; // Q.nextInLML = Q.next
  1183. }
  1184. if( !Closed )
  1185. locMin.LeftBound->WindDelta = 0;
  1186. else if( locMin.LeftBound->Next == locMin.RightBound )
  1187. locMin.LeftBound->WindDelta = -1;
  1188. else
  1189. locMin.LeftBound->WindDelta = 1;
  1190. locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta;
  1191. E = ProcessBound( locMin.LeftBound, leftBoundIsForward );
  1192. if( E->OutIdx == Skip )
  1193. E = ProcessBound( E, leftBoundIsForward );
  1194. TEdge* E2 = ProcessBound( locMin.RightBound, !leftBoundIsForward );
  1195. if( E2->OutIdx == Skip )
  1196. E2 = ProcessBound( E2, !leftBoundIsForward );
  1197. if( locMin.LeftBound->OutIdx == Skip )
  1198. locMin.LeftBound = 0;
  1199. else if( locMin.RightBound->OutIdx == Skip )
  1200. locMin.RightBound = 0;
  1201. m_MinimaList.push_back( locMin );
  1202. if( !leftBoundIsForward )
  1203. E = E2;
  1204. }
  1205. return true;
  1206. }
  1207. // ------------------------------------------------------------------------------
  1208. bool ClipperBase::AddPaths( const Paths& ppg, PolyType PolyTyp, bool Closed )
  1209. {
  1210. bool result = false;
  1211. for( Paths::size_type i = 0; i < ppg.size(); ++i )
  1212. if( AddPath( ppg[i], PolyTyp, Closed ) )
  1213. result = true;
  1214. return result;
  1215. }
  1216. // ------------------------------------------------------------------------------
  1217. void ClipperBase::Clear()
  1218. {
  1219. DisposeLocalMinimaList();
  1220. for( EdgeList::size_type i = 0; i < m_edges.size(); ++i )
  1221. {
  1222. TEdge* edges = m_edges[i];
  1223. delete [] edges;
  1224. }
  1225. m_edges.clear();
  1226. m_UseFullRange = false;
  1227. m_HasOpenPaths = false;
  1228. }
  1229. // ------------------------------------------------------------------------------
  1230. void ClipperBase::Reset()
  1231. {
  1232. m_CurrentLM = m_MinimaList.begin();
  1233. if( m_CurrentLM == m_MinimaList.end() )
  1234. return; // ie nothing to process
  1235. std::sort( m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter() );
  1236. m_Scanbeam = ScanbeamList(); // clears/resets priority_queue
  1237. // reset all edges ...
  1238. for( MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm )
  1239. {
  1240. InsertScanbeam( lm->Y );
  1241. TEdge* e = lm->LeftBound;
  1242. if( e )
  1243. {
  1244. e->Curr = e->Bot;
  1245. e->Side = esLeft;
  1246. e->OutIdx = Unassigned;
  1247. }
  1248. e = lm->RightBound;
  1249. if( e )
  1250. {
  1251. e->Curr = e->Bot;
  1252. e->Side = esRight;
  1253. e->OutIdx = Unassigned;
  1254. }
  1255. }
  1256. m_ActiveEdges = 0;
  1257. m_CurrentLM = m_MinimaList.begin();
  1258. }
  1259. // ------------------------------------------------------------------------------
  1260. void ClipperBase::DisposeLocalMinimaList()
  1261. {
  1262. m_MinimaList.clear();
  1263. m_CurrentLM = m_MinimaList.begin();
  1264. }
  1265. // ------------------------------------------------------------------------------
  1266. bool ClipperBase::PopLocalMinima( cInt Y, const LocalMinimum*& locMin )
  1267. {
  1268. if( m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y )
  1269. return false;
  1270. locMin = &(*m_CurrentLM);
  1271. ++m_CurrentLM;
  1272. return true;
  1273. }
  1274. // ------------------------------------------------------------------------------
  1275. IntRect ClipperBase::GetBounds()
  1276. {
  1277. IntRect result;
  1278. MinimaList::iterator lm = m_MinimaList.begin();
  1279. if( lm == m_MinimaList.end() )
  1280. {
  1281. result.left = result.top = result.right = result.bottom = 0;
  1282. return result;
  1283. }
  1284. result.left = lm->LeftBound->Bot.X;
  1285. result.top = lm->LeftBound->Bot.Y;
  1286. result.right = lm->LeftBound->Bot.X;
  1287. result.bottom = lm->LeftBound->Bot.Y;
  1288. while( lm != m_MinimaList.end() )
  1289. {
  1290. // todo - needs fixing for open paths
  1291. result.bottom = std::max( result.bottom, lm->LeftBound->Bot.Y );
  1292. TEdge* e = lm->LeftBound;
  1293. for( ; ; )
  1294. {
  1295. TEdge* bottomE = e;
  1296. while( e->NextInLML )
  1297. {
  1298. if( e->Bot.X < result.left )
  1299. result.left = e->Bot.X;
  1300. if( e->Bot.X > result.right )
  1301. result.right = e->Bot.X;
  1302. e = e->NextInLML;
  1303. }
  1304. result.left = std::min( result.left, e->Bot.X );
  1305. result.right = std::max( result.right, e->Bot.X );
  1306. result.left = std::min( result.left, e->Top.X );
  1307. result.right = std::max( result.right, e->Top.X );
  1308. result.top = std::min( result.top, e->Top.Y );
  1309. if( bottomE == lm->LeftBound )
  1310. e = lm->RightBound;
  1311. else
  1312. break;
  1313. }
  1314. ++lm;
  1315. }
  1316. return result;
  1317. }
  1318. // ------------------------------------------------------------------------------
  1319. void ClipperBase::InsertScanbeam( const cInt Y )
  1320. {
  1321. m_Scanbeam.push( Y );
  1322. }
  1323. // ------------------------------------------------------------------------------
  1324. bool ClipperBase::PopScanbeam( cInt& Y )
  1325. {
  1326. if( m_Scanbeam.empty() )
  1327. return false;
  1328. Y = m_Scanbeam.top();
  1329. m_Scanbeam.pop();
  1330. while( !m_Scanbeam.empty() && Y == m_Scanbeam.top() )
  1331. {
  1332. m_Scanbeam.pop();
  1333. } // Pop duplicates.
  1334. return true;
  1335. }
  1336. // ------------------------------------------------------------------------------
  1337. void ClipperBase::DisposeAllOutRecs()
  1338. {
  1339. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  1340. DisposeOutRec( i );
  1341. m_PolyOuts.clear();
  1342. }
  1343. // ------------------------------------------------------------------------------
  1344. void ClipperBase::DisposeOutRec( PolyOutList::size_type index )
  1345. {
  1346. OutRec* outRec = m_PolyOuts[index];
  1347. if( outRec->Pts )
  1348. DisposeOutPts( outRec->Pts );
  1349. delete outRec;
  1350. m_PolyOuts[index] = 0;
  1351. }
  1352. // ------------------------------------------------------------------------------
  1353. void ClipperBase::DeleteFromAEL( TEdge* e )
  1354. {
  1355. TEdge* AelPrev = e->PrevInAEL;
  1356. TEdge* AelNext = e->NextInAEL;
  1357. if( !AelPrev && !AelNext && (e != m_ActiveEdges) )
  1358. return; // already deleted
  1359. if( AelPrev )
  1360. AelPrev->NextInAEL = AelNext;
  1361. else
  1362. m_ActiveEdges = AelNext;
  1363. if( AelNext )
  1364. AelNext->PrevInAEL = AelPrev;
  1365. e->NextInAEL = 0;
  1366. e->PrevInAEL = 0;
  1367. }
  1368. // ------------------------------------------------------------------------------
  1369. OutRec* ClipperBase::CreateOutRec()
  1370. {
  1371. OutRec* result = new OutRec;
  1372. result->IsHole = false;
  1373. result->IsOpen = false;
  1374. result->FirstLeft = 0;
  1375. result->Pts = 0;
  1376. result->BottomPt = 0;
  1377. result->PolyNd = 0;
  1378. m_PolyOuts.push_back( result );
  1379. result->Idx = (int) m_PolyOuts.size() - 1;
  1380. return result;
  1381. }
  1382. // ------------------------------------------------------------------------------
  1383. void ClipperBase::SwapPositionsInAEL( TEdge* Edge1, TEdge* Edge2 )
  1384. {
  1385. // check that one or other edge hasn't already been removed from AEL ...
  1386. if( Edge1->NextInAEL == Edge1->PrevInAEL
  1387. || Edge2->NextInAEL == Edge2->PrevInAEL )
  1388. return;
  1389. if( Edge1->NextInAEL == Edge2 )
  1390. {
  1391. TEdge* Next = Edge2->NextInAEL;
  1392. if( Next )
  1393. Next->PrevInAEL = Edge1;
  1394. TEdge* Prev = Edge1->PrevInAEL;
  1395. if( Prev )
  1396. Prev->NextInAEL = Edge2;
  1397. Edge2->PrevInAEL = Prev;
  1398. Edge2->NextInAEL = Edge1;
  1399. Edge1->PrevInAEL = Edge2;
  1400. Edge1->NextInAEL = Next;
  1401. }
  1402. else if( Edge2->NextInAEL == Edge1 )
  1403. {
  1404. TEdge* Next = Edge1->NextInAEL;
  1405. if( Next )
  1406. Next->PrevInAEL = Edge2;
  1407. TEdge* Prev = Edge2->PrevInAEL;
  1408. if( Prev )
  1409. Prev->NextInAEL = Edge1;
  1410. Edge1->PrevInAEL = Prev;
  1411. Edge1->NextInAEL = Edge2;
  1412. Edge2->PrevInAEL = Edge1;
  1413. Edge2->NextInAEL = Next;
  1414. }
  1415. else
  1416. {
  1417. TEdge* Next = Edge1->NextInAEL;
  1418. TEdge* Prev = Edge1->PrevInAEL;
  1419. Edge1->NextInAEL = Edge2->NextInAEL;
  1420. if( Edge1->NextInAEL )
  1421. Edge1->NextInAEL->PrevInAEL = Edge1;
  1422. Edge1->PrevInAEL = Edge2->PrevInAEL;
  1423. if( Edge1->PrevInAEL )
  1424. Edge1->PrevInAEL->NextInAEL = Edge1;
  1425. Edge2->NextInAEL = Next;
  1426. if( Edge2->NextInAEL )
  1427. Edge2->NextInAEL->PrevInAEL = Edge2;
  1428. Edge2->PrevInAEL = Prev;
  1429. if( Edge2->PrevInAEL )
  1430. Edge2->PrevInAEL->NextInAEL = Edge2;
  1431. }
  1432. if( !Edge1->PrevInAEL )
  1433. m_ActiveEdges = Edge1;
  1434. else if( !Edge2->PrevInAEL )
  1435. m_ActiveEdges = Edge2;
  1436. }
  1437. // ------------------------------------------------------------------------------
  1438. void ClipperBase::UpdateEdgeIntoAEL( TEdge*& e )
  1439. {
  1440. if( !e->NextInLML )
  1441. throw clipperException( "UpdateEdgeIntoAEL: invalid call" );
  1442. e->NextInLML->OutIdx = e->OutIdx;
  1443. TEdge* AelPrev = e->PrevInAEL;
  1444. TEdge* AelNext = e->NextInAEL;
  1445. if( AelPrev )
  1446. AelPrev->NextInAEL = e->NextInLML;
  1447. else
  1448. m_ActiveEdges = e->NextInLML;
  1449. if( AelNext )
  1450. AelNext->PrevInAEL = e->NextInLML;
  1451. e->NextInLML->Side = e->Side;
  1452. e->NextInLML->WindDelta = e->WindDelta;
  1453. e->NextInLML->WindCnt = e->WindCnt;
  1454. e->NextInLML->WindCnt2 = e->WindCnt2;
  1455. e = e->NextInLML;
  1456. e->Curr = e->Bot;
  1457. e->PrevInAEL = AelPrev;
  1458. e->NextInAEL = AelNext;
  1459. if( !IsHorizontal( *e ) )
  1460. InsertScanbeam( e->Top.Y );
  1461. }
  1462. // ------------------------------------------------------------------------------
  1463. bool ClipperBase::LocalMinimaPending()
  1464. {
  1465. return m_CurrentLM != m_MinimaList.end();
  1466. }
  1467. // ------------------------------------------------------------------------------
  1468. // TClipper methods ...
  1469. // ------------------------------------------------------------------------------
  1470. Clipper::Clipper( int initOptions ) : ClipperBase() // constructor
  1471. {
  1472. m_ExecuteLocked = false;
  1473. m_UseFullRange = false;
  1474. m_ReverseOutput = ( (initOptions & ioReverseSolution) != 0 );
  1475. m_StrictSimple = ( (initOptions & ioStrictlySimple) != 0 );
  1476. m_PreserveCollinear = ( (initOptions & ioPreserveCollinear) != 0 );
  1477. m_HasOpenPaths = false;
  1478. #ifdef use_xyz
  1479. m_ZFill = 0;
  1480. #endif
  1481. }
  1482. // ------------------------------------------------------------------------------
  1483. #ifdef use_xyz
  1484. void Clipper::ZFillFunction( ZFillCallback zFillFunc )
  1485. {
  1486. m_ZFill = zFillFunc;
  1487. }
  1488. // ------------------------------------------------------------------------------
  1489. #endif
  1490. bool Clipper::Execute( ClipType clipType, Paths& solution, PolyFillType fillType )
  1491. {
  1492. return Execute( clipType, solution, fillType, fillType );
  1493. }
  1494. // ------------------------------------------------------------------------------
  1495. bool Clipper::Execute( ClipType clipType, PolyTree& polytree, PolyFillType fillType )
  1496. {
  1497. return Execute( clipType, polytree, fillType, fillType );
  1498. }
  1499. // ------------------------------------------------------------------------------
  1500. bool Clipper::Execute( ClipType clipType, Paths& solution,
  1501. PolyFillType subjFillType, PolyFillType clipFillType )
  1502. {
  1503. if( m_ExecuteLocked )
  1504. return false;
  1505. if( m_HasOpenPaths )
  1506. throw clipperException( "Error: PolyTree struct is needed for open path clipping." );
  1507. m_ExecuteLocked = true;
  1508. solution.resize( 0 );
  1509. m_SubjFillType = subjFillType;
  1510. m_ClipFillType = clipFillType;
  1511. m_ClipType = clipType;
  1512. m_UsingPolyTree = false;
  1513. bool succeeded = ExecuteInternal();
  1514. if( succeeded )
  1515. BuildResult( solution );
  1516. DisposeAllOutRecs();
  1517. m_ExecuteLocked = false;
  1518. return succeeded;
  1519. }
  1520. // ------------------------------------------------------------------------------
  1521. bool Clipper::Execute( ClipType clipType, PolyTree& polytree,
  1522. PolyFillType subjFillType, PolyFillType clipFillType )
  1523. {
  1524. if( m_ExecuteLocked )
  1525. return false;
  1526. m_ExecuteLocked = true;
  1527. m_SubjFillType = subjFillType;
  1528. m_ClipFillType = clipFillType;
  1529. m_ClipType = clipType;
  1530. m_UsingPolyTree = true;
  1531. bool succeeded = ExecuteInternal();
  1532. if( succeeded )
  1533. BuildResult2( polytree );
  1534. DisposeAllOutRecs();
  1535. m_ExecuteLocked = false;
  1536. return succeeded;
  1537. }
  1538. // ------------------------------------------------------------------------------
  1539. void Clipper::FixHoleLinkage( OutRec& outrec )
  1540. {
  1541. // skip OutRecs that (a) contain outermost polygons or
  1542. // (b) already have the correct owner/child linkage ...
  1543. if( !outrec.FirstLeft
  1544. || (outrec.IsHole != outrec.FirstLeft->IsHole
  1545. && outrec.FirstLeft->Pts) )
  1546. return;
  1547. OutRec* orfl = outrec.FirstLeft;
  1548. while( orfl && ( (orfl->IsHole == outrec.IsHole) || !orfl->Pts ) )
  1549. orfl = orfl->FirstLeft;
  1550. outrec.FirstLeft = orfl;
  1551. }
  1552. // ------------------------------------------------------------------------------
  1553. bool Clipper::ExecuteInternal()
  1554. {
  1555. bool succeeded = true;
  1556. try
  1557. {
  1558. Reset();
  1559. m_Maxima = MaximaList();
  1560. m_SortedEdges = 0;
  1561. succeeded = true;
  1562. cInt botY, topY;
  1563. if( !PopScanbeam( botY ) )
  1564. return false;
  1565. InsertLocalMinimaIntoAEL( botY );
  1566. while( PopScanbeam( topY ) || LocalMinimaPending() )
  1567. {
  1568. ProcessHorizontals();
  1569. ClearGhostJoins();
  1570. if( !ProcessIntersections( topY ) )
  1571. {
  1572. succeeded = false;
  1573. break;
  1574. }
  1575. ProcessEdgesAtTopOfScanbeam( topY );
  1576. botY = topY;
  1577. InsertLocalMinimaIntoAEL( botY );
  1578. }
  1579. }
  1580. catch( ... )
  1581. {
  1582. succeeded = false;
  1583. }
  1584. if( succeeded )
  1585. {
  1586. // fix orientations ...
  1587. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  1588. {
  1589. OutRec* outRec = m_PolyOuts[i];
  1590. if( !outRec->Pts || outRec->IsOpen )
  1591. continue;
  1592. if( (outRec->IsHole ^ m_ReverseOutput) == (Area( *outRec ) > 0) )
  1593. ReversePolyPtLinks( outRec->Pts );
  1594. }
  1595. if( !m_Joins.empty() )
  1596. JoinCommonEdges();
  1597. // unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
  1598. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  1599. {
  1600. OutRec* outRec = m_PolyOuts[i];
  1601. if( !outRec->Pts )
  1602. continue;
  1603. if( outRec->IsOpen )
  1604. FixupOutPolyline( *outRec );
  1605. else
  1606. FixupOutPolygon( *outRec );
  1607. }
  1608. if( m_StrictSimple )
  1609. DoSimplePolygons();
  1610. }
  1611. ClearJoins();
  1612. ClearGhostJoins();
  1613. return succeeded;
  1614. }
  1615. // ------------------------------------------------------------------------------
  1616. void Clipper::SetWindingCount( TEdge& edge )
  1617. {
  1618. TEdge* e = edge.PrevInAEL;
  1619. // find the edge of the same polytype that immediately preceeds 'edge' in AEL
  1620. while( e && ( (e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0) ) )
  1621. e = e->PrevInAEL;
  1622. if( !e )
  1623. {
  1624. if( edge.WindDelta == 0 )
  1625. {
  1626. PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType);
  1627. edge.WindCnt = (pft == pftNegative ? -1 : 1);
  1628. }
  1629. else
  1630. edge.WindCnt = edge.WindDelta;
  1631. edge.WindCnt2 = 0;
  1632. e = m_ActiveEdges; // ie get ready to calc WindCnt2
  1633. }
  1634. else if( edge.WindDelta == 0 && m_ClipType != ctUnion )
  1635. {
  1636. edge.WindCnt = 1;
  1637. edge.WindCnt2 = e->WindCnt2;
  1638. e = e->NextInAEL; // ie get ready to calc WindCnt2
  1639. }
  1640. else if( IsEvenOddFillType( edge ) )
  1641. {
  1642. // EvenOdd filling ...
  1643. if( edge.WindDelta == 0 )
  1644. {
  1645. // are we inside a subj polygon ...
  1646. bool Inside = true;
  1647. TEdge* e2 = e->PrevInAEL;
  1648. while( e2 )
  1649. {
  1650. if( e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0 )
  1651. Inside = !Inside;
  1652. e2 = e2->PrevInAEL;
  1653. }
  1654. edge.WindCnt = (Inside ? 0 : 1);
  1655. }
  1656. else
  1657. {
  1658. edge.WindCnt = edge.WindDelta;
  1659. }
  1660. edge.WindCnt2 = e->WindCnt2;
  1661. e = e->NextInAEL; // ie get ready to calc WindCnt2
  1662. }
  1663. else
  1664. {
  1665. // nonZero, Positive or Negative filling ...
  1666. if( e->WindCnt * e->WindDelta < 0 )
  1667. {
  1668. // prev edge is 'decreasing' WindCount (WC) toward zero
  1669. // so we're outside the previous polygon ...
  1670. if( Abs( e->WindCnt ) > 1 )
  1671. {
  1672. // outside prev poly but still inside another.
  1673. // when reversing direction of prev poly use the same WC
  1674. if( e->WindDelta * edge.WindDelta < 0 )
  1675. edge.WindCnt = e->WindCnt;
  1676. // otherwise continue to 'decrease' WC ...
  1677. else
  1678. edge.WindCnt = e->WindCnt + edge.WindDelta;
  1679. }
  1680. else
  1681. // now outside all polys of same polytype so set own WC ...
  1682. edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
  1683. }
  1684. else
  1685. {
  1686. // prev edge is 'increasing' WindCount (WC) away from zero
  1687. // so we're inside the previous polygon ...
  1688. if( edge.WindDelta == 0 )
  1689. edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1);
  1690. // if wind direction is reversing prev then use same WC
  1691. else if( e->WindDelta * edge.WindDelta < 0 )
  1692. edge.WindCnt = e->WindCnt;
  1693. // otherwise add to WC ...
  1694. else
  1695. edge.WindCnt = e->WindCnt + edge.WindDelta;
  1696. }
  1697. edge.WindCnt2 = e->WindCnt2;
  1698. e = e->NextInAEL; // ie get ready to calc WindCnt2
  1699. }
  1700. // update WindCnt2 ...
  1701. if( IsEvenOddAltFillType( edge ) )
  1702. {
  1703. // EvenOdd filling ...
  1704. while( e != &edge )
  1705. {
  1706. if( e->WindDelta != 0 )
  1707. edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
  1708. e = e->NextInAEL;
  1709. }
  1710. }
  1711. else
  1712. {
  1713. // nonZero, Positive or Negative filling ...
  1714. while( e != &edge )
  1715. {
  1716. edge.WindCnt2 += e->WindDelta;
  1717. e = e->NextInAEL;
  1718. }
  1719. }
  1720. }
  1721. // ------------------------------------------------------------------------------
  1722. bool Clipper::IsEvenOddFillType( const TEdge& edge ) const
  1723. {
  1724. if( edge.PolyTyp == ptSubject )
  1725. return m_SubjFillType == pftEvenOdd;
  1726. else
  1727. return m_ClipFillType == pftEvenOdd;
  1728. }
  1729. // ------------------------------------------------------------------------------
  1730. bool Clipper::IsEvenOddAltFillType( const TEdge& edge ) const
  1731. {
  1732. if( edge.PolyTyp == ptSubject )
  1733. return m_ClipFillType == pftEvenOdd;
  1734. else
  1735. return m_SubjFillType == pftEvenOdd;
  1736. }
  1737. // ------------------------------------------------------------------------------
  1738. bool Clipper::IsContributing( const TEdge& edge ) const
  1739. {
  1740. PolyFillType pft, pft2;
  1741. if( edge.PolyTyp == ptSubject )
  1742. {
  1743. pft = m_SubjFillType;
  1744. pft2 = m_ClipFillType;
  1745. }
  1746. else
  1747. {
  1748. pft = m_ClipFillType;
  1749. pft2 = m_SubjFillType;
  1750. }
  1751. switch( pft )
  1752. {
  1753. case pftEvenOdd:
  1754. // return false if a subj line has been flagged as inside a subj polygon
  1755. if( edge.WindDelta == 0 && edge.WindCnt != 1 )
  1756. return false;
  1757. break;
  1758. case pftNonZero:
  1759. if( Abs( edge.WindCnt ) != 1 )
  1760. return false;
  1761. break;
  1762. case pftPositive:
  1763. if( edge.WindCnt != 1 )
  1764. return false;
  1765. break;
  1766. default: // pftNegative
  1767. if( edge.WindCnt != -1 )
  1768. return false;
  1769. }
  1770. switch( m_ClipType )
  1771. {
  1772. case ctIntersection:
  1773. switch( pft2 )
  1774. {
  1775. case pftEvenOdd:
  1776. case pftNonZero:
  1777. return edge.WindCnt2 != 0;
  1778. case pftPositive:
  1779. return edge.WindCnt2 > 0;
  1780. default:
  1781. return edge.WindCnt2 < 0;
  1782. }
  1783. break;
  1784. case ctUnion:
  1785. switch( pft2 )
  1786. {
  1787. case pftEvenOdd:
  1788. case pftNonZero:
  1789. return edge.WindCnt2 == 0;
  1790. case pftPositive:
  1791. return edge.WindCnt2 <= 0;
  1792. default:
  1793. return edge.WindCnt2 >= 0;
  1794. }
  1795. break;
  1796. case ctDifference:
  1797. if( edge.PolyTyp == ptSubject )
  1798. switch( pft2 )
  1799. {
  1800. case pftEvenOdd:
  1801. case pftNonZero:
  1802. return edge.WindCnt2 == 0;
  1803. case pftPositive:
  1804. return edge.WindCnt2 <= 0;
  1805. default:
  1806. return edge.WindCnt2 >= 0;
  1807. }
  1808. else
  1809. switch( pft2 )
  1810. {
  1811. case pftEvenOdd:
  1812. case pftNonZero:
  1813. return edge.WindCnt2 != 0;
  1814. case pftPositive:
  1815. return edge.WindCnt2 > 0;
  1816. default:
  1817. return edge.WindCnt2 < 0;
  1818. }
  1819. break;
  1820. case ctXor:
  1821. if( edge.WindDelta == 0 ) // XOr always contributing unless open
  1822. switch( pft2 )
  1823. {
  1824. case pftEvenOdd:
  1825. case pftNonZero:
  1826. return edge.WindCnt2 == 0;
  1827. case pftPositive:
  1828. return edge.WindCnt2 <= 0;
  1829. default:
  1830. return edge.WindCnt2 >= 0;
  1831. }
  1832. else
  1833. return true;
  1834. break;
  1835. default:
  1836. return true;
  1837. }
  1838. }
  1839. // ------------------------------------------------------------------------------
  1840. OutPt* Clipper::AddLocalMinPoly( TEdge* e1, TEdge* e2, const IntPoint& Pt )
  1841. {
  1842. OutPt* result;
  1843. TEdge* e, * prevE;
  1844. if( IsHorizontal( *e2 ) || ( e1->Dx > e2->Dx ) )
  1845. {
  1846. result = AddOutPt( e1, Pt );
  1847. e2->OutIdx = e1->OutIdx;
  1848. e1->Side = esLeft;
  1849. e2->Side = esRight;
  1850. e = e1;
  1851. if( e->PrevInAEL == e2 )
  1852. prevE = e2->PrevInAEL;
  1853. else
  1854. prevE = e->PrevInAEL;
  1855. }
  1856. else
  1857. {
  1858. result = AddOutPt( e2, Pt );
  1859. e1->OutIdx = e2->OutIdx;
  1860. e1->Side = esRight;
  1861. e2->Side = esLeft;
  1862. e = e2;
  1863. if( e->PrevInAEL == e1 )
  1864. prevE = e1->PrevInAEL;
  1865. else
  1866. prevE = e->PrevInAEL;
  1867. }
  1868. if( prevE && prevE->OutIdx >= 0 && prevE->Top.Y < Pt.Y && e->Top.Y < Pt.Y )
  1869. {
  1870. cInt xPrev = TopX( *prevE, Pt.Y );
  1871. cInt xE = TopX( *e, Pt.Y );
  1872. if( xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0)
  1873. && SlopesEqual( IntPoint( xPrev, Pt.Y ), prevE->Top, IntPoint( xE, Pt.Y ), e->Top,
  1874. m_UseFullRange ) )
  1875. {
  1876. OutPt* outPt = AddOutPt( prevE, Pt );
  1877. AddJoin( result, outPt, e->Top );
  1878. }
  1879. }
  1880. return result;
  1881. }
  1882. // ------------------------------------------------------------------------------
  1883. void Clipper::AddLocalMaxPoly( TEdge* e1, TEdge* e2, const IntPoint& Pt )
  1884. {
  1885. AddOutPt( e1, Pt );
  1886. if( e2->WindDelta == 0 )
  1887. AddOutPt( e2, Pt );
  1888. if( e1->OutIdx == e2->OutIdx )
  1889. {
  1890. e1->OutIdx = Unassigned;
  1891. e2->OutIdx = Unassigned;
  1892. }
  1893. else if( e1->OutIdx < e2->OutIdx )
  1894. AppendPolygon( e1, e2 );
  1895. else
  1896. AppendPolygon( e2, e1 );
  1897. }
  1898. // ------------------------------------------------------------------------------
  1899. void Clipper::AddEdgeToSEL( TEdge* edge )
  1900. {
  1901. // SEL pointers in PEdge are reused to build a list of horizontal edges.
  1902. // However, we don't need to worry about order with horizontal edge processing.
  1903. if( !m_SortedEdges )
  1904. {
  1905. m_SortedEdges = edge;
  1906. edge->PrevInSEL = 0;
  1907. edge->NextInSEL = 0;
  1908. }
  1909. else
  1910. {
  1911. edge->NextInSEL = m_SortedEdges;
  1912. edge->PrevInSEL = 0;
  1913. m_SortedEdges->PrevInSEL = edge;
  1914. m_SortedEdges = edge;
  1915. }
  1916. }
  1917. // ------------------------------------------------------------------------------
  1918. bool Clipper::PopEdgeFromSEL( TEdge*& edge )
  1919. {
  1920. if( !m_SortedEdges )
  1921. return false;
  1922. edge = m_SortedEdges;
  1923. DeleteFromSEL( m_SortedEdges );
  1924. return true;
  1925. }
  1926. // ------------------------------------------------------------------------------
  1927. void Clipper::CopyAELToSEL()
  1928. {
  1929. TEdge* e = m_ActiveEdges;
  1930. m_SortedEdges = e;
  1931. while( e )
  1932. {
  1933. e->PrevInSEL = e->PrevInAEL;
  1934. e->NextInSEL = e->NextInAEL;
  1935. e = e->NextInAEL;
  1936. }
  1937. }
  1938. // ------------------------------------------------------------------------------
  1939. void Clipper::AddJoin( OutPt* op1, OutPt* op2, const IntPoint OffPt )
  1940. {
  1941. Join* j = new Join;
  1942. j->OutPt1 = op1;
  1943. j->OutPt2 = op2;
  1944. j->OffPt = OffPt;
  1945. m_Joins.push_back( j );
  1946. }
  1947. // ------------------------------------------------------------------------------
  1948. void Clipper::ClearJoins()
  1949. {
  1950. for( JoinList::size_type i = 0; i < m_Joins.size(); i++ )
  1951. delete m_Joins[i];
  1952. m_Joins.resize( 0 );
  1953. }
  1954. // ------------------------------------------------------------------------------
  1955. void Clipper::ClearGhostJoins()
  1956. {
  1957. for( JoinList::size_type i = 0; i < m_GhostJoins.size(); i++ )
  1958. delete m_GhostJoins[i];
  1959. m_GhostJoins.resize( 0 );
  1960. }
  1961. // ------------------------------------------------------------------------------
  1962. void Clipper::AddGhostJoin( OutPt* op, const IntPoint OffPt )
  1963. {
  1964. Join* j = new Join;
  1965. j->OutPt1 = op;
  1966. j->OutPt2 = 0;
  1967. j->OffPt = OffPt;
  1968. m_GhostJoins.push_back( j );
  1969. }
  1970. // ------------------------------------------------------------------------------
  1971. void Clipper::InsertLocalMinimaIntoAEL( const cInt botY )
  1972. {
  1973. const LocalMinimum* lm;
  1974. while( PopLocalMinima( botY, lm ) )
  1975. {
  1976. TEdge* lb = lm->LeftBound;
  1977. TEdge* rb = lm->RightBound;
  1978. OutPt* Op1 = 0;
  1979. if( !lb )
  1980. {
  1981. // nb: don't insert LB into either AEL or SEL
  1982. InsertEdgeIntoAEL( rb, 0 );
  1983. SetWindingCount( *rb );
  1984. if( IsContributing( *rb ) )
  1985. Op1 = AddOutPt( rb, rb->Bot );
  1986. }
  1987. else if( !rb )
  1988. {
  1989. InsertEdgeIntoAEL( lb, 0 );
  1990. SetWindingCount( *lb );
  1991. if( IsContributing( *lb ) )
  1992. Op1 = AddOutPt( lb, lb->Bot );
  1993. InsertScanbeam( lb->Top.Y );
  1994. }
  1995. else
  1996. {
  1997. InsertEdgeIntoAEL( lb, 0 );
  1998. InsertEdgeIntoAEL( rb, lb );
  1999. SetWindingCount( *lb );
  2000. rb->WindCnt = lb->WindCnt;
  2001. rb->WindCnt2 = lb->WindCnt2;
  2002. if( IsContributing( *lb ) )
  2003. Op1 = AddLocalMinPoly( lb, rb, lb->Bot );
  2004. InsertScanbeam( lb->Top.Y );
  2005. }
  2006. if( rb )
  2007. {
  2008. if( IsHorizontal( *rb ) )
  2009. {
  2010. AddEdgeToSEL( rb );
  2011. if( rb->NextInLML )
  2012. InsertScanbeam( rb->NextInLML->Top.Y );
  2013. }
  2014. else
  2015. InsertScanbeam( rb->Top.Y );
  2016. }
  2017. if( !lb || !rb )
  2018. continue;
  2019. // if any output polygons share an edge, they'll need joining later ...
  2020. if( Op1 && IsHorizontal( *rb )
  2021. && m_GhostJoins.size() > 0 && (rb->WindDelta != 0) )
  2022. {
  2023. for( JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i )
  2024. {
  2025. Join* jr = m_GhostJoins[i];
  2026. // if the horizontal Rb and a 'ghost' horizontal overlap, then convert
  2027. // the 'ghost' join to a real join ready for later ...
  2028. if( HorzSegmentsOverlap( jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X ) )
  2029. AddJoin( jr->OutPt1, Op1, jr->OffPt );
  2030. }
  2031. }
  2032. if( lb->OutIdx >= 0 && lb->PrevInAEL
  2033. && lb->PrevInAEL->Curr.X == lb->Bot.X
  2034. && lb->PrevInAEL->OutIdx >= 0
  2035. && SlopesEqual( lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top,
  2036. m_UseFullRange )
  2037. && (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0) )
  2038. {
  2039. OutPt* Op2 = AddOutPt( lb->PrevInAEL, lb->Bot );
  2040. AddJoin( Op1, Op2, lb->Top );
  2041. }
  2042. if( lb->NextInAEL != rb )
  2043. {
  2044. if( rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0
  2045. && SlopesEqual( rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top,
  2046. m_UseFullRange )
  2047. && (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0) )
  2048. {
  2049. OutPt* Op2 = AddOutPt( rb->PrevInAEL, rb->Bot );
  2050. AddJoin( Op1, Op2, rb->Top );
  2051. }
  2052. TEdge* e = lb->NextInAEL;
  2053. if( e )
  2054. {
  2055. while( e != rb )
  2056. {
  2057. // nb: For calculating winding counts etc, IntersectEdges() assumes
  2058. // that param1 will be to the Right of param2 ABOVE the intersection ...
  2059. IntersectEdges( rb, e, lb->Curr ); // order important here
  2060. e = e->NextInAEL;
  2061. }
  2062. }
  2063. }
  2064. }
  2065. }
  2066. // ------------------------------------------------------------------------------
  2067. void Clipper::DeleteFromSEL( TEdge* e )
  2068. {
  2069. TEdge* SelPrev = e->PrevInSEL;
  2070. TEdge* SelNext = e->NextInSEL;
  2071. if( !SelPrev && !SelNext && (e != m_SortedEdges) )
  2072. return; // already deleted
  2073. if( SelPrev )
  2074. SelPrev->NextInSEL = SelNext;
  2075. else
  2076. m_SortedEdges = SelNext;
  2077. if( SelNext )
  2078. SelNext->PrevInSEL = SelPrev;
  2079. e->NextInSEL = 0;
  2080. e->PrevInSEL = 0;
  2081. }
  2082. // ------------------------------------------------------------------------------
  2083. #ifdef use_xyz
  2084. void Clipper::SetZ( IntPoint& pt, TEdge& e1, TEdge& e2 )
  2085. {
  2086. if( pt.Z != 0 || !m_ZFill )
  2087. return;
  2088. else if( pt == e1.Bot )
  2089. pt.Z = e1.Bot.Z;
  2090. else if( pt == e1.Top )
  2091. pt.Z = e1.Top.Z;
  2092. else if( pt == e2.Bot )
  2093. pt.Z = e2.Bot.Z;
  2094. else if( pt == e2.Top )
  2095. pt.Z = e2.Top.Z;
  2096. else
  2097. (*m_ZFill)( e1.Bot, e1.Top, e2.Bot, e2.Top, pt );
  2098. }
  2099. // ------------------------------------------------------------------------------
  2100. #endif
  2101. void Clipper::IntersectEdges( TEdge* e1, TEdge* e2, IntPoint& Pt )
  2102. {
  2103. bool e1Contributing = ( e1->OutIdx >= 0 );
  2104. bool e2Contributing = ( e2->OutIdx >= 0 );
  2105. #ifdef use_xyz
  2106. SetZ( Pt, *e1, *e2 );
  2107. #endif
  2108. #ifdef use_lines
  2109. // if either edge is on an OPEN path ...
  2110. if( e1->WindDelta == 0 || e2->WindDelta == 0 )
  2111. {
  2112. // ignore subject-subject open path intersections UNLESS they
  2113. // are both open paths, AND they are both 'contributing maximas' ...
  2114. if( e1->WindDelta == 0 && e2->WindDelta == 0 )
  2115. return;
  2116. // if intersecting a subj line with a subj poly ...
  2117. else if( e1->PolyTyp == e2->PolyTyp
  2118. && e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion )
  2119. {
  2120. if( e1->WindDelta == 0 )
  2121. {
  2122. if( e2Contributing )
  2123. {
  2124. AddOutPt( e1, Pt );
  2125. if( e1Contributing )
  2126. e1->OutIdx = Unassigned;
  2127. }
  2128. }
  2129. else
  2130. {
  2131. if( e1Contributing )
  2132. {
  2133. AddOutPt( e2, Pt );
  2134. if( e2Contributing )
  2135. e2->OutIdx = Unassigned;
  2136. }
  2137. }
  2138. }
  2139. else if( e1->PolyTyp != e2->PolyTyp )
  2140. {
  2141. // toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
  2142. if( (e1->WindDelta == 0) && abs( e2->WindCnt ) == 1
  2143. && (m_ClipType != ctUnion || e2->WindCnt2 == 0) )
  2144. {
  2145. AddOutPt( e1, Pt );
  2146. if( e1Contributing )
  2147. e1->OutIdx = Unassigned;
  2148. }
  2149. else if( (e2->WindDelta == 0) && (abs( e1->WindCnt ) == 1)
  2150. && (m_ClipType != ctUnion || e1->WindCnt2 == 0) )
  2151. {
  2152. AddOutPt( e2, Pt );
  2153. if( e2Contributing )
  2154. e2->OutIdx = Unassigned;
  2155. }
  2156. }
  2157. return;
  2158. }
  2159. #endif
  2160. // update winding counts...
  2161. // assumes that e1 will be to the Right of e2 ABOVE the intersection
  2162. if( e1->PolyTyp == e2->PolyTyp )
  2163. {
  2164. if( IsEvenOddFillType( *e1 ) )
  2165. {
  2166. int oldE1WindCnt = e1->WindCnt;
  2167. e1->WindCnt = e2->WindCnt;
  2168. e2->WindCnt = oldE1WindCnt;
  2169. }
  2170. else
  2171. {
  2172. if( e1->WindCnt + e2->WindDelta == 0 )
  2173. e1->WindCnt = -e1->WindCnt;
  2174. else
  2175. e1->WindCnt += e2->WindDelta;
  2176. if( e2->WindCnt - e1->WindDelta == 0 )
  2177. e2->WindCnt = -e2->WindCnt;
  2178. else
  2179. e2->WindCnt -= e1->WindDelta;
  2180. }
  2181. }
  2182. else
  2183. {
  2184. if( !IsEvenOddFillType( *e2 ) )
  2185. e1->WindCnt2 += e2->WindDelta;
  2186. else
  2187. e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0;
  2188. if( !IsEvenOddFillType( *e1 ) )
  2189. e2->WindCnt2 -= e1->WindDelta;
  2190. else
  2191. e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0;
  2192. }
  2193. PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
  2194. if( e1->PolyTyp == ptSubject )
  2195. {
  2196. e1FillType = m_SubjFillType;
  2197. e1FillType2 = m_ClipFillType;
  2198. }
  2199. else
  2200. {
  2201. e1FillType = m_ClipFillType;
  2202. e1FillType2 = m_SubjFillType;
  2203. }
  2204. if( e2->PolyTyp == ptSubject )
  2205. {
  2206. e2FillType = m_SubjFillType;
  2207. e2FillType2 = m_ClipFillType;
  2208. }
  2209. else
  2210. {
  2211. e2FillType = m_ClipFillType;
  2212. e2FillType2 = m_SubjFillType;
  2213. }
  2214. cInt e1Wc, e2Wc;
  2215. switch( e1FillType )
  2216. {
  2217. case pftPositive:
  2218. e1Wc = e1->WindCnt; break;
  2219. case pftNegative:
  2220. e1Wc = -e1->WindCnt; break;
  2221. default:
  2222. e1Wc = Abs( e1->WindCnt );
  2223. }
  2224. switch( e2FillType )
  2225. {
  2226. case pftPositive:
  2227. e2Wc = e2->WindCnt; break;
  2228. case pftNegative:
  2229. e2Wc = -e2->WindCnt; break;
  2230. default:
  2231. e2Wc = Abs( e2->WindCnt );
  2232. }
  2233. if( e1Contributing && e2Contributing )
  2234. {
  2235. if( (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1)
  2236. || (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )
  2237. {
  2238. AddLocalMaxPoly( e1, e2, Pt );
  2239. }
  2240. else
  2241. {
  2242. AddOutPt( e1, Pt );
  2243. AddOutPt( e2, Pt );
  2244. SwapSides( *e1, *e2 );
  2245. SwapPolyIndexes( *e1, *e2 );
  2246. }
  2247. }
  2248. else if( e1Contributing )
  2249. {
  2250. if( e2Wc == 0 || e2Wc == 1 )
  2251. {
  2252. AddOutPt( e1, Pt );
  2253. SwapSides( *e1, *e2 );
  2254. SwapPolyIndexes( *e1, *e2 );
  2255. }
  2256. }
  2257. else if( e2Contributing )
  2258. {
  2259. if( e1Wc == 0 || e1Wc == 1 )
  2260. {
  2261. AddOutPt( e2, Pt );
  2262. SwapSides( *e1, *e2 );
  2263. SwapPolyIndexes( *e1, *e2 );
  2264. }
  2265. }
  2266. else if( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) )
  2267. {
  2268. // neither edge is currently contributing ...
  2269. cInt e1Wc2, e2Wc2;
  2270. switch( e1FillType2 )
  2271. {
  2272. case pftPositive:
  2273. e1Wc2 = e1->WindCnt2; break;
  2274. case pftNegative:
  2275. e1Wc2 = -e1->WindCnt2; break;
  2276. default:
  2277. e1Wc2 = Abs( e1->WindCnt2 );
  2278. }
  2279. switch( e2FillType2 )
  2280. {
  2281. case pftPositive:
  2282. e2Wc2 = e2->WindCnt2; break;
  2283. case pftNegative:
  2284. e2Wc2 = -e2->WindCnt2; break;
  2285. default:
  2286. e2Wc2 = Abs( e2->WindCnt2 );
  2287. }
  2288. if( e1->PolyTyp != e2->PolyTyp )
  2289. {
  2290. AddLocalMinPoly( e1, e2, Pt );
  2291. }
  2292. else if( e1Wc == 1 && e2Wc == 1 )
  2293. switch( m_ClipType )
  2294. {
  2295. case ctIntersection:
  2296. if( e1Wc2 > 0 && e2Wc2 > 0 )
  2297. AddLocalMinPoly( e1, e2, Pt );
  2298. break;
  2299. case ctUnion:
  2300. if( e1Wc2 <= 0 && e2Wc2 <= 0 )
  2301. AddLocalMinPoly( e1, e2, Pt );
  2302. break;
  2303. case ctDifference:
  2304. if( ( (e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0) )
  2305. || ( (e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0) ) )
  2306. AddLocalMinPoly( e1, e2, Pt );
  2307. break;
  2308. case ctXor:
  2309. AddLocalMinPoly( e1, e2, Pt );
  2310. }
  2311. else
  2312. SwapSides( *e1, *e2 );
  2313. }
  2314. }
  2315. // ------------------------------------------------------------------------------
  2316. void Clipper::SetHoleState( TEdge* e, OutRec* outrec )
  2317. {
  2318. TEdge* e2 = e->PrevInAEL;
  2319. TEdge* eTmp = 0;
  2320. while( e2 )
  2321. {
  2322. if( e2->OutIdx >= 0 && e2->WindDelta != 0 )
  2323. {
  2324. if( !eTmp )
  2325. eTmp = e2;
  2326. else if( eTmp->OutIdx == e2->OutIdx )
  2327. eTmp = 0;
  2328. }
  2329. e2 = e2->PrevInAEL;
  2330. }
  2331. if( !eTmp )
  2332. {
  2333. outrec->FirstLeft = 0;
  2334. outrec->IsHole = false;
  2335. }
  2336. else
  2337. {
  2338. outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx];
  2339. outrec->IsHole = !outrec->FirstLeft->IsHole;
  2340. }
  2341. }
  2342. // ------------------------------------------------------------------------------
  2343. OutRec* GetLowermostRec( OutRec* outRec1, OutRec* outRec2 )
  2344. {
  2345. // work out which polygon fragment has the correct hole state ...
  2346. if( !outRec1->BottomPt )
  2347. outRec1->BottomPt = GetBottomPt( outRec1->Pts );
  2348. if( !outRec2->BottomPt )
  2349. outRec2->BottomPt = GetBottomPt( outRec2->Pts );
  2350. OutPt* OutPt1 = outRec1->BottomPt;
  2351. OutPt* OutPt2 = outRec2->BottomPt;
  2352. if( OutPt1->Pt.Y > OutPt2->Pt.Y )
  2353. return outRec1;
  2354. else if( OutPt1->Pt.Y < OutPt2->Pt.Y )
  2355. return outRec2;
  2356. else if( OutPt1->Pt.X < OutPt2->Pt.X )
  2357. return outRec1;
  2358. else if( OutPt1->Pt.X > OutPt2->Pt.X )
  2359. return outRec2;
  2360. else if( OutPt1->Next == OutPt1 )
  2361. return outRec2;
  2362. else if( OutPt2->Next == OutPt2 )
  2363. return outRec1;
  2364. else if( FirstIsBottomPt( OutPt1, OutPt2 ) )
  2365. return outRec1;
  2366. else
  2367. return outRec2;
  2368. }
  2369. // ------------------------------------------------------------------------------
  2370. bool OutRec1RightOfOutRec2( OutRec* outRec1, OutRec* outRec2 )
  2371. {
  2372. do
  2373. {
  2374. outRec1 = outRec1->FirstLeft;
  2375. if( outRec1 == outRec2 )
  2376. return true;
  2377. } while( outRec1 );
  2378. return false;
  2379. }
  2380. // ------------------------------------------------------------------------------
  2381. OutRec* Clipper::GetOutRec( int Idx )
  2382. {
  2383. OutRec* outrec = m_PolyOuts[Idx];
  2384. while( outrec != m_PolyOuts[outrec->Idx] )
  2385. outrec = m_PolyOuts[outrec->Idx];
  2386. return outrec;
  2387. }
  2388. // ------------------------------------------------------------------------------
  2389. void Clipper::AppendPolygon( TEdge* e1, TEdge* e2 )
  2390. {
  2391. // get the start and ends of both output polygons ...
  2392. OutRec* outRec1 = m_PolyOuts[e1->OutIdx];
  2393. OutRec* outRec2 = m_PolyOuts[e2->OutIdx];
  2394. OutRec* holeStateRec;
  2395. if( OutRec1RightOfOutRec2( outRec1, outRec2 ) )
  2396. holeStateRec = outRec2;
  2397. else if( OutRec1RightOfOutRec2( outRec2, outRec1 ) )
  2398. holeStateRec = outRec1;
  2399. else
  2400. holeStateRec = GetLowermostRec( outRec1, outRec2 );
  2401. // get the start and ends of both output polygons and
  2402. // join e2 poly onto e1 poly and delete pointers to e2 ...
  2403. OutPt* p1_lft = outRec1->Pts;
  2404. OutPt* p1_rt = p1_lft->Prev;
  2405. OutPt* p2_lft = outRec2->Pts;
  2406. OutPt* p2_rt = p2_lft->Prev;
  2407. // join e2 poly onto e1 poly and delete pointers to e2 ...
  2408. if( e1->Side == esLeft )
  2409. {
  2410. if( e2->Side == esLeft )
  2411. {
  2412. // z y x a b c
  2413. ReversePolyPtLinks( p2_lft );
  2414. p2_lft->Next = p1_lft;
  2415. p1_lft->Prev = p2_lft;
  2416. p1_rt->Next = p2_rt;
  2417. p2_rt->Prev = p1_rt;
  2418. outRec1->Pts = p2_rt;
  2419. }
  2420. else
  2421. {
  2422. // x y z a b c
  2423. p2_rt->Next = p1_lft;
  2424. p1_lft->Prev = p2_rt;
  2425. p2_lft->Prev = p1_rt;
  2426. p1_rt->Next = p2_lft;
  2427. outRec1->Pts = p2_lft;
  2428. }
  2429. }
  2430. else
  2431. {
  2432. if( e2->Side == esRight )
  2433. {
  2434. // a b c z y x
  2435. ReversePolyPtLinks( p2_lft );
  2436. p1_rt->Next = p2_rt;
  2437. p2_rt->Prev = p1_rt;
  2438. p2_lft->Next = p1_lft;
  2439. p1_lft->Prev = p2_lft;
  2440. }
  2441. else
  2442. {
  2443. // a b c x y z
  2444. p1_rt->Next = p2_lft;
  2445. p2_lft->Prev = p1_rt;
  2446. p1_lft->Prev = p2_rt;
  2447. p2_rt->Next = p1_lft;
  2448. }
  2449. }
  2450. outRec1->BottomPt = 0;
  2451. if( holeStateRec == outRec2 )
  2452. {
  2453. if( outRec2->FirstLeft != outRec1 )
  2454. outRec1->FirstLeft = outRec2->FirstLeft;
  2455. outRec1->IsHole = outRec2->IsHole;
  2456. }
  2457. outRec2->Pts = 0;
  2458. outRec2->BottomPt = 0;
  2459. outRec2->FirstLeft = outRec1;
  2460. int OKIdx = e1->OutIdx;
  2461. int ObsoleteIdx = e2->OutIdx;
  2462. e1->OutIdx = Unassigned; // nb: safe because we only get here via AddLocalMaxPoly
  2463. e2->OutIdx = Unassigned;
  2464. TEdge* e = m_ActiveEdges;
  2465. while( e )
  2466. {
  2467. if( e->OutIdx == ObsoleteIdx )
  2468. {
  2469. e->OutIdx = OKIdx;
  2470. e->Side = e1->Side;
  2471. break;
  2472. }
  2473. e = e->NextInAEL;
  2474. }
  2475. outRec2->Idx = outRec1->Idx;
  2476. }
  2477. // ------------------------------------------------------------------------------
  2478. OutPt* Clipper::AddOutPt( TEdge* e, const IntPoint& pt )
  2479. {
  2480. if( e->OutIdx < 0 )
  2481. {
  2482. OutRec* outRec = CreateOutRec();
  2483. outRec->IsOpen = (e->WindDelta == 0);
  2484. OutPt* newOp = new OutPt;
  2485. outRec->Pts = newOp;
  2486. newOp->Idx = outRec->Idx;
  2487. newOp->Pt = pt;
  2488. newOp->Next = newOp;
  2489. newOp->Prev = newOp;
  2490. if( !outRec->IsOpen )
  2491. SetHoleState( e, outRec );
  2492. e->OutIdx = outRec->Idx;
  2493. return newOp;
  2494. }
  2495. else
  2496. {
  2497. OutRec* outRec = m_PolyOuts[e->OutIdx];
  2498. // OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
  2499. OutPt* op = outRec->Pts;
  2500. bool ToFront = (e->Side == esLeft);
  2501. if( ToFront && (pt == op->Pt) )
  2502. return op;
  2503. else if( !ToFront && (pt == op->Prev->Pt) )
  2504. return op->Prev;
  2505. OutPt* newOp = new OutPt;
  2506. newOp->Idx = outRec->Idx;
  2507. newOp->Pt = pt;
  2508. newOp->Next = op;
  2509. newOp->Prev = op->Prev;
  2510. newOp->Prev->Next = newOp;
  2511. op->Prev = newOp;
  2512. if( ToFront )
  2513. outRec->Pts = newOp;
  2514. return newOp;
  2515. }
  2516. }
  2517. // ------------------------------------------------------------------------------
  2518. OutPt* Clipper::GetLastOutPt( TEdge* e )
  2519. {
  2520. OutRec* outRec = m_PolyOuts[e->OutIdx];
  2521. if( e->Side == esLeft )
  2522. return outRec->Pts;
  2523. else
  2524. return outRec->Pts->Prev;
  2525. }
  2526. // ------------------------------------------------------------------------------
  2527. void Clipper::ProcessHorizontals()
  2528. {
  2529. TEdge* horzEdge;
  2530. while( PopEdgeFromSEL( horzEdge ) )
  2531. ProcessHorizontal( horzEdge );
  2532. }
  2533. // ------------------------------------------------------------------------------
  2534. inline bool IsMinima( TEdge* e )
  2535. {
  2536. return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e);
  2537. }
  2538. // ------------------------------------------------------------------------------
  2539. inline bool IsMaxima( TEdge* e, const cInt Y )
  2540. {
  2541. return e && e->Top.Y == Y && !e->NextInLML;
  2542. }
  2543. // ------------------------------------------------------------------------------
  2544. inline bool IsIntermediate( TEdge* e, const cInt Y )
  2545. {
  2546. return e->Top.Y == Y && e->NextInLML;
  2547. }
  2548. // ------------------------------------------------------------------------------
  2549. TEdge* GetMaximaPair( TEdge* e )
  2550. {
  2551. if( (e->Next->Top == e->Top) && !e->Next->NextInLML )
  2552. return e->Next;
  2553. else if( (e->Prev->Top == e->Top) && !e->Prev->NextInLML )
  2554. return e->Prev;
  2555. else
  2556. return 0;
  2557. }
  2558. // ------------------------------------------------------------------------------
  2559. TEdge* GetMaximaPairEx( TEdge* e )
  2560. {
  2561. // as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal)
  2562. TEdge* result = GetMaximaPair( e );
  2563. if( result && ( result->OutIdx == Skip
  2564. || ( result->NextInAEL == result->PrevInAEL && !IsHorizontal( *result ) ) ) )
  2565. return 0;
  2566. return result;
  2567. }
  2568. // ------------------------------------------------------------------------------
  2569. void Clipper::SwapPositionsInSEL( TEdge* Edge1, TEdge* Edge2 )
  2570. {
  2571. if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) )
  2572. return;
  2573. if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) )
  2574. return;
  2575. if( Edge1->NextInSEL == Edge2 )
  2576. {
  2577. TEdge* Next = Edge2->NextInSEL;
  2578. if( Next )
  2579. Next->PrevInSEL = Edge1;
  2580. TEdge* Prev = Edge1->PrevInSEL;
  2581. if( Prev )
  2582. Prev->NextInSEL = Edge2;
  2583. Edge2->PrevInSEL = Prev;
  2584. Edge2->NextInSEL = Edge1;
  2585. Edge1->PrevInSEL = Edge2;
  2586. Edge1->NextInSEL = Next;
  2587. }
  2588. else if( Edge2->NextInSEL == Edge1 )
  2589. {
  2590. TEdge* Next = Edge1->NextInSEL;
  2591. if( Next )
  2592. Next->PrevInSEL = Edge2;
  2593. TEdge* Prev = Edge2->PrevInSEL;
  2594. if( Prev )
  2595. Prev->NextInSEL = Edge1;
  2596. Edge1->PrevInSEL = Prev;
  2597. Edge1->NextInSEL = Edge2;
  2598. Edge2->PrevInSEL = Edge1;
  2599. Edge2->NextInSEL = Next;
  2600. }
  2601. else
  2602. {
  2603. TEdge* Next = Edge1->NextInSEL;
  2604. TEdge* Prev = Edge1->PrevInSEL;
  2605. Edge1->NextInSEL = Edge2->NextInSEL;
  2606. if( Edge1->NextInSEL )
  2607. Edge1->NextInSEL->PrevInSEL = Edge1;
  2608. Edge1->PrevInSEL = Edge2->PrevInSEL;
  2609. if( Edge1->PrevInSEL )
  2610. Edge1->PrevInSEL->NextInSEL = Edge1;
  2611. Edge2->NextInSEL = Next;
  2612. if( Edge2->NextInSEL )
  2613. Edge2->NextInSEL->PrevInSEL = Edge2;
  2614. Edge2->PrevInSEL = Prev;
  2615. if( Edge2->PrevInSEL )
  2616. Edge2->PrevInSEL->NextInSEL = Edge2;
  2617. }
  2618. if( !Edge1->PrevInSEL )
  2619. m_SortedEdges = Edge1;
  2620. else if( !Edge2->PrevInSEL )
  2621. m_SortedEdges = Edge2;
  2622. }
  2623. // ------------------------------------------------------------------------------
  2624. TEdge* GetNextInAEL( TEdge* e, Direction dir )
  2625. {
  2626. return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL;
  2627. }
  2628. // ------------------------------------------------------------------------------
  2629. void GetHorzDirection( TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right )
  2630. {
  2631. if( HorzEdge.Bot.X < HorzEdge.Top.X )
  2632. {
  2633. Left = HorzEdge.Bot.X;
  2634. Right = HorzEdge.Top.X;
  2635. Dir = dLeftToRight;
  2636. }
  2637. else
  2638. {
  2639. Left = HorzEdge.Top.X;
  2640. Right = HorzEdge.Bot.X;
  2641. Dir = dRightToLeft;
  2642. }
  2643. }
  2644. // ------------------------------------------------------------------------
  2645. /*******************************************************************************
  2646. * Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or *
  2647. * Bottom of a scanbeam) are processed as if layered. The order in which HEs *
  2648. * are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] *
  2649. * (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), *
  2650. * and with other non-horizontal edges [*]. Once these intersections are *
  2651. * processed, intermediate HEs then 'promote' the Edge above (NextInLML) into *
  2652. * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
  2653. *******************************************************************************/
  2654. void Clipper::ProcessHorizontal( TEdge* horzEdge )
  2655. {
  2656. Direction dir;
  2657. cInt horzLeft, horzRight;
  2658. bool IsOpen = (horzEdge->WindDelta == 0);
  2659. GetHorzDirection( *horzEdge, dir, horzLeft, horzRight );
  2660. TEdge* eLastHorz = horzEdge, * eMaxPair = 0;
  2661. while( eLastHorz->NextInLML && IsHorizontal( *eLastHorz->NextInLML ) )
  2662. eLastHorz = eLastHorz->NextInLML;
  2663. if( !eLastHorz->NextInLML )
  2664. eMaxPair = GetMaximaPair( eLastHorz );
  2665. MaximaList::const_iterator maxIt;
  2666. MaximaList::const_reverse_iterator maxRit;
  2667. if( m_Maxima.size() > 0 )
  2668. {
  2669. // get the first maxima in range (X) ...
  2670. if( dir == dLeftToRight )
  2671. {
  2672. maxIt = m_Maxima.begin();
  2673. while( maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X )
  2674. maxIt++;
  2675. if( maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X )
  2676. maxIt = m_Maxima.end();
  2677. }
  2678. else
  2679. {
  2680. maxRit = m_Maxima.rbegin();
  2681. while( maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X )
  2682. maxRit++;
  2683. if( maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X )
  2684. maxRit = m_Maxima.rend();
  2685. }
  2686. }
  2687. OutPt* op1 = 0;
  2688. for( ; ; ) // loop through consec. horizontal edges
  2689. {
  2690. bool IsLastHorz = (horzEdge == eLastHorz);
  2691. TEdge* e = GetNextInAEL( horzEdge, dir );
  2692. while( e )
  2693. {
  2694. // this code block inserts extra coords into horizontal edges (in output
  2695. // polygons) whereever maxima touch these horizontal edges. This helps
  2696. // 'simplifying' polygons (ie if the Simplify property is set).
  2697. if( m_Maxima.size() > 0 )
  2698. {
  2699. if( dir == dLeftToRight )
  2700. {
  2701. while( maxIt != m_Maxima.end() && *maxIt < e->Curr.X )
  2702. {
  2703. if( horzEdge->OutIdx >= 0 && !IsOpen )
  2704. AddOutPt( horzEdge, IntPoint( *maxIt, horzEdge->Bot.Y ) );
  2705. maxIt++;
  2706. }
  2707. }
  2708. else
  2709. {
  2710. while( maxRit != m_Maxima.rend() && *maxRit > e->Curr.X )
  2711. {
  2712. if( horzEdge->OutIdx >= 0 && !IsOpen )
  2713. AddOutPt( horzEdge, IntPoint( *maxRit, horzEdge->Bot.Y ) );
  2714. maxRit++;
  2715. }
  2716. }
  2717. }
  2718. ;
  2719. if( (dir == dLeftToRight && e->Curr.X > horzRight)
  2720. || (dir == dRightToLeft && e->Curr.X < horzLeft) )
  2721. break;
  2722. // Also break if we've got to the end of an intermediate horizontal edge ...
  2723. // nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
  2724. if( e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML
  2725. && e->Dx < horzEdge->NextInLML->Dx )
  2726. break;
  2727. if( horzEdge->OutIdx >= 0 && !IsOpen ) // note: may be done multiple times
  2728. {
  2729. #ifdef use_xyz
  2730. if( dir == dLeftToRight )
  2731. SetZ( e->Curr, *horzEdge, *e );
  2732. else
  2733. SetZ( e->Curr, *e, *horzEdge );
  2734. #endif
  2735. op1 = AddOutPt( horzEdge, e->Curr );
  2736. TEdge* eNextHorz = m_SortedEdges;
  2737. while( eNextHorz )
  2738. {
  2739. if( eNextHorz->OutIdx >= 0
  2740. && HorzSegmentsOverlap( horzEdge->Bot.X,
  2741. horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X ) )
  2742. {
  2743. OutPt* op2 = GetLastOutPt( eNextHorz );
  2744. AddJoin( op2, op1, eNextHorz->Top );
  2745. }
  2746. eNextHorz = eNextHorz->NextInSEL;
  2747. }
  2748. AddGhostJoin( op1, horzEdge->Bot );
  2749. }
  2750. // OK, so far we're still in range of the horizontal Edge but make sure
  2751. // we're at the last of consec. horizontals when matching with eMaxPair
  2752. if( e == eMaxPair && IsLastHorz )
  2753. {
  2754. if( horzEdge->OutIdx >= 0 )
  2755. AddLocalMaxPoly( horzEdge, eMaxPair, horzEdge->Top );
  2756. DeleteFromAEL( horzEdge );
  2757. DeleteFromAEL( eMaxPair );
  2758. return;
  2759. }
  2760. if( dir == dLeftToRight )
  2761. {
  2762. IntPoint Pt = IntPoint( e->Curr.X, horzEdge->Curr.Y );
  2763. IntersectEdges( horzEdge, e, Pt );
  2764. }
  2765. else
  2766. {
  2767. IntPoint Pt = IntPoint( e->Curr.X, horzEdge->Curr.Y );
  2768. IntersectEdges( e, horzEdge, Pt );
  2769. }
  2770. TEdge* eNext = GetNextInAEL( e, dir );
  2771. SwapPositionsInAEL( horzEdge, e );
  2772. e = eNext;
  2773. } // end while(e)
  2774. // Break out of loop if HorzEdge.NextInLML is not also horizontal ...
  2775. if( !horzEdge->NextInLML || !IsHorizontal( *horzEdge->NextInLML ) )
  2776. break;
  2777. UpdateEdgeIntoAEL( horzEdge );
  2778. if( horzEdge->OutIdx >= 0 )
  2779. AddOutPt( horzEdge, horzEdge->Bot );
  2780. GetHorzDirection( *horzEdge, dir, horzLeft, horzRight );
  2781. } // end for (;;)
  2782. if( horzEdge->OutIdx >= 0 && !op1 )
  2783. {
  2784. op1 = GetLastOutPt( horzEdge );
  2785. TEdge* eNextHorz = m_SortedEdges;
  2786. while( eNextHorz )
  2787. {
  2788. if( eNextHorz->OutIdx >= 0
  2789. && HorzSegmentsOverlap( horzEdge->Bot.X,
  2790. horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X ) )
  2791. {
  2792. OutPt* op2 = GetLastOutPt( eNextHorz );
  2793. AddJoin( op2, op1, eNextHorz->Top );
  2794. }
  2795. eNextHorz = eNextHorz->NextInSEL;
  2796. }
  2797. AddGhostJoin( op1, horzEdge->Top );
  2798. }
  2799. if( horzEdge->NextInLML )
  2800. {
  2801. if( horzEdge->OutIdx >= 0 )
  2802. {
  2803. op1 = AddOutPt( horzEdge, horzEdge->Top );
  2804. UpdateEdgeIntoAEL( horzEdge );
  2805. if( horzEdge->WindDelta == 0 )
  2806. return;
  2807. // nb: HorzEdge is no longer horizontal here
  2808. TEdge* ePrev = horzEdge->PrevInAEL;
  2809. TEdge* eNext = horzEdge->NextInAEL;
  2810. if( ePrev && ePrev->Curr.X == horzEdge->Bot.X
  2811. && ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0
  2812. && ( ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y
  2813. && SlopesEqual( *horzEdge, *ePrev, m_UseFullRange ) ) )
  2814. {
  2815. OutPt* op2 = AddOutPt( ePrev, horzEdge->Bot );
  2816. AddJoin( op1, op2, horzEdge->Top );
  2817. }
  2818. else if( eNext && eNext->Curr.X == horzEdge->Bot.X
  2819. && eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0
  2820. && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y
  2821. && SlopesEqual( *horzEdge, *eNext, m_UseFullRange ) )
  2822. {
  2823. OutPt* op2 = AddOutPt( eNext, horzEdge->Bot );
  2824. AddJoin( op1, op2, horzEdge->Top );
  2825. }
  2826. }
  2827. else
  2828. UpdateEdgeIntoAEL( horzEdge );
  2829. }
  2830. else
  2831. {
  2832. if( horzEdge->OutIdx >= 0 )
  2833. AddOutPt( horzEdge, horzEdge->Top );
  2834. DeleteFromAEL( horzEdge );
  2835. }
  2836. }
  2837. // ------------------------------------------------------------------------------
  2838. bool Clipper::ProcessIntersections( const cInt topY )
  2839. {
  2840. if( !m_ActiveEdges )
  2841. return true;
  2842. try
  2843. {
  2844. BuildIntersectList( topY );
  2845. size_t IlSize = m_IntersectList.size();
  2846. if( IlSize == 0 )
  2847. return true;
  2848. if( IlSize == 1 || FixupIntersectionOrder() )
  2849. ProcessIntersectList();
  2850. else
  2851. return false;
  2852. }
  2853. catch( ... )
  2854. {
  2855. m_SortedEdges = 0;
  2856. DisposeIntersectNodes();
  2857. throw clipperException( "ProcessIntersections error" );
  2858. }
  2859. m_SortedEdges = 0;
  2860. return true;
  2861. }
  2862. // ------------------------------------------------------------------------------
  2863. void Clipper::DisposeIntersectNodes()
  2864. {
  2865. for( size_t i = 0; i < m_IntersectList.size(); ++i )
  2866. delete m_IntersectList[i];
  2867. m_IntersectList.clear();
  2868. }
  2869. // ------------------------------------------------------------------------------
  2870. void Clipper::BuildIntersectList( const cInt topY )
  2871. {
  2872. if( !m_ActiveEdges )
  2873. return;
  2874. // prepare for sorting ...
  2875. TEdge* e = m_ActiveEdges;
  2876. m_SortedEdges = e;
  2877. while( e )
  2878. {
  2879. e->PrevInSEL = e->PrevInAEL;
  2880. e->NextInSEL = e->NextInAEL;
  2881. e->Curr.X = TopX( *e, topY );
  2882. e = e->NextInAEL;
  2883. }
  2884. // bubblesort ...
  2885. bool isModified;
  2886. do
  2887. {
  2888. isModified = false;
  2889. e = m_SortedEdges;
  2890. while( e->NextInSEL )
  2891. {
  2892. TEdge* eNext = e->NextInSEL;
  2893. IntPoint Pt;
  2894. if( e->Curr.X > eNext->Curr.X )
  2895. {
  2896. IntersectPoint( *e, *eNext, Pt );
  2897. if( Pt.Y < topY )
  2898. Pt = IntPoint( TopX( *e, topY ), topY );
  2899. IntersectNode* newNode = new IntersectNode;
  2900. newNode->Edge1 = e;
  2901. newNode->Edge2 = eNext;
  2902. newNode->Pt = Pt;
  2903. m_IntersectList.push_back( newNode );
  2904. SwapPositionsInSEL( e, eNext );
  2905. isModified = true;
  2906. }
  2907. else
  2908. e = eNext;
  2909. }
  2910. if( e->PrevInSEL )
  2911. e->PrevInSEL->NextInSEL = 0;
  2912. else
  2913. break;
  2914. } while( isModified );
  2915. m_SortedEdges = 0; // important
  2916. }
  2917. // ------------------------------------------------------------------------------
  2918. void Clipper::ProcessIntersectList()
  2919. {
  2920. for( size_t i = 0; i < m_IntersectList.size(); ++i )
  2921. {
  2922. IntersectNode* iNode = m_IntersectList[i];
  2923. {
  2924. IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt );
  2925. SwapPositionsInAEL( iNode->Edge1, iNode->Edge2 );
  2926. }
  2927. delete iNode;
  2928. }
  2929. m_IntersectList.clear();
  2930. }
  2931. // ------------------------------------------------------------------------------
  2932. bool IntersectListSort( IntersectNode* node1, IntersectNode* node2 )
  2933. {
  2934. return node2->Pt.Y < node1->Pt.Y;
  2935. }
  2936. // ------------------------------------------------------------------------------
  2937. inline bool EdgesAdjacent( const IntersectNode& inode )
  2938. {
  2939. return (inode.Edge1->NextInSEL == inode.Edge2)
  2940. || (inode.Edge1->PrevInSEL == inode.Edge2);
  2941. }
  2942. // ------------------------------------------------------------------------------
  2943. bool Clipper::FixupIntersectionOrder()
  2944. {
  2945. // pre-condition: intersections are sorted Bottom-most first.
  2946. // Now it's crucial that intersections are made only between adjacent edges,
  2947. // so to ensure this the order of intersections may need adjusting ...
  2948. CopyAELToSEL();
  2949. std::sort( m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort );
  2950. size_t cnt = m_IntersectList.size();
  2951. for( size_t i = 0; i < cnt; ++i )
  2952. {
  2953. if( !EdgesAdjacent( *m_IntersectList[i] ) )
  2954. {
  2955. size_t j = i + 1;
  2956. while( j < cnt && !EdgesAdjacent( *m_IntersectList[j] ) )
  2957. j++;
  2958. if( j == cnt )
  2959. return false;
  2960. std::swap( m_IntersectList[i], m_IntersectList[j] );
  2961. }
  2962. SwapPositionsInSEL( m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2 );
  2963. }
  2964. return true;
  2965. }
  2966. // ------------------------------------------------------------------------------
  2967. void Clipper::DoMaxima( TEdge* e )
  2968. {
  2969. TEdge* eMaxPair = GetMaximaPairEx( e );
  2970. if( !eMaxPair )
  2971. {
  2972. if( e->OutIdx >= 0 )
  2973. AddOutPt( e, e->Top );
  2974. DeleteFromAEL( e );
  2975. return;
  2976. }
  2977. TEdge* eNext = e->NextInAEL;
  2978. while( eNext && eNext != eMaxPair )
  2979. {
  2980. IntersectEdges( e, eNext, e->Top );
  2981. SwapPositionsInAEL( e, eNext );
  2982. eNext = e->NextInAEL;
  2983. }
  2984. if( e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned )
  2985. {
  2986. DeleteFromAEL( e );
  2987. DeleteFromAEL( eMaxPair );
  2988. }
  2989. else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
  2990. {
  2991. if( e->OutIdx >= 0 )
  2992. AddLocalMaxPoly( e, eMaxPair, e->Top );
  2993. DeleteFromAEL( e );
  2994. DeleteFromAEL( eMaxPair );
  2995. }
  2996. #ifdef use_lines
  2997. else if( e->WindDelta == 0 )
  2998. {
  2999. if( e->OutIdx >= 0 )
  3000. {
  3001. AddOutPt( e, e->Top );
  3002. e->OutIdx = Unassigned;
  3003. }
  3004. DeleteFromAEL( e );
  3005. if( eMaxPair->OutIdx >= 0 )
  3006. {
  3007. AddOutPt( eMaxPair, e->Top );
  3008. eMaxPair->OutIdx = Unassigned;
  3009. }
  3010. DeleteFromAEL( eMaxPair );
  3011. }
  3012. #endif
  3013. else
  3014. throw clipperException( "DoMaxima error" );
  3015. }
  3016. // ------------------------------------------------------------------------------
  3017. void Clipper::ProcessEdgesAtTopOfScanbeam( const cInt topY )
  3018. {
  3019. TEdge* e = m_ActiveEdges;
  3020. while( e )
  3021. {
  3022. // 1. process maxima, treating them as if they're 'bent' horizontal edges,
  3023. // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
  3024. bool IsMaximaEdge = IsMaxima( e, topY );
  3025. if( IsMaximaEdge )
  3026. {
  3027. TEdge* eMaxPair = GetMaximaPairEx( e );
  3028. IsMaximaEdge = ( !eMaxPair || !IsHorizontal( *eMaxPair ) );
  3029. }
  3030. if( IsMaximaEdge )
  3031. {
  3032. if( m_StrictSimple )
  3033. m_Maxima.push_back( e->Top.X );
  3034. TEdge* ePrev = e->PrevInAEL;
  3035. DoMaxima( e );
  3036. if( !ePrev )
  3037. e = m_ActiveEdges;
  3038. else
  3039. e = ePrev->NextInAEL;
  3040. }
  3041. else
  3042. {
  3043. // 2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
  3044. if( IsIntermediate( e, topY ) && IsHorizontal( *e->NextInLML ) )
  3045. {
  3046. UpdateEdgeIntoAEL( e );
  3047. if( e->OutIdx >= 0 )
  3048. AddOutPt( e, e->Bot );
  3049. AddEdgeToSEL( e );
  3050. }
  3051. else
  3052. {
  3053. e->Curr.X = TopX( *e, topY );
  3054. e->Curr.Y = topY;
  3055. #ifdef use_xyz
  3056. e->Curr.Z = topY == e->Top.Y ? e->Top.Z : (topY == e->Bot.Y ? e->Bot.Z : 0);
  3057. #endif
  3058. }
  3059. // When StrictlySimple and 'e' is being touched by another edge, then
  3060. // make sure both edges have a vertex here ...
  3061. if( m_StrictSimple )
  3062. {
  3063. TEdge* ePrev = e->PrevInAEL;
  3064. if( (e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0)
  3065. && (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0) )
  3066. {
  3067. IntPoint pt = e->Curr;
  3068. #ifdef use_xyz
  3069. SetZ( pt, *ePrev, *e );
  3070. #endif
  3071. OutPt* op = AddOutPt( ePrev, pt );
  3072. OutPt* op2 = AddOutPt( e, pt );
  3073. AddJoin( op, op2, pt ); // StrictlySimple (type-3) join
  3074. }
  3075. }
  3076. e = e->NextInAEL;
  3077. }
  3078. }
  3079. // 3. Process horizontals at the Top of the scanbeam ...
  3080. m_Maxima.sort();
  3081. ProcessHorizontals();
  3082. m_Maxima.clear();
  3083. // 4. Promote intermediate vertices ...
  3084. e = m_ActiveEdges;
  3085. while( e )
  3086. {
  3087. if( IsIntermediate( e, topY ) )
  3088. {
  3089. OutPt* op = 0;
  3090. if( e->OutIdx >= 0 )
  3091. op = AddOutPt( e, e->Top );
  3092. UpdateEdgeIntoAEL( e );
  3093. // if output polygons share an edge, they'll need joining later ...
  3094. TEdge* ePrev = e->PrevInAEL;
  3095. TEdge* eNext = e->NextInAEL;
  3096. if( ePrev && ePrev->Curr.X == e->Bot.X
  3097. && ePrev->Curr.Y == e->Bot.Y && op
  3098. && ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y
  3099. && SlopesEqual( e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange )
  3100. && (e->WindDelta != 0) && (ePrev->WindDelta != 0) )
  3101. {
  3102. OutPt* op2 = AddOutPt( ePrev, e->Bot );
  3103. AddJoin( op, op2, e->Top );
  3104. }
  3105. else if( eNext && eNext->Curr.X == e->Bot.X
  3106. && eNext->Curr.Y == e->Bot.Y && op
  3107. && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y
  3108. && SlopesEqual( e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange )
  3109. && (e->WindDelta != 0) && (eNext->WindDelta != 0) )
  3110. {
  3111. OutPt* op2 = AddOutPt( eNext, e->Bot );
  3112. AddJoin( op, op2, e->Top );
  3113. }
  3114. }
  3115. e = e->NextInAEL;
  3116. }
  3117. }
  3118. // ------------------------------------------------------------------------------
  3119. void Clipper::FixupOutPolyline( OutRec& outrec )
  3120. {
  3121. OutPt* pp = outrec.Pts;
  3122. OutPt* lastPP = pp->Prev;
  3123. while( pp != lastPP )
  3124. {
  3125. pp = pp->Next;
  3126. if( pp->Pt == pp->Prev->Pt )
  3127. {
  3128. if( pp == lastPP )
  3129. lastPP = pp->Prev;
  3130. OutPt* tmpPP = pp->Prev;
  3131. tmpPP->Next = pp->Next;
  3132. pp->Next->Prev = tmpPP;
  3133. delete pp;
  3134. pp = tmpPP;
  3135. }
  3136. }
  3137. if( pp == pp->Prev )
  3138. {
  3139. DisposeOutPts( pp );
  3140. outrec.Pts = 0;
  3141. return;
  3142. }
  3143. }
  3144. // ------------------------------------------------------------------------------
  3145. void Clipper::FixupOutPolygon( OutRec& outrec )
  3146. {
  3147. // FixupOutPolygon() - removes duplicate points and simplifies consecutive
  3148. // parallel edges by removing the middle vertex.
  3149. OutPt* lastOK = 0;
  3150. outrec.BottomPt = 0;
  3151. OutPt* pp = outrec.Pts;
  3152. bool preserveCol = m_PreserveCollinear || m_StrictSimple;
  3153. for( ; ; )
  3154. {
  3155. if( pp->Prev == pp || pp->Prev == pp->Next )
  3156. {
  3157. DisposeOutPts( pp );
  3158. outrec.Pts = 0;
  3159. return;
  3160. }
  3161. // test for duplicate points and collinear edges ...
  3162. if( (pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt)
  3163. || ( SlopesEqual( pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange )
  3164. && ( !preserveCol
  3165. || !Pt2IsBetweenPt1AndPt3( pp->Prev->Pt, pp->Pt, pp->Next->Pt ) ) ) )
  3166. {
  3167. lastOK = 0;
  3168. OutPt* tmp = pp;
  3169. pp->Prev->Next = pp->Next;
  3170. pp->Next->Prev = pp->Prev;
  3171. pp = pp->Prev;
  3172. delete tmp;
  3173. }
  3174. else if( pp == lastOK )
  3175. break;
  3176. else
  3177. {
  3178. if( !lastOK )
  3179. lastOK = pp;
  3180. pp = pp->Next;
  3181. }
  3182. }
  3183. outrec.Pts = pp;
  3184. }
  3185. // ------------------------------------------------------------------------------
  3186. int PointCount( OutPt* Pts )
  3187. {
  3188. if( !Pts )
  3189. return 0;
  3190. int result = 0;
  3191. OutPt* p = Pts;
  3192. do
  3193. {
  3194. result++;
  3195. p = p->Next;
  3196. } while( p != Pts );
  3197. return result;
  3198. }
  3199. // ------------------------------------------------------------------------------
  3200. void Clipper::BuildResult( Paths& polys )
  3201. {
  3202. polys.reserve( m_PolyOuts.size() );
  3203. for( PolyOutList::size_type ii = 0; ii < m_PolyOuts.size(); ++ii )
  3204. {
  3205. if( !m_PolyOuts[ii]->Pts )
  3206. continue;
  3207. Path pg;
  3208. OutPt* p = m_PolyOuts[ii]->Pts->Prev;
  3209. int cnt = PointCount( p );
  3210. if( cnt < 2 )
  3211. continue;
  3212. pg.reserve( cnt );
  3213. for( int jj = 0; jj < cnt; ++jj )
  3214. {
  3215. pg.push_back( p->Pt );
  3216. p = p->Prev;
  3217. }
  3218. polys.push_back( pg );
  3219. }
  3220. }
  3221. // ------------------------------------------------------------------------------
  3222. void Clipper::BuildResult2( PolyTree& polytree )
  3223. {
  3224. polytree.Clear();
  3225. polytree.AllNodes.reserve( m_PolyOuts.size() );
  3226. // add each output polygon/contour to polytree ...
  3227. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++ )
  3228. {
  3229. OutRec* outRec = m_PolyOuts[i];
  3230. int cnt = PointCount( outRec->Pts );
  3231. if( (outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3) )
  3232. continue;
  3233. FixHoleLinkage( *outRec );
  3234. PolyNode* pn = new PolyNode();
  3235. // nb: polytree takes ownership of all the PolyNodes
  3236. polytree.AllNodes.push_back( pn );
  3237. outRec->PolyNd = pn;
  3238. pn->Parent = 0;
  3239. pn->Index = 0;
  3240. pn->Contour.reserve( cnt );
  3241. OutPt* op = outRec->Pts->Prev;
  3242. for( int j = 0; j < cnt; j++ )
  3243. {
  3244. pn->Contour.push_back( op->Pt );
  3245. op = op->Prev;
  3246. }
  3247. }
  3248. // fixup PolyNode links etc ...
  3249. polytree.Childs.reserve( m_PolyOuts.size() );
  3250. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++ )
  3251. {
  3252. OutRec* outRec = m_PolyOuts[i];
  3253. if( !outRec->PolyNd )
  3254. continue;
  3255. if( outRec->IsOpen )
  3256. {
  3257. outRec->PolyNd->m_IsOpen = true;
  3258. polytree.AddChild( *outRec->PolyNd );
  3259. }
  3260. else if( outRec->FirstLeft && outRec->FirstLeft->PolyNd )
  3261. outRec->FirstLeft->PolyNd->AddChild( *outRec->PolyNd );
  3262. else
  3263. polytree.AddChild( *outRec->PolyNd );
  3264. }
  3265. }
  3266. // ------------------------------------------------------------------------------
  3267. void SwapIntersectNodes( IntersectNode& int1, IntersectNode& int2 )
  3268. {
  3269. // just swap the contents (because fIntersectNodes is a single-linked-list)
  3270. IntersectNode inode = int1; // gets a copy of Int1
  3271. int1.Edge1 = int2.Edge1;
  3272. int1.Edge2 = int2.Edge2;
  3273. int1.Pt = int2.Pt;
  3274. int2.Edge1 = inode.Edge1;
  3275. int2.Edge2 = inode.Edge2;
  3276. int2.Pt = inode.Pt;
  3277. }
  3278. // ------------------------------------------------------------------------------
  3279. inline bool E2InsertsBeforeE1( TEdge& e1, TEdge& e2 )
  3280. {
  3281. if( e2.Curr.X == e1.Curr.X )
  3282. {
  3283. if( e2.Top.Y > e1.Top.Y )
  3284. return e2.Top.X < TopX( e1, e2.Top.Y );
  3285. else
  3286. return e1.Top.X > TopX( e2, e1.Top.Y );
  3287. }
  3288. else
  3289. return e2.Curr.X < e1.Curr.X;
  3290. }
  3291. // ------------------------------------------------------------------------------
  3292. bool GetOverlap( const cInt a1, const cInt a2, const cInt b1, const cInt b2,
  3293. cInt& Left, cInt& Right )
  3294. {
  3295. if( a1 < a2 )
  3296. {
  3297. if( b1 < b2 )
  3298. {
  3299. Left = std::max( a1, b1 ); Right = std::min( a2, b2 );
  3300. }
  3301. else
  3302. {
  3303. Left = std::max( a1, b2 ); Right = std::min( a2, b1 );
  3304. }
  3305. }
  3306. else
  3307. {
  3308. if( b1 < b2 )
  3309. {
  3310. Left = std::max( a2, b1 ); Right = std::min( a1, b2 );
  3311. }
  3312. else
  3313. {
  3314. Left = std::max( a2, b2 ); Right = std::min( a1, b1 );
  3315. }
  3316. }
  3317. return Left < Right;
  3318. }
  3319. // ------------------------------------------------------------------------------
  3320. inline void UpdateOutPtIdxs( OutRec& outrec )
  3321. {
  3322. OutPt* op = outrec.Pts;
  3323. do
  3324. {
  3325. op->Idx = outrec.Idx;
  3326. op = op->Prev;
  3327. } while( op != outrec.Pts );
  3328. }
  3329. // ------------------------------------------------------------------------------
  3330. void Clipper::InsertEdgeIntoAEL( TEdge* edge, TEdge* startEdge )
  3331. {
  3332. if( !m_ActiveEdges )
  3333. {
  3334. edge->PrevInAEL = 0;
  3335. edge->NextInAEL = 0;
  3336. m_ActiveEdges = edge;
  3337. }
  3338. else if( !startEdge && E2InsertsBeforeE1( *m_ActiveEdges, *edge ) )
  3339. {
  3340. edge->PrevInAEL = 0;
  3341. edge->NextInAEL = m_ActiveEdges;
  3342. m_ActiveEdges->PrevInAEL = edge;
  3343. m_ActiveEdges = edge;
  3344. }
  3345. else
  3346. {
  3347. if( !startEdge )
  3348. startEdge = m_ActiveEdges;
  3349. while( startEdge->NextInAEL
  3350. && !E2InsertsBeforeE1( *startEdge->NextInAEL, *edge ) )
  3351. startEdge = startEdge->NextInAEL;
  3352. edge->NextInAEL = startEdge->NextInAEL;
  3353. if( startEdge->NextInAEL )
  3354. startEdge->NextInAEL->PrevInAEL = edge;
  3355. edge->PrevInAEL = startEdge;
  3356. startEdge->NextInAEL = edge;
  3357. }
  3358. }
  3359. // ----------------------------------------------------------------------
  3360. OutPt* DupOutPt( OutPt* outPt, bool InsertAfter )
  3361. {
  3362. OutPt* result = new OutPt;
  3363. result->Pt = outPt->Pt;
  3364. result->Idx = outPt->Idx;
  3365. if( InsertAfter )
  3366. {
  3367. result->Next = outPt->Next;
  3368. result->Prev = outPt;
  3369. outPt->Next->Prev = result;
  3370. outPt->Next = result;
  3371. }
  3372. else
  3373. {
  3374. result->Prev = outPt->Prev;
  3375. result->Next = outPt;
  3376. outPt->Prev->Next = result;
  3377. outPt->Prev = result;
  3378. }
  3379. return result;
  3380. }
  3381. // ------------------------------------------------------------------------------
  3382. bool JoinHorz( OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
  3383. const IntPoint Pt, bool DiscardLeft )
  3384. {
  3385. Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);
  3386. Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight);
  3387. if( Dir1 == Dir2 )
  3388. return false;
  3389. // When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
  3390. // want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
  3391. // So, to facilitate this while inserting Op1b and Op2b ...
  3392. // when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
  3393. // otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
  3394. if( Dir1 == dLeftToRight )
  3395. {
  3396. while( op1->Next->Pt.X <= Pt.X
  3397. && op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y )
  3398. op1 = op1->Next;
  3399. if( DiscardLeft && (op1->Pt.X != Pt.X) )
  3400. op1 = op1->Next;
  3401. op1b = DupOutPt( op1, !DiscardLeft );
  3402. if( op1b->Pt != Pt )
  3403. {
  3404. op1 = op1b;
  3405. op1->Pt = Pt;
  3406. op1b = DupOutPt( op1, !DiscardLeft );
  3407. }
  3408. }
  3409. else
  3410. {
  3411. while( op1->Next->Pt.X >= Pt.X
  3412. && op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y )
  3413. op1 = op1->Next;
  3414. if( !DiscardLeft && (op1->Pt.X != Pt.X) )
  3415. op1 = op1->Next;
  3416. op1b = DupOutPt( op1, DiscardLeft );
  3417. if( op1b->Pt != Pt )
  3418. {
  3419. op1 = op1b;
  3420. op1->Pt = Pt;
  3421. op1b = DupOutPt( op1, DiscardLeft );
  3422. }
  3423. }
  3424. if( Dir2 == dLeftToRight )
  3425. {
  3426. while( op2->Next->Pt.X <= Pt.X
  3427. && op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y )
  3428. op2 = op2->Next;
  3429. if( DiscardLeft && (op2->Pt.X != Pt.X) )
  3430. op2 = op2->Next;
  3431. op2b = DupOutPt( op2, !DiscardLeft );
  3432. if( op2b->Pt != Pt )
  3433. {
  3434. op2 = op2b;
  3435. op2->Pt = Pt;
  3436. op2b = DupOutPt( op2, !DiscardLeft );
  3437. }
  3438. ;
  3439. }
  3440. else
  3441. {
  3442. while( op2->Next->Pt.X >= Pt.X
  3443. && op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y )
  3444. op2 = op2->Next;
  3445. if( !DiscardLeft && (op2->Pt.X != Pt.X) )
  3446. op2 = op2->Next;
  3447. op2b = DupOutPt( op2, DiscardLeft );
  3448. if( op2b->Pt != Pt )
  3449. {
  3450. op2 = op2b;
  3451. op2->Pt = Pt;
  3452. op2b = DupOutPt( op2, DiscardLeft );
  3453. }
  3454. ;
  3455. }
  3456. ;
  3457. if( (Dir1 == dLeftToRight) == DiscardLeft )
  3458. {
  3459. op1->Prev = op2;
  3460. op2->Next = op1;
  3461. op1b->Next = op2b;
  3462. op2b->Prev = op1b;
  3463. }
  3464. else
  3465. {
  3466. op1->Next = op2;
  3467. op2->Prev = op1;
  3468. op1b->Prev = op2b;
  3469. op2b->Next = op1b;
  3470. }
  3471. return true;
  3472. }
  3473. // ------------------------------------------------------------------------------
  3474. bool Clipper::JoinPoints( Join* j, OutRec* outRec1, OutRec* outRec2 )
  3475. {
  3476. OutPt* op1 = j->OutPt1, * op1b;
  3477. OutPt* op2 = j->OutPt2, * op2b;
  3478. // There are 3 kinds of joins for output polygons ...
  3479. // 1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
  3480. // along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
  3481. // 2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
  3482. // location at the Bottom of the overlapping segment (& Join.OffPt is above).
  3483. // 3. StrictSimple joins where edges touch but are not collinear and where
  3484. // Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
  3485. bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y);
  3486. if( isHorizontal && (j->OffPt == j->OutPt1->Pt)
  3487. && (j->OffPt == j->OutPt2->Pt) )
  3488. {
  3489. // Strictly Simple join ...
  3490. if( outRec1 != outRec2 )
  3491. return false;
  3492. op1b = j->OutPt1->Next;
  3493. while( op1b != op1 && (op1b->Pt == j->OffPt) )
  3494. op1b = op1b->Next;
  3495. bool reverse1 = (op1b->Pt.Y > j->OffPt.Y);
  3496. op2b = j->OutPt2->Next;
  3497. while( op2b != op2 && (op2b->Pt == j->OffPt) )
  3498. op2b = op2b->Next;
  3499. bool reverse2 = (op2b->Pt.Y > j->OffPt.Y);
  3500. if( reverse1 == reverse2 )
  3501. return false;
  3502. if( reverse1 )
  3503. {
  3504. op1b = DupOutPt( op1, false );
  3505. op2b = DupOutPt( op2, true );
  3506. op1->Prev = op2;
  3507. op2->Next = op1;
  3508. op1b->Next = op2b;
  3509. op2b->Prev = op1b;
  3510. j->OutPt1 = op1;
  3511. j->OutPt2 = op1b;
  3512. return true;
  3513. }
  3514. else
  3515. {
  3516. op1b = DupOutPt( op1, true );
  3517. op2b = DupOutPt( op2, false );
  3518. op1->Next = op2;
  3519. op2->Prev = op1;
  3520. op1b->Prev = op2b;
  3521. op2b->Next = op1b;
  3522. j->OutPt1 = op1;
  3523. j->OutPt2 = op1b;
  3524. return true;
  3525. }
  3526. }
  3527. else if( isHorizontal )
  3528. {
  3529. // treat horizontal joins differently to non-horizontal joins since with
  3530. // them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
  3531. // may be anywhere along the horizontal edge.
  3532. op1b = op1;
  3533. while( op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2 )
  3534. op1 = op1->Prev;
  3535. while( op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2 )
  3536. op1b = op1b->Next;
  3537. if( op1b->Next == op1 || op1b->Next == op2 )
  3538. return false; // a flat 'polygon'
  3539. op2b = op2;
  3540. while( op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b )
  3541. op2 = op2->Prev;
  3542. while( op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1 )
  3543. op2b = op2b->Next;
  3544. if( op2b->Next == op2 || op2b->Next == op1 )
  3545. return false; // a flat 'polygon'
  3546. cInt Left, Right;
  3547. // Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
  3548. if( !GetOverlap( op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right ) )
  3549. return false;
  3550. // DiscardLeftSide: when overlapping edges are joined, a spike will created
  3551. // which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
  3552. // on the discard Side as either may still be needed for other joins ...
  3553. IntPoint Pt;
  3554. bool DiscardLeftSide;
  3555. if( op1->Pt.X >= Left && op1->Pt.X <= Right )
  3556. {
  3557. Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X);
  3558. }
  3559. else if( op2->Pt.X >= Left&& op2->Pt.X <= Right )
  3560. {
  3561. Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X);
  3562. }
  3563. else if( op1b->Pt.X >= Left && op1b->Pt.X <= Right )
  3564. {
  3565. Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X;
  3566. }
  3567. else
  3568. {
  3569. Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X);
  3570. }
  3571. j->OutPt1 = op1; j->OutPt2 = op2;
  3572. return JoinHorz( op1, op1b, op2, op2b, Pt, DiscardLeftSide );
  3573. }
  3574. else
  3575. {
  3576. // nb: For non-horizontal joins ...
  3577. // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
  3578. // 2. Jr.OutPt1.Pt > Jr.OffPt.Y
  3579. // make sure the polygons are correctly oriented ...
  3580. op1b = op1->Next;
  3581. while( (op1b->Pt == op1->Pt) && (op1b != op1) )
  3582. op1b = op1b->Next;
  3583. bool Reverse1 = ( (op1b->Pt.Y > op1->Pt.Y)
  3584. || !SlopesEqual( op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange ) );
  3585. if( Reverse1 )
  3586. {
  3587. op1b = op1->Prev;
  3588. while( (op1b->Pt == op1->Pt) && (op1b != op1) )
  3589. op1b = op1b->Prev;
  3590. if( (op1b->Pt.Y > op1->Pt.Y)
  3591. || !SlopesEqual( op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange ) )
  3592. return false;
  3593. }
  3594. ;
  3595. op2b = op2->Next;
  3596. while( (op2b->Pt == op2->Pt) && (op2b != op2) )
  3597. op2b = op2b->Next;
  3598. bool Reverse2 = ( (op2b->Pt.Y > op2->Pt.Y)
  3599. || !SlopesEqual( op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange ) );
  3600. if( Reverse2 )
  3601. {
  3602. op2b = op2->Prev;
  3603. while( (op2b->Pt == op2->Pt) && (op2b != op2) )
  3604. op2b = op2b->Prev;
  3605. if( (op2b->Pt.Y > op2->Pt.Y)
  3606. || !SlopesEqual( op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange ) )
  3607. return false;
  3608. }
  3609. if( (op1b == op1) || (op2b == op2) || (op1b == op2b)
  3610. || ( (outRec1 == outRec2) && (Reverse1 == Reverse2) ) )
  3611. return false;
  3612. if( Reverse1 )
  3613. {
  3614. op1b = DupOutPt( op1, false );
  3615. op2b = DupOutPt( op2, true );
  3616. op1->Prev = op2;
  3617. op2->Next = op1;
  3618. op1b->Next = op2b;
  3619. op2b->Prev = op1b;
  3620. j->OutPt1 = op1;
  3621. j->OutPt2 = op1b;
  3622. return true;
  3623. }
  3624. else
  3625. {
  3626. op1b = DupOutPt( op1, true );
  3627. op2b = DupOutPt( op2, false );
  3628. op1->Next = op2;
  3629. op2->Prev = op1;
  3630. op1b->Prev = op2b;
  3631. op2b->Next = op1b;
  3632. j->OutPt1 = op1;
  3633. j->OutPt2 = op1b;
  3634. return true;
  3635. }
  3636. }
  3637. }
  3638. // ----------------------------------------------------------------------
  3639. static OutRec* ParseFirstLeft( OutRec* FirstLeft )
  3640. {
  3641. while( FirstLeft && !FirstLeft->Pts )
  3642. FirstLeft = FirstLeft->FirstLeft;
  3643. return FirstLeft;
  3644. }
  3645. // ------------------------------------------------------------------------------
  3646. void Clipper::FixupFirstLefts1( OutRec* OldOutRec, OutRec* NewOutRec )
  3647. {
  3648. // tests if NewOutRec contains the polygon before reassigning FirstLeft
  3649. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  3650. {
  3651. OutRec* outRec = m_PolyOuts[i];
  3652. OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft );
  3653. if( outRec->Pts && firstLeft == OldOutRec )
  3654. {
  3655. if( Poly2ContainsPoly1( outRec->Pts, NewOutRec->Pts ) )
  3656. outRec->FirstLeft = NewOutRec;
  3657. }
  3658. }
  3659. }
  3660. // ----------------------------------------------------------------------
  3661. void Clipper::FixupFirstLefts2( OutRec* InnerOutRec, OutRec* OuterOutRec )
  3662. {
  3663. // A polygon has split into two such that one is now the inner of the other.
  3664. // It's possible that these polygons now wrap around other polygons, so check
  3665. // every polygon that's also contained by OuterOutRec's FirstLeft container
  3666. // (including 0) to see if they've become inner to the new inner polygon ...
  3667. OutRec* orfl = OuterOutRec->FirstLeft;
  3668. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  3669. {
  3670. OutRec* outRec = m_PolyOuts[i];
  3671. if( !outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec )
  3672. continue;
  3673. OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft );
  3674. if( firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec )
  3675. continue;
  3676. if( Poly2ContainsPoly1( outRec->Pts, InnerOutRec->Pts ) )
  3677. outRec->FirstLeft = InnerOutRec;
  3678. else if( Poly2ContainsPoly1( outRec->Pts, OuterOutRec->Pts ) )
  3679. outRec->FirstLeft = OuterOutRec;
  3680. else if( outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec )
  3681. outRec->FirstLeft = orfl;
  3682. }
  3683. }
  3684. // ----------------------------------------------------------------------
  3685. void Clipper::FixupFirstLefts3( OutRec* OldOutRec, OutRec* NewOutRec )
  3686. {
  3687. // reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
  3688. for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i )
  3689. {
  3690. OutRec* outRec = m_PolyOuts[i];
  3691. OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft );
  3692. if( outRec->Pts && firstLeft == OldOutRec )
  3693. outRec->FirstLeft = NewOutRec;
  3694. }
  3695. }
  3696. // ----------------------------------------------------------------------
  3697. void Clipper::JoinCommonEdges()
  3698. {
  3699. for( JoinList::size_type i = 0; i < m_Joins.size(); i++ )
  3700. {
  3701. Join* join = m_Joins[i];
  3702. OutRec* outRec1 = GetOutRec( join->OutPt1->Idx );
  3703. OutRec* outRec2 = GetOutRec( join->OutPt2->Idx );
  3704. if( !outRec1->Pts || !outRec2->Pts )
  3705. continue;
  3706. if( outRec1->IsOpen || outRec2->IsOpen )
  3707. continue;
  3708. // get the polygon fragment with the correct hole state (FirstLeft)
  3709. // before calling JoinPoints() ...
  3710. OutRec* holeStateRec;
  3711. if( outRec1 == outRec2 )
  3712. holeStateRec = outRec1;
  3713. else if( OutRec1RightOfOutRec2( outRec1, outRec2 ) )
  3714. holeStateRec = outRec2;
  3715. else if( OutRec1RightOfOutRec2( outRec2, outRec1 ) )
  3716. holeStateRec = outRec1;
  3717. else
  3718. holeStateRec = GetLowermostRec( outRec1, outRec2 );
  3719. if( !JoinPoints( join, outRec1, outRec2 ) )
  3720. continue;
  3721. if( outRec1 == outRec2 )
  3722. {
  3723. // instead of joining two polygons, we've just created a new one by
  3724. // splitting one polygon into two.
  3725. outRec1->Pts = join->OutPt1;
  3726. outRec1->BottomPt = 0;
  3727. outRec2 = CreateOutRec();
  3728. outRec2->Pts = join->OutPt2;
  3729. // update all OutRec2.Pts Idx's ...
  3730. UpdateOutPtIdxs( *outRec2 );
  3731. if( Poly2ContainsPoly1( outRec2->Pts, outRec1->Pts ) )
  3732. {
  3733. // outRec1 contains outRec2 ...
  3734. outRec2->IsHole = !outRec1->IsHole;
  3735. outRec2->FirstLeft = outRec1;
  3736. if( m_UsingPolyTree )
  3737. FixupFirstLefts2( outRec2, outRec1 );
  3738. if( (outRec2->IsHole ^ m_ReverseOutput) == (Area( *outRec2 ) > 0) )
  3739. ReversePolyPtLinks( outRec2->Pts );
  3740. }
  3741. else if( Poly2ContainsPoly1( outRec1->Pts, outRec2->Pts ) )
  3742. {
  3743. // outRec2 contains outRec1 ...
  3744. outRec2->IsHole = outRec1->IsHole;
  3745. outRec1->IsHole = !outRec2->IsHole;
  3746. outRec2->FirstLeft = outRec1->FirstLeft;
  3747. outRec1->FirstLeft = outRec2;
  3748. if( m_UsingPolyTree )
  3749. FixupFirstLefts2( outRec1, outRec2 );
  3750. if( (outRec1->IsHole ^ m_ReverseOutput) == (Area( *outRec1 ) > 0) )
  3751. ReversePolyPtLinks( outRec1->Pts );
  3752. }
  3753. else
  3754. {
  3755. // the 2 polygons are completely separate ...
  3756. outRec2->IsHole = outRec1->IsHole;
  3757. outRec2->FirstLeft = outRec1->FirstLeft;
  3758. // fixup FirstLeft pointers that may need reassigning to OutRec2
  3759. if( m_UsingPolyTree )
  3760. FixupFirstLefts1( outRec1, outRec2 );
  3761. }
  3762. }
  3763. else
  3764. {
  3765. // joined 2 polygons together ...
  3766. outRec2->Pts = 0;
  3767. outRec2->BottomPt = 0;
  3768. outRec2->Idx = outRec1->Idx;
  3769. outRec1->IsHole = holeStateRec->IsHole;
  3770. if( holeStateRec == outRec2 )
  3771. outRec1->FirstLeft = outRec2->FirstLeft;
  3772. outRec2->FirstLeft = outRec1;
  3773. if( m_UsingPolyTree )
  3774. FixupFirstLefts3( outRec2, outRec1 );
  3775. }
  3776. }
  3777. }
  3778. // ------------------------------------------------------------------------------
  3779. // ClipperOffset support functions ...
  3780. // ------------------------------------------------------------------------------
  3781. DoublePoint GetUnitNormal( const IntPoint& pt1, const IntPoint& pt2 )
  3782. {
  3783. if( pt2.X == pt1.X && pt2.Y == pt1.Y )
  3784. return DoublePoint( 0, 0 );
  3785. double Dx = (double) (pt2.X - pt1.X);
  3786. double dy = (double) (pt2.Y - pt1.Y);
  3787. double f = 1 * 1.0 / std::sqrt( Dx * Dx + dy * dy );
  3788. Dx *= f;
  3789. dy *= f;
  3790. return DoublePoint( dy, -Dx );
  3791. }
  3792. // ------------------------------------------------------------------------------
  3793. // ClipperOffset class
  3794. // ------------------------------------------------------------------------------
  3795. ClipperOffset::ClipperOffset( double miterLimit, double arcTolerance )
  3796. {
  3797. this->MiterLimit = miterLimit;
  3798. this->ArcTolerance = arcTolerance;
  3799. m_lowest.X = -1;
  3800. }
  3801. // ------------------------------------------------------------------------------
  3802. ClipperOffset::~ClipperOffset()
  3803. {
  3804. Clear();
  3805. }
  3806. // ------------------------------------------------------------------------------
  3807. void ClipperOffset::Clear()
  3808. {
  3809. for( int i = 0; i < m_polyNodes.ChildCount(); ++i )
  3810. delete m_polyNodes.Childs[i];
  3811. m_polyNodes.Childs.clear();
  3812. m_lowest.X = -1;
  3813. }
  3814. // ------------------------------------------------------------------------------
  3815. void ClipperOffset::AddPath( const Path& path, JoinType joinType, EndType endType )
  3816. {
  3817. int highI = (int) path.size() - 1;
  3818. if( highI < 0 )
  3819. return;
  3820. PolyNode* newNode = new PolyNode();
  3821. newNode->m_jointype = joinType;
  3822. newNode->m_endtype = endType;
  3823. // strip duplicate points from path and also get index to the lowest point ...
  3824. if( endType == etClosedLine || endType == etClosedPolygon )
  3825. while( highI > 0 && path[0] == path[highI] )
  3826. highI--;
  3827. newNode->Contour.reserve( highI + 1 );
  3828. newNode->Contour.push_back( path[0] );
  3829. int j = 0, k = 0;
  3830. for( int i = 1; i <= highI; i++ )
  3831. if( newNode->Contour[j] != path[i] )
  3832. {
  3833. j++;
  3834. newNode->Contour.push_back( path[i] );
  3835. if( path[i].Y > newNode->Contour[k].Y
  3836. || (path[i].Y == newNode->Contour[k].Y
  3837. && path[i].X < newNode->Contour[k].X) )
  3838. k = j;
  3839. }
  3840. if( endType == etClosedPolygon && j < 2 )
  3841. {
  3842. delete newNode;
  3843. return;
  3844. }
  3845. m_polyNodes.AddChild( *newNode );
  3846. // if this path's lowest pt is lower than all the others then update m_lowest
  3847. if( endType != etClosedPolygon )
  3848. return;
  3849. if( m_lowest.X < 0 )
  3850. m_lowest = IntPoint( m_polyNodes.ChildCount() - 1, k );
  3851. else
  3852. {
  3853. IntPoint ip = m_polyNodes.Childs[(int) m_lowest.X]->Contour[(int) m_lowest.Y];
  3854. if( newNode->Contour[k].Y > ip.Y
  3855. || (newNode->Contour[k].Y == ip.Y
  3856. && newNode->Contour[k].X < ip.X) )
  3857. m_lowest = IntPoint( m_polyNodes.ChildCount() - 1, k );
  3858. }
  3859. }
  3860. // ------------------------------------------------------------------------------
  3861. void ClipperOffset::AddPaths( const Paths& paths, JoinType joinType, EndType endType )
  3862. {
  3863. for( Paths::size_type i = 0; i < paths.size(); ++i )
  3864. AddPath( paths[i], joinType, endType );
  3865. }
  3866. // ------------------------------------------------------------------------------
  3867. void ClipperOffset::FixOrientations()
  3868. {
  3869. // fixup orientations of all closed paths if the orientation of the
  3870. // closed path with the lowermost vertex is wrong ...
  3871. if( m_lowest.X >= 0
  3872. && !Orientation( m_polyNodes.Childs[(int) m_lowest.X]->Contour ) )
  3873. {
  3874. for( int i = 0; i < m_polyNodes.ChildCount(); ++i )
  3875. {
  3876. PolyNode& node = *m_polyNodes.Childs[i];
  3877. if( node.m_endtype == etClosedPolygon
  3878. || ( node.m_endtype == etClosedLine && Orientation( node.Contour ) ) )
  3879. ReversePath( node.Contour );
  3880. }
  3881. }
  3882. else
  3883. {
  3884. for( int i = 0; i < m_polyNodes.ChildCount(); ++i )
  3885. {
  3886. PolyNode& node = *m_polyNodes.Childs[i];
  3887. if( node.m_endtype == etClosedLine && !Orientation( node.Contour ) )
  3888. ReversePath( node.Contour );
  3889. }
  3890. }
  3891. }
  3892. // ------------------------------------------------------------------------------
  3893. void ClipperOffset::Execute( Paths& solution, double delta )
  3894. {
  3895. solution.clear();
  3896. FixOrientations();
  3897. DoOffset( delta );
  3898. // now clean up 'corners' ...
  3899. Clipper clpr;
  3900. clpr.AddPaths( m_destPolys, ptSubject, true );
  3901. if( delta > 0 )
  3902. {
  3903. clpr.Execute( ctUnion, solution, pftPositive, pftPositive );
  3904. }
  3905. else
  3906. {
  3907. IntRect r = clpr.GetBounds();
  3908. Path outer( 4 );
  3909. outer[0] = IntPoint( r.left - 10, r.bottom + 10 );
  3910. outer[1] = IntPoint( r.right + 10, r.bottom + 10 );
  3911. outer[2] = IntPoint( r.right + 10, r.top - 10 );
  3912. outer[3] = IntPoint( r.left - 10, r.top - 10 );
  3913. clpr.AddPath( outer, ptSubject, true );
  3914. clpr.ReverseSolution( true );
  3915. clpr.Execute( ctUnion, solution, pftNegative, pftNegative );
  3916. if( solution.size() > 0 )
  3917. solution.erase( solution.begin() );
  3918. }
  3919. }
  3920. // ------------------------------------------------------------------------------
  3921. void ClipperOffset::Execute( PolyTree& solution, double delta )
  3922. {
  3923. solution.Clear();
  3924. FixOrientations();
  3925. DoOffset( delta );
  3926. // now clean up 'corners' ...
  3927. Clipper clpr;
  3928. clpr.AddPaths( m_destPolys, ptSubject, true );
  3929. if( delta > 0 )
  3930. {
  3931. clpr.Execute( ctUnion, solution, pftPositive, pftPositive );
  3932. }
  3933. else
  3934. {
  3935. IntRect r = clpr.GetBounds();
  3936. Path outer( 4 );
  3937. outer[0] = IntPoint( r.left - 10, r.bottom + 10 );
  3938. outer[1] = IntPoint( r.right + 10, r.bottom + 10 );
  3939. outer[2] = IntPoint( r.right + 10, r.top - 10 );
  3940. outer[3] = IntPoint( r.left - 10, r.top - 10 );
  3941. clpr.AddPath( outer, ptSubject, true );
  3942. clpr.ReverseSolution( true );
  3943. clpr.Execute( ctUnion, solution, pftNegative, pftNegative );
  3944. // remove the outer PolyNode rectangle ...
  3945. if( solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0 )
  3946. {
  3947. PolyNode* outerNode = solution.Childs[0];
  3948. solution.Childs.reserve( outerNode->ChildCount() );
  3949. solution.Childs[0] = outerNode->Childs[0];
  3950. solution.Childs[0]->Parent = outerNode->Parent;
  3951. for( int i = 1; i < outerNode->ChildCount(); ++i )
  3952. solution.AddChild( *outerNode->Childs[i] );
  3953. }
  3954. else
  3955. solution.Clear();
  3956. }
  3957. }
  3958. // ------------------------------------------------------------------------------
  3959. void ClipperOffset::DoOffset( double delta )
  3960. {
  3961. m_destPolys.clear();
  3962. m_delta = delta;
  3963. // if Zero offset, just copy any CLOSED polygons to m_p and return ...
  3964. if( NEAR_ZERO( delta ) )
  3965. {
  3966. m_destPolys.reserve( m_polyNodes.ChildCount() );
  3967. for( int i = 0; i < m_polyNodes.ChildCount(); i++ )
  3968. {
  3969. PolyNode& node = *m_polyNodes.Childs[i];
  3970. if( node.m_endtype == etClosedPolygon )
  3971. m_destPolys.push_back( node.Contour );
  3972. }
  3973. return;
  3974. }
  3975. // see offset_triginometry3.svg in the documentation folder ...
  3976. if( MiterLimit > 2 )
  3977. m_miterLim = 2 / (MiterLimit * MiterLimit);
  3978. else
  3979. m_miterLim = 0.5;
  3980. double y;
  3981. if( ArcTolerance <= 0.0 )
  3982. y = def_arc_tolerance;
  3983. else if( ArcTolerance > std::fabs( delta ) * def_arc_tolerance )
  3984. y = std::fabs( delta ) * def_arc_tolerance;
  3985. else
  3986. y = ArcTolerance;
  3987. // see offset_triginometry2.svg in the documentation folder ...
  3988. double steps = pi / std::acos( 1 - y / std::fabs( delta ) );
  3989. if( steps > std::fabs( delta ) * pi )
  3990. steps = std::fabs( delta ) * pi; // ie excessive precision check
  3991. m_sin = std::sin( two_pi / steps );
  3992. m_cos = std::cos( two_pi / steps );
  3993. m_StepsPerRad = steps / two_pi;
  3994. if( delta < 0.0 )
  3995. m_sin = -m_sin;
  3996. m_destPolys.reserve( m_polyNodes.ChildCount() * 2 );
  3997. for( int i = 0; i < m_polyNodes.ChildCount(); i++ )
  3998. {
  3999. PolyNode& node = *m_polyNodes.Childs[i];
  4000. m_srcPoly = node.Contour;
  4001. int len = (int) m_srcPoly.size();
  4002. if( len == 0 || ( delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon) ) )
  4003. continue;
  4004. m_destPoly.clear();
  4005. if( len == 1 )
  4006. {
  4007. if( node.m_jointype == jtRound )
  4008. {
  4009. double X = 1.0, Y = 0.0;
  4010. for( cInt j = 1; j <= steps; j++ )
  4011. {
  4012. m_destPoly.push_back( IntPoint(
  4013. Round( m_srcPoly[0].X + X * delta ),
  4014. Round( m_srcPoly[0].Y + Y * delta ) ) );
  4015. double X2 = X;
  4016. X = X * m_cos - m_sin * Y;
  4017. Y = X2 * m_sin + Y * m_cos;
  4018. }
  4019. }
  4020. else
  4021. {
  4022. double X = -1.0, Y = -1.0;
  4023. for( int j = 0; j < 4; ++j )
  4024. {
  4025. m_destPoly.push_back( IntPoint(
  4026. Round( m_srcPoly[0].X + X * delta ),
  4027. Round( m_srcPoly[0].Y + Y * delta ) ) );
  4028. if( X < 0 )
  4029. X = 1;
  4030. else if( Y < 0 )
  4031. Y = 1;
  4032. else
  4033. X = -1;
  4034. }
  4035. }
  4036. m_destPolys.push_back( m_destPoly );
  4037. continue;
  4038. }
  4039. // build m_normals ...
  4040. m_normals.clear();
  4041. m_normals.reserve( len );
  4042. for( int j = 0; j < len - 1; ++j )
  4043. m_normals.push_back( GetUnitNormal( m_srcPoly[j], m_srcPoly[j + 1] ) );
  4044. if( node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon )
  4045. m_normals.push_back( GetUnitNormal( m_srcPoly[len - 1], m_srcPoly[0] ) );
  4046. else
  4047. m_normals.push_back( DoublePoint( m_normals[len - 2] ) );
  4048. if( node.m_endtype == etClosedPolygon )
  4049. {
  4050. int k = len - 1;
  4051. for( int j = 0; j < len; ++j )
  4052. OffsetPoint( j, k, node.m_jointype );
  4053. m_destPolys.push_back( m_destPoly );
  4054. }
  4055. else if( node.m_endtype == etClosedLine )
  4056. {
  4057. int k = len - 1;
  4058. for( int j = 0; j < len; ++j )
  4059. OffsetPoint( j, k, node.m_jointype );
  4060. m_destPolys.push_back( m_destPoly );
  4061. m_destPoly.clear();
  4062. // re-build m_normals ...
  4063. DoublePoint n = m_normals[len - 1];
  4064. for( int j = len - 1; j > 0; j-- )
  4065. m_normals[j] = DoublePoint( -m_normals[j - 1].X, -m_normals[j - 1].Y );
  4066. m_normals[0] = DoublePoint( -n.X, -n.Y );
  4067. k = 0;
  4068. for( int j = len - 1; j >= 0; j-- )
  4069. OffsetPoint( j, k, node.m_jointype );
  4070. m_destPolys.push_back( m_destPoly );
  4071. }
  4072. else
  4073. {
  4074. int k = 0;
  4075. for( int j = 1; j < len - 1; ++j )
  4076. OffsetPoint( j, k, node.m_jointype );
  4077. IntPoint pt1;
  4078. if( node.m_endtype == etOpenButt )
  4079. {
  4080. int j = len - 1;
  4081. pt1 = IntPoint( (cInt) Round( m_srcPoly[j].X + m_normals[j].X *
  4082. delta ), (cInt) Round( m_srcPoly[j].Y + m_normals[j].Y * delta ) );
  4083. m_destPoly.push_back( pt1 );
  4084. pt1 = IntPoint( (cInt) Round( m_srcPoly[j].X - m_normals[j].X *
  4085. delta ), (cInt) Round( m_srcPoly[j].Y - m_normals[j].Y * delta ) );
  4086. m_destPoly.push_back( pt1 );
  4087. }
  4088. else
  4089. {
  4090. int j = len - 1;
  4091. k = len - 2;
  4092. m_sinA = 0;
  4093. m_normals[j] = DoublePoint( -m_normals[j].X, -m_normals[j].Y );
  4094. if( node.m_endtype == etOpenSquare )
  4095. DoSquare( j, k );
  4096. else
  4097. DoRound( j, k );
  4098. }
  4099. // re-build m_normals ...
  4100. for( int j = len - 1; j > 0; j-- )
  4101. m_normals[j] = DoublePoint( -m_normals[j - 1].X, -m_normals[j - 1].Y );
  4102. m_normals[0] = DoublePoint( -m_normals[1].X, -m_normals[1].Y );
  4103. k = len - 1;
  4104. for( int j = k - 1; j > 0; --j )
  4105. OffsetPoint( j, k, node.m_jointype );
  4106. if( node.m_endtype == etOpenButt )
  4107. {
  4108. pt1 = IntPoint( (cInt) Round( m_srcPoly[0].X - m_normals[0].X * delta ),
  4109. (cInt) Round( m_srcPoly[0].Y - m_normals[0].Y * delta ) );
  4110. m_destPoly.push_back( pt1 );
  4111. pt1 = IntPoint( (cInt) Round( m_srcPoly[0].X + m_normals[0].X * delta ),
  4112. (cInt) Round( m_srcPoly[0].Y + m_normals[0].Y * delta ) );
  4113. m_destPoly.push_back( pt1 );
  4114. }
  4115. else
  4116. {
  4117. k = 1;
  4118. m_sinA = 0;
  4119. if( node.m_endtype == etOpenSquare )
  4120. DoSquare( 0, 1 );
  4121. else
  4122. DoRound( 0, 1 );
  4123. }
  4124. m_destPolys.push_back( m_destPoly );
  4125. }
  4126. }
  4127. }
  4128. // ------------------------------------------------------------------------------
  4129. void ClipperOffset::OffsetPoint( int j, int& k, JoinType jointype )
  4130. {
  4131. // cross product ...
  4132. m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
  4133. if( std::fabs( m_sinA * m_delta ) < 1.0 )
  4134. {
  4135. // dot product ...
  4136. double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y );
  4137. if( cosA > 0 ) // angle => 0 degrees
  4138. {
  4139. m_destPoly.push_back( IntPoint( Round( m_srcPoly[j].X + m_normals[k].X * m_delta ),
  4140. Round( m_srcPoly[j].Y + m_normals[k].Y * m_delta ) ) );
  4141. return;
  4142. }
  4143. // else angle => 180 degrees
  4144. }
  4145. else if( m_sinA > 1.0 )
  4146. m_sinA = 1.0;
  4147. else if( m_sinA < -1.0 )
  4148. m_sinA = -1.0;
  4149. if( m_sinA * m_delta < 0 )
  4150. {
  4151. m_destPoly.push_back( IntPoint( Round( m_srcPoly[j].X + m_normals[k].X * m_delta ),
  4152. Round( m_srcPoly[j].Y + m_normals[k].Y * m_delta ) ) );
  4153. m_destPoly.push_back( m_srcPoly[j] );
  4154. m_destPoly.push_back( IntPoint( Round( m_srcPoly[j].X + m_normals[j].X * m_delta ),
  4155. Round( m_srcPoly[j].Y + m_normals[j].Y * m_delta ) ) );
  4156. }
  4157. else
  4158. switch( jointype )
  4159. {
  4160. case jtMiter:
  4161. {
  4162. double r = 1 + (m_normals[j].X * m_normals[k].X +
  4163. m_normals[j].Y * m_normals[k].Y);
  4164. if( r >= m_miterLim )
  4165. DoMiter( j, k, r );
  4166. else
  4167. DoSquare( j, k );
  4168. break;
  4169. }
  4170. case jtSquare:
  4171. DoSquare( j, k ); break;
  4172. case jtRound:
  4173. DoRound( j, k ); break;
  4174. }
  4175. k = j;
  4176. }
  4177. // ------------------------------------------------------------------------------
  4178. void ClipperOffset::DoSquare( int j, int k )
  4179. {
  4180. double dx = std::tan( std::atan2( m_sinA,
  4181. m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y ) / 4 );
  4182. m_destPoly.push_back( IntPoint(
  4183. Round( m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx) ),
  4184. Round( m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx) ) ) );
  4185. m_destPoly.push_back( IntPoint(
  4186. Round( m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx) ),
  4187. Round( m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx) ) ) );
  4188. }
  4189. // ------------------------------------------------------------------------------
  4190. void ClipperOffset::DoMiter( int j, int k, double r )
  4191. {
  4192. double q = m_delta / r;
  4193. m_destPoly.push_back( IntPoint( Round( m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q ),
  4194. Round( m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q ) ) );
  4195. }
  4196. // ------------------------------------------------------------------------------
  4197. void ClipperOffset::DoRound( int j, int k )
  4198. {
  4199. double a = std::atan2( m_sinA,
  4200. m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y );
  4201. int steps = std::max( (int) Round( m_StepsPerRad * std::fabs( a ) ), 1 );
  4202. double X = m_normals[k].X, Y = m_normals[k].Y, X2;
  4203. for( int i = 0; i < steps; ++i )
  4204. {
  4205. m_destPoly.push_back( IntPoint(
  4206. Round( m_srcPoly[j].X + X * m_delta ),
  4207. Round( m_srcPoly[j].Y + Y * m_delta ) ) );
  4208. X2 = X;
  4209. X = X * m_cos - m_sin * Y;
  4210. Y = X2 * m_sin + Y * m_cos;
  4211. }
  4212. m_destPoly.push_back( IntPoint(
  4213. Round( m_srcPoly[j].X + m_normals[j].X * m_delta ),
  4214. Round( m_srcPoly[j].Y + m_normals[j].Y * m_delta ) ) );
  4215. }
  4216. // ------------------------------------------------------------------------------
  4217. // Miscellaneous public functions
  4218. // ------------------------------------------------------------------------------
  4219. void Clipper::DoSimplePolygons()
  4220. {
  4221. PolyOutList::size_type i = 0;
  4222. while( i < m_PolyOuts.size() )
  4223. {
  4224. OutRec* outrec = m_PolyOuts[i++];
  4225. OutPt* op = outrec->Pts;
  4226. if( !op || outrec->IsOpen )
  4227. continue;
  4228. do // for each Pt in Polygon until duplicate found do ...
  4229. {
  4230. OutPt* op2 = op->Next;
  4231. while( op2 != outrec->Pts )
  4232. {
  4233. if( (op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op )
  4234. {
  4235. // split the polygon into two ...
  4236. OutPt* op3 = op->Prev;
  4237. OutPt* op4 = op2->Prev;
  4238. op->Prev = op4;
  4239. op4->Next = op;
  4240. op2->Prev = op3;
  4241. op3->Next = op2;
  4242. outrec->Pts = op;
  4243. OutRec* outrec2 = CreateOutRec();
  4244. outrec2->Pts = op2;
  4245. UpdateOutPtIdxs( *outrec2 );
  4246. if( Poly2ContainsPoly1( outrec2->Pts, outrec->Pts ) )
  4247. {
  4248. // OutRec2 is contained by OutRec1 ...
  4249. outrec2->IsHole = !outrec->IsHole;
  4250. outrec2->FirstLeft = outrec;
  4251. if( m_UsingPolyTree )
  4252. FixupFirstLefts2( outrec2, outrec );
  4253. }
  4254. else
  4255. if( Poly2ContainsPoly1( outrec->Pts, outrec2->Pts ) )
  4256. {
  4257. // OutRec1 is contained by OutRec2 ...
  4258. outrec2->IsHole = outrec->IsHole;
  4259. outrec->IsHole = !outrec2->IsHole;
  4260. outrec2->FirstLeft = outrec->FirstLeft;
  4261. outrec->FirstLeft = outrec2;
  4262. if( m_UsingPolyTree )
  4263. FixupFirstLefts2( outrec, outrec2 );
  4264. }
  4265. else
  4266. {
  4267. // the 2 polygons are separate ...
  4268. outrec2->IsHole = outrec->IsHole;
  4269. outrec2->FirstLeft = outrec->FirstLeft;
  4270. if( m_UsingPolyTree )
  4271. FixupFirstLefts1( outrec, outrec2 );
  4272. }
  4273. op2 = op; // ie get ready for the Next iteration
  4274. }
  4275. op2 = op2->Next;
  4276. }
  4277. op = op->Next;
  4278. } while( op != outrec->Pts );
  4279. }
  4280. }
  4281. // ------------------------------------------------------------------------------
  4282. void ReversePath( Path& p )
  4283. {
  4284. std::reverse( p.begin(), p.end() );
  4285. }
  4286. // ------------------------------------------------------------------------------
  4287. void ReversePaths( Paths& p )
  4288. {
  4289. for( Paths::size_type i = 0; i < p.size(); ++i )
  4290. ReversePath( p[i] );
  4291. }
  4292. // ------------------------------------------------------------------------------
  4293. void SimplifyPolygon( const Path& in_poly, Paths& out_polys, PolyFillType fillType )
  4294. {
  4295. Clipper c;
  4296. c.StrictlySimple( true );
  4297. c.AddPath( in_poly, ptSubject, true );
  4298. c.Execute( ctUnion, out_polys, fillType, fillType );
  4299. }
  4300. // ------------------------------------------------------------------------------
  4301. void SimplifyPolygons( const Paths& in_polys, Paths& out_polys, PolyFillType fillType )
  4302. {
  4303. Clipper c;
  4304. c.StrictlySimple( true );
  4305. c.AddPaths( in_polys, ptSubject, true );
  4306. c.Execute( ctUnion, out_polys, fillType, fillType );
  4307. }
  4308. // ------------------------------------------------------------------------------
  4309. void SimplifyPolygons( Paths& polys, PolyFillType fillType )
  4310. {
  4311. SimplifyPolygons( polys, polys, fillType );
  4312. }
  4313. // ------------------------------------------------------------------------------
  4314. inline double DistanceSqrd( const IntPoint& pt1, const IntPoint& pt2 )
  4315. {
  4316. double Dx = ( (double) pt1.X - pt2.X );
  4317. double dy = ( (double) pt1.Y - pt2.Y );
  4318. return Dx * Dx + dy * dy;
  4319. }
  4320. // ------------------------------------------------------------------------------
  4321. double DistanceFromLineSqrd( const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2 )
  4322. {
  4323. // The equation of a line in general form (Ax + By + C = 0)
  4324. // given 2 points (x¹,y¹) & (x²,y²) is ...
  4325. // (y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
  4326. // A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
  4327. // perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
  4328. // see http://en.wikipedia.org/wiki/Perpendicular_distance
  4329. double A = double(ln1.Y - ln2.Y);
  4330. double B = double(ln2.X - ln1.X);
  4331. double C = A * ln1.X + B * ln1.Y;
  4332. C = A * pt.X + B * pt.Y - C;
  4333. return (C * C) / (A * A + B * B);
  4334. }
  4335. // ---------------------------------------------------------------------------
  4336. bool SlopesNearCollinear( const IntPoint& pt1,
  4337. const IntPoint& pt2, const IntPoint& pt3, double distSqrd )
  4338. {
  4339. // this function is more accurate when the point that's geometrically
  4340. // between the other 2 points is the one that's tested for distance.
  4341. // ie makes it more likely to pick up 'spikes' ...
  4342. if( Abs( pt1.X - pt2.X ) > Abs( pt1.Y - pt2.Y ) )
  4343. {
  4344. if( (pt1.X > pt2.X) == (pt1.X < pt3.X) )
  4345. return DistanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd;
  4346. else if( (pt2.X > pt1.X) == (pt2.X < pt3.X) )
  4347. return DistanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd;
  4348. else
  4349. return DistanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd;
  4350. }
  4351. else
  4352. {
  4353. if( (pt1.Y > pt2.Y) == (pt1.Y < pt3.Y) )
  4354. return DistanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd;
  4355. else if( (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y) )
  4356. return DistanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd;
  4357. else
  4358. return DistanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd;
  4359. }
  4360. }
  4361. // ------------------------------------------------------------------------------
  4362. bool PointsAreClose( IntPoint pt1, IntPoint pt2, double distSqrd )
  4363. {
  4364. double Dx = (double) pt1.X - pt2.X;
  4365. double dy = (double) pt1.Y - pt2.Y;
  4366. return (Dx * Dx) + (dy * dy) <= distSqrd;
  4367. }
  4368. // ------------------------------------------------------------------------------
  4369. OutPt* ExcludeOp( OutPt* op )
  4370. {
  4371. OutPt* result = op->Prev;
  4372. result->Next = op->Next;
  4373. op->Next->Prev = result;
  4374. result->Idx = 0;
  4375. return result;
  4376. }
  4377. // ------------------------------------------------------------------------------
  4378. void CleanPolygon( const Path& in_poly, Path& out_poly, double distance )
  4379. {
  4380. // distance = proximity in units/pixels below which vertices
  4381. // will be stripped. Default ~= sqrt(2).
  4382. size_t size = in_poly.size();
  4383. if( size == 0 )
  4384. {
  4385. out_poly.clear();
  4386. return;
  4387. }
  4388. OutPt* outPts = new OutPt[size];
  4389. for( size_t i = 0; i < size; ++i )
  4390. {
  4391. outPts[i].Pt = in_poly[i];
  4392. outPts[i].Next = &outPts[(i + 1) % size];
  4393. outPts[i].Next->Prev = &outPts[i];
  4394. outPts[i].Idx = 0;
  4395. }
  4396. double distSqrd = distance * distance;
  4397. OutPt* op = &outPts[0];
  4398. while( op->Idx == 0 && op->Next != op->Prev )
  4399. {
  4400. if( PointsAreClose( op->Pt, op->Prev->Pt, distSqrd ) )
  4401. {
  4402. op = ExcludeOp( op );
  4403. size--;
  4404. }
  4405. else if( PointsAreClose( op->Prev->Pt, op->Next->Pt, distSqrd ) )
  4406. {
  4407. ExcludeOp( op->Next );
  4408. op = ExcludeOp( op );
  4409. size -= 2;
  4410. }
  4411. else if( SlopesNearCollinear( op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd ) )
  4412. {
  4413. op = ExcludeOp( op );
  4414. size--;
  4415. }
  4416. else
  4417. {
  4418. op->Idx = 1;
  4419. op = op->Next;
  4420. }
  4421. }
  4422. if( size < 3 )
  4423. size = 0;
  4424. out_poly.resize( size );
  4425. for( size_t i = 0; i < size; ++i )
  4426. {
  4427. out_poly[i] = op->Pt;
  4428. op = op->Next;
  4429. }
  4430. delete [] outPts;
  4431. }
  4432. // ------------------------------------------------------------------------------
  4433. void CleanPolygon( Path& poly, double distance )
  4434. {
  4435. CleanPolygon( poly, poly, distance );
  4436. }
  4437. // ------------------------------------------------------------------------------
  4438. void CleanPolygons( const Paths& in_polys, Paths& out_polys, double distance )
  4439. {
  4440. out_polys.resize( in_polys.size() );
  4441. for( Paths::size_type i = 0; i < in_polys.size(); ++i )
  4442. CleanPolygon( in_polys[i], out_polys[i], distance );
  4443. }
  4444. // ------------------------------------------------------------------------------
  4445. void CleanPolygons( Paths& polys, double distance )
  4446. {
  4447. CleanPolygons( polys, polys, distance );
  4448. }
  4449. // ------------------------------------------------------------------------------
  4450. void Minkowski( const Path& poly, const Path& path,
  4451. Paths& solution, bool isSum, bool isClosed )
  4452. {
  4453. int delta = (isClosed ? 1 : 0);
  4454. size_t polyCnt = poly.size();
  4455. size_t pathCnt = path.size();
  4456. Paths pp;
  4457. pp.reserve( pathCnt );
  4458. if( isSum )
  4459. for( size_t i = 0; i < pathCnt; ++i )
  4460. {
  4461. Path p;
  4462. p.reserve( polyCnt );
  4463. for( size_t j = 0; j < poly.size(); ++j )
  4464. p.push_back( IntPoint( path[i].X + poly[j].X, path[i].Y + poly[j].Y ) );
  4465. pp.push_back( p );
  4466. }
  4467. else
  4468. for( size_t i = 0; i < pathCnt; ++i )
  4469. {
  4470. Path p;
  4471. p.reserve( polyCnt );
  4472. for( size_t j = 0; j < poly.size(); ++j )
  4473. p.push_back( IntPoint( path[i].X - poly[j].X, path[i].Y - poly[j].Y ) );
  4474. pp.push_back( p );
  4475. }
  4476. solution.clear();
  4477. solution.reserve( (pathCnt + delta) * (polyCnt + 1) );
  4478. for( size_t i = 0; i < pathCnt - 1 + delta; ++i )
  4479. for( size_t j = 0; j < polyCnt; ++j )
  4480. {
  4481. Path quad;
  4482. quad.reserve( 4 );
  4483. quad.push_back( pp[i % pathCnt][j % polyCnt] );
  4484. quad.push_back( pp[(i + 1) % pathCnt][j % polyCnt] );
  4485. quad.push_back( pp[(i + 1) % pathCnt][(j + 1) % polyCnt] );
  4486. quad.push_back( pp[i % pathCnt][(j + 1) % polyCnt] );
  4487. if( !Orientation( quad ) )
  4488. ReversePath( quad );
  4489. solution.push_back( quad );
  4490. }
  4491. }
  4492. // ------------------------------------------------------------------------------
  4493. void MinkowskiSum( const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed )
  4494. {
  4495. Minkowski( pattern, path, solution, true, pathIsClosed );
  4496. Clipper c;
  4497. c.AddPaths( solution, ptSubject, true );
  4498. c.Execute( ctUnion, solution, pftNonZero, pftNonZero );
  4499. }
  4500. // ------------------------------------------------------------------------------
  4501. void TranslatePath( const Path& input, Path& output, const IntPoint delta )
  4502. {
  4503. // precondition: input != output
  4504. output.resize( input.size() );
  4505. for( size_t i = 0; i < input.size(); ++i )
  4506. output[i] = IntPoint( input[i].X + delta.X, input[i].Y + delta.Y );
  4507. }
  4508. // ------------------------------------------------------------------------------
  4509. void MinkowskiSum( const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed )
  4510. {
  4511. Clipper c;
  4512. for( size_t i = 0; i < paths.size(); ++i )
  4513. {
  4514. Paths tmp;
  4515. Minkowski( pattern, paths[i], tmp, true, pathIsClosed );
  4516. c.AddPaths( tmp, ptSubject, true );
  4517. if( pathIsClosed )
  4518. {
  4519. Path tmp2;
  4520. TranslatePath( paths[i], tmp2, pattern[0] );
  4521. c.AddPath( tmp2, ptClip, true );
  4522. }
  4523. }
  4524. c.Execute( ctUnion, solution, pftNonZero, pftNonZero );
  4525. }
  4526. // ------------------------------------------------------------------------------
  4527. void MinkowskiDiff( const Path& poly1, const Path& poly2, Paths& solution )
  4528. {
  4529. Minkowski( poly1, poly2, solution, false, true );
  4530. Clipper c;
  4531. c.AddPaths( solution, ptSubject, true );
  4532. c.Execute( ctUnion, solution, pftNonZero, pftNonZero );
  4533. }
  4534. // ------------------------------------------------------------------------------
  4535. enum NodeType
  4536. {
  4537. ntAny, ntOpen, ntClosed
  4538. };
  4539. void AddPolyNodeToPaths( const PolyNode& polynode, NodeType nodetype, Paths& paths )
  4540. {
  4541. bool match = true;
  4542. if( nodetype == ntClosed )
  4543. match = !polynode.IsOpen();
  4544. else if( nodetype == ntOpen )
  4545. return;
  4546. if( !polynode.Contour.empty() && match )
  4547. paths.push_back( polynode.Contour );
  4548. for( int i = 0; i < polynode.ChildCount(); ++i )
  4549. AddPolyNodeToPaths( *polynode.Childs[i], nodetype, paths );
  4550. }
  4551. // ------------------------------------------------------------------------------
  4552. void PolyTreeToPaths( const PolyTree& polytree, Paths& paths )
  4553. {
  4554. paths.resize( 0 );
  4555. paths.reserve( polytree.Total() );
  4556. AddPolyNodeToPaths( polytree, ntAny, paths );
  4557. }
  4558. // ------------------------------------------------------------------------------
  4559. void ClosedPathsFromPolyTree( const PolyTree& polytree, Paths& paths )
  4560. {
  4561. paths.resize( 0 );
  4562. paths.reserve( polytree.Total() );
  4563. AddPolyNodeToPaths( polytree, ntClosed, paths );
  4564. }
  4565. // ------------------------------------------------------------------------------
  4566. void OpenPathsFromPolyTree( PolyTree& polytree, Paths& paths )
  4567. {
  4568. paths.resize( 0 );
  4569. paths.reserve( polytree.Total() );
  4570. // Open paths are top level only, so ...
  4571. for( int i = 0; i < polytree.ChildCount(); ++i )
  4572. if( polytree.Childs[i]->IsOpen() )
  4573. paths.push_back( polytree.Childs[i]->Contour );
  4574. }
  4575. // ------------------------------------------------------------------------------
  4576. std::ostream& operator <<( std::ostream& s, const IntPoint& p )
  4577. {
  4578. s << "(" << p.X << "," << p.Y << ")";
  4579. return s;
  4580. }
  4581. // ------------------------------------------------------------------------------
  4582. std::ostream& operator <<( std::ostream& s, const Path& p )
  4583. {
  4584. if( p.empty() )
  4585. return s;
  4586. Path::size_type last = p.size() - 1;
  4587. for( Path::size_type i = 0; i < last; i++ )
  4588. s << "(" << p[i].X << "," << p[i].Y << "), ";
  4589. s << "(" << p[last].X << "," << p[last].Y << ")\n";
  4590. return s;
  4591. }
  4592. // ------------------------------------------------------------------------------
  4593. std::ostream& operator <<( std::ostream& s, const Paths& p )
  4594. {
  4595. for( Paths::size_type i = 0; i < p.size(); i++ )
  4596. s << p[i];
  4597. s << "\n";
  4598. return s;
  4599. }
  4600. // ------------------------------------------------------------------------------
  4601. } // ClipperLib namespace