LibreOffice Module basegfx (master) 1
b2drangeclipper.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <osl/diagnose.h>
21
26
27#include <o3tl/vector_pool.hxx>
28
29#include <algorithm>
30#include <cassert>
31#include <list>
32#include <iterator>
33
34namespace basegfx
35{
36 namespace
37 {
38 // Generating a poly-polygon from a bunch of rectangles
39
40 // Helper functionality for sweep-line algorithm
41 // ====================================================
42
43 class ImplPolygon;
44 typedef o3tl::vector_pool<ImplPolygon> VectorOfPolygons;
45
53 class ActiveEdge
54 {
55 public:
56
57 enum EdgeDirection {
59 PROCEED_LEFT=0,
61 PROCEED_RIGHT=1
62 };
63
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 )
84 {}
85
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; }
91
92 private:
97
108
118 std::ptrdiff_t mnPolygonIdx;
119
121 EdgeDirection meEdgeDirection;
122 };
123
124 // Needs to be list - various places hold ptrs to elements
125 typedef std::list< ActiveEdge > ListOfEdges;
126
138 class SweepLineEvent
139 {
140 public:
145 enum EdgeType {
147 STARTING_EDGE=0,
149 FINISHING_EDGE=1
150 };
151
154 enum EdgeDirection {
155 PROCEED_UP=0,
156 PROCEED_DOWN=1
157 };
158
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 )
179 {}
180
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; }
185
187 bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; }
188
189 private:
191 double mfPos;
192
203
206
208 EdgeDirection meEdgeDirection;
209 };
210
211 typedef std::vector< SweepLineEvent > VectorOfEvents;
212
220 class ImplPolygon
221 {
222 public:
225 ImplPolygon() :
226 mpLeadingRightEdge(nullptr),
227 mnIdx(-1),
228 mbIsFinished(false)
229 {
230 // completely ad-hoc. but what the hell.
231 maPoints.reserve(11);
232 }
233
234 void setPolygonPoolIndex( std::ptrdiff_t nIdx ) { mnIdx = nIdx; }
235
237 void append( const B2DPoint& rPoint )
238 {
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!" );
243
244 if( maPoints.empty() ||
245 maPoints.back() != rPoint )
246 {
247 // avoid duplicate points
248 maPoints.push_back( rPoint );
249 }
250 }
251
274 std::ptrdiff_t intersect( SweepLineEvent const & rEvent,
275 ActiveEdge& rActiveEdge,
276 VectorOfPolygons& rPolygonPool,
277 B2DPolyPolygon& rRes,
278 bool isFinishingEdge )
279 {
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!" );
284
285 const B2DPoint aIntersectionPoint( rEvent.getPos(),
286 rActiveEdge.getInvariantCoord() );
287
288 // intersection point, goes to our polygon
289 // unconditionally
290 append(aIntersectionPoint);
291
292 if( isFinishingEdge )
293 {
294 // isSweepLineEnteringRect ?
295 if( rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE)
296 handleFinalOwnRightEdge(rActiveEdge);
297 else
298 handleFinalOwnLeftEdge(rActiveEdge,
299 rPolygonPool,
300 rRes);
301
302 // we're done with this rect & sweep line
303 return -1;
304 }
305 else if( metOwnEdge(rEvent,rActiveEdge) )
306 {
307 handleInitialOwnEdge(rEvent, rActiveEdge);
308
309 // point already added, all init done, continue
310 // with same poly
311 return mnIdx;
312 }
313 else
314 {
315 OSL_ENSURE( rActiveEdge.getTargetPolygonIndex() != -1,
316 "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
317
318 const bool isHittingLeftEdge(
319 rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
320
321 if( isHittingLeftEdge )
322 return handleComplexLeftEdge(rActiveEdge,
323 aIntersectionPoint,
324 rPolygonPool,
325 rRes);
326 else
327 return handleComplexRightEdge(rActiveEdge,
328 aIntersectionPoint,
329 rPolygonPool);
330 }
331 }
332
333 private:
334 void handleInitialOwnEdge(SweepLineEvent const & rEvent,
335 ActiveEdge const & rActiveEdge) const
336 {
337 const bool isActiveEdgeProceedLeft(
338 rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
339 const bool isSweepLineEnteringRect(
340 rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE);
341
342 OSL_ENSURE( isSweepLineEnteringRect == isActiveEdgeProceedLeft,
343 "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
344
345 OSL_ENSURE( isSweepLineEnteringRect ||
346 mpLeadingRightEdge == &rActiveEdge,
347 "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
348 }
349
350 void handleFinalOwnRightEdge(ActiveEdge& rActiveEdge)
351 {
352 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT,
353 "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
354
355 rActiveEdge.setTargetPolygonIndex(mnIdx);
356 mpLeadingRightEdge = &rActiveEdge;
357 }
358
359 void handleFinalOwnLeftEdge(ActiveEdge const & rActiveEdge,
360 VectorOfPolygons& rPolygonPool,
361 B2DPolyPolygon& rRes)
362 {
363 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT,
364 "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
365
366 const bool isHittingOurTail(
367 rActiveEdge.getTargetPolygonIndex() == mnIdx);
368
369 if( isHittingOurTail )
370 finish(rRes); // just finish. no fuss.
371 else
372 {
373 // temp poly hits final left edge
374 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
375 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
376
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());
382
383 // adjust leading edges, we're switching the polygon
384 ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
385
386 mpLeadingRightEdge = pFarEdge;
387 pFarEdge->setTargetPolygonIndex(mnIdx);
388
389 // nTmpIdx is an empty shell, get rid of it
390 rPolygonPool.free(nTmpIdx);
391 }
392 }
393
394 std::ptrdiff_t handleComplexLeftEdge(ActiveEdge& rActiveEdge,
395 const B2DPoint& rIntersectionPoint,
396 VectorOfPolygons& rPolygonPool,
397 B2DPolyPolygon& rRes)
398 {
399 const bool isHittingOurTail(
400 rActiveEdge.getTargetPolygonIndex() == mnIdx);
401 if( isHittingOurTail )
402 {
403 finish(rRes);
404
405 // so "this" is done - need new polygon to collect
406 // further points
407 const std::ptrdiff_t nIdxNewPolygon=rPolygonPool.alloc();
408 rPolygonPool.get(nIdxNewPolygon).setPolygonPoolIndex(nIdxNewPolygon);
409 rPolygonPool.get(nIdxNewPolygon).append(rIntersectionPoint);
410
411 rActiveEdge.setTargetPolygonIndex(nIdxNewPolygon);
412
413 return nIdxNewPolygon;
414 }
415 else
416 {
417 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
418 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
419
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());
425
426 rTmp.maPoints.clear();
427 rTmp.append(rIntersectionPoint);
428
429 // adjust leading edges, we're switching the polygon
430 ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
431 ActiveEdge* const pNearEdge=&rActiveEdge;
432
433 rTmp.mpLeadingRightEdge = nullptr;
434 pNearEdge->setTargetPolygonIndex(nTmpIdx);
435
436 mpLeadingRightEdge = pFarEdge;
437 pFarEdge->setTargetPolygonIndex(mnIdx);
438
439 return nTmpIdx;
440 }
441 }
442
443 std::ptrdiff_t handleComplexRightEdge(ActiveEdge& rActiveEdge,
444 const B2DPoint& rIntersectionPoint,
445 VectorOfPolygons& rPolygonPool)
446 {
447 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
448 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
449
450 rTmp.append(rIntersectionPoint);
451
452 rActiveEdge.setTargetPolygonIndex(mnIdx);
453 mpLeadingRightEdge = &rActiveEdge;
454
455 rTmp.mpLeadingRightEdge = nullptr;
456
457 return nTmpIdx;
458 }
459
461 static bool metOwnEdge(SweepLineEvent const & rEvent,
462 ActiveEdge const & rActiveEdge)
463 {
464 const bool bHitOwnEdge=&rEvent.getRect() == &rActiveEdge.getRect();
465 return bHitOwnEdge;
466 }
467
469 B2DPolygon getPolygon() const
470 {
471 B2DPolygon aRes;
472 for (auto const& aPoint : maPoints)
473 aRes.append(aPoint, 1);
474 aRes.setClosed( true );
475 return aRes;
476 }
477
480 void finish(B2DPolyPolygon& rRes)
481 {
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!" );
486
487 mbIsFinished = true;
488 mpLeadingRightEdge = nullptr;
489
490 rRes.append(getPolygon());
491 }
492
500
502 std::ptrdiff_t mnIdx;
503
505 std::vector<B2DPoint> maPoints;
506
509 };
510
517 void setupSweepLineEventListFromRanges( VectorOfEvents& o_rEventVector,
518 const std::vector<B2DRange>& rRanges,
519 const std::vector<B2VectorOrientation>& rOrientations )
520 {
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() );
525
526 // generate events
527 // ===============
528
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 )
535 {
536 const B2DRectangle& rCurrRect( *aCurrRect++ );
537
538 o_rEventVector.emplace_back( rCurrRect.getMinX(),
539 rCurrRect,
540 SweepLineEvent::STARTING_EDGE,
541 (*aCurrOrientation++) == B2VectorOrientation::Positive ?
542 SweepLineEvent::PROCEED_UP : SweepLineEvent::PROCEED_DOWN );
543 }
544
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 )
550 {
551 const B2DRectangle& rCurrRect( *aCurrRectR++ );
552
553 o_rEventVector.emplace_back( rCurrRect.getMaxX(),
554 rCurrRect,
555 SweepLineEvent::FINISHING_EDGE,
556 (*aCurrOrientationR++) == B2VectorOrientation::Positive ?
557 SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP );
558 }
559
560 // sort events
561 // ===========
562
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().
570
571 std::stable_sort( o_rEventVector.begin(),
572 o_rEventVector.end() );
573 }
574
598 void createActiveEdgesFromStartEvent( ListOfEdges & io_rEdgeList,
599 VectorOfPolygons & io_rPolygonPool,
600 SweepLineEvent const & rCurrEvent )
601 {
602 ListOfEdges aNewEdges;
603 const B2DRectangle& rRect=rCurrEvent.getRect();
604 const bool bGoesDown=rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN;
605
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);
610
611 // upper edge
612 aNewEdges.emplace_back(
613 rRect,
614 rRect.getMinY(),
615 bGoesDown ? nIdxPolygon : -1,
616 bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT );
617 // lower edge
618 aNewEdges.emplace_back(
619 rRect,
620 rRect.getMaxY(),
621 bGoesDown ? -1 : nIdxPolygon,
622 bGoesDown ? ActiveEdge::PROCEED_RIGHT : ActiveEdge::PROCEED_LEFT );
623
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
634 // set).
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 )
640 {
641 const double nCurrY( aCurr->getInvariantCoord() );
642
643 if( nCurrY >= nMinY &&
644 aNewEdges.size() == 2 ) // only add, if not yet done.
645 {
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,
651 aNewEdges,
652 aNewEdges.begin() );
653 }
654
655 if( nCurrY > nMaxY )
656 {
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,
663 aNewEdges,
664 aNewEdges.begin() );
665 // done with insertion, can early-exit here.
666 return;
667 }
668
669 ++aCurr;
670 }
671
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,
675 aNewEdges );
676 }
677
678 bool isSameRect(ActiveEdge const & rEdge,
679 basegfx::B2DRange const & rRect)
680 {
681 return &rEdge.getRect() == &rRect;
682 }
683
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)
689 {
690 return rList.erase(aIter);
691 }
692 template<> ListOfEdges::reverse_iterator eraseFromList(
693 ListOfEdges& rList, const ListOfEdges::reverse_iterator& aIter)
694 {
695 return ListOfEdges::reverse_iterator(
696 rList.erase(std::prev(aIter.base())));
697 }
698
699 template<int bPerformErase,
700 typename Iterator> void processActiveEdges(
701 Iterator first,
702 Iterator last,
703 ListOfEdges& rActiveEdgeList,
704 SweepLineEvent const & rCurrEvent,
705 VectorOfPolygons& rPolygonPool,
706 B2DPolyPolygon& rRes )
707 {
708 const basegfx::B2DRange& rCurrRect=rCurrEvent.getRect();
709
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
713 // started earlier
714 first = std::find_if(first, last,
715 [&rCurrRect](ActiveEdge& anEdge) { return isSameRect(anEdge, rCurrRect); });
716
717 if(first == last)
718 return;
719
720 int nCount=0;
721 std::ptrdiff_t nCurrPolyIdx=-1;
722 while(first != last)
723 {
724 if( nCurrPolyIdx == -1 )
725 nCurrPolyIdx=first->getTargetPolygonIndex();
726
727 assert(nCurrPolyIdx != -1);
728
729 // second encounter of my rect -> second edge
730 // encountered, done
731 const bool bExit=
732 nCount &&
733 isSameRect(*first,
734 rCurrRect);
735
736 // deal with current active edge
737 nCurrPolyIdx =
738 rPolygonPool.get(nCurrPolyIdx).intersect(
739 rCurrEvent,
740 *first,
741 rPolygonPool,
742 rRes,
743 bExit);
744
745 // prune upper & lower active edges, if requested
746 if( bPerformErase && (bExit || !nCount) )
747 first = eraseFromList(rActiveEdgeList,first);
748 else
749 ++first;
750
751 // delayed exit, had to prune first
752 if( bExit )
753 return;
754
755 ++nCount;
756 }
757 }
758
759 template<int bPerformErase> void processActiveEdgesTopDown(
760 SweepLineEvent& rCurrEvent,
761 ListOfEdges& rActiveEdgeList,
762 VectorOfPolygons& rPolygonPool,
763 B2DPolyPolygon& rRes )
764 {
765 processActiveEdges<bPerformErase>(
766 rActiveEdgeList. begin(),
767 rActiveEdgeList. end(),
768 rActiveEdgeList,
769 rCurrEvent,
770 rPolygonPool,
771 rRes);
772 }
773
774 template<int bPerformErase> void processActiveEdgesBottomUp(
775 SweepLineEvent& rCurrEvent,
776 ListOfEdges& rActiveEdgeList,
777 VectorOfPolygons& rPolygonPool,
778 B2DPolyPolygon& rRes )
779 {
780 processActiveEdges<bPerformErase>(
781 rActiveEdgeList. rbegin(),
782 rActiveEdgeList. rend(),
783 rActiveEdgeList,
784 rCurrEvent,
785 rPolygonPool,
786 rRes);
787 }
788
789 enum{ NoErase=0, PerformErase=1 };
790
791 void handleStartingEdge( SweepLineEvent& rCurrEvent,
792 ListOfEdges& rActiveEdgeList,
793 VectorOfPolygons& rPolygonPool,
794 B2DPolyPolygon& rRes)
795 {
796 // inject two new active edges for rect
797 createActiveEdgesFromStartEvent( rActiveEdgeList,
798 rPolygonPool,
799 rCurrEvent );
800
801 if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN )
802 processActiveEdgesTopDown<NoErase>(
803 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
804 else
805 processActiveEdgesBottomUp<NoErase>(
806 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
807 }
808
809 void handleFinishingEdge( SweepLineEvent& rCurrEvent,
810 ListOfEdges& rActiveEdgeList,
811 VectorOfPolygons& rPolygonPool,
812 B2DPolyPolygon& rRes)
813 {
814 if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN )
815 processActiveEdgesTopDown<PerformErase>(
816 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
817 else
818 processActiveEdgesBottomUp<PerformErase>(
819 rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
820 }
821
822 void handleSweepLineEvent( SweepLineEvent& rCurrEvent,
823 ListOfEdges& rActiveEdgeList,
824 VectorOfPolygons& rPolygonPool,
825 B2DPolyPolygon& rRes)
826 {
827 if( rCurrEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE )
828 handleStartingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
829 else
830 handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
831 }
832 }
833
834 namespace utils
835 {
836 B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges,
837 const std::vector<B2VectorOrientation>& rOrientations)
838 {
839 // sweep-line algorithm to generate a poly-polygon
840 // from a bunch of rectangles
841 // ===============================================
842
843 // This algorithm uses the well-known sweep line
844 // concept, explained in every good text book about
845 // computational geometry.
846
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.
852
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,
859 rRanges,
860 rOrientations );
861
862 B2DPolyPolygon aRes;
863 VectorOfPolygons aPolygonPool;
864 ListOfEdges aActiveEdgeList;
865
866 // sometimes not enough, but a usable compromise
867 aPolygonPool.reserve( rRanges.size() );
868
869 for (auto& aSweepLineEvent : aSweepLineEvents)
870 handleSweepLineEvent(aSweepLineEvent, aActiveEdgeList, aPolygonPool, aRes);
871
872 return aRes;
873 }
874 }
875}
876
877/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const B2DRectangle * mpAssociatedRect
Associated rectangle.
ActiveEdge * mpLeadingRightEdge
Refers to the current leading edge element of this polygon, or NULL.
double mfPos
position of the event, in the direction of the line sweep
std::ptrdiff_t mnPolygonIdx
Index of the polygon this edge is currently involved with.
double mfInvariantCoord
The invariant coordinate value of this edge (e.g.
EdgeType meEdgeType
'upper' or 'lower' edge of original rectangle.
std::ptrdiff_t mnIdx
current index into vector pool
EdgeDirection meEdgeDirection
'left' or 'right'
std::vector< B2DPoint > maPoints
Container for the actual polygon points.
bool mbIsFinished
When true, this polygon is 'done', i.e. nothing must be added anymore.
EdgeType
void reserve(sal_uInt32 nCount)
A two-dimensional interval over doubles.
Definition: b2drange.hxx:54
int nCount
B2DPolyPolygon solveCrossovers(const B2DPolyPolygon &rCandidate, size_t *pPointLimit)
Solve all crossovers (aka self-intersections) in a polyPolygon.
@ Positive
mathematically positive oriented
B2DRange B2DRectangle
Alias name for interface clarity (not everybody is aware of the identity)
constexpr OUStringLiteral first
enumrange< T >::Iterator begin(enumrange< T >)
end
::std::vector< EventSharedPtr > VectorOfEvents
bool operator<(const wwFont &r1, const wwFont &r2)