nss: upgrade to release 3.73
[LibreOffice.git] / basegfx / source / range / b2drangeclipper.cxx
blob5dc4527287720b1c9404c716bfe19851983c6ff2
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 <cassert>
32 #include <list>
33 #include <iterator>
35 namespace basegfx
37 namespace
39 // Generating a poly-polygon from a bunch of rectangles
41 // Helper functionality for sweep-line algorithm
42 // ====================================================
44 class ImplPolygon;
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.
54 class ActiveEdge
56 public:
58 enum EdgeDirection {
59 /// edge proceeds to the left
60 PROCEED_LEFT=0,
61 /// edge proceeds to the right
62 PROCEED_RIGHT=1
65 /** Create active edge
67 @param rRect
68 Rectangle this edge is part of
70 @param fInvariantCoord
71 The invariant coordinate value of this edge
73 @param eEdgeType
74 Is fInvariantCoord the lower or the higher value, for
75 this rect?
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; }
93 private:
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
111 with.
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.
139 class SweepLineEvent
141 public:
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.
146 enum EdgeType {
147 /// edge with lower coordinate value
148 STARTING_EDGE=0,
149 /// edge with higher coordinate value
150 FINISHING_EDGE=1
153 /** The two possible sweep line directions
155 enum EdgeDirection {
156 PROCEED_UP=0,
157 PROCEED_DOWN=1
160 /** Create sweep line event
162 @param fPos
163 Coordinate position of the event
165 @param rRect
166 Rectangle this event is generated for.
168 @param eEdgeType
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,
174 EdgeType eEdgeType,
175 EdgeDirection eDirection) :
176 mfPos( fPos ),
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; }
187 /// For STL sort
188 bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; }
190 private:
191 /// position of the event, in the direction of the line sweep
192 double mfPos;
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.
206 EdgeType meEdgeType;
208 /// 'up' or 'down'
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
219 implemented here.
221 class ImplPolygon
223 public:
224 /** Create polygon
226 ImplPolygon() :
227 mpLeadingRightEdge(nullptr),
228 mnIdx(-1),
229 maPoints(),
230 mbIsFinished(false)
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
255 active edge.
257 @param rEvent
258 The vertical line event that generated the
259 intersection
261 @param rActiveEdge
262 The active edge that generated the intersection
264 @param rPolygonPool
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
291 // unconditionally
292 append(aIntersectionPoint);
294 if( isFinishingEdge )
296 // isSweepLineEnteringRect ?
297 if( rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE)
298 handleFinalOwnRightEdge(rActiveEdge);
299 else
300 handleFinalOwnLeftEdge(rActiveEdge,
301 rPolygonPool,
302 rRes);
304 // we're done with this rect & sweep line
305 return -1;
307 else if( metOwnEdge(rEvent,rActiveEdge) )
309 handleInitialOwnEdge(rEvent, rActiveEdge);
311 // point already added, all init done, continue
312 // with same poly
313 return mnIdx;
315 else
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,
325 aIntersectionPoint,
326 rPolygonPool,
327 rRes);
328 else
329 return handleComplexRightEdge(rActiveEdge,
330 aIntersectionPoint,
331 rPolygonPool);
335 private:
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.
373 else
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 )
405 finish(rRes);
407 // so "this" is done - need new polygon to collect
408 // further points
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;
417 else
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);
441 return nTmpIdx;
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;
459 return nTmpIdx;
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();
467 return bHitOwnEdge;
470 /// Retrieve B2DPolygon from this object
471 B2DPolygon getPolygon() const
473 B2DPolygon aRes;
474 for (auto const& aPoint : maPoints)
475 aRes.append(aPoint, 1);
476 aRes.setClosed( true );
477 return aRes;
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!" );
489 mbIsFinished = true;
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
499 maPoints.front()
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.
510 bool mbIsFinished;
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
517 with increasing x.
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() );
528 // generate events
529 // ===============
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(),
541 rCurrRect,
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(),
556 rCurrRect,
557 SweepLineEvent::FINISHING_EDGE,
558 (*aCurrOrientationR++) == B2VectorOrientation::Positive ?
559 SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP );
562 // sort events
563 // ===========
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).
583 @param io_rEdgeList
584 Active edge list to insert into
586 @param io_rPolygons
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
595 event
597 @param rCurrEvent
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);
613 // upper edge
614 aNewEdges.emplace_back(
615 rRect,
616 rRect.getMinY(),
617 bGoesDown ? nIdxPolygon : -1,
618 bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT );
619 // lower edge
620 aNewEdges.emplace_back(
621 rRect,
622 rRect.getMaxY(),
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
636 // set).
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,
653 aNewEdges,
654 aNewEdges.begin() );
657 if( nCurrY > nMaxY )
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,
665 aNewEdges,
666 aNewEdges.begin() );
667 // done with insertion, can early-exit here.
668 return;
671 ++aCurr;
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,
677 aNewEdges );
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(
703 Iterator first,
704 Iterator last,
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
715 // started earlier
716 first = std::find_if(first, last,
717 [&rCurrRect](ActiveEdge& anEdge) { return isSameRect(anEdge, rCurrRect); });
719 if(first == last)
720 return;
722 int nCount=0;
723 std::ptrdiff_t nCurrPolyIdx=-1;
724 while(first != last)
726 if( nCurrPolyIdx == -1 )
727 nCurrPolyIdx=first->getTargetPolygonIndex();
729 assert(nCurrPolyIdx != -1);
731 // second encounter of my rect -> second edge
732 // encountered, done
733 const bool bExit=
734 nCount &&
735 isSameRect(*first,
736 rCurrRect);
738 // deal with current active edge
739 nCurrPolyIdx =
740 rPolygonPool.get(nCurrPolyIdx).intersect(
741 rCurrEvent,
742 *first,
743 rPolygonPool,
744 rRes,
745 bExit);
747 // prune upper & lower active edges, if requested
748 if( bPerformErase && (bExit || !nCount) )
749 first = eraseFromList(rActiveEdgeList,first);
750 else
751 ++first;
753 // delayed exit, had to prune first
754 if( bExit )
755 return;
757 ++nCount;
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(),
770 rActiveEdgeList,
771 rCurrEvent,
772 rPolygonPool,
773 rRes);
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(),
785 rActiveEdgeList,
786 rCurrEvent,
787 rPolygonPool,
788 rRes);
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,
800 rPolygonPool,
801 rCurrEvent );
803 if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN )
804 processActiveEdgesTopDown<NoErase>(
805 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
806 else
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);
819 else
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);
831 else
832 handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
836 namespace utils
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,
861 rRanges,
862 rOrientations );
864 B2DPolyPolygon aRes;
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);
874 return aRes;
879 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */