LCOV - code coverage report
Current view: top level - basegfx/source/tools - b2dclipstate.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 157 212 74.1 %
Date: 2015-06-13 12:38:46 Functions: 32 40 80.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 <basegfx/tools/b2dclipstate.hxx>
      21             : 
      22             : #include <basegfx/range/b2drange.hxx>
      23             : #include <basegfx/range/b2dpolyrange.hxx>
      24             : #include <basegfx/range/b2drangeclipper.hxx>
      25             : #include <basegfx/polygon/b2dpolygon.hxx>
      26             : #include <basegfx/polygon/b2dpolygontools.hxx>
      27             : #include <basegfx/polygon/b2dpolypolygon.hxx>
      28             : #include <basegfx/polygon/b2dpolypolygontools.hxx>
      29             : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
      30             : 
      31             : #include <osl/diagnose.h>
      32             : 
      33             : namespace basegfx
      34             : {
      35             : namespace tools
      36             : {
      37        1876 :     class ImplB2DClipState
      38             :     {
      39             :     public:
      40             :         enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
      41             : 
      42        1245 :         ImplB2DClipState() :
      43             :             maPendingPolygons(),
      44             :             maPendingRanges(),
      45             :             maClipPoly(),
      46        1245 :             mePendingOps(UNION)
      47        1245 :         {}
      48             : 
      49           1 :         explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
      50             :             maPendingPolygons(),
      51             :             maPendingRanges(),
      52             :             maClipPoly(rPoly),
      53           1 :             mePendingOps(UNION)
      54           1 :         {}
      55             : 
      56         818 :         bool isCleared() const
      57             :         {
      58         818 :             return !maClipPoly.count()
      59         771 :                 && !maPendingPolygons.count()
      60        1575 :                 && !maPendingRanges.count();
      61             :         }
      62             : 
      63         530 :         bool isNullClipPoly() const
      64             :         {
      65         530 :             return maClipPoly.count() == 1
      66        1060 :                 && !maClipPoly.getB2DPolygon(0).count();
      67             :         }
      68             : 
      69         347 :         bool isNull() const
      70             :         {
      71         347 :             return !maPendingPolygons.count()
      72         346 :                 && !maPendingRanges.count()
      73         670 :                 && isNullClipPoly();
      74             :         }
      75             : 
      76           4 :         void makeNull()
      77             :         {
      78           4 :             maPendingPolygons.clear();
      79           4 :             maPendingRanges.clear();
      80           4 :             maClipPoly.clear();
      81           4 :             maClipPoly.append(B2DPolygon());
      82           4 :             mePendingOps = UNION;
      83           4 :         }
      84             : 
      85         413 :         bool operator==(const ImplB2DClipState& rRHS) const
      86             :         {
      87         413 :             return maPendingPolygons == rRHS.maPendingPolygons
      88         413 :                 && maPendingRanges == rRHS.maPendingRanges
      89         308 :                 && maClipPoly == rRHS.maClipPoly
      90         496 :                 && mePendingOps == rRHS.mePendingOps;
      91             :         }
      92             : 
      93         350 :         void addRange(const B2DRange& rRange, Operation eOp)
      94             :         {
      95         350 :             if( rRange.isEmpty() )
      96         350 :                 return;
      97             : 
      98         350 :             commitPendingPolygons();
      99         350 :             if( mePendingOps != eOp )
     100         311 :                 commitPendingRanges();
     101             : 
     102         350 :             mePendingOps = eOp;
     103             :             maPendingRanges.appendElement(
     104             :                 rRange,
     105         350 :                 B2VectorOrientation::Positive);
     106             :         }
     107             : 
     108          18 :         void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp)
     109             :         {
     110          18 :             commitPendingRanges();
     111          18 :             if( mePendingOps != eOp )
     112          18 :                 commitPendingPolygons();
     113             : 
     114          18 :             mePendingOps = eOp;
     115          18 :             maPendingPolygons.append(aPoly);
     116          18 :         }
     117             : 
     118          20 :         void unionRange(const B2DRange& rRange)
     119             :         {
     120          20 :             if( isCleared() )
     121          30 :                 return;
     122             : 
     123          10 :             addRange(rRange,UNION);
     124             :         }
     125             : 
     126           0 :         void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     127             :         {
     128           0 :             if( isCleared() )
     129           0 :                 return;
     130             : 
     131           0 :             addPolyPolygon(rPolyPoly,UNION);
     132             :         }
     133             : 
     134         319 :         void intersectRange(const B2DRange& rRange)
     135             :         {
     136         319 :             if( isNull() )
     137         319 :                 return;
     138             : 
     139         319 :             addRange(rRange,INTERSECT);
     140             :         }
     141             : 
     142          18 :         void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     143             :         {
     144          18 :             if( isNull() )
     145          18 :                 return;
     146             : 
     147          18 :             addPolyPolygon(rPolyPoly,INTERSECT);
     148             :         }
     149             : 
     150          10 :         void subtractRange(const B2DRange& rRange )
     151             :         {
     152          10 :             if( isNull() )
     153          10 :                 return;
     154             : 
     155          10 :             addRange(rRange,SUBTRACT);
     156             :         }
     157             : 
     158           0 :         void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     159             :         {
     160           0 :             if( isNull() )
     161           0 :                 return;
     162             : 
     163           0 :             addPolyPolygon(rPolyPoly,SUBTRACT);
     164             :         }
     165             : 
     166          11 :         void xorRange(const B2DRange& rRange)
     167             :         {
     168          11 :             addRange(rRange,XOR);
     169          11 :         }
     170             : 
     171           0 :         void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     172             :         {
     173           0 :             addPolyPolygon(rPolyPoly,XOR);
     174           0 :         }
     175             : 
     176         247 :         B2DPolyPolygon getClipPoly() const
     177             :         {
     178         247 :             commitPendingRanges();
     179         247 :             commitPendingPolygons();
     180             : 
     181         247 :             return maClipPoly;
     182             :         }
     183             : 
     184             :     private:
     185         615 :         void commitPendingPolygons() const
     186             :         {
     187         615 :             if( !maPendingPolygons.count() )
     188        1212 :                 return;
     189             : 
     190             :             // assumption: maClipPoly has kept polygons prepared for
     191             :             // clipping; i.e. no neutral polygons & correct
     192             :             // orientation
     193          18 :             maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons);
     194          18 :             const bool bIsEmpty=isNullClipPoly();
     195          18 :             const bool bIsCleared=!maClipPoly.count();
     196          18 :             switch(mePendingOps)
     197             :             {
     198             :                 case UNION:
     199             :                     OSL_ASSERT( !bIsCleared );
     200             : 
     201           0 :                     if( bIsEmpty )
     202           0 :                         maClipPoly = maPendingPolygons;
     203             :                     else
     204           0 :                         maClipPoly = tools::solvePolygonOperationOr(
     205             :                             maClipPoly,
     206           0 :                             maPendingPolygons);
     207           0 :                     break;
     208             :                 case INTERSECT:
     209             :                     OSL_ASSERT( !bIsEmpty );
     210             : 
     211          18 :                     if( bIsCleared )
     212          15 :                         maClipPoly = maPendingPolygons;
     213             :                     else
     214           6 :                         maClipPoly = tools::solvePolygonOperationAnd(
     215             :                             maClipPoly,
     216           3 :                             maPendingPolygons);
     217          18 :                     break;
     218             :                 case XOR:
     219           0 :                     if( bIsEmpty )
     220           0 :                         maClipPoly = maPendingPolygons;
     221           0 :                     else if( bIsCleared )
     222             :                     {
     223             :                         // not representable, strictly speaking,
     224             :                         // using polygons with the common even/odd
     225             :                         // or nonzero winding number fill rule. If
     226             :                         // we'd want to represent it, fill rule
     227             :                         // would need to be "non-negative winding
     228             :                         // number" (and we then would return
     229             :                         // 'holes' here)
     230             : 
     231             :                         // going for an ugly hack meanwhile
     232           0 :                         maClipPoly = tools::solvePolygonOperationXor(
     233             :                             B2DPolyPolygon(
     234             :                                 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
     235           0 :                             maPendingPolygons);
     236             :                     }
     237             :                     else
     238           0 :                         maClipPoly = tools::solvePolygonOperationXor(
     239             :                             maClipPoly,
     240           0 :                             maPendingPolygons);
     241           0 :                     break;
     242             :                 case SUBTRACT:
     243             :                     OSL_ASSERT( !bIsEmpty );
     244             : 
     245             :                     // first union all pending ones, subtract en bloc then
     246           0 :                     maPendingPolygons = solveCrossovers(maPendingPolygons);
     247           0 :                     maPendingPolygons = stripNeutralPolygons(maPendingPolygons);
     248           0 :                     maPendingPolygons = stripDispensablePolygons(maPendingPolygons, false);
     249             : 
     250           0 :                     if( bIsCleared )
     251             :                     {
     252             :                         // not representable, strictly speaking,
     253             :                         // using polygons with the common even/odd
     254             :                         // or nonzero winding number fill rule. If
     255             :                         // we'd want to represent it, fill rule
     256             :                         // would need to be "non-negative winding
     257             :                         // number" (and we then would return
     258             :                         // 'holes' here)
     259             : 
     260             :                         // going for an ugly hack meanwhile
     261           0 :                         maClipPoly = tools::solvePolygonOperationDiff(
     262             :                             B2DPolyPolygon(
     263             :                                 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
     264           0 :                             maPendingPolygons);
     265             :                     }
     266             :                     else
     267           0 :                         maClipPoly = tools::solvePolygonOperationDiff(
     268             :                             maClipPoly,
     269           0 :                             maPendingPolygons);
     270           0 :                     break;
     271             :             }
     272             : 
     273          18 :             maPendingPolygons.clear();
     274          18 :             mePendingOps = UNION;
     275             :         }
     276             : 
     277         576 :         void commitPendingRanges() const
     278             :         {
     279         576 :             if( !maPendingRanges.count() )
     280         963 :                 return;
     281             : 
     282             :             // use the specialized range clipper for the win
     283         189 :             B2DPolyPolygon aCollectedRanges;
     284         189 :             const bool bIsEmpty=isNullClipPoly();
     285         189 :             const bool bIsCleared=!maClipPoly.count();
     286         189 :             switch(mePendingOps)
     287             :             {
     288             :                 case UNION:
     289             :                     OSL_ASSERT( !bIsCleared );
     290             : 
     291           1 :                     aCollectedRanges = maPendingRanges.solveCrossovers();
     292           1 :                     aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
     293           1 :                     aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
     294           1 :                     if( bIsEmpty )
     295           1 :                         maClipPoly = aCollectedRanges;
     296             :                     else
     297           0 :                         maClipPoly = tools::solvePolygonOperationOr(
     298             :                             maClipPoly,
     299           0 :                             aCollectedRanges);
     300           1 :                     break;
     301             :                 case INTERSECT:
     302             :                     OSL_ASSERT( !bIsEmpty );
     303             : 
     304         184 :                     aCollectedRanges = maPendingRanges.solveCrossovers();
     305         184 :                     aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
     306         184 :                     if( maPendingRanges.count() > 1 )
     307           3 :                         aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
     308             : 
     309         184 :                     if( bIsCleared )
     310         184 :                         maClipPoly = aCollectedRanges;
     311             :                     else
     312           0 :                         maClipPoly = tools::solvePolygonOperationAnd(
     313             :                             maClipPoly,
     314           0 :                             aCollectedRanges);
     315         184 :                     break;
     316             :                 case XOR:
     317           2 :                     aCollectedRanges = maPendingRanges.solveCrossovers();
     318           2 :                     aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
     319           2 :                     aCollectedRanges = correctOrientations(aCollectedRanges);
     320             : 
     321           2 :                     if( bIsEmpty )
     322           1 :                         maClipPoly = aCollectedRanges;
     323           1 :                     else if( bIsCleared )
     324             :                     {
     325             :                         // not representable, strictly speaking,
     326             :                         // using polygons with the common even/odd
     327             :                         // or nonzero winding number fill rule. If
     328             :                         // we'd want to represent it, fill rule
     329             :                         // would need to be "non-negative winding
     330             :                         // number" (and we then would return
     331             :                         // 'holes' here)
     332             : 
     333             :                         // going for an ugly hack meanwhile
     334           0 :                         maClipPoly = tools::solvePolygonOperationXor(
     335             :                             B2DPolyPolygon(
     336             :                                 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
     337           0 :                             aCollectedRanges);
     338             :                     }
     339             :                     else
     340           2 :                         maClipPoly = tools::solvePolygonOperationXor(
     341             :                             maClipPoly,
     342           1 :                             aCollectedRanges);
     343           2 :                     break;
     344             :                 case SUBTRACT:
     345             :                     OSL_ASSERT( !bIsEmpty );
     346             : 
     347             :                     // first union all pending ranges, subtract en bloc then
     348           2 :                     aCollectedRanges = maPendingRanges.solveCrossovers();
     349           2 :                     aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
     350           2 :                     aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
     351             : 
     352           2 :                     if( bIsCleared )
     353             :                     {
     354             :                         // not representable, strictly speaking,
     355             :                         // using polygons with the common even/odd
     356             :                         // or nonzero winding number fill rule. If
     357             :                         // we'd want to represent it, fill rule
     358             :                         // would need to be "non-negative winding
     359             :                         // number" (and we then would return
     360             :                         // 'holes' here)
     361             : 
     362             :                         // going for an ugly hack meanwhile
     363           0 :                         maClipPoly = tools::solvePolygonOperationDiff(
     364             :                             B2DPolyPolygon(
     365             :                                 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
     366           0 :                             aCollectedRanges);
     367             :                     }
     368             :                     else
     369           4 :                         maClipPoly = tools::solvePolygonOperationDiff(
     370             :                             maClipPoly,
     371           2 :                             aCollectedRanges);
     372           2 :                     break;
     373             :             }
     374             : 
     375         189 :             maPendingRanges.clear();
     376         189 :             mePendingOps = UNION;
     377             :         }
     378             : 
     379             :         mutable B2DPolyPolygon maPendingPolygons;
     380             :         mutable B2DPolyRange   maPendingRanges;
     381             :         mutable B2DPolyPolygon maClipPoly;
     382             :         mutable Operation      mePendingOps;
     383             :     };
     384             : 
     385        1245 :     B2DClipState::B2DClipState() :
     386        1245 :         mpImpl()
     387        1245 :     {}
     388             : 
     389        1246 :     B2DClipState::~B2DClipState()
     390        1246 :     {}
     391             : 
     392           0 :     B2DClipState::B2DClipState( const B2DClipState& rOrig ) :
     393           0 :         mpImpl(rOrig.mpImpl)
     394           0 :     {}
     395             : 
     396           1 :     B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) :
     397           1 :         mpImpl( ImplB2DClipState(rPolyPoly) )
     398           1 :     {}
     399             : 
     400        1329 :     B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS )
     401             :     {
     402        1329 :         mpImpl = rRHS.mpImpl;
     403        1329 :         return *this;
     404             :     }
     405             : 
     406           4 :     void B2DClipState::makeNull()
     407             :     {
     408           4 :         mpImpl->makeNull();
     409           4 :     }
     410             : 
     411         798 :     bool B2DClipState::isCleared() const
     412             :     {
     413         798 :         return mpImpl->isCleared();
     414             :     }
     415             : 
     416         825 :     bool B2DClipState::operator==(const B2DClipState& rRHS) const
     417             :     {
     418         825 :         if(mpImpl.same_object(rRHS.mpImpl))
     419         412 :             return true;
     420             : 
     421         413 :         return ((*mpImpl) == (*rRHS.mpImpl));
     422             :     }
     423             : 
     424           0 :     bool B2DClipState::operator!=(const B2DClipState& rRHS) const
     425             :     {
     426           0 :         return !(*this == rRHS);
     427             :     }
     428             : 
     429          20 :     void B2DClipState::unionRange(const B2DRange& rRange)
     430             :     {
     431          20 :         mpImpl->unionRange(rRange);
     432          20 :     }
     433             : 
     434           0 :     void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     435             :     {
     436           0 :         mpImpl->unionPolyPolygon(rPolyPoly);
     437           0 :     }
     438             : 
     439         319 :     void B2DClipState::intersectRange(const B2DRange& rRange)
     440             :     {
     441         319 :         mpImpl->intersectRange(rRange);
     442         319 :     }
     443             : 
     444          18 :     void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     445             :     {
     446          18 :         mpImpl->intersectPolyPolygon(rPolyPoly);
     447          18 :     }
     448             : 
     449          10 :     void B2DClipState::subtractRange(const B2DRange& rRange)
     450             :     {
     451          10 :         mpImpl->subtractRange(rRange);
     452          10 :     }
     453             : 
     454           0 :     void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     455             :     {
     456           0 :         mpImpl->subtractPolyPolygon(rPolyPoly);
     457           0 :     }
     458             : 
     459          11 :     void B2DClipState::xorRange(const B2DRange& rRange)
     460             :     {
     461          11 :         mpImpl->xorRange(rRange);
     462          11 :     }
     463             : 
     464           0 :     void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
     465             :     {
     466           0 :         mpImpl->xorPolyPolygon(rPolyPoly);
     467           0 :     }
     468             : 
     469         247 :     B2DPolyPolygon B2DClipState::getClipPoly() const
     470             :     {
     471         247 :         return mpImpl->getClipPoly();
     472             :     }
     473             : 
     474             : } // end of namespace tools
     475             : } // end of namespace basegfx
     476             : 
     477             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11