Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / basegfx / source / range / b2drangeclipper.cxx
blob796551832dc3cd9cb98ee9743e0db032a0d79de8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
30 #include <algorithm>
31 #include <list>
32 #include <iterator>
34 namespace basegfx
36 namespace
38 // Generating a poly-polygon from a bunch of rectangles
40 // Helper functionality for sweep-line algorithm
41 // ====================================================
43 class ImplPolygon;
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.
53 class ActiveEdge
55 public:
57 enum EdgeDirection {
58 /// edge proceeds to the left
59 PROCEED_LEFT=0,
60 /// edge proceeds to the right
61 PROCEED_RIGHT=1
64 /** Create active edge
66 @param rRect
67 Rectangle this edge is part of
69 @param fInvariantCoord
70 The invariant coordinate value of this edge
72 @param eEdgeType
73 Is fInvariantCoord the lower or the higher value, for
74 this rect?
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; }
92 private:
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
110 with.
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.
138 class SweepLineEvent
140 public:
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.
145 enum EdgeType {
146 /// edge with lower coordinate value
147 STARTING_EDGE=0,
148 /// edge with higher coordinate value
149 FINISHING_EDGE=1
152 /** The two possible sweep line directions
154 enum EdgeDirection {
155 PROCEED_UP=0,
156 PROCEED_DOWN=1
159 /** Create sweep line event
161 @param fPos
162 Coordinate position of the event
164 @param rRect
165 Rectangle this event is generated for.
167 @param eEdgeType
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,
173 EdgeType eEdgeType,
174 EdgeDirection eDirection) :
175 mfPos( fPos ),
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; }
186 /// For STL sort
187 bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; }
189 private:
190 /// position of the event, in the direction of the line sweep
191 double mfPos;
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.
205 EdgeType meEdgeType;
207 /// 'up' or 'down'
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
218 implemented here.
220 class ImplPolygon
222 public:
223 /** Create polygon
225 ImplPolygon() :
226 mpLeadingRightEdge(nullptr),
227 mnIdx(-1),
228 maPoints(),
229 mbIsFinished(false)
231 // completely ad-hoc. but what the hell.
232 maPoints.reserve(11);
235 void setPolygonPoolIndex( std::ptrdiff_t nIdx ) { mnIdx = nIdx; }
237 /// Add point to the end of the existing points
238 void append( const B2DPoint& rPoint )
240 OSL_PRECOND( maPoints.empty() ||
241 maPoints.back().getX() == rPoint.getX() ||
242 maPoints.back().getY() == rPoint.getY(),
243 "ImplPolygon::append(): added point violates 90 degree line angle constraint!" );
245 if( maPoints.empty() ||
246 maPoints.back() != rPoint )
248 // avoid duplicate points
249 maPoints.push_back( rPoint );
253 /** Perform the intersection of this polygon with an
254 active edge.
256 @param rEvent
257 The vertical line event that generated the
258 intersection
260 @param rActiveEdge
261 The active edge that generated the intersection
263 @param rPolygonPool
264 Polygon pool, we sometimes need to allocate a new one
266 @param bIsFinishingEdge
267 True, when this is hitting the last edge of the
268 vertical sweep - every vertical sweep starts and ends
269 with upper and lower edge of the _same_ rectangle.
271 @return the new current polygon (that's the one
272 processing must proceed with, when going through the
273 list of upcoming active edges).
275 std::ptrdiff_t intersect( SweepLineEvent const & rEvent,
276 ActiveEdge& rActiveEdge,
277 VectorOfPolygons& rPolygonPool,
278 B2DPolyPolygon& rRes,
279 bool isFinishingEdge )
281 OSL_PRECOND( !mbIsFinished,
282 "ImplPolygon::intersect(): called on already finished polygon!" );
283 OSL_PRECOND( !isFinishingEdge || &rEvent.getRect() == &rActiveEdge.getRect(),
284 "ImplPolygon::intersect(): inconsistent ending!" );
286 const B2DPoint aIntersectionPoint( rEvent.getPos(),
287 rActiveEdge.getInvariantCoord() );
289 // intersection point, goes to our polygon
290 // unconditionally
291 append(aIntersectionPoint);
293 if( isFinishingEdge )
295 // isSweepLineEnteringRect ?
296 if( rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE)
297 handleFinalOwnRightEdge(rActiveEdge);
298 else
299 handleFinalOwnLeftEdge(rActiveEdge,
300 rPolygonPool,
301 rRes);
303 // we're done with this rect & sweep line
304 return -1;
306 else if( metOwnEdge(rEvent,rActiveEdge) )
308 handleInitialOwnEdge(rEvent, rActiveEdge);
310 // point already added, all init done, continue
311 // with same poly
312 return mnIdx;
314 else
316 OSL_ENSURE( rActiveEdge.getTargetPolygonIndex() != -1,
317 "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
319 const bool isHittingLeftEdge(
320 rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
322 if( isHittingLeftEdge )
323 return handleComplexLeftEdge(rActiveEdge,
324 aIntersectionPoint,
325 rPolygonPool,
326 rRes);
327 else
328 return handleComplexRightEdge(rActiveEdge,
329 aIntersectionPoint,
330 rPolygonPool);
334 private:
335 void handleInitialOwnEdge(SweepLineEvent const & rEvent,
336 ActiveEdge& rActiveEdge) const
338 const bool isActiveEdgeProceedLeft(
339 rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
340 const bool isSweepLineEnteringRect(
341 rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE);
343 OSL_ENSURE( isSweepLineEnteringRect == isActiveEdgeProceedLeft,
344 "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
346 OSL_ENSURE( isSweepLineEnteringRect ||
347 mpLeadingRightEdge == &rActiveEdge,
348 "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
351 void handleFinalOwnRightEdge(ActiveEdge& rActiveEdge)
353 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT,
354 "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
356 rActiveEdge.setTargetPolygonIndex(mnIdx);
357 mpLeadingRightEdge = &rActiveEdge;
360 void handleFinalOwnLeftEdge(ActiveEdge const & rActiveEdge,
361 VectorOfPolygons& rPolygonPool,
362 B2DPolyPolygon& rRes)
364 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT,
365 "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
367 const bool isHittingOurTail(
368 rActiveEdge.getTargetPolygonIndex() == mnIdx);
370 if( isHittingOurTail )
371 finish(rRes); // just finish. no fuss.
372 else
374 // temp poly hits final left edge
375 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
376 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
378 // active edge's polygon has points
379 // already. ours need to go in front of them.
380 maPoints.insert(maPoints.end(),
381 rTmp.maPoints.begin(),
382 rTmp.maPoints.end());
384 // adjust leading edges, we're switching the polygon
385 ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
387 mpLeadingRightEdge = pFarEdge;
388 pFarEdge->setTargetPolygonIndex(mnIdx);
390 // nTmpIdx is an empty shell, get rid of it
391 rPolygonPool.free(nTmpIdx);
395 std::ptrdiff_t handleComplexLeftEdge(ActiveEdge& rActiveEdge,
396 const B2DPoint& rIntersectionPoint,
397 VectorOfPolygons& rPolygonPool,
398 B2DPolyPolygon& rRes)
400 const bool isHittingOurTail(
401 rActiveEdge.getTargetPolygonIndex() == mnIdx);
402 if( isHittingOurTail )
404 finish(rRes);
406 // so "this" is done - need new polygon to collect
407 // further points
408 const std::ptrdiff_t nIdxNewPolygon=rPolygonPool.alloc();
409 rPolygonPool.get(nIdxNewPolygon).setPolygonPoolIndex(nIdxNewPolygon);
410 rPolygonPool.get(nIdxNewPolygon).append(rIntersectionPoint);
412 rActiveEdge.setTargetPolygonIndex(nIdxNewPolygon);
414 return nIdxNewPolygon;
416 else
418 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
419 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
421 // active edge's polygon has points
422 // already. ours need to go in front of them.
423 maPoints.insert(maPoints.end(),
424 rTmp.maPoints.begin(),
425 rTmp.maPoints.end());
427 rTmp.maPoints.clear();
428 rTmp.append(rIntersectionPoint);
430 // adjust leading edges, we're switching the polygon
431 ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
432 ActiveEdge* const pNearEdge=&rActiveEdge;
434 rTmp.mpLeadingRightEdge = nullptr;
435 pNearEdge->setTargetPolygonIndex(nTmpIdx);
437 mpLeadingRightEdge = pFarEdge;
438 pFarEdge->setTargetPolygonIndex(mnIdx);
440 return nTmpIdx;
444 std::ptrdiff_t handleComplexRightEdge(ActiveEdge& rActiveEdge,
445 const B2DPoint& rIntersectionPoint,
446 VectorOfPolygons& rPolygonPool)
448 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
449 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
451 rTmp.append(rIntersectionPoint);
453 rActiveEdge.setTargetPolygonIndex(mnIdx);
454 mpLeadingRightEdge = &rActiveEdge;
456 rTmp.mpLeadingRightEdge = nullptr;
458 return nTmpIdx;
461 /// True when sweep line hits our own active edge
462 static bool metOwnEdge(SweepLineEvent const & rEvent,
463 ActiveEdge const & rActiveEdge)
465 const bool bHitOwnEdge=&rEvent.getRect() == &rActiveEdge.getRect();
466 return bHitOwnEdge;
469 /// Retrieve B2DPolygon from this object
470 B2DPolygon getPolygon() const
472 B2DPolygon aRes;
473 for (auto const& aPoint : maPoints)
474 aRes.append(aPoint, 1);
475 aRes.setClosed( true );
476 return aRes;
479 /** Finish this polygon, push to result set.
481 void finish(B2DPolyPolygon& rRes)
483 OSL_PRECOND( maPoints.empty() ||
484 maPoints.front().getX() == maPoints.back().getX() ||
485 maPoints.front().getY() == maPoints.back().getY(),
486 "ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" );
488 mbIsFinished = true;
489 mpLeadingRightEdge = nullptr;
491 rRes.append(getPolygon());
494 /** Refers to the current leading edge element of this
495 polygon, or NULL. The leading edge denotes the 'front'
496 of the polygon vertex sequence, i.e. the coordinates
497 at the polygon's leading edge are returned from
498 maPoints.front()
500 ActiveEdge* mpLeadingRightEdge;
502 /// current index into vector pool
503 std::ptrdiff_t mnIdx;
505 /// Container for the actual polygon points
506 std::vector<B2DPoint> maPoints;
508 /// When true, this polygon is 'done', i.e. nothing must be added anymore.
509 bool mbIsFinished;
512 /** Init sweep line event list
514 This method fills the event list with the sweep line
515 events generated from the input rectangles, and sorts them
516 with increasing x.
518 void setupSweepLineEventListFromRanges( VectorOfEvents& o_rEventVector,
519 const std::vector<B2DRange>& rRanges,
520 const std::vector<B2VectorOrientation>& rOrientations )
522 // we need exactly 2*rectVec.size() events: one for the
523 // left, and one for the right edge of each rectangle
524 o_rEventVector.clear();
525 o_rEventVector.reserve( 2*rRanges.size() );
527 // generate events
528 // ===============
530 // first pass: add all left edges in increasing order
531 std::vector<B2DRange>::const_iterator aCurrRect=rRanges.begin();
532 std::vector<B2VectorOrientation>::const_iterator aCurrOrientation=rOrientations.begin();
533 const std::vector<B2DRange>::const_iterator aEnd=rRanges.end();
534 const std::vector<B2VectorOrientation>::const_iterator aEndOrientation=rOrientations.end();
535 while( aCurrRect != aEnd && aCurrOrientation != aEndOrientation )
537 const B2DRectangle& rCurrRect( *aCurrRect++ );
539 o_rEventVector.emplace_back( rCurrRect.getMinX(),
540 rCurrRect,
541 SweepLineEvent::STARTING_EDGE,
542 (*aCurrOrientation++) == B2VectorOrientation::Positive ?
543 SweepLineEvent::PROCEED_UP : SweepLineEvent::PROCEED_DOWN );
546 // second pass: add all right edges in reversed order
547 std::vector<B2DRange>::const_reverse_iterator aCurrRectR=rRanges.rbegin();
548 std::vector<B2VectorOrientation>::const_reverse_iterator aCurrOrientationR=rOrientations.rbegin();
549 const std::vector<B2DRange>::const_reverse_iterator aEndR=rRanges.rend();
550 while( aCurrRectR != aEndR )
552 const B2DRectangle& rCurrRect( *aCurrRectR++ );
554 o_rEventVector.emplace_back( rCurrRect.getMaxX(),
555 rCurrRect,
556 SweepLineEvent::FINISHING_EDGE,
557 (*aCurrOrientationR++) == B2VectorOrientation::Positive ?
558 SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP );
561 // sort events
562 // ===========
564 // since we use stable_sort, the order of events with the
565 // same x value will not change. The elaborate two-pass
566 // add above thus ensures, that for each two rectangles
567 // with similar left and right x coordinates, the
568 // rectangle whose left event comes first will have its
569 // right event come last. This is advantageous for the
570 // clip algorithm below, see handleRightEdgeCrossing().
572 std::stable_sort( o_rEventVector.begin(),
573 o_rEventVector.end() );
576 /** Insert two active edge segments for the given rectangle.
578 This method creates two active edge segments from the
579 given rect, and inserts them into the active edge list,
580 such that this stays sorted (if it was before).
582 @param io_rEdgeList
583 Active edge list to insert into
585 @param io_rPolygons
586 Vector of polygons. Each rectangle added creates one
587 tentative result polygon in this vector, and the edge list
588 entries holds a reference to that polygon (this _requires_
589 that the polygon vector does not reallocate, i.e. it must
590 have at least the maximal number of rectangles reserved)
592 @param o_CurrentPolygon
593 The then-current polygon when processing this sweep line
594 event
596 @param rCurrEvent
597 The actual event that caused this call
599 void createActiveEdgesFromStartEvent( ListOfEdges & io_rEdgeList,
600 VectorOfPolygons & io_rPolygonPool,
601 SweepLineEvent const & rCurrEvent )
603 ListOfEdges aNewEdges;
604 const B2DRectangle& rRect=rCurrEvent.getRect();
605 const bool bGoesDown=rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN;
607 // start event - new rect starts here, needs polygon to
608 // collect points into
609 const std::ptrdiff_t nIdxPolygon=io_rPolygonPool.alloc();
610 io_rPolygonPool.get(nIdxPolygon).setPolygonPoolIndex(nIdxPolygon);
612 // upper edge
613 aNewEdges.emplace_back(
614 rRect,
615 rRect.getMinY(),
616 bGoesDown ? nIdxPolygon : -1,
617 bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT );
618 // lower edge
619 aNewEdges.emplace_back(
620 rRect,
621 rRect.getMaxY(),
622 bGoesDown ? -1 : nIdxPolygon,
623 bGoesDown ? ActiveEdge::PROCEED_RIGHT : ActiveEdge::PROCEED_LEFT );
625 // furthermore, have to respect a special tie-breaking
626 // rule here, for edges which share the same y value:
627 // newly added upper edges must be inserted _before_ any
628 // other edge with the same y value, and newly added lower
629 // edges must be _after_ all other edges with the same
630 // y. This ensures that the left vertical edge processing
631 // below encounters the upper edge of the current rect
632 // first, and the lower edge last, which automatically
633 // starts and finishes this rect correctly (as only then,
634 // the polygon will have their associated active edges
635 // set).
636 const double nMinY( rRect.getMinY() );
637 const double nMaxY( rRect.getMaxY() );
638 ListOfEdges::iterator aCurr( io_rEdgeList.begin() );
639 const ListOfEdges::iterator aEnd ( io_rEdgeList.end() );
640 while( aCurr != aEnd )
642 const double nCurrY( aCurr->getInvariantCoord() );
644 if( nCurrY >= nMinY &&
645 aNewEdges.size() == 2 ) // only add, if not yet done.
647 // insert upper edge _before_ aCurr. Thus, it will
648 // be the first entry for a range of equal y
649 // values. Using splice here, since we hold
650 // references to the moved list element!
651 io_rEdgeList.splice( aCurr,
652 aNewEdges,
653 aNewEdges.begin() );
656 if( nCurrY > nMaxY )
658 // insert lower edge _before_ aCurr. Thus, it will
659 // be the last entry for a range of equal y values
660 // (aCurr is the first entry strictly larger than
661 // nMaxY). Using splice here, since we hold
662 // references to the moved list element!
663 io_rEdgeList.splice( aCurr,
664 aNewEdges,
665 aNewEdges.begin() );
666 // done with insertion, can early-exit here.
667 return;
670 ++aCurr;
673 // append remainder of aNewList (might still contain 2 or
674 // 1 elements, depending of the contents of io_rEdgeList).
675 io_rEdgeList.splice( aCurr,
676 aNewEdges );
679 bool isSameRect(ActiveEdge const & rEdge,
680 basegfx::B2DRange const & rRect)
682 return &rEdge.getRect() == &rRect;
685 // wow what a hack. necessary because stl's list::erase does
686 // not eat reverse_iterator
687 template<typename Cont, typename Iter> Iter eraseFromList(Cont&, const Iter&);
688 template<> ListOfEdges::iterator eraseFromList(
689 ListOfEdges& rList, const ListOfEdges::iterator& aIter)
691 return rList.erase(aIter);
693 template<> ListOfEdges::reverse_iterator eraseFromList(
694 ListOfEdges& rList, const ListOfEdges::reverse_iterator& aIter)
696 return ListOfEdges::reverse_iterator(
697 rList.erase(std::prev(aIter.base())));
700 template<int bPerformErase,
701 typename Iterator> void processActiveEdges(
702 Iterator first,
703 Iterator last,
704 ListOfEdges& rActiveEdgeList,
705 SweepLineEvent const & rCurrEvent,
706 VectorOfPolygons& rPolygonPool,
707 B2DPolyPolygon& rRes )
709 const basegfx::B2DRange& rCurrRect=rCurrEvent.getRect();
711 // fast-forward to rCurrEvent's first active edge (holds
712 // for both starting and finishing sweep line events, a
713 // rect is regarded _outside_ any rects whose events have
714 // started earlier
715 first = std::find_if(first, last,
716 [&rCurrRect](ActiveEdge& anEdge) { return isSameRect(anEdge, rCurrRect); });
718 if(first == last)
719 return;
721 int nCount=0;
722 std::ptrdiff_t nCurrPolyIdx=-1;
723 while(first != last)
725 if( nCurrPolyIdx == -1 )
726 nCurrPolyIdx=first->getTargetPolygonIndex();
728 OSL_ASSERT(nCurrPolyIdx != -1);
730 // second encounter of my rect -> second edge
731 // encountered, done
732 const bool bExit=
733 nCount &&
734 isSameRect(*first,
735 rCurrRect);
737 // deal with current active edge
738 nCurrPolyIdx =
739 rPolygonPool.get(nCurrPolyIdx).intersect(
740 rCurrEvent,
741 *first,
742 rPolygonPool,
743 rRes,
744 bExit);
746 // prune upper & lower active edges, if requested
747 if( bPerformErase && (bExit || !nCount) )
748 first = eraseFromList(rActiveEdgeList,first);
749 else
750 ++first;
752 // delayed exit, had to prune first
753 if( bExit )
754 return;
756 ++nCount;
760 template<int bPerformErase> void processActiveEdgesTopDown(
761 SweepLineEvent& rCurrEvent,
762 ListOfEdges& rActiveEdgeList,
763 VectorOfPolygons& rPolygonPool,
764 B2DPolyPolygon& rRes )
766 processActiveEdges<bPerformErase>(
767 rActiveEdgeList. begin(),
768 rActiveEdgeList. end(),
769 rActiveEdgeList,
770 rCurrEvent,
771 rPolygonPool,
772 rRes);
775 template<int bPerformErase> void processActiveEdgesBottomUp(
776 SweepLineEvent& rCurrEvent,
777 ListOfEdges& rActiveEdgeList,
778 VectorOfPolygons& rPolygonPool,
779 B2DPolyPolygon& rRes )
781 processActiveEdges<bPerformErase>(
782 rActiveEdgeList. rbegin(),
783 rActiveEdgeList. rend(),
784 rActiveEdgeList,
785 rCurrEvent,
786 rPolygonPool,
787 rRes);
790 enum{ NoErase=0, PerformErase=1 };
792 void handleStartingEdge( SweepLineEvent& rCurrEvent,
793 ListOfEdges& rActiveEdgeList,
794 VectorOfPolygons& rPolygonPool,
795 B2DPolyPolygon& rRes)
797 // inject two new active edges for rect
798 createActiveEdgesFromStartEvent( rActiveEdgeList,
799 rPolygonPool,
800 rCurrEvent );
802 if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN )
803 processActiveEdgesTopDown<NoErase>(
804 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
805 else
806 processActiveEdgesBottomUp<NoErase>(
807 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
810 void handleFinishingEdge( SweepLineEvent& rCurrEvent,
811 ListOfEdges& rActiveEdgeList,
812 VectorOfPolygons& rPolygonPool,
813 B2DPolyPolygon& rRes)
815 if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN )
816 processActiveEdgesTopDown<PerformErase>(
817 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
818 else
819 processActiveEdgesBottomUp<PerformErase>(
820 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
823 void handleSweepLineEvent( SweepLineEvent& rCurrEvent,
824 ListOfEdges& rActiveEdgeList,
825 VectorOfPolygons& rPolygonPool,
826 B2DPolyPolygon& rRes)
828 if( rCurrEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE )
829 handleStartingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
830 else
831 handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
835 namespace utils
837 B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges,
838 const std::vector<B2VectorOrientation>& rOrientations)
840 // sweep-line algorithm to generate a poly-polygon
841 // from a bunch of rectangles
842 // ===============================================
844 // This algorithm uses the well-known sweep line
845 // concept, explained in every good text book about
846 // computational geometry.
848 // We start with creating two structures for every
849 // rectangle, one representing the left x coordinate,
850 // one representing the right x coordinate (and both
851 // referencing the original rect). These structs are
852 // sorted with increasing x coordinates.
854 // Then, we start processing the resulting list from
855 // the beginning. Every entry in the list defines a
856 // point in time of the line sweeping from left to
857 // right across all rectangles.
858 VectorOfEvents aSweepLineEvents;
859 setupSweepLineEventListFromRanges( aSweepLineEvents,
860 rRanges,
861 rOrientations );
863 B2DPolyPolygon aRes;
864 VectorOfPolygons aPolygonPool;
865 ListOfEdges aActiveEdgeList;
867 // sometimes not enough, but a usable compromise
868 aPolygonPool.reserve( rRanges.size() );
870 for (auto& aSweepLineEvent : aSweepLineEvents)
871 handleSweepLineEvent(aSweepLineEvent, aActiveEdgeList, aPolygonPool, aRes);
873 return aRes;
878 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */