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/range/b2drange.hxx>
23 #include <basegfx/range/b2drangeclipper.hxx>
24 #include <basegfx/polygon/b2dpolypolygon.hxx>
25 #include <basegfx/range/b2drectangle.hxx>
27 #include <o3tl/vector_pool.hxx>
38 // Generating a poly-polygon from a bunch of rectangles
40 // Helper functionality for sweep-line algorithm
41 // ====================================================
44 typedef o3tl::vector_pool
<ImplPolygon
> VectorOfPolygons
;
46 /** This class represents an active edge
48 As the sweep line traverses across the overall area,
49 rectangle edges parallel to it generate events, and
50 rectangle edges orthogonal to it generate active
51 edges. This class represents the latter.
58 /// edge proceeds to the left
60 /// edge proceeds to the right
64 /** Create active edge
67 Rectangle this edge is part of
69 @param fInvariantCoord
70 The invariant coordinate value of this edge
73 Is fInvariantCoord the lower or the higher value, for
76 ActiveEdge( const B2DRectangle
& rRect
,
77 const double& fInvariantCoord
,
78 std::ptrdiff_t nPolyIdx
,
79 EdgeDirection eEdgeDirection
) :
80 mfInvariantCoord(fInvariantCoord
),
81 mpAssociatedRect( &rRect
),
82 mnPolygonIdx( nPolyIdx
),
83 meEdgeDirection( eEdgeDirection
)
86 double getInvariantCoord() const { return mfInvariantCoord
; }
87 const B2DRectangle
& getRect() const { return *mpAssociatedRect
; }
88 std::ptrdiff_t getTargetPolygonIndex() const { return mnPolygonIdx
; }
89 void setTargetPolygonIndex( std::ptrdiff_t nIdx
) { mnPolygonIdx
= nIdx
; }
90 EdgeDirection
getEdgeDirection() const { return meEdgeDirection
; }
93 /** The invariant coordinate value of this edge (e.g. the
94 common y value, for a horizontal edge)
96 double mfInvariantCoord
;
98 /** Associated rectangle
100 This on the one hand saves some storage space (the
101 vector of rectangles is persistent, anyway), and on
102 the other hand provides an identifier to match active
103 edges and x events (see below)
105 Ptr because class needs to be assignable
107 const B2DRectangle
* mpAssociatedRect
;
109 /** Index of the polygon this edge is currently involved
112 Note that this can change for some kinds of edge
113 intersection, as the algorithm tends to swap
114 associated polygons there.
116 -1 denotes no assigned polygon
118 std::ptrdiff_t mnPolygonIdx
;
120 /// 'left' or 'right'
121 EdgeDirection meEdgeDirection
;
124 // Needs to be list - various places hold ptrs to elements
125 typedef std::list
< ActiveEdge
> ListOfEdges
;
127 /** Element of the sweep line event list
129 As the sweep line traverses across the overall area,
130 rectangle edges parallel to it generate events, and
131 rectangle edges orthogonal to it generate active
132 edges. This class represents the former.
134 The class defines an element of the sweep line list. The
135 sweep line's position jumps in steps defined by the
136 coordinates of the sorted SweepLineEvent entries.
141 /** The two possible sweep line rectangle edges differ by
142 one coordinate value - the starting edge has the
143 lower, the finishing edge the higher value.
146 /// edge with lower coordinate value
148 /// edge with higher coordinate value
152 /** The two possible sweep line directions
159 /** Create sweep line event
162 Coordinate position of the event
165 Rectangle this event is generated for.
168 Is fPos the lower or the higher value, for the
169 rectangle this event is generated for?
171 SweepLineEvent( double fPos
,
172 const B2DRectangle
& rRect
,
174 EdgeDirection eDirection
) :
176 mpAssociatedRect( &rRect
),
177 meEdgeType( eEdgeType
),
178 meEdgeDirection( eDirection
)
181 double getPos() const { return mfPos
; }
182 const B2DRectangle
& getRect() const { return *mpAssociatedRect
; }
183 EdgeType
getEdgeType() const { return meEdgeType
; }
184 EdgeDirection
getEdgeDirection() const { return meEdgeDirection
; }
187 bool operator<( const SweepLineEvent
& rRHS
) const { return mfPos
< rRHS
.mfPos
; }
190 /// position of the event, in the direction of the line sweep
193 /** Rectangle this event is generated for
195 This on the one hand saves some storage space (the
196 vector of rectangles is persistent, anyway), and on
197 the other hand provides an identifier to match active
198 edges and events (see below)
200 Ptr because class needs to be assignable
202 const B2DRectangle
* mpAssociatedRect
;
204 /// 'upper' or 'lower' edge of original rectangle.
208 EdgeDirection meEdgeDirection
;
211 typedef std::vector
< SweepLineEvent
> VectorOfEvents
;
213 /** Smart point container for B2DMultiRange::getPolyPolygon()
215 This class provides methods needed only here, and is used
216 as a place to store some additional information per
217 polygon. Also, most of the intersection logic is
226 mpLeadingRightEdge(nullptr),
230 // completely ad-hoc. but what the hell.
231 maPoints
.reserve(11);
234 void setPolygonPoolIndex( std::ptrdiff_t nIdx
) { mnIdx
= nIdx
; }
236 /// Add point to the end of the existing points
237 void append( const B2DPoint
& rPoint
)
239 OSL_PRECOND( maPoints
.empty() ||
240 maPoints
.back().getX() == rPoint
.getX() ||
241 maPoints
.back().getY() == rPoint
.getY(),
242 "ImplPolygon::append(): added point violates 90 degree line angle constraint!" );
244 if( maPoints
.empty() ||
245 maPoints
.back() != rPoint
)
247 // avoid duplicate points
248 maPoints
.push_back( rPoint
);
252 /** Perform the intersection of this polygon with an
256 The vertical line event that generated the
260 The active edge that generated the intersection
263 Polygon pool, we sometimes need to allocate a new one
265 @param bIsFinishingEdge
266 True, when this is hitting the last edge of the
267 vertical sweep - every vertical sweep starts and ends
268 with upper and lower edge of the _same_ rectangle.
270 @return the new current polygon (that's the one
271 processing must proceed with, when going through the
272 list of upcoming active edges).
274 std::ptrdiff_t intersect( SweepLineEvent
const & rEvent
,
275 ActiveEdge
& rActiveEdge
,
276 VectorOfPolygons
& rPolygonPool
,
277 B2DPolyPolygon
& rRes
,
278 bool isFinishingEdge
)
280 OSL_PRECOND( !mbIsFinished
,
281 "ImplPolygon::intersect(): called on already finished polygon!" );
282 OSL_PRECOND( !isFinishingEdge
|| &rEvent
.getRect() == &rActiveEdge
.getRect(),
283 "ImplPolygon::intersect(): inconsistent ending!" );
285 const B2DPoint
aIntersectionPoint( rEvent
.getPos(),
286 rActiveEdge
.getInvariantCoord() );
288 // intersection point, goes to our polygon
290 append(aIntersectionPoint
);
292 if( isFinishingEdge
)
294 // isSweepLineEnteringRect ?
295 if( rEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
)
296 handleFinalOwnRightEdge(rActiveEdge
);
298 handleFinalOwnLeftEdge(rActiveEdge
,
302 // we're done with this rect & sweep line
305 else if( metOwnEdge(rEvent
,rActiveEdge
) )
307 handleInitialOwnEdge(rEvent
, rActiveEdge
);
309 // point already added, all init done, continue
315 OSL_ENSURE( rActiveEdge
.getTargetPolygonIndex() != -1,
316 "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
318 const bool isHittingLeftEdge(
319 rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
);
321 if( isHittingLeftEdge
)
322 return handleComplexLeftEdge(rActiveEdge
,
327 return handleComplexRightEdge(rActiveEdge
,
334 void handleInitialOwnEdge(SweepLineEvent
const & rEvent
,
335 ActiveEdge
const & rActiveEdge
) const
337 const bool isActiveEdgeProceedLeft(
338 rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
);
339 const bool isSweepLineEnteringRect(
340 rEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
);
342 OSL_ENSURE( isSweepLineEnteringRect
== isActiveEdgeProceedLeft
,
343 "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
345 OSL_ENSURE( isSweepLineEnteringRect
||
346 mpLeadingRightEdge
== &rActiveEdge
,
347 "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
350 void handleFinalOwnRightEdge(ActiveEdge
& rActiveEdge
)
352 OSL_ENSURE( rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT
,
353 "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
355 rActiveEdge
.setTargetPolygonIndex(mnIdx
);
356 mpLeadingRightEdge
= &rActiveEdge
;
359 void handleFinalOwnLeftEdge(ActiveEdge
const & rActiveEdge
,
360 VectorOfPolygons
& rPolygonPool
,
361 B2DPolyPolygon
& rRes
)
363 OSL_ENSURE( rActiveEdge
.getEdgeDirection() == ActiveEdge::PROCEED_LEFT
,
364 "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
366 const bool isHittingOurTail(
367 rActiveEdge
.getTargetPolygonIndex() == mnIdx
);
369 if( isHittingOurTail
)
370 finish(rRes
); // just finish. no fuss.
373 // temp poly hits final left edge
374 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
375 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
377 // active edge's polygon has points
378 // already. ours need to go in front of them.
379 maPoints
.insert(maPoints
.end(),
380 rTmp
.maPoints
.begin(),
381 rTmp
.maPoints
.end());
383 // adjust leading edges, we're switching the polygon
384 ActiveEdge
* const pFarEdge
=rTmp
.mpLeadingRightEdge
;
386 mpLeadingRightEdge
= pFarEdge
;
387 pFarEdge
->setTargetPolygonIndex(mnIdx
);
389 // nTmpIdx is an empty shell, get rid of it
390 rPolygonPool
.free(nTmpIdx
);
394 std::ptrdiff_t handleComplexLeftEdge(ActiveEdge
& rActiveEdge
,
395 const B2DPoint
& rIntersectionPoint
,
396 VectorOfPolygons
& rPolygonPool
,
397 B2DPolyPolygon
& rRes
)
399 const bool isHittingOurTail(
400 rActiveEdge
.getTargetPolygonIndex() == mnIdx
);
401 if( isHittingOurTail
)
405 // so "this" is done - need new polygon to collect
407 const std::ptrdiff_t nIdxNewPolygon
=rPolygonPool
.alloc();
408 rPolygonPool
.get(nIdxNewPolygon
).setPolygonPoolIndex(nIdxNewPolygon
);
409 rPolygonPool
.get(nIdxNewPolygon
).append(rIntersectionPoint
);
411 rActiveEdge
.setTargetPolygonIndex(nIdxNewPolygon
);
413 return nIdxNewPolygon
;
417 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
418 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
420 // active edge's polygon has points
421 // already. ours need to go in front of them.
422 maPoints
.insert(maPoints
.end(),
423 rTmp
.maPoints
.begin(),
424 rTmp
.maPoints
.end());
426 rTmp
.maPoints
.clear();
427 rTmp
.append(rIntersectionPoint
);
429 // adjust leading edges, we're switching the polygon
430 ActiveEdge
* const pFarEdge
=rTmp
.mpLeadingRightEdge
;
431 ActiveEdge
* const pNearEdge
=&rActiveEdge
;
433 rTmp
.mpLeadingRightEdge
= nullptr;
434 pNearEdge
->setTargetPolygonIndex(nTmpIdx
);
436 mpLeadingRightEdge
= pFarEdge
;
437 pFarEdge
->setTargetPolygonIndex(mnIdx
);
443 std::ptrdiff_t handleComplexRightEdge(ActiveEdge
& rActiveEdge
,
444 const B2DPoint
& rIntersectionPoint
,
445 VectorOfPolygons
& rPolygonPool
)
447 const std::ptrdiff_t nTmpIdx
=rActiveEdge
.getTargetPolygonIndex();
448 ImplPolygon
& rTmp
=rPolygonPool
.get(nTmpIdx
);
450 rTmp
.append(rIntersectionPoint
);
452 rActiveEdge
.setTargetPolygonIndex(mnIdx
);
453 mpLeadingRightEdge
= &rActiveEdge
;
455 rTmp
.mpLeadingRightEdge
= nullptr;
460 /// True when sweep line hits our own active edge
461 static bool metOwnEdge(SweepLineEvent
const & rEvent
,
462 ActiveEdge
const & rActiveEdge
)
464 const bool bHitOwnEdge
=&rEvent
.getRect() == &rActiveEdge
.getRect();
468 /// Retrieve B2DPolygon from this object
469 B2DPolygon
getPolygon() const
472 for (auto const& aPoint
: maPoints
)
473 aRes
.append(aPoint
, 1);
474 aRes
.setClosed( true );
478 /** Finish this polygon, push to result set.
480 void finish(B2DPolyPolygon
& rRes
)
482 OSL_PRECOND( maPoints
.empty() ||
483 maPoints
.front().getX() == maPoints
.back().getX() ||
484 maPoints
.front().getY() == maPoints
.back().getY(),
485 "ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" );
488 mpLeadingRightEdge
= nullptr;
490 rRes
.append(getPolygon());
493 /** Refers to the current leading edge element of this
494 polygon, or NULL. The leading edge denotes the 'front'
495 of the polygon vertex sequence, i.e. the coordinates
496 at the polygon's leading edge are returned from
499 ActiveEdge
* mpLeadingRightEdge
;
501 /// current index into vector pool
502 std::ptrdiff_t mnIdx
;
504 /// Container for the actual polygon points
505 std::vector
<B2DPoint
> maPoints
;
507 /// When true, this polygon is 'done', i.e. nothing must be added anymore.
511 /** Init sweep line event list
513 This method fills the event list with the sweep line
514 events generated from the input rectangles, and sorts them
517 void setupSweepLineEventListFromRanges( VectorOfEvents
& o_rEventVector
,
518 const std::vector
<B2DRange
>& rRanges
,
519 const std::vector
<B2VectorOrientation
>& rOrientations
)
521 // we need exactly 2*rectVec.size() events: one for the
522 // left, and one for the right edge of each rectangle
523 o_rEventVector
.clear();
524 o_rEventVector
.reserve( 2*rRanges
.size() );
529 // first pass: add all left edges in increasing order
530 std::vector
<B2DRange
>::const_iterator aCurrRect
=rRanges
.begin();
531 std::vector
<B2VectorOrientation
>::const_iterator aCurrOrientation
=rOrientations
.begin();
532 const std::vector
<B2DRange
>::const_iterator aEnd
=rRanges
.end();
533 const std::vector
<B2VectorOrientation
>::const_iterator aEndOrientation
=rOrientations
.end();
534 while( aCurrRect
!= aEnd
&& aCurrOrientation
!= aEndOrientation
)
536 const B2DRectangle
& rCurrRect( *aCurrRect
++ );
538 o_rEventVector
.emplace_back( rCurrRect
.getMinX(),
540 SweepLineEvent::STARTING_EDGE
,
541 (*aCurrOrientation
++) == B2VectorOrientation::Positive
?
542 SweepLineEvent::PROCEED_UP
: SweepLineEvent::PROCEED_DOWN
);
545 // second pass: add all right edges in reversed order
546 std::vector
<B2DRange
>::const_reverse_iterator aCurrRectR
=rRanges
.rbegin();
547 std::vector
<B2VectorOrientation
>::const_reverse_iterator aCurrOrientationR
=rOrientations
.rbegin();
548 const std::vector
<B2DRange
>::const_reverse_iterator aEndR
=rRanges
.rend();
549 while( aCurrRectR
!= aEndR
)
551 const B2DRectangle
& rCurrRect( *aCurrRectR
++ );
553 o_rEventVector
.emplace_back( rCurrRect
.getMaxX(),
555 SweepLineEvent::FINISHING_EDGE
,
556 (*aCurrOrientationR
++) == B2VectorOrientation::Positive
?
557 SweepLineEvent::PROCEED_DOWN
: SweepLineEvent::PROCEED_UP
);
563 // since we use stable_sort, the order of events with the
564 // same x value will not change. The elaborate two-pass
565 // add above thus ensures, that for each two rectangles
566 // with similar left and right x coordinates, the
567 // rectangle whose left event comes first will have its
568 // right event come last. This is advantageous for the
569 // clip algorithm below, see handleRightEdgeCrossing().
571 std::stable_sort( o_rEventVector
.begin(),
572 o_rEventVector
.end() );
575 /** Insert two active edge segments for the given rectangle.
577 This method creates two active edge segments from the
578 given rect, and inserts them into the active edge list,
579 such that this stays sorted (if it was before).
582 Active edge list to insert into
585 Vector of polygons. Each rectangle added creates one
586 tentative result polygon in this vector, and the edge list
587 entries holds a reference to that polygon (this _requires_
588 that the polygon vector does not reallocate, i.e. it must
589 have at least the maximal number of rectangles reserved)
591 @param o_CurrentPolygon
592 The then-current polygon when processing this sweep line
596 The actual event that caused this call
598 void createActiveEdgesFromStartEvent( ListOfEdges
& io_rEdgeList
,
599 VectorOfPolygons
& io_rPolygonPool
,
600 SweepLineEvent
const & rCurrEvent
)
602 ListOfEdges aNewEdges
;
603 const B2DRectangle
& rRect
=rCurrEvent
.getRect();
604 const bool bGoesDown
=rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
;
606 // start event - new rect starts here, needs polygon to
607 // collect points into
608 const std::ptrdiff_t nIdxPolygon
=io_rPolygonPool
.alloc();
609 io_rPolygonPool
.get(nIdxPolygon
).setPolygonPoolIndex(nIdxPolygon
);
612 aNewEdges
.emplace_back(
615 bGoesDown
? nIdxPolygon
: -1,
616 bGoesDown
? ActiveEdge::PROCEED_LEFT
: ActiveEdge::PROCEED_RIGHT
);
618 aNewEdges
.emplace_back(
621 bGoesDown
? -1 : nIdxPolygon
,
622 bGoesDown
? ActiveEdge::PROCEED_RIGHT
: ActiveEdge::PROCEED_LEFT
);
624 // furthermore, have to respect a special tie-breaking
625 // rule here, for edges which share the same y value:
626 // newly added upper edges must be inserted _before_ any
627 // other edge with the same y value, and newly added lower
628 // edges must be _after_ all other edges with the same
629 // y. This ensures that the left vertical edge processing
630 // below encounters the upper edge of the current rect
631 // first, and the lower edge last, which automatically
632 // starts and finishes this rect correctly (as only then,
633 // the polygon will have their associated active edges
635 const double nMinY( rRect
.getMinY() );
636 const double nMaxY( rRect
.getMaxY() );
637 ListOfEdges::iterator
aCurr( io_rEdgeList
.begin() );
638 const ListOfEdges::iterator
aEnd ( io_rEdgeList
.end() );
639 while( aCurr
!= aEnd
)
641 const double nCurrY( aCurr
->getInvariantCoord() );
643 if( nCurrY
>= nMinY
&&
644 aNewEdges
.size() == 2 ) // only add, if not yet done.
646 // insert upper edge _before_ aCurr. Thus, it will
647 // be the first entry for a range of equal y
648 // values. Using splice here, since we hold
649 // references to the moved list element!
650 io_rEdgeList
.splice( aCurr
,
657 // insert lower edge _before_ aCurr. Thus, it will
658 // be the last entry for a range of equal y values
659 // (aCurr is the first entry strictly larger than
660 // nMaxY). Using splice here, since we hold
661 // references to the moved list element!
662 io_rEdgeList
.splice( aCurr
,
665 // done with insertion, can early-exit here.
672 // append remainder of aNewList (might still contain 2 or
673 // 1 elements, depending of the contents of io_rEdgeList).
674 io_rEdgeList
.splice( aCurr
,
678 bool isSameRect(ActiveEdge
const & rEdge
,
679 basegfx::B2DRange
const & rRect
)
681 return &rEdge
.getRect() == &rRect
;
684 // wow what a hack. necessary because stl's list::erase does
685 // not eat reverse_iterator
686 template<typename Cont
, typename Iter
> Iter
eraseFromList(Cont
&, const Iter
&);
687 template<> ListOfEdges::iterator
eraseFromList(
688 ListOfEdges
& rList
, const ListOfEdges::iterator
& aIter
)
690 return rList
.erase(aIter
);
692 template<> ListOfEdges::reverse_iterator
eraseFromList(
693 ListOfEdges
& rList
, const ListOfEdges::reverse_iterator
& aIter
)
695 return ListOfEdges::reverse_iterator(
696 rList
.erase(std::prev(aIter
.base())));
699 template<int bPerformErase
,
700 typename Iterator
> void processActiveEdges(
703 ListOfEdges
& rActiveEdgeList
,
704 SweepLineEvent
const & rCurrEvent
,
705 VectorOfPolygons
& rPolygonPool
,
706 B2DPolyPolygon
& rRes
)
708 const basegfx::B2DRange
& rCurrRect
=rCurrEvent
.getRect();
710 // fast-forward to rCurrEvent's first active edge (holds
711 // for both starting and finishing sweep line events, a
712 // rect is regarded _outside_ any rects whose events have
714 first
= std::find_if(first
, last
,
715 [&rCurrRect
](ActiveEdge
& anEdge
) { return isSameRect(anEdge
, rCurrRect
); });
721 std::ptrdiff_t nCurrPolyIdx
=-1;
724 if( nCurrPolyIdx
== -1 )
725 nCurrPolyIdx
=first
->getTargetPolygonIndex();
727 assert(nCurrPolyIdx
!= -1);
729 // second encounter of my rect -> second edge
736 // deal with current active edge
738 rPolygonPool
.get(nCurrPolyIdx
).intersect(
745 // prune upper & lower active edges, if requested
746 if( bPerformErase
&& (bExit
|| !nCount
) )
747 first
= eraseFromList(rActiveEdgeList
,first
);
751 // delayed exit, had to prune first
759 template<int bPerformErase
> void processActiveEdgesTopDown(
760 SweepLineEvent
& rCurrEvent
,
761 ListOfEdges
& rActiveEdgeList
,
762 VectorOfPolygons
& rPolygonPool
,
763 B2DPolyPolygon
& rRes
)
765 processActiveEdges
<bPerformErase
>(
766 rActiveEdgeList
. begin(),
767 rActiveEdgeList
. end(),
774 template<int bPerformErase
> void processActiveEdgesBottomUp(
775 SweepLineEvent
& rCurrEvent
,
776 ListOfEdges
& rActiveEdgeList
,
777 VectorOfPolygons
& rPolygonPool
,
778 B2DPolyPolygon
& rRes
)
780 processActiveEdges
<bPerformErase
>(
781 rActiveEdgeList
. rbegin(),
782 rActiveEdgeList
. rend(),
789 enum{ NoErase
=0, PerformErase
=1 };
791 void handleStartingEdge( SweepLineEvent
& rCurrEvent
,
792 ListOfEdges
& rActiveEdgeList
,
793 VectorOfPolygons
& rPolygonPool
,
794 B2DPolyPolygon
& rRes
)
796 // inject two new active edges for rect
797 createActiveEdgesFromStartEvent( rActiveEdgeList
,
801 if( rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
)
802 processActiveEdgesTopDown
<NoErase
>(
803 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
805 processActiveEdgesBottomUp
<NoErase
>(
806 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
809 void handleFinishingEdge( SweepLineEvent
& rCurrEvent
,
810 ListOfEdges
& rActiveEdgeList
,
811 VectorOfPolygons
& rPolygonPool
,
812 B2DPolyPolygon
& rRes
)
814 if( rCurrEvent
.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN
)
815 processActiveEdgesTopDown
<PerformErase
>(
816 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
818 processActiveEdgesBottomUp
<PerformErase
>(
819 rCurrEvent
, rActiveEdgeList
, rPolygonPool
, rRes
);
822 void handleSweepLineEvent( SweepLineEvent
& rCurrEvent
,
823 ListOfEdges
& rActiveEdgeList
,
824 VectorOfPolygons
& rPolygonPool
,
825 B2DPolyPolygon
& rRes
)
827 if( rCurrEvent
.getEdgeType() == SweepLineEvent::STARTING_EDGE
)
828 handleStartingEdge(rCurrEvent
,rActiveEdgeList
,rPolygonPool
,rRes
);
830 handleFinishingEdge(rCurrEvent
,rActiveEdgeList
,rPolygonPool
,rRes
);
836 B2DPolyPolygon
solveCrossovers(const std::vector
<B2DRange
>& rRanges
,
837 const std::vector
<B2VectorOrientation
>& rOrientations
)
839 // sweep-line algorithm to generate a poly-polygon
840 // from a bunch of rectangles
841 // ===============================================
843 // This algorithm uses the well-known sweep line
844 // concept, explained in every good text book about
845 // computational geometry.
847 // We start with creating two structures for every
848 // rectangle, one representing the left x coordinate,
849 // one representing the right x coordinate (and both
850 // referencing the original rect). These structs are
851 // sorted with increasing x coordinates.
853 // Then, we start processing the resulting list from
854 // the beginning. Every entry in the list defines a
855 // point in time of the line sweeping from left to
856 // right across all rectangles.
857 VectorOfEvents aSweepLineEvents
;
858 setupSweepLineEventListFromRanges( aSweepLineEvents
,
863 VectorOfPolygons aPolygonPool
;
864 ListOfEdges aActiveEdgeList
;
866 // sometimes not enough, but a usable compromise
867 aPolygonPool
.reserve( rRanges
.size() );
869 for (auto& aSweepLineEvent
: aSweepLineEvents
)
870 handleSweepLineEvent(aSweepLineEvent
, aActiveEdgeList
, aPolygonPool
, aRes
);
877 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */