1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
22 #include <basegfx/tuple/b2dtuple.hxx>
23 #include <basegfx/range/b2drange.hxx>
24 #include <basegfx/range/b2drangeclipper.hxx>
25 #include <basegfx/polygon/b2dpolypolygon.hxx>
26 #include <basegfx/range/b2drectangle.hxx>
28 #include <o3tl/vector_pool.hxx>
39 // Generating a poly-polygon from a bunch of rectangles
41 // Helper functionality for sweep-line algorithm
42 // ====================================================
45 typedef o3tl::vector_pool
<ImplPolygon
> VectorOfPolygons
;
47 /** This class represents an active edge
49 As the sweep line traverses across the overall area,
50 rectangle edges parallel to it generate events, and
51 rectangle edges orthogonal to it generate active
52 edges. This class represents the latter.
59 /// edge proceeds to the left
61 /// edge proceeds to the right
65 /** Create active edge
68 Rectangle this edge is part of
70 @param fInvariantCoord
71 The invariant coordinate value of this edge
74 Is fInvariantCoord the lower or the higher value, for
77 ActiveEdge( const B2DRectangle
& rRect
,
78 const double& fInvariantCoord
,
79 std::ptrdiff_t nPolyIdx
,
80 EdgeDirection eEdgeDirection
) :
81 mfInvariantCoord(fInvariantCoord
),
82 mpAssociatedRect( &rRect
),
83 mnPolygonIdx( nPolyIdx
),
84 meEdgeDirection( eEdgeDirection
)
87 double getInvariantCoord() const { return mfInvariantCoord
; }
88 const B2DRectangle
& getRect() const { return *mpAssociatedRect
; }
89 std::ptrdiff_t getTargetPolygonIndex() const { return mnPolygonIdx
; }
90 void setTargetPolygonIndex( std::ptrdiff_t nIdx
) { mnPolygonIdx
= nIdx
; }
91 EdgeDirection
getEdgeDirection() const { return meEdgeDirection
; }
94 /** The invariant coordinate value of this edge (e.g. the
95 common y value, for a horizontal edge)
97 double mfInvariantCoord
;
99 /** Associated rectangle
101 This on the one hand saves some storage space (the
102 vector of rectangles is persistent, anyway), and on
103 the other hand provides an identifier to match active
104 edges and x events (see below)
106 Ptr because class needs to be assignable
108 const B2DRectangle
* mpAssociatedRect
;
110 /** Index of the polygon this edge is currently involved
113 Note that this can change for some kinds of edge
114 intersection, as the algorithm tends to swap
115 associated polygons there.
117 -1 denotes no assigned polygon
119 std::ptrdiff_t mnPolygonIdx
;
121 /// 'left' or 'right'
122 EdgeDirection meEdgeDirection
;
125 // Needs to be list - various places hold ptrs to elements
126 typedef std::list
< ActiveEdge
> ListOfEdges
;
128 /** Element of the sweep line event list
130 As the sweep line traverses across the overall area,
131 rectangle edges parallel to it generate events, and
132 rectangle edges orthogonal to it generate active
133 edges. This class represents the former.
135 The class defines an element of the sweep line list. The
136 sweep line's position jumps in steps defined by the
137 coordinates of the sorted SweepLineEvent entries.
142 /** The two possible sweep line rectangle edges differ by
143 one coordinate value - the starting edge has the
144 lower, the finishing edge the higher value.
147 /// edge with lower coordinate value
149 /// edge with higher coordinate value
153 /** The two possible sweep line directions
160 /** Create sweep line event
163 Coordinate position of the event
166 Rectangle this event is generated for.
169 Is fPos the lower or the higher value, for the
170 rectangle this event is generated for?
172 SweepLineEvent( double fPos
,
173 const B2DRectangle
& rRect
,
175 EdgeDirection eDirection
) :
177 mpAssociatedRect( &rRect
),
178 meEdgeType( eEdgeType
),
179 meEdgeDirection( eDirection
)
182 double getPos() const { return mfPos
; }
183 const B2DRectangle
& getRect() const { return *mpAssociatedRect
; }
184 EdgeType
getEdgeType() const { return meEdgeType
; }
185 EdgeDirection
getEdgeDirection() const { return meEdgeDirection
; }
188 bool operator<( const SweepLineEvent
& rRHS
) const { return mfPos
< rRHS
.mfPos
; }
191 /// position of the event, in the direction of the line sweep
194 /** Rectangle this event is generated for
196 This on the one hand saves some storage space (the
197 vector of rectangles is persistent, anyway), and on
198 the other hand provides an identifier to match active
199 edges and events (see below)
201 Ptr because class needs to be assignable
203 const B2DRectangle
* mpAssociatedRect
;
205 /// 'upper' or 'lower' edge of original rectangle.
209 EdgeDirection meEdgeDirection
;
212 typedef std::vector
< SweepLineEvent
> VectorOfEvents
;
214 /** Smart point container for B2DMultiRange::getPolyPolygon()
216 This class provides methods needed only here, and is used
217 as a place to store some additional information per
218 polygon. Also, most of the intersection logic is
227 mpLeadingRightEdge(nullptr),
232 // completely ad-hoc. but what the hell.
233 maPoints
.reserve(11);
236 void setPolygonPoolIndex( std::ptrdiff_t nIdx
) { mnIdx
= nIdx
; }
238 /// Add point to the end of the existing points
239 void append( const B2DPoint
& rPoint
)
241 OSL_PRECOND( maPoints
.empty() ||
242 maPoints
.back().getX() == rPoint
.getX() ||
243 maPoints
.back().getY() == rPoint
.getY(),
244 "ImplPolygon::append(): added point violates 90 degree line angle constraint!" );
246 if( maPoints
.empty() ||
247 maPoints
.back() != rPoint
)
249 // avoid duplicate points
250 maPoints
.push_back( rPoint
);
254 /** Perform the intersection of this polygon with an
258 The vertical line event that generated the
262 The active edge that generated the intersection
265 Polygon pool, we sometimes need to allocate a new one
267 @param bIsFinishingEdge
268 True, when this is hitting the last edge of the
269 vertical sweep - every vertical sweep starts and ends
270 with upper and lower edge of the _same_ rectangle.
272 @return the new current polygon (that's the one
273 processing must proceed with, when going through the
274 list of upcoming active edges).
276 std::ptrdiff_t intersect( SweepLineEvent
const & rEvent
,
277 ActiveEdge
& rActiveEdge
,
278 VectorOfPolygons
& rPolygonPool
,
279 B2DPolyPolygon
& rRes
,
280 bool isFinishingEdge
)
282 OSL_PRECOND( !mbIsFinished
,
283 "ImplPolygon::intersect(): called on already finished polygon!" );
284 OSL_PRECOND( !isFinishingEdge
|| &rEvent
.getRect() == &rActiveEdge
.getRect(),
285 "ImplPolygon::intersect(): inconsistent ending!" );
287 const B2DPoint
aIntersectionPoint( rEvent
.getPos(),
288 rActiveEdge
.getInvariantCoord() );
290 // intersection point, goes to our polygon
292 append(aIntersectionPoint
);
294 if( isFinishingEdge
)
296 // isSweepLineEnteringRect ?
297 if( rEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
)
298 handleFinalOwnRightEdge(rActiveEdge
);
300 handleFinalOwnLeftEdge(rActiveEdge
,
304 // we're done with this rect & sweep line
307 else if( metOwnEdge(rEvent
,rActiveEdge
) )
309 handleInitialOwnEdge(rEvent
, rActiveEdge
);
311 // point already added, all init done, continue
317 OSL_ENSURE( rActiveEdge
.getTargetPolygonIndex() != -1,
318 "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
320 const bool isHittingLeftEdge(
321 rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
);
323 if( isHittingLeftEdge
)
324 return handleComplexLeftEdge(rActiveEdge
,
329 return handleComplexRightEdge(rActiveEdge
,
336 void handleInitialOwnEdge(SweepLineEvent
const & rEvent
,
337 ActiveEdge
& rActiveEdge
) const
339 const bool isActiveEdgeProceedLeft(
340 rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
);
341 const bool isSweepLineEnteringRect(
342 rEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
);
344 OSL_ENSURE( isSweepLineEnteringRect
== isActiveEdgeProceedLeft
,
345 "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
347 OSL_ENSURE( isSweepLineEnteringRect
||
348 mpLeadingRightEdge
== &rActiveEdge
,
349 "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
352 void handleFinalOwnRightEdge(ActiveEdge
& rActiveEdge
)
354 OSL_ENSURE( rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT
,
355 "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
357 rActiveEdge
.setTargetPolygonIndex(mnIdx
);
358 mpLeadingRightEdge
= &rActiveEdge
;
361 void handleFinalOwnLeftEdge(ActiveEdge
const & rActiveEdge
,
362 VectorOfPolygons
& rPolygonPool
,
363 B2DPolyPolygon
& rRes
)
365 OSL_ENSURE( rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
,
366 "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
368 const bool isHittingOurTail(
369 rActiveEdge
.getTargetPolygonIndex() == mnIdx
);
371 if( isHittingOurTail
)
372 finish(rRes
); // just finish. no fuss.
375 // temp poly hits final left edge
376 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
377 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
379 // active edge's polygon has points
380 // already. ours need to go in front of them.
381 maPoints
.insert(maPoints
.end(),
382 rTmp
.maPoints
.begin(),
383 rTmp
.maPoints
.end());
385 // adjust leading edges, we're switching the polygon
386 ActiveEdge
* const pFarEdge
=rTmp
.mpLeadingRightEdge
;
388 mpLeadingRightEdge
= pFarEdge
;
389 pFarEdge
->setTargetPolygonIndex(mnIdx
);
391 // nTmpIdx is an empty shell, get rid of it
392 rPolygonPool
.free(nTmpIdx
);
396 std::ptrdiff_t handleComplexLeftEdge(ActiveEdge
& rActiveEdge
,
397 const B2DPoint
& rIntersectionPoint
,
398 VectorOfPolygons
& rPolygonPool
,
399 B2DPolyPolygon
& rRes
)
401 const bool isHittingOurTail(
402 rActiveEdge
.getTargetPolygonIndex() == mnIdx
);
403 if( isHittingOurTail
)
407 // so "this" is done - need new polygon to collect
409 const std::ptrdiff_t nIdxNewPolygon
=rPolygonPool
.alloc();
410 rPolygonPool
.get(nIdxNewPolygon
).setPolygonPoolIndex(nIdxNewPolygon
);
411 rPolygonPool
.get(nIdxNewPolygon
).append(rIntersectionPoint
);
413 rActiveEdge
.setTargetPolygonIndex(nIdxNewPolygon
);
415 return nIdxNewPolygon
;
419 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
420 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
422 // active edge's polygon has points
423 // already. ours need to go in front of them.
424 maPoints
.insert(maPoints
.end(),
425 rTmp
.maPoints
.begin(),
426 rTmp
.maPoints
.end());
428 rTmp
.maPoints
.clear();
429 rTmp
.append(rIntersectionPoint
);
431 // adjust leading edges, we're switching the polygon
432 ActiveEdge
* const pFarEdge
=rTmp
.mpLeadingRightEdge
;
433 ActiveEdge
* const pNearEdge
=&rActiveEdge
;
435 rTmp
.mpLeadingRightEdge
= nullptr;
436 pNearEdge
->setTargetPolygonIndex(nTmpIdx
);
438 mpLeadingRightEdge
= pFarEdge
;
439 pFarEdge
->setTargetPolygonIndex(mnIdx
);
445 std::ptrdiff_t handleComplexRightEdge(ActiveEdge
& rActiveEdge
,
446 const B2DPoint
& rIntersectionPoint
,
447 VectorOfPolygons
& rPolygonPool
)
449 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
450 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
452 rTmp
.append(rIntersectionPoint
);
454 rActiveEdge
.setTargetPolygonIndex(mnIdx
);
455 mpLeadingRightEdge
= &rActiveEdge
;
457 rTmp
.mpLeadingRightEdge
= nullptr;
462 /// True when sweep line hits our own active edge
463 static bool metOwnEdge(SweepLineEvent
const & rEvent
,
464 ActiveEdge
const & rActiveEdge
)
466 const bool bHitOwnEdge
=&rEvent
.getRect() == &rActiveEdge
.getRect();
470 /// Retrieve B2DPolygon from this object
471 B2DPolygon
getPolygon() const
474 for (auto const& aPoint
: maPoints
)
475 aRes
.append(aPoint
, 1);
476 aRes
.setClosed( true );
480 /** Finish this polygon, push to result set.
482 void finish(B2DPolyPolygon
& rRes
)
484 OSL_PRECOND( maPoints
.empty() ||
485 maPoints
.front().getX() == maPoints
.back().getX() ||
486 maPoints
.front().getY() == maPoints
.back().getY(),
487 "ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" );
490 mpLeadingRightEdge
= nullptr;
492 rRes
.append(getPolygon());
495 /** Refers to the current leading edge element of this
496 polygon, or NULL. The leading edge denotes the 'front'
497 of the polygon vertex sequence, i.e. the coordinates
498 at the polygon's leading edge are returned from
501 ActiveEdge
* mpLeadingRightEdge
;
503 /// current index into vector pool
504 std::ptrdiff_t mnIdx
;
506 /// Container for the actual polygon points
507 std::vector
<B2DPoint
> maPoints
;
509 /// When true, this polygon is 'done', i.e. nothing must be added anymore.
513 /** Init sweep line event list
515 This method fills the event list with the sweep line
516 events generated from the input rectangles, and sorts them
519 void setupSweepLineEventListFromRanges( VectorOfEvents
& o_rEventVector
,
520 const std::vector
<B2DRange
>& rRanges
,
521 const std::vector
<B2VectorOrientation
>& rOrientations
)
523 // we need exactly 2*rectVec.size() events: one for the
524 // left, and one for the right edge of each rectangle
525 o_rEventVector
.clear();
526 o_rEventVector
.reserve( 2*rRanges
.size() );
531 // first pass: add all left edges in increasing order
532 std::vector
<B2DRange
>::const_iterator aCurrRect
=rRanges
.begin();
533 std::vector
<B2VectorOrientation
>::const_iterator aCurrOrientation
=rOrientations
.begin();
534 const std::vector
<B2DRange
>::const_iterator aEnd
=rRanges
.end();
535 const std::vector
<B2VectorOrientation
>::const_iterator aEndOrientation
=rOrientations
.end();
536 while( aCurrRect
!= aEnd
&& aCurrOrientation
!= aEndOrientation
)
538 const B2DRectangle
& rCurrRect( *aCurrRect
++ );
540 o_rEventVector
.emplace_back( rCurrRect
.getMinX(),
542 SweepLineEvent::STARTING_EDGE
,
543 (*aCurrOrientation
++) == B2VectorOrientation::Positive
?
544 SweepLineEvent::PROCEED_UP
: SweepLineEvent::PROCEED_DOWN
);
547 // second pass: add all right edges in reversed order
548 std::vector
<B2DRange
>::const_reverse_iterator aCurrRectR
=rRanges
.rbegin();
549 std::vector
<B2VectorOrientation
>::const_reverse_iterator aCurrOrientationR
=rOrientations
.rbegin();
550 const std::vector
<B2DRange
>::const_reverse_iterator aEndR
=rRanges
.rend();
551 while( aCurrRectR
!= aEndR
)
553 const B2DRectangle
& rCurrRect( *aCurrRectR
++ );
555 o_rEventVector
.emplace_back( rCurrRect
.getMaxX(),
557 SweepLineEvent::FINISHING_EDGE
,
558 (*aCurrOrientationR
++) == B2VectorOrientation::Positive
?
559 SweepLineEvent::PROCEED_DOWN
: SweepLineEvent::PROCEED_UP
);
565 // since we use stable_sort, the order of events with the
566 // same x value will not change. The elaborate two-pass
567 // add above thus ensures, that for each two rectangles
568 // with similar left and right x coordinates, the
569 // rectangle whose left event comes first will have its
570 // right event come last. This is advantageous for the
571 // clip algorithm below, see handleRightEdgeCrossing().
573 std::stable_sort( o_rEventVector
.begin(),
574 o_rEventVector
.end() );
577 /** Insert two active edge segments for the given rectangle.
579 This method creates two active edge segments from the
580 given rect, and inserts them into the active edge list,
581 such that this stays sorted (if it was before).
584 Active edge list to insert into
587 Vector of polygons. Each rectangle added creates one
588 tentative result polygon in this vector, and the edge list
589 entries holds a reference to that polygon (this _requires_
590 that the polygon vector does not reallocate, i.e. it must
591 have at least the maximal number of rectangles reserved)
593 @param o_CurrentPolygon
594 The then-current polygon when processing this sweep line
598 The actual event that caused this call
600 void createActiveEdgesFromStartEvent( ListOfEdges
& io_rEdgeList
,
601 VectorOfPolygons
& io_rPolygonPool
,
602 SweepLineEvent
const & rCurrEvent
)
604 ListOfEdges aNewEdges
;
605 const B2DRectangle
& rRect
=rCurrEvent
.getRect();
606 const bool bGoesDown
=rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
;
608 // start event - new rect starts here, needs polygon to
609 // collect points into
610 const std::ptrdiff_t nIdxPolygon
=io_rPolygonPool
.alloc();
611 io_rPolygonPool
.get(nIdxPolygon
).setPolygonPoolIndex(nIdxPolygon
);
614 aNewEdges
.emplace_back(
617 bGoesDown
? nIdxPolygon
: -1,
618 bGoesDown
? ActiveEdge::PROCEED_LEFT
: ActiveEdge::PROCEED_RIGHT
);
620 aNewEdges
.emplace_back(
623 bGoesDown
? -1 : nIdxPolygon
,
624 bGoesDown
? ActiveEdge::PROCEED_RIGHT
: ActiveEdge::PROCEED_LEFT
);
626 // furthermore, have to respect a special tie-breaking
627 // rule here, for edges which share the same y value:
628 // newly added upper edges must be inserted _before_ any
629 // other edge with the same y value, and newly added lower
630 // edges must be _after_ all other edges with the same
631 // y. This ensures that the left vertical edge processing
632 // below encounters the upper edge of the current rect
633 // first, and the lower edge last, which automatically
634 // starts and finishes this rect correctly (as only then,
635 // the polygon will have their associated active edges
637 const double nMinY( rRect
.getMinY() );
638 const double nMaxY( rRect
.getMaxY() );
639 ListOfEdges::iterator
aCurr( io_rEdgeList
.begin() );
640 const ListOfEdges::iterator
aEnd ( io_rEdgeList
.end() );
641 while( aCurr
!= aEnd
)
643 const double nCurrY( aCurr
->getInvariantCoord() );
645 if( nCurrY
>= nMinY
&&
646 aNewEdges
.size() == 2 ) // only add, if not yet done.
648 // insert upper edge _before_ aCurr. Thus, it will
649 // be the first entry for a range of equal y
650 // values. Using splice here, since we hold
651 // references to the moved list element!
652 io_rEdgeList
.splice( aCurr
,
659 // insert lower edge _before_ aCurr. Thus, it will
660 // be the last entry for a range of equal y values
661 // (aCurr is the first entry strictly larger than
662 // nMaxY). Using splice here, since we hold
663 // references to the moved list element!
664 io_rEdgeList
.splice( aCurr
,
667 // done with insertion, can early-exit here.
674 // append remainder of aNewList (might still contain 2 or
675 // 1 elements, depending of the contents of io_rEdgeList).
676 io_rEdgeList
.splice( aCurr
,
680 bool isSameRect(ActiveEdge
const & rEdge
,
681 basegfx::B2DRange
const & rRect
)
683 return &rEdge
.getRect() == &rRect
;
686 // wow what a hack. necessary because stl's list::erase does
687 // not eat reverse_iterator
688 template<typename Cont
, typename Iter
> Iter
eraseFromList(Cont
&, const Iter
&);
689 template<> ListOfEdges::iterator
eraseFromList(
690 ListOfEdges
& rList
, const ListOfEdges::iterator
& aIter
)
692 return rList
.erase(aIter
);
694 template<> ListOfEdges::reverse_iterator
eraseFromList(
695 ListOfEdges
& rList
, const ListOfEdges::reverse_iterator
& aIter
)
697 return ListOfEdges::reverse_iterator(
698 rList
.erase(std::prev(aIter
.base())));
701 template<int bPerformErase
,
702 typename Iterator
> void processActiveEdges(
705 ListOfEdges
& rActiveEdgeList
,
706 SweepLineEvent
const & rCurrEvent
,
707 VectorOfPolygons
& rPolygonPool
,
708 B2DPolyPolygon
& rRes
)
710 const basegfx::B2DRange
& rCurrRect
=rCurrEvent
.getRect();
712 // fast-forward to rCurrEvent's first active edge (holds
713 // for both starting and finishing sweep line events, a
714 // rect is regarded _outside_ any rects whose events have
716 first
= std::find_if(first
, last
,
717 [&rCurrRect
](ActiveEdge
& anEdge
) { return isSameRect(anEdge
, rCurrRect
); });
723 std::ptrdiff_t nCurrPolyIdx
=-1;
726 if( nCurrPolyIdx
== -1 )
727 nCurrPolyIdx
=first
->getTargetPolygonIndex();
729 assert(nCurrPolyIdx
!= -1);
731 // second encounter of my rect -> second edge
738 // deal with current active edge
740 rPolygonPool
.get(nCurrPolyIdx
).intersect(
747 // prune upper & lower active edges, if requested
748 if( bPerformErase
&& (bExit
|| !nCount
) )
749 first
= eraseFromList(rActiveEdgeList
,first
);
753 // delayed exit, had to prune first
761 template<int bPerformErase
> void processActiveEdgesTopDown(
762 SweepLineEvent
& rCurrEvent
,
763 ListOfEdges
& rActiveEdgeList
,
764 VectorOfPolygons
& rPolygonPool
,
765 B2DPolyPolygon
& rRes
)
767 processActiveEdges
<bPerformErase
>(
768 rActiveEdgeList
. begin(),
769 rActiveEdgeList
. end(),
776 template<int bPerformErase
> void processActiveEdgesBottomUp(
777 SweepLineEvent
& rCurrEvent
,
778 ListOfEdges
& rActiveEdgeList
,
779 VectorOfPolygons
& rPolygonPool
,
780 B2DPolyPolygon
& rRes
)
782 processActiveEdges
<bPerformErase
>(
783 rActiveEdgeList
. rbegin(),
784 rActiveEdgeList
. rend(),
791 enum{ NoErase
=0, PerformErase
=1 };
793 void handleStartingEdge( SweepLineEvent
& rCurrEvent
,
794 ListOfEdges
& rActiveEdgeList
,
795 VectorOfPolygons
& rPolygonPool
,
796 B2DPolyPolygon
& rRes
)
798 // inject two new active edges for rect
799 createActiveEdgesFromStartEvent( rActiveEdgeList
,
803 if( rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
)
804 processActiveEdgesTopDown
<NoErase
>(
805 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
807 processActiveEdgesBottomUp
<NoErase
>(
808 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
811 void handleFinishingEdge( SweepLineEvent
& rCurrEvent
,
812 ListOfEdges
& rActiveEdgeList
,
813 VectorOfPolygons
& rPolygonPool
,
814 B2DPolyPolygon
& rRes
)
816 if( rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
)
817 processActiveEdgesTopDown
<PerformErase
>(
818 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
820 processActiveEdgesBottomUp
<PerformErase
>(
821 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
824 void handleSweepLineEvent( SweepLineEvent
& rCurrEvent
,
825 ListOfEdges
& rActiveEdgeList
,
826 VectorOfPolygons
& rPolygonPool
,
827 B2DPolyPolygon
& rRes
)
829 if( rCurrEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
)
830 handleStartingEdge(rCurrEvent
,rActiveEdgeList
,rPolygonPool
,rRes
);
832 handleFinishingEdge(rCurrEvent
,rActiveEdgeList
,rPolygonPool
,rRes
);
838 B2DPolyPolygon
solveCrossovers(const std::vector
<B2DRange
>& rRanges
,
839 const std::vector
<B2VectorOrientation
>& rOrientations
)
841 // sweep-line algorithm to generate a poly-polygon
842 // from a bunch of rectangles
843 // ===============================================
845 // This algorithm uses the well-known sweep line
846 // concept, explained in every good text book about
847 // computational geometry.
849 // We start with creating two structures for every
850 // rectangle, one representing the left x coordinate,
851 // one representing the right x coordinate (and both
852 // referencing the original rect). These structs are
853 // sorted with increasing x coordinates.
855 // Then, we start processing the resulting list from
856 // the beginning. Every entry in the list defines a
857 // point in time of the line sweeping from left to
858 // right across all rectangles.
859 VectorOfEvents aSweepLineEvents
;
860 setupSweepLineEventListFromRanges( aSweepLineEvents
,
865 VectorOfPolygons aPolygonPool
;
866 ListOfEdges aActiveEdgeList
;
868 // sometimes not enough, but a usable compromise
869 aPolygonPool
.reserve( rRanges
.size() );
871 for (auto& aSweepLineEvent
: aSweepLineEvents
)
872 handleSweepLineEvent(aSweepLineEvent
, aActiveEdgeList
, aPolygonPool
, aRes
);
879 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */