LCOV - code coverage report
Current view: top level - basegfx/source/range - b2drangeclipper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 210 211 99.5 %
Date: 2015-06-13 12:38:46 Functions: 47 47 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 <rtl/math.hxx>
      21             : #include <osl/diagnose.h>
      22             : 
      23             : #include <basegfx/tuple/b2dtuple.hxx>
      24             : #include <basegfx/range/b2drange.hxx>
      25             : #include <basegfx/range/b2drangeclipper.hxx>
      26             : #include <basegfx/range/b2dpolyrange.hxx>
      27             : #include <basegfx/polygon/b2dpolypolygon.hxx>
      28             : #include <basegfx/polygon/b2dpolygontools.hxx>
      29             : #include <basegfx/polygon/b2dpolypolygontools.hxx>
      30             : 
      31             : #include <o3tl/vector_pool.hxx>
      32             : #include <boost/bind.hpp>
      33             : #include <boost/utility.hpp>
      34             : 
      35             : #include <algorithm>
      36             : #include <deque>
      37             : #include <list>
      38             : 
      39             : namespace basegfx
      40             : {
      41             :     namespace
      42             :     {
      43             :         // Generating a poly-polygon from a bunch of rectangles
      44             : 
      45             :         // Helper functionality for sweep-line algorithm
      46             :         // ====================================================
      47             : 
      48             :         typedef std::vector<B2DRange> VectorOfRanges;
      49             : 
      50             :         class ImplPolygon;
      51             :         typedef o3tl::vector_pool<ImplPolygon> VectorOfPolygons;
      52             : 
      53             :         /** This class represents an active edge
      54             : 
      55             :             As the sweep line traverses across the overall area,
      56             :             rectangle edges parallel to it generate events, and
      57             :             rectangle edges orthogonal to it generate active
      58             :             edges. This class represents the latter.
      59             :          */
      60             :         class ActiveEdge
      61             :         {
      62             :         public:
      63             :             /** The two possible active rectangle edges differ by one
      64             :                 coordinate value - the upper edge has the lower, the
      65             :                 lower edge the higher value.
      66             :              */
      67             :             enum EdgeType {
      68             :                 /// edge with lower coordinate value
      69             :                 UPPER=0,
      70             :                 /// edge with higher coordinate value
      71             :                 LOWER=1
      72             :             };
      73             : 
      74             :             enum EdgeDirection {
      75             :                 /// edge proceeds to the left
      76             :                 PROCEED_LEFT=0,
      77             :                 /// edge proceeds to the right
      78             :                 PROCEED_RIGHT=1
      79             :             };
      80             : 
      81             :             /** Create active edge
      82             : 
      83             :                 @param rRect
      84             :                 Rectangle this edge is part of
      85             : 
      86             :                 @param fInvariantCoord
      87             :                 The invariant coordinate value of this edge
      88             : 
      89             :                 @param eEdgeType
      90             :                 Is fInvariantCoord the lower or the higher value, for
      91             :                 this rect?
      92             :              */
      93         826 :             ActiveEdge( const B2DRectangle& rRect,
      94             :                         const double&       fInvariantCoord,
      95             :                         std::ptrdiff_t      nPolyIdx,
      96             :                         EdgeType            eEdgeType,
      97             :                         EdgeDirection       eEdgeDirection ) :
      98             :                 mfInvariantCoord(fInvariantCoord),
      99             :                 mpAssociatedRect( &rRect ),
     100             :                 mnPolygonIdx( nPolyIdx ),
     101             :                 meEdgeType( eEdgeType ),
     102         826 :                 meEdgeDirection( eEdgeDirection )
     103         826 :             {}
     104             : 
     105        4016 :             double              getInvariantCoord() const { return mfInvariantCoord; }
     106        6454 :             const B2DRectangle& getRect() const { return *mpAssociatedRect; }
     107        1834 :             std::ptrdiff_t      getTargetPolygonIndex() const { return mnPolygonIdx; }
     108        1008 :             void                setTargetPolygonIndex( std::ptrdiff_t nIdx ) { mnPolygonIdx = nIdx; }
     109        1232 :             EdgeDirection       getEdgeDirection() const { return meEdgeDirection; }
     110             : 
     111             :         private:
     112             :             /** The invariant coordinate value of this edge (e.g. the
     113             :                 common y value, for a horizontal edge)
     114             :              */
     115             :             double              mfInvariantCoord;
     116             : 
     117             :             /** Associated rectangle
     118             : 
     119             :                 This on the one hand saves some storage space (the
     120             :                 vector of rectangles is persistent, anyway), and on
     121             :                 the other hand provides an identifier to match active
     122             :                 edges and x events (see below)
     123             : 
     124             :                 Ptr because class needs to be assignable
     125             :              */
     126             :             const B2DRectangle* mpAssociatedRect;
     127             : 
     128             :             /** Index of the polygon this edge is currently involved
     129             :                 with.
     130             : 
     131             :                 Note that this can change for some kinds of edge
     132             :                 intersection, as the algorithm tends to swap
     133             :                 associated polygons there.
     134             : 
     135             :                 -1 denotes no assigned polygon
     136             :              */
     137             :             std::ptrdiff_t      mnPolygonIdx;
     138             : 
     139             :             /// 'upper' or 'lower' edge of original rectangle.
     140             :             EdgeType            meEdgeType;
     141             : 
     142             :             /// 'left' or 'right'
     143             :             EdgeDirection       meEdgeDirection;
     144             :         };
     145             : 
     146             :         // Needs to be list - various places hold ptrs to elements
     147             :         typedef std::list< ActiveEdge > ListOfEdges;
     148             : 
     149             :         /** Element of the sweep line event list
     150             : 
     151             :             As the sweep line traverses across the overall area,
     152             :             rectangle edges parallel to it generate events, and
     153             :             rectangle edges orthogonal to it generate active
     154             :             edges. This class represents the former.
     155             : 
     156             :             The class defines an element of the sweep line list. The
     157             :             sweep line's position jumps in steps defined by the
     158             :             coordinates of the sorted SweepLineEvent entries.
     159             :          */
     160             :         class SweepLineEvent
     161             :         {
     162             :         public:
     163             :             /** The two possible sweep line rectangle edges differ by
     164             :                 one coordinate value - the starting edge has the
     165             :                 lower, the finishing edge the higher value.
     166             :              */
     167             :             enum EdgeType {
     168             :                 /// edge with lower coordinate value
     169             :                 STARTING_EDGE=0,
     170             :                 /// edge with higher coordinate value
     171             :                 FINISHING_EDGE=1
     172             :             };
     173             : 
     174             :             /** The two possible sweep line directions
     175             :              */
     176             :             enum EdgeDirection {
     177             :                 PROCEED_UP=0,
     178             :                 PROCEED_DOWN=1
     179             :             };
     180             : 
     181             :             /** Create sweep line event
     182             : 
     183             :                 @param fPos
     184             :                 Coordinate position of the event
     185             : 
     186             :                 @param rRect
     187             :                 Rectangle this event is generated for.
     188             : 
     189             :                 @param eEdgeType
     190             :                 Is fPos the lower or the higher value, for the
     191             :                 rectangle this event is generated for?
     192             :              */
     193         826 :             SweepLineEvent( double              fPos,
     194             :                             const B2DRectangle& rRect,
     195             :                             EdgeType            eEdgeType,
     196             :                             EdgeDirection       eDirection) :
     197             :                 mfPos( fPos ),
     198             :                 mpAssociatedRect( &rRect ),
     199             :                 meEdgeType( eEdgeType ),
     200         826 :                 meEdgeDirection( eDirection )
     201         826 :             {}
     202             : 
     203        2058 :             double              getPos() const { return mfPos; }
     204        2471 :             const B2DRectangle& getRect() const { return *mpAssociatedRect; }
     205        2478 :             EdgeType            getEdgeType() const { return meEdgeType; }
     206        1239 :             EdgeDirection       getEdgeDirection() const { return meEdgeDirection; }
     207             : 
     208             :             /// For STL sort
     209        1937 :             bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; }
     210             : 
     211             :         private:
     212             :             /// position of the event, in the direction of the line sweep
     213             :             double                mfPos;
     214             : 
     215             :             /** Rectangle this event is generated for
     216             : 
     217             :                 This on the one hand saves some storage space (the
     218             :                 vector of rectangles is persistent, anyway), and on
     219             :                 the other hand provides an identifier to match active
     220             :                 edges and events (see below)
     221             : 
     222             :                 Ptr because class needs to be assignable
     223             :              */
     224             :             const B2DRectangle*   mpAssociatedRect;
     225             : 
     226             :             /// 'upper' or 'lower' edge of original rectangle.
     227             :             EdgeType              meEdgeType;
     228             : 
     229             :             /// 'up' or 'down'
     230             :             EdgeDirection         meEdgeDirection;
     231             :         };
     232             : 
     233             :         typedef std::vector< SweepLineEvent > VectorOfEvents;
     234             : 
     235             :         /** Smart point container for B2DMultiRange::getPolyPolygon()
     236             : 
     237             :             This class provides methods needed only here, and is used
     238             :             as a place to store some additional information per
     239             :             polygon. Also, most of the intersection logic is
     240             :             implemented here.
     241             :          */
     242        2655 :         class ImplPolygon
     243             :         {
     244             :         public:
     245             :             /** Create polygon
     246             :              */
     247         488 :             ImplPolygon() :
     248             :                 mpLeadingRightEdge(NULL),
     249             :                 mnIdx(-1),
     250             :                 maPoints(),
     251         488 :                 mbIsFinished(false)
     252             :             {
     253             :                 // completely ad-hoc. but what the hell.
     254         488 :                 maPoints.reserve(11);
     255         488 :             }
     256             : 
     257         488 :             void setPolygonPoolIndex( std::ptrdiff_t nIdx ) { mnIdx = nIdx; }
     258             : 
     259             :             /// Add point to the end of the existing points
     260        2464 :             void append( const B2DPoint& rPoint )
     261             :             {
     262             :                 OSL_PRECOND( maPoints.empty() ||
     263             :                              maPoints.back().getX() == rPoint.getX() ||
     264             :                              maPoints.back().getY() == rPoint.getY(),
     265             :                              "ImplPolygon::append(): added point violates 90 degree line angle constraint!" );
     266             : 
     267        4318 :                 if( maPoints.empty() ||
     268        1854 :                     maPoints.back() != rPoint )
     269             :                 {
     270             :                     // avoid duplicate points
     271        2190 :                     maPoints.push_back( rPoint );
     272             :                 }
     273        2464 :             }
     274             : 
     275             :             /** Perform the intersection of this polygon with an
     276             :                 active edge.
     277             : 
     278             :                 @param rEvent
     279             :                 The vertical line event that generated the
     280             :                 intersection
     281             : 
     282             :                 @param rActiveEdge
     283             :                 The active edge that generated the intersection
     284             : 
     285             :                 @param rPolygonPool
     286             :                 Polygon pool, we sometimes need to allocate a new one
     287             : 
     288             :                 @param bIsFinishingEdge
     289             :                 True, when this is hitting the last edge of the
     290             :                 vertical sweep - every vertical sweep starts and ends
     291             :                 with upper and lower edge of the _same_ rectangle.
     292             : 
     293             :                 @return the new current polygon (that's the one
     294             :                 processing must proceed with, when going through the
     295             :                 list of upcoming active edges).
     296             :              */
     297        2058 :             std::ptrdiff_t intersect( SweepLineEvent&   rEvent,
     298             :                                       ActiveEdge&       rActiveEdge,
     299             :                                       VectorOfPolygons& rPolygonPool,
     300             :                                       B2DPolyPolygon&   rRes,
     301             :                                       bool              isFinishingEdge )
     302             :             {
     303             :                 OSL_PRECOND( !mbIsFinished,
     304             :                              "ImplPolygon::intersect(): called on already finished polygon!" );
     305             :                 OSL_PRECOND( !isFinishingEdge
     306             :                              || (isFinishingEdge && &rEvent.getRect() == &rActiveEdge.getRect()),
     307             :                              "ImplPolygon::intersect(): inconsistent ending!" );
     308             : 
     309             :                 const B2DPoint aIntersectionPoint( rEvent.getPos(),
     310        2058 :                                                    rActiveEdge.getInvariantCoord() );
     311             : 
     312             :                 // intersection point, goes to our polygon
     313             :                 // unconditionally
     314        2058 :                 append(aIntersectionPoint);
     315             : 
     316        2058 :                 if( isFinishingEdge )
     317             :                 {
     318             :                     // isSweepLineEnteringRect ?
     319         826 :                     if( rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE)
     320         413 :                         handleFinalOwnRightEdge(rActiveEdge);
     321             :                     else
     322             :                         handleFinalOwnLeftEdge(rActiveEdge,
     323             :                                                rPolygonPool,
     324         413 :                                                rRes);
     325             : 
     326             :                     // we're done with this rect & sweep line
     327         826 :                     return -1;
     328             :                 }
     329        1232 :                 else if( metOwnEdge(rEvent,rActiveEdge) )
     330             :                 {
     331         826 :                     handleInitialOwnEdge(rEvent, rActiveEdge);
     332             : 
     333             :                     // point already added, all init done, continue
     334             :                     // with same poly
     335         826 :                     return mnIdx;
     336             :                 }
     337             :                 else
     338             :                 {
     339             :                     OSL_ENSURE( rActiveEdge.getTargetPolygonIndex() != -1,
     340             :                                 "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
     341             : 
     342             :                     const bool isHittingLeftEdge(
     343         406 :                         rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
     344             : 
     345         406 :                     if( isHittingLeftEdge )
     346             :                         return handleComplexLeftEdge(rActiveEdge,
     347             :                                                      aIntersectionPoint,
     348             :                                                      rPolygonPool,
     349         197 :                                                      rRes);
     350             :                     else
     351             :                         return handleComplexRightEdge(rActiveEdge,
     352             :                                                       aIntersectionPoint,
     353         209 :                                                       rPolygonPool);
     354        2058 :                 }
     355             :             }
     356             : 
     357             :         private:
     358         826 :             void handleInitialOwnEdge(SweepLineEvent& rEvent,
     359             :                                       ActiveEdge&     rActiveEdge)
     360             :             {
     361             :                 const bool isActiveEdgeProceedLeft(
     362         826 :                     rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
     363             :                 const bool isSweepLineEnteringRect(
     364         826 :                     rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE);
     365             :                 (void)isActiveEdgeProceedLeft;
     366             :                 (void)isSweepLineEnteringRect;
     367             : 
     368             :                 OSL_ENSURE( isSweepLineEnteringRect == isActiveEdgeProceedLeft,
     369             :                             "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
     370             : 
     371             :                 OSL_ENSURE( isSweepLineEnteringRect ||
     372             :                             mpLeadingRightEdge == &rActiveEdge,
     373             :                             "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
     374         826 :             }
     375             : 
     376         413 :             void handleFinalOwnRightEdge(ActiveEdge& rActiveEdge)
     377             :             {
     378             :                 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT,
     379             :                             "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
     380             : 
     381         413 :                 rActiveEdge.setTargetPolygonIndex(mnIdx);
     382         413 :                 mpLeadingRightEdge = &rActiveEdge;
     383         413 :             }
     384             : 
     385         413 :             void handleFinalOwnLeftEdge(ActiveEdge&       rActiveEdge,
     386             :                                         VectorOfPolygons& rPolygonPool,
     387             :                                         B2DPolyPolygon&   rRes)
     388             :             {
     389             :                 OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT,
     390             :                             "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
     391             : 
     392             :                 const bool isHittingOurTail(
     393         413 :                     rActiveEdge.getTargetPolygonIndex() == mnIdx);
     394             : 
     395         413 :                 if( isHittingOurTail )
     396         346 :                     finish(rRes); // just finish. no fuss.
     397             :                 else
     398             :                 {
     399             :                     // temp poly hits final left edge
     400          67 :                     const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
     401          67 :                     ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
     402             : 
     403             :                     // active edge's polygon has points
     404             :                     // already. ours need to go in front of them.
     405             :                     maPoints.insert(maPoints.end(),
     406             :                                     rTmp.maPoints.begin(),
     407          67 :                                     rTmp.maPoints.end());
     408             : 
     409             :                     // adjust leading edges, we're switching the polygon
     410          67 :                     ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
     411             : 
     412          67 :                     mpLeadingRightEdge = pFarEdge;
     413          67 :                     pFarEdge->setTargetPolygonIndex(mnIdx);
     414             : 
     415             :                     // nTmpIdx is an empty shell, get rid of it
     416          67 :                     rPolygonPool.free(nTmpIdx);
     417             :                 }
     418         413 :             }
     419             : 
     420         197 :             std::ptrdiff_t handleComplexLeftEdge(ActiveEdge&       rActiveEdge,
     421             :                                                  const B2DPoint&   rIntersectionPoint,
     422             :                                                  VectorOfPolygons& rPolygonPool,
     423             :                                                  B2DPolyPolygon&   rRes)
     424             :             {
     425             :                 const bool isHittingOurTail(
     426         197 :                     rActiveEdge.getTargetPolygonIndex() == mnIdx);
     427         197 :                 if( isHittingOurTail )
     428             :                 {
     429          75 :                     finish(rRes);
     430             : 
     431             :                     // so "this" is done - need new polygon to collect
     432             :                     // further points
     433          75 :                     const std::ptrdiff_t nIdxNewPolygon=rPolygonPool.alloc();
     434          75 :                     rPolygonPool.get(nIdxNewPolygon).setPolygonPoolIndex(nIdxNewPolygon);
     435          75 :                     rPolygonPool.get(nIdxNewPolygon).append(rIntersectionPoint);
     436             : 
     437          75 :                     rActiveEdge.setTargetPolygonIndex(nIdxNewPolygon);
     438             : 
     439          75 :                     return nIdxNewPolygon;
     440             :                 }
     441             :                 else
     442             :                 {
     443         122 :                     const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
     444         122 :                     ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
     445             : 
     446             :                     // active edge's polygon has points
     447             :                     // already. ours need to go in front of them.
     448             :                     maPoints.insert(maPoints.end(),
     449             :                                     rTmp.maPoints.begin(),
     450         122 :                                     rTmp.maPoints.end());
     451             : 
     452         122 :                     rTmp.maPoints.clear();
     453         122 :                     rTmp.append(rIntersectionPoint);
     454             : 
     455             :                     // adjust leading edges, we're switching the polygon
     456         122 :                     ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
     457         122 :                     ActiveEdge* const pNearEdge=&rActiveEdge;
     458             : 
     459         122 :                     rTmp.mpLeadingRightEdge = NULL;
     460         122 :                     pNearEdge->setTargetPolygonIndex(nTmpIdx);
     461             : 
     462         122 :                     mpLeadingRightEdge = pFarEdge;
     463         122 :                     pFarEdge->setTargetPolygonIndex(mnIdx);
     464             : 
     465         122 :                     return nTmpIdx;
     466             :                 }
     467             :             }
     468             : 
     469         209 :             std::ptrdiff_t handleComplexRightEdge(ActiveEdge&       rActiveEdge,
     470             :                                                   const B2DPoint&   rIntersectionPoint,
     471             :                                                   VectorOfPolygons& rPolygonPool)
     472             :             {
     473         209 :                 const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
     474         209 :                 ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
     475             : 
     476         209 :                 rTmp.append(rIntersectionPoint);
     477             : 
     478         209 :                 rActiveEdge.setTargetPolygonIndex(mnIdx);
     479         209 :                 mpLeadingRightEdge = &rActiveEdge;
     480             : 
     481         209 :                 rTmp.mpLeadingRightEdge = NULL;
     482             : 
     483         209 :                 return nTmpIdx;
     484             :             }
     485             : 
     486             :             /// True when sweep line hits our own active edge
     487        1232 :             static bool metOwnEdge(const SweepLineEvent& rEvent,
     488             :                             ActiveEdge&           rActiveEdge)
     489             :             {
     490        1232 :                 const bool bHitOwnEdge=&rEvent.getRect() == &rActiveEdge.getRect();
     491        1232 :                 return bHitOwnEdge;
     492             :             }
     493             : 
     494             :             /// Retrieve B2DPolygon from this object
     495         421 :             B2DPolygon getPolygon() const
     496             :             {
     497         421 :                 B2DPolygon aRes;
     498             :                 std::for_each( maPoints.begin(),
     499             :                                maPoints.end(),
     500             :                                boost::bind(
     501             :                      &B2DPolygon::append,
     502             :                                    boost::ref(aRes),
     503             :                                    _1,
     504         421 :                                    1 ) );
     505         421 :                 aRes.setClosed( true );
     506         421 :                 return aRes;
     507             :             }
     508             : 
     509             :             /** Finish this polygon, push to result set.
     510             :              */
     511         421 :             void finish(B2DPolyPolygon& rRes)
     512             :             {
     513             :                 OSL_PRECOND( maPoints.empty() ||
     514             :                              maPoints.front().getX() == maPoints.back().getX() ||
     515             :                              maPoints.front().getY() == maPoints.back().getY(),
     516             :                              "ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" );
     517             : 
     518         421 :                 mbIsFinished = true;
     519         421 :                 mpLeadingRightEdge = NULL;
     520             : 
     521         421 :                 rRes.append(getPolygon());
     522         421 :             }
     523             : 
     524             :             /** Refers to the current leading edge element of this
     525             :                 polygon, or NULL. The leading edge denotes the 'front'
     526             :                 of the polygon vertex sequence, i.e. the coordinates
     527             :                 at the polygon's leading edge are returned from
     528             :                 maPoints.front()
     529             :              */
     530             :             ActiveEdge*           mpLeadingRightEdge;
     531             : 
     532             :             /// current index into vector pool
     533             :             std::ptrdiff_t        mnIdx;
     534             : 
     535             :             /// Container for the actual polygon points
     536             :             std::vector<B2DPoint> maPoints;
     537             : 
     538             :             /// When true, this polygon is 'done', i.e. nothing must be added anymore.
     539             :             bool                  mbIsFinished;
     540             :         };
     541             : 
     542             :         /** Init sweep line event list
     543             : 
     544             :             This method fills the event list with the sweep line
     545             :             events generated from the input rectangles, and sorts them
     546             :             with increasing x.
     547             :          */
     548         229 :         void setupSweepLineEventListFromRanges( VectorOfEvents& o_rEventVector,
     549             :                                                 const std::vector<B2DRange>& rRanges,
     550             :                                                 const std::vector<B2VectorOrientation>& rOrientations )
     551             :         {
     552             :             // we need exactly 2*rectVec.size() events: one for the
     553             :             // left, and one for the right edge of each rectangle
     554         229 :             o_rEventVector.clear();
     555         229 :             o_rEventVector.reserve( 2*rRanges.size() );
     556             : 
     557             :             // generate events
     558             :             // ===============
     559             : 
     560             :             // first pass: add all left edges in increasing order
     561         229 :             std::vector<B2DRange>::const_iterator aCurrRect=rRanges.begin();
     562         229 :             std::vector<B2VectorOrientation>::const_iterator aCurrOrientation=rOrientations.begin();
     563         229 :             const std::vector<B2DRange>::const_iterator aEnd=rRanges.end();
     564         229 :             const std::vector<B2VectorOrientation>::const_iterator aEndOrientation=rOrientations.end();
     565         871 :             while( aCurrRect != aEnd && aCurrOrientation != aEndOrientation )
     566             :             {
     567         413 :                 const B2DRectangle& rCurrRect( *aCurrRect++ );
     568             : 
     569             :                 o_rEventVector.push_back(
     570             :                     SweepLineEvent( rCurrRect.getMinX(),
     571             :                                     rCurrRect,
     572             :                                     SweepLineEvent::STARTING_EDGE,
     573         826 :                                     (*aCurrOrientation++) == B2VectorOrientation::Positive ?
     574         413 :                                     SweepLineEvent::PROCEED_UP : SweepLineEvent::PROCEED_DOWN) );
     575             :             }
     576             : 
     577             :             // second pass: add all right edges in reversed order
     578         229 :             std::vector<B2DRange>::const_reverse_iterator aCurrRectR=rRanges.rbegin();
     579         229 :             std::vector<B2VectorOrientation>::const_reverse_iterator aCurrOrientationR=rOrientations.rbegin();
     580         229 :             const std::vector<B2DRange>::const_reverse_iterator aEndR=rRanges.rend();
     581         871 :             while( aCurrRectR != aEndR )
     582             :             {
     583         413 :                 const B2DRectangle& rCurrRect( *aCurrRectR++ );
     584             : 
     585             :                 o_rEventVector.push_back(
     586             :                     SweepLineEvent( rCurrRect.getMaxX(),
     587             :                                     rCurrRect,
     588             :                                     SweepLineEvent::FINISHING_EDGE,
     589         826 :                                     (*aCurrOrientationR++) == B2VectorOrientation::Positive ?
     590         413 :                                     SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP ) );
     591             :             }
     592             : 
     593             :             // sort events
     594             :             // ===========
     595             : 
     596             :             // since we use stable_sort, the order of events with the
     597             :             // same x value will not change. The elaborate two-pass
     598             :             // add above thus ensures, that for each two rectangles
     599             :             // with similar left and right x coordinates, the
     600             :             // rectangle whose left event comes first will have its
     601             :             // right event come last. This is advantageous for the
     602             :             // clip algorithm below, see handleRightEdgeCrossing().
     603             : 
     604             :             std::stable_sort( o_rEventVector.begin(),
     605         229 :                               o_rEventVector.end() );
     606         229 :         }
     607             : 
     608             :         /** Insert two active edge segments for the given rectangle.
     609             : 
     610             :             This method creates two active edge segments from the
     611             :             given rect, and inserts them into the active edge list,
     612             :             such that this stays sorted (if it was before).
     613             : 
     614             :             @param io_rEdgeList
     615             :             Active edge list to insert into
     616             : 
     617             :             @param io_rPolygons
     618             :             Vector of polygons. Each rectangle added creates one
     619             :             tentative result polygon in this vector, and the edge list
     620             :             entries holds a reference to that polygon (this _requires_
     621             :             that the polygon vector does not reallocate, i.e. it must
     622             :             have at least the maximal number of rectangles reserved)
     623             : 
     624             :             @param o_CurrentPolygon
     625             :             The then-current polygon when processing this sweep line
     626             :             event
     627             : 
     628             :             @param rCurrEvent
     629             :             The actual event that caused this call
     630             :          */
     631         413 :         void createActiveEdgesFromStartEvent( ListOfEdges&      io_rEdgeList,
     632             :                                               VectorOfPolygons& io_rPolygonPool,
     633             :                                               SweepLineEvent&   rCurrEvent )
     634             :         {
     635         413 :             ListOfEdges         aNewEdges;
     636         413 :             const B2DRectangle& rRect=rCurrEvent.getRect();
     637         413 :             const bool          bGoesDown=rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN;
     638             : 
     639             :             // start event - new rect starts here, needs polygon to
     640             :             // collect points into
     641         413 :             const std::ptrdiff_t nIdxPolygon=io_rPolygonPool.alloc();
     642         413 :             io_rPolygonPool.get(nIdxPolygon).setPolygonPoolIndex(nIdxPolygon);
     643             : 
     644             :             // upper edge
     645             :             aNewEdges.push_back(
     646             :                 ActiveEdge(
     647             :                     rRect,
     648         413 :                     rRect.getMinY(),
     649             :                     bGoesDown ? nIdxPolygon : -1,
     650             :                     ActiveEdge::UPPER,
     651         826 :                     bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT) );
     652             :             // lower edge
     653             :             aNewEdges.push_back(
     654             :                 ActiveEdge(
     655             :                     rRect,
     656         413 :                     rRect.getMaxY(),
     657             :                     bGoesDown ? -1 : nIdxPolygon,
     658             :                     ActiveEdge::LOWER,
     659         826 :                     bGoesDown ? ActiveEdge::PROCEED_RIGHT : ActiveEdge::PROCEED_LEFT ) );
     660             : 
     661             :             // furthermore, have to respect a special tie-breaking
     662             :             // rule here, for edges which share the same y value:
     663             :             // newly added upper edges must be inserted _before_ any
     664             :             // other edge with the same y value, and newly added lower
     665             :             // edges must be _after_ all other edges with the same
     666             :             // y. This ensures that the left vertical edge processing
     667             :             // below encounters the upper edge of the current rect
     668             :             // first, and the lower edge last, which automatically
     669             :             // starts and finishes this rect correctly (as only then,
     670             :             // the polygon will have their associated active edges
     671             :             // set).
     672         413 :             const double                nMinY( rRect.getMinY() );
     673         413 :             const double                nMaxY( rRect.getMaxY() );
     674         413 :             ListOfEdges::iterator       aCurr( io_rEdgeList.begin() );
     675         413 :             const ListOfEdges::iterator aEnd ( io_rEdgeList.end() );
     676        2650 :             while( aCurr != aEnd )
     677             :             {
     678        1958 :                 const double nCurrY( aCurr->getInvariantCoord() );
     679             : 
     680        2325 :                 if( nCurrY >= nMinY &&
     681         367 :                     aNewEdges.size() == 2 ) // only add, if not yet done.
     682             :                 {
     683             :                     // insert upper edge _before_ aCurr. Thus, it will
     684             :                     // be the first entry for a range of equal y
     685             :                     // values. Using splice here, since we hold
     686             :                     // references to the moved list element!
     687             :                     io_rEdgeList.splice( aCurr,
     688             :                                          aNewEdges,
     689         170 :                                          aNewEdges.begin() );
     690             :                 }
     691             : 
     692        1958 :                 if( nCurrY > nMaxY )
     693             :                 {
     694             :                     // insert lower edge _before_ aCurr. Thus, it will
     695             :                     // be the last entry for a range of equal y values
     696             :                     // (aCurr is the first entry strictly larger than
     697             :                     // nMaxY). Using splice here, since we hold
     698             :                     // references to the moved list element!
     699             :                     io_rEdgeList.splice( aCurr,
     700             :                                          aNewEdges,
     701         134 :                                          aNewEdges.begin() );
     702             :                     // done with insertion, can early-exit here.
     703         547 :                     return;
     704             :                 }
     705             : 
     706        1824 :                 ++aCurr;
     707             :             }
     708             : 
     709             :             // append remainder of aNewList (might still contain 2 or
     710             :             // 1 elements, depending of the contents of io_rEdgeList).
     711             :             io_rEdgeList.splice( aCurr,
     712         279 :                                  aNewEdges );
     713             :         }
     714             : 
     715        5222 :         inline bool isSameRect(ActiveEdge&              rEdge,
     716             :                                const basegfx::B2DRange& rRect)
     717             :         {
     718        5222 :             return &rEdge.getRect() == &rRect;
     719             :         }
     720             : 
     721             :         // wow what a hack. necessary because stl's list::erase does
     722             :         // not eat reverse_iterator
     723             :         template<typename Cont, typename Iter> Iter eraseFromList(Cont&, Iter);
     724         414 :         template<> inline ListOfEdges::iterator eraseFromList(
     725             :             ListOfEdges& rList, ListOfEdges::iterator aIter)
     726             :         {
     727         414 :             return rList.erase(aIter);
     728             :         }
     729         412 :         template<> inline ListOfEdges::reverse_iterator eraseFromList(
     730             :             ListOfEdges& rList, ListOfEdges::reverse_iterator aIter)
     731             :         {
     732             :             return ListOfEdges::reverse_iterator(
     733         412 :                     rList.erase(boost::prior(aIter.base())));
     734             :         }
     735             : 
     736             :         template<int bPerformErase,
     737         826 :                  typename Iterator> inline void processActiveEdges(
     738             :             Iterator          first,
     739             :             Iterator          last,
     740             :             ListOfEdges&      rActiveEdgeList,
     741             :             SweepLineEvent&   rCurrEvent,
     742             :             VectorOfPolygons& rPolygonPool,
     743             :             B2DPolyPolygon&   rRes )
     744             :         {
     745         826 :             const basegfx::B2DRange& rCurrRect=rCurrEvent.getRect();
     746             : 
     747             :             // fast-forward to rCurrEvent's first active edge (holds
     748             :             // for both starting and finishing sweep line events, a
     749             :             // rect is regarded _outside_ any rects whose events have
     750             :             // started earlier
     751         826 :             first = std::find_if(first, last,
     752             :                                  boost::bind(
     753             :                          &isSameRect,
     754             :                                      _1,
     755         413 :                                      boost::cref(rCurrRect)));
     756             : 
     757         826 :             if(first == last)
     758           0 :                 return;
     759             : 
     760         826 :             int nCount=0;
     761         826 :             std::ptrdiff_t nCurrPolyIdx=-1;
     762        2884 :             while(first != last)
     763             :             {
     764        2058 :                 if( nCurrPolyIdx == -1 )
     765         826 :                     nCurrPolyIdx=first->getTargetPolygonIndex();
     766             : 
     767             :                 OSL_ASSERT(nCurrPolyIdx != -1);
     768             : 
     769             :                 // second encounter of my rect -> second edge
     770             :                 // encountered, done
     771             :                 const bool bExit=
     772             :                     nCount &&
     773        1232 :                     isSameRect(*first,
     774        3290 :                                rCurrRect);
     775             : 
     776             :                 // deal with current active edge
     777        4116 :                 nCurrPolyIdx =
     778        2058 :                     rPolygonPool.get(nCurrPolyIdx).intersect(
     779             :                         rCurrEvent,
     780        2058 :                         *first,
     781             :                         rPolygonPool,
     782             :                         rRes,
     783             :                         bExit);
     784             : 
     785             :                 // prune upper & lower active edges, if requested
     786         999 :                 if( bPerformErase && (bExit || !nCount) )
     787         826 :                     first = eraseFromList(rActiveEdgeList,first);
     788             :                 else
     789        1232 :                     ++first;
     790             : 
     791             :                 // delayed exit, had to prune first
     792        2058 :                 if( bExit )
     793         826 :                     return;
     794             : 
     795        1232 :                 ++nCount;
     796             :             }
     797             :         }
     798             : 
     799         413 :         template<int bPerformErase> inline void processActiveEdgesTopDown(
     800             :             SweepLineEvent&   rCurrEvent,
     801             :             ListOfEdges&      rActiveEdgeList,
     802             :             VectorOfPolygons& rPolygonPool,
     803             :             B2DPolyPolygon&   rRes )
     804             :         {
     805         413 :             processActiveEdges<bPerformErase>(
     806             :                 rActiveEdgeList. begin(),
     807             :                 rActiveEdgeList. end(),
     808             :                 rActiveEdgeList,
     809             :                 rCurrEvent,
     810             :                 rPolygonPool,
     811         413 :                 rRes);
     812         413 :         }
     813             : 
     814         413 :         template<int bPerformErase> inline void processActiveEdgesBottomUp(
     815             :             SweepLineEvent&   rCurrEvent,
     816             :             ListOfEdges&      rActiveEdgeList,
     817             :             VectorOfPolygons& rPolygonPool,
     818             :             B2DPolyPolygon&   rRes )
     819             :         {
     820         413 :             processActiveEdges<bPerformErase>(
     821             :                 rActiveEdgeList. rbegin(),
     822             :                 rActiveEdgeList. rend(),
     823             :                 rActiveEdgeList,
     824             :                 rCurrEvent,
     825             :                 rPolygonPool,
     826         826 :                 rRes);
     827         413 :         }
     828             : 
     829             :         enum{ NoErase=0, PerformErase=1 };
     830             : 
     831         413 :         void handleStartingEdge( SweepLineEvent&   rCurrEvent,
     832             :                                  ListOfEdges&      rActiveEdgeList,
     833             :                                  VectorOfPolygons& rPolygonPool,
     834             :                                  B2DPolyPolygon&   rRes)
     835             :         {
     836             :             // inject two new active edges for rect
     837             :             createActiveEdgesFromStartEvent( rActiveEdgeList,
     838             :                                              rPolygonPool,
     839         413 :                                              rCurrEvent );
     840             : 
     841         413 :             if( SweepLineEvent::PROCEED_DOWN == rCurrEvent.getEdgeDirection() )
     842             :                 processActiveEdgesTopDown<NoErase>(
     843         206 :                     rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
     844             :             else
     845             :                 processActiveEdgesBottomUp<NoErase>(
     846         207 :                     rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
     847         413 :         }
     848             : 
     849         413 :         void handleFinishingEdge( SweepLineEvent&   rCurrEvent,
     850             :                                   ListOfEdges&      rActiveEdgeList,
     851             :                                   VectorOfPolygons& rPolygonPool,
     852             :                                   B2DPolyPolygon&   rRes)
     853             :         {
     854         413 :             if( SweepLineEvent::PROCEED_DOWN == rCurrEvent.getEdgeDirection() )
     855             :                 processActiveEdgesTopDown<PerformErase>(
     856         207 :                     rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
     857             :             else
     858             :                 processActiveEdgesBottomUp<PerformErase>(
     859         206 :                     rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
     860         413 :         }
     861             : 
     862         826 :         inline void handleSweepLineEvent( SweepLineEvent&   rCurrEvent,
     863             :                                           ListOfEdges&      rActiveEdgeList,
     864             :                                           VectorOfPolygons& rPolygonPool,
     865             :                                           B2DPolyPolygon&   rRes)
     866             :         {
     867         826 :             if( SweepLineEvent::STARTING_EDGE == rCurrEvent.getEdgeType() )
     868         413 :                 handleStartingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
     869             :             else
     870         413 :                 handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
     871         826 :         }
     872             :     }
     873             : 
     874             :     namespace tools
     875             :     {
     876         229 :         B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges,
     877             :                                        const std::vector<B2VectorOrientation>& rOrientations)
     878             :         {
     879             :             // sweep-line algorithm to generate a poly-polygon
     880             :             // from a bunch of rectangles
     881             :             // ===============================================
     882             : 
     883             :             // This algorithm uses the well-known sweep line
     884             :             // concept, explained in every good text book about
     885             :             // computational geometry.
     886             : 
     887             :             // We start with creating two structures for every
     888             :             // rectangle, one representing the left x coordinate,
     889             :             // one representing the right x coordinate (and both
     890             :             // referencing the original rect). These structs are
     891             :             // sorted with increasing x coordinates.
     892             : 
     893             :             // Then, we start processing the resulting list from
     894             :             // the beginning. Every entry in the list defines a
     895             :             // point in time of the line sweeping from left to
     896             :             // right across all rectangles.
     897         229 :             VectorOfEvents aSweepLineEvents;
     898             :             setupSweepLineEventListFromRanges( aSweepLineEvents,
     899             :                                                rRanges,
     900         229 :                                                rOrientations );
     901             : 
     902         229 :             B2DPolyPolygon   aRes;
     903         458 :             VectorOfPolygons aPolygonPool;
     904         458 :             ListOfEdges      aActiveEdgeList;
     905             : 
     906             :             // sometimes not enough, but a usable compromise
     907         229 :             aPolygonPool.reserve( rRanges.size() );
     908             : 
     909             :             std::for_each( aSweepLineEvents.begin(),
     910             :                            aSweepLineEvents.end(),
     911             :                            boost::bind(
     912             :                                &handleSweepLineEvent,
     913             :                                _1,
     914             :                                boost::ref(aActiveEdgeList),
     915             :                                boost::ref(aPolygonPool),
     916         229 :                                boost::ref(aRes)) );
     917             : 
     918         458 :             return aRes;
     919             :         }
     920             :     }
     921        2106 : }
     922             : 
     923             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11