LCOV - code coverage report
Current view: top level - basegfx/source/polygon - b2dpolygontools.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 1037 1594 65.1 %
Date: 2014-11-03 Functions: 53 76 69.7 %
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/numeric/ftools.hxx>
      21             : #include <basegfx/polygon/b2dpolygontools.hxx>
      22             : #include <osl/diagnose.h>
      23             : #include <rtl/math.hxx>
      24             : #include <rtl/instance.hxx>
      25             : #include <sal/log.hxx>
      26             : #include <basegfx/polygon/b2dpolygon.hxx>
      27             : #include <basegfx/polygon/b2dpolypolygon.hxx>
      28             : #include <basegfx/range/b2drange.hxx>
      29             : #include <basegfx/curve/b2dcubicbezier.hxx>
      30             : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
      31             : #include <basegfx/point/b3dpoint.hxx>
      32             : #include <basegfx/matrix/b3dhommatrix.hxx>
      33             : #include <basegfx/matrix/b2dhommatrix.hxx>
      34             : #include <basegfx/curve/b2dbeziertools.hxx>
      35             : #include <basegfx/matrix/b2dhommatrixtools.hxx>
      36             : 
      37             : #include <numeric>
      38             : #include <limits>
      39             : 
      40             : // #i37443#
      41             : #define ANGLE_BOUND_START_VALUE     (2.25)
      42             : #define ANGLE_BOUND_MINIMUM_VALUE   (0.1)
      43             : #define COUNT_SUBDIVIDE_DEFAULT     (4L)
      44             : #ifdef DBG_UTIL
      45             : static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE;
      46             : #endif
      47             : #define STEPSPERQUARTER     (3)
      48             : 
      49             : namespace basegfx
      50             : {
      51             :     namespace tools
      52             :     {
      53        1598 :         void openWithGeometryChange(B2DPolygon& rCandidate)
      54             :         {
      55        1598 :             if(rCandidate.isClosed())
      56             :             {
      57        1598 :                 if(rCandidate.count())
      58             :                 {
      59        1598 :                     rCandidate.append(rCandidate.getB2DPoint(0));
      60             : 
      61        1598 :                     if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0))
      62             :                     {
      63          20 :                         rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0));
      64          20 :                         rCandidate.resetPrevControlPoint(0);
      65             :                     }
      66             :                 }
      67             : 
      68        1598 :                 rCandidate.setClosed(false);
      69             :             }
      70        1598 :         }
      71             : 
      72       43021 :         void closeWithGeometryChange(B2DPolygon& rCandidate)
      73             :         {
      74       43021 :             if(!rCandidate.isClosed())
      75             :             {
      76      141746 :                 while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
      77             :                 {
      78       55824 :                     if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1))
      79             :                     {
      80        4623 :                         rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1));
      81             :                     }
      82             : 
      83       55824 :                     rCandidate.remove(rCandidate.count() - 1);
      84             :                 }
      85             : 
      86       42961 :                 rCandidate.setClosed(true);
      87             :             }
      88       43021 :         }
      89             : 
      90       54841 :         void checkClosed(B2DPolygon& rCandidate)
      91             :         {
      92             :             // #i80172# Removed unnecessary assertion
      93             :             // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");
      94             : 
      95       54841 :             if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
      96             :             {
      97       33527 :                 closeWithGeometryChange(rCandidate);
      98             :             }
      99       54841 :         }
     100             : 
     101             :         // Get successor and predecessor indices. Returning the same index means there
     102             :         // is none. Same for successor.
     103          74 :         sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
     104             :         {
     105             :             OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
     106             : 
     107          74 :             if(nIndex)
     108             :             {
     109           0 :                 return nIndex - 1L;
     110             :             }
     111          74 :             else if(rCandidate.count())
     112             :             {
     113          74 :                 return rCandidate.count() - 1L;
     114             :             }
     115             :             else
     116             :             {
     117           0 :                 return nIndex;
     118             :             }
     119             :         }
     120             : 
     121          74 :         sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
     122             :         {
     123             :             OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
     124             : 
     125          74 :             if(nIndex + 1L < rCandidate.count())
     126             :             {
     127          66 :                 return nIndex + 1L;
     128             :             }
     129           8 :             else if(nIndex + 1L == rCandidate.count())
     130             :             {
     131           8 :                 return 0L;
     132             :             }
     133             :             else
     134             :             {
     135           0 :                 return nIndex;
     136             :             }
     137             :         }
     138             : 
     139       12307 :         B2VectorOrientation getOrientation(const B2DPolygon& rCandidate)
     140             :         {
     141       12307 :             B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
     142             : 
     143       12307 :             if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed())
     144             :             {
     145       11699 :                 const double fSignedArea(getSignedArea(rCandidate));
     146             : 
     147       11699 :                 if(fTools::equalZero(fSignedArea))
     148             :                 {
     149             :                     // ORIENTATION_NEUTRAL, already set
     150             :                 }
     151       11699 :                 if(fSignedArea > 0.0)
     152             :                 {
     153        6918 :                     eRetval = ORIENTATION_POSITIVE;
     154             :                 }
     155        4781 :                 else if(fSignedArea < 0.0)
     156             :                 {
     157        4664 :                     eRetval = ORIENTATION_NEGATIVE;
     158             :                 }
     159             :             }
     160             : 
     161       12307 :             return eRetval;
     162             :         }
     163             : 
     164           0 :         B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
     165             :         {
     166           0 :             return rCandidate.getContinuityInPoint(nIndex);
     167             :         }
     168             : 
     169           0 :         B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound)
     170             :         {
     171           0 :             if(rCandidate.areControlPointsUsed())
     172             :             {
     173           0 :                 const sal_uInt32 nPointCount(rCandidate.count());
     174           0 :                 B2DPolygon aRetval;
     175             : 
     176           0 :                 if(nPointCount)
     177             :                 {
     178             :                     // prepare edge-oriented loop
     179           0 :                     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     180           0 :                     B2DCubicBezier aBezier;
     181           0 :                     aBezier.setStartPoint(rCandidate.getB2DPoint(0));
     182             : 
     183             :                     // perf: try to avoid too many realloctions by guessing the result's pointcount
     184           0 :                     aRetval.reserve(nPointCount*4);
     185             : 
     186             :                     // add start point (always)
     187           0 :                     aRetval.append(aBezier.getStartPoint());
     188             : 
     189           0 :                     for(sal_uInt32 a(0L); a < nEdgeCount; a++)
     190             :                     {
     191             :                         // get next and control points
     192           0 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     193           0 :                         aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     194           0 :                         aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
     195           0 :                         aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     196           0 :                         aBezier.testAndSolveTrivialBezier();
     197             : 
     198           0 :                         if(aBezier.isBezier())
     199             :                         {
     200             :                             // add curved edge and generate DistanceBound
     201           0 :                             double fBound(0.0);
     202             : 
     203           0 :                             if(0.0 == fDistanceBound)
     204             :                             {
     205             :                                 // If not set, use B2DCubicBezier functionality to guess a rough value
     206           0 :                                 const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0);
     207             : 
     208             :                                 // take 1/100th of the rough curve length
     209           0 :                                 fBound = fRoughLength * 0.01;
     210             :                             }
     211             :                             else
     212             :                             {
     213             :                                 // use given bound value
     214           0 :                                 fBound = fDistanceBound;
     215             :                             }
     216             : 
     217             :                             // make sure bound value is not too small. The base units are 1/100th mm, thus
     218             :                             // just make sure it's not smaller then 1/100th of that
     219           0 :                             if(fBound < 0.01)
     220             :                             {
     221           0 :                                 fBound = 0.01;
     222             :                             }
     223             : 
     224             :                             // call adaptive subdivide which adds edges to aRetval accordingly
     225           0 :                             aBezier.adaptiveSubdivideByDistance(aRetval, fBound);
     226             :                         }
     227             :                         else
     228             :                         {
     229             :                             // add non-curved edge
     230           0 :                             aRetval.append(aBezier.getEndPoint());
     231             :                         }
     232             : 
     233             :                         // prepare next step
     234           0 :                         aBezier.setStartPoint(aBezier.getEndPoint());
     235             :                     }
     236             : 
     237           0 :                     if(rCandidate.isClosed())
     238             :                     {
     239             :                         // set closed flag and correct last point (which is added double now).
     240           0 :                         closeWithGeometryChange(aRetval);
     241           0 :                     }
     242             :                 }
     243             : 
     244           0 :                 return aRetval;
     245             :             }
     246             :             else
     247             :             {
     248           0 :                 return rCandidate;
     249             :             }
     250             :         }
     251             : 
     252        1630 :         B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound)
     253             :         {
     254        1630 :             if(rCandidate.areControlPointsUsed())
     255             :             {
     256        1630 :                 const sal_uInt32 nPointCount(rCandidate.count());
     257        1630 :                 B2DPolygon aRetval;
     258             : 
     259        1630 :                 if(nPointCount)
     260             :                 {
     261             :                     // prepare edge-oriented loop
     262        1630 :                     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     263        1630 :                     B2DCubicBezier aBezier;
     264        1630 :                     aBezier.setStartPoint(rCandidate.getB2DPoint(0));
     265             : 
     266             :                     // perf: try to avoid too many realloctions by guessing the result's pointcount
     267        1630 :                     aRetval.reserve(nPointCount*4);
     268             : 
     269             :                     // add start point (always)
     270        1630 :                     aRetval.append(aBezier.getStartPoint());
     271             : 
     272             :                     // #i37443# prepare convenient AngleBound if none was given
     273        1630 :                     if(0.0 == fAngleBound)
     274             :                     {
     275             : #ifdef DBG_UTIL
     276             :                         fAngleBound = fAngleBoundStartValue;
     277             : #else
     278        1630 :                         fAngleBound = ANGLE_BOUND_START_VALUE;
     279             : #endif
     280             :                     }
     281           0 :                     else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE))
     282             :                     {
     283           0 :                         fAngleBound = 0.1;
     284             :                     }
     285             : 
     286       29298 :                     for(sal_uInt32 a(0L); a < nEdgeCount; a++)
     287             :                     {
     288             :                         // get next and control points
     289       27668 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     290       27668 :                         aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     291       27668 :                         aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
     292       27668 :                         aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     293       27668 :                         aBezier.testAndSolveTrivialBezier();
     294             : 
     295       27668 :                         if(aBezier.isBezier())
     296             :                         {
     297             :                             // call adaptive subdivide
     298       18640 :                             aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true);
     299             :                         }
     300             :                         else
     301             :                         {
     302             :                             // add non-curved edge
     303        9028 :                             aRetval.append(aBezier.getEndPoint());
     304             :                         }
     305             : 
     306             :                         // prepare next step
     307       27668 :                         aBezier.setStartPoint(aBezier.getEndPoint());
     308             :                     }
     309             : 
     310        1630 :                     if(rCandidate.isClosed())
     311             :                     {
     312             :                         // set closed flag and correct last point (which is added double now).
     313        1574 :                         closeWithGeometryChange(aRetval);
     314        1630 :                     }
     315             :                 }
     316             : 
     317        1630 :                 return aRetval;
     318             :             }
     319             :             else
     320             :             {
     321           0 :                 return rCandidate;
     322             :             }
     323             :         }
     324             : 
     325        2591 :         B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount)
     326             :         {
     327        2591 :             if(rCandidate.areControlPointsUsed())
     328             :             {
     329        2591 :                 const sal_uInt32 nPointCount(rCandidate.count());
     330        2591 :                 B2DPolygon aRetval;
     331             : 
     332        2591 :                 if(nPointCount)
     333             :                 {
     334             :                     // prepare edge-oriented loop
     335        2591 :                     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     336        2591 :                     B2DCubicBezier aBezier;
     337        2591 :                     aBezier.setStartPoint(rCandidate.getB2DPoint(0));
     338             : 
     339             :                     // perf: try to avoid too many realloctions by guessing the result's pointcount
     340        2591 :                     aRetval.reserve(nPointCount*4);
     341             : 
     342             :                     // add start point (always)
     343        2591 :                     aRetval.append(aBezier.getStartPoint());
     344             : 
     345             :                     // #i37443# prepare convenient count if none was given
     346        2591 :                     if(0L == nCount)
     347             :                     {
     348           0 :                         nCount = COUNT_SUBDIVIDE_DEFAULT;
     349             :                     }
     350             : 
     351       33411 :                     for(sal_uInt32 a(0L); a < nEdgeCount; a++)
     352             :                     {
     353             :                         // get next and control points
     354       30820 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     355       30820 :                         aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     356       30820 :                         aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
     357       30820 :                         aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     358       30820 :                         aBezier.testAndSolveTrivialBezier();
     359             : 
     360       30820 :                         if(aBezier.isBezier())
     361             :                         {
     362             :                             // call adaptive subdivide
     363       13436 :                             aBezier.adaptiveSubdivideByCount(aRetval, nCount);
     364             :                         }
     365             :                         else
     366             :                         {
     367             :                             // add non-curved edge
     368       17384 :                             aRetval.append(aBezier.getEndPoint());
     369             :                         }
     370             : 
     371             :                         // prepare next step
     372       30820 :                         aBezier.setStartPoint(aBezier.getEndPoint());
     373             :                     }
     374             : 
     375        2591 :                     if(rCandidate.isClosed())
     376             :                     {
     377             :                         // set closed flag and correct last point (which is added double now).
     378        2576 :                         closeWithGeometryChange(aRetval);
     379        2591 :                     }
     380             :                 }
     381             : 
     382        2591 :                 return aRetval;
     383             :             }
     384             :             else
     385             :             {
     386           0 :                 return rCandidate;
     387             :             }
     388             :         }
     389             : 
     390       40366 :         bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
     391             :         {
     392       40366 :             const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
     393             : 
     394       40366 :             if(bWithBorder && isPointOnPolygon(aCandidate, rPoint, true))
     395             :             {
     396         362 :                 return true;
     397             :             }
     398             :             else
     399             :             {
     400       40004 :                 bool bRetval(false);
     401       40004 :                 const sal_uInt32 nPointCount(aCandidate.count());
     402             : 
     403       40004 :                 if(nPointCount)
     404             :                 {
     405       40004 :                     B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1L));
     406             : 
     407     4049815 :                     for(sal_uInt32 a(0L); a < nPointCount; a++)
     408             :                     {
     409     4009811 :                         const B2DPoint aPreviousPoint(aCurrentPoint);
     410     4009811 :                         aCurrentPoint = aCandidate.getB2DPoint(a);
     411             : 
     412             :                         // cross-over in Y?
     413     4009811 :                         const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
     414     4009811 :                         const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
     415             : 
     416     4009811 :                         if(bCompYA != bCompYB)
     417             :                         {
     418             :                             // cross-over in X?
     419       78524 :                             const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
     420       78524 :                             const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
     421             : 
     422       78524 :                             if(bCompXA == bCompXB)
     423             :                             {
     424       74112 :                                 if(bCompXA)
     425             :                                 {
     426       35138 :                                     bRetval = !bRetval;
     427             :                                 }
     428             :                             }
     429             :                             else
     430             :                             {
     431             :                                 const double fCompare(
     432       13236 :                                     aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
     433        8824 :                                     (aPreviousPoint.getX() - aCurrentPoint.getX()) /
     434        8824 :                                     (aPreviousPoint.getY() - aCurrentPoint.getY()));
     435             : 
     436        4412 :                                 if(fTools::more(fCompare, rPoint.getX()))
     437             :                                 {
     438        2412 :                                     bRetval = !bRetval;
     439             :                                 }
     440             :                             }
     441             :                         }
     442     4049815 :                     }
     443             :                 }
     444             : 
     445       40004 :                 return bRetval;
     446       40366 :             }
     447             :         }
     448             : 
     449        2702 :         bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder)
     450             :         {
     451        2702 :             const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
     452        5404 :             const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon);
     453        2702 :             const sal_uInt32 nPointCount(aPolygon.count());
     454             : 
     455       33780 :             for(sal_uInt32 a(0L); a < nPointCount; a++)
     456             :             {
     457       33154 :                 const B2DPoint aTestPoint(aPolygon.getB2DPoint(a));
     458             : 
     459       33154 :                 if(!isInside(aCandidate, aTestPoint, bWithBorder))
     460             :                 {
     461        2076 :                     return false;
     462             :                 }
     463       31078 :             }
     464             : 
     465        3328 :             return true;
     466             :         }
     467             : 
     468     5163771 :         B2DRange getRange(const B2DPolygon& rCandidate)
     469             :         {
     470             :             // changed to use internally buffered version at B2DPolygon
     471     5163771 :             return rCandidate.getB2DRange();
     472             :         }
     473             : 
     474       11715 :         double getSignedArea(const B2DPolygon& rCandidate)
     475             :         {
     476       11715 :             const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
     477       11715 :             double fRetval(0.0);
     478       11715 :             const sal_uInt32 nPointCount(aCandidate.count());
     479             : 
     480       11715 :             if(nPointCount > 2)
     481             :             {
     482      272059 :                 for(sal_uInt32 a(0L); a < nPointCount; a++)
     483             :                 {
     484      260348 :                     const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L));
     485      520696 :                     const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a));
     486             : 
     487      260348 :                     fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
     488      260348 :                     fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
     489      260348 :                 }
     490             : 
     491             :                 // correct to zero if small enough. Also test the quadratic
     492             :                 // of the result since the precision is near quadratic due to
     493             :                 // the algorithm
     494       11711 :                 if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval))
     495             :                 {
     496         113 :                     fRetval = 0.0;
     497             :                 }
     498             :             }
     499             : 
     500       11715 :             return fRetval;
     501             :         }
     502             : 
     503          16 :         double getArea(const B2DPolygon& rCandidate)
     504             :         {
     505          16 :             double fRetval(0.0);
     506             : 
     507          16 :             if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
     508             :             {
     509          16 :                 fRetval = getSignedArea(rCandidate);
     510          16 :                 const double fZero(0.0);
     511             : 
     512          16 :                 if(fTools::less(fRetval, fZero))
     513             :                 {
     514           0 :                     fRetval = -fRetval;
     515             :                 }
     516             :             }
     517             : 
     518          16 :             return fRetval;
     519             :         }
     520             : 
     521        1140 :         double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
     522             :         {
     523        1140 :             const sal_uInt32 nPointCount(rCandidate.count());
     524             :             OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)");
     525        1140 :             double fRetval(0.0);
     526             : 
     527        1140 :             if(nPointCount)
     528             :             {
     529        1140 :                 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
     530             : 
     531        1140 :                 if(rCandidate.areControlPointsUsed())
     532             :                 {
     533          38 :                     B2DCubicBezier aEdge;
     534             : 
     535          38 :                     aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex));
     536          38 :                     aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex));
     537          38 :                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     538          38 :                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     539             : 
     540          38 :                     fRetval = aEdge.getLength();
     541             :                 }
     542             :                 else
     543             :                 {
     544        1102 :                     const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex));
     545        2204 :                     const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
     546             : 
     547        2204 :                     fRetval = B2DVector(aNext - aCurrent).getLength();
     548             :                 }
     549             :             }
     550             : 
     551        1140 :             return fRetval;
     552             :         }
     553             : 
     554        1652 :         double getLength(const B2DPolygon& rCandidate)
     555             :         {
     556        1652 :             double fRetval(0.0);
     557        1652 :             const sal_uInt32 nPointCount(rCandidate.count());
     558             : 
     559        1652 :             if(nPointCount)
     560             :             {
     561        1652 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
     562             : 
     563        1652 :                 if(rCandidate.areControlPointsUsed())
     564             :                 {
     565           6 :                     B2DCubicBezier aEdge;
     566           6 :                     aEdge.setStartPoint(rCandidate.getB2DPoint(0));
     567             : 
     568          26 :                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
     569             :                     {
     570          20 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     571          20 :                         aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
     572          20 :                         aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     573          20 :                         aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     574             : 
     575          20 :                         fRetval += aEdge.getLength();
     576          20 :                         aEdge.setStartPoint(aEdge.getEndPoint());
     577           6 :                     }
     578             :                 }
     579             :                 else
     580             :                 {
     581        1646 :                     B2DPoint aCurrent(rCandidate.getB2DPoint(0));
     582             : 
     583        3806 :                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
     584             :                     {
     585        2160 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     586        2160 :                         const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
     587             : 
     588        2160 :                         fRetval += B2DVector(aNext - aCurrent).getLength();
     589        2160 :                         aCurrent = aNext;
     590        3806 :                     }
     591             :                 }
     592             :             }
     593             : 
     594        1652 :             return fRetval;
     595             :         }
     596             : 
     597         536 :         B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength)
     598             :         {
     599         536 :             B2DPoint aRetval;
     600         536 :             const sal_uInt32 nPointCount(rCandidate.count());
     601             : 
     602         536 :             if( 1L == nPointCount )
     603             :             {
     604             :                 // only one point (i.e. no edge) - simply take that point
     605           0 :                 aRetval = rCandidate.getB2DPoint(0);
     606             :             }
     607         536 :             else if(nPointCount > 1L)
     608             :             {
     609         536 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     610         536 :                 sal_uInt32 nIndex(0L);
     611         536 :                 bool bIndexDone(false);
     612             : 
     613             :                 // get length if not given
     614         536 :                 if(fTools::equalZero(fLength))
     615             :                 {
     616         116 :                     fLength = getLength(rCandidate);
     617             :                 }
     618             : 
     619         536 :                 if(fTools::less(fDistance, 0.0))
     620             :                 {
     621             :                     // handle fDistance < 0.0
     622          64 :                     if(rCandidate.isClosed())
     623             :                     {
     624             :                         // if fDistance < 0.0 increment with multiple of fLength
     625           0 :                         sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
     626           0 :                         fDistance += double(nCount + 1L) * fLength;
     627             :                     }
     628             :                     else
     629             :                     {
     630             :                         // crop to polygon start
     631          64 :                         fDistance = 0.0;
     632          64 :                         bIndexDone = true;
     633             :                     }
     634             :                 }
     635         472 :                 else if(fTools::moreOrEqual(fDistance, fLength))
     636             :                 {
     637             :                     // handle fDistance >= fLength
     638          52 :                     if(rCandidate.isClosed())
     639             :                     {
     640             :                         // if fDistance >= fLength decrement with multiple of fLength
     641           0 :                         sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
     642           0 :                         fDistance -= (double)(nCount) * fLength;
     643             :                     }
     644             :                     else
     645             :                     {
     646             :                         // crop to polygon end
     647          52 :                         fDistance = 0.0;
     648          52 :                         nIndex = nEdgeCount;
     649          52 :                         bIndexDone = true;
     650             :                     }
     651             :                 }
     652             : 
     653             :                 // look for correct index. fDistance is now [0.0 .. fLength[
     654         536 :                 double fEdgeLength(getEdgeLength(rCandidate, nIndex));
     655             : 
     656        1628 :                 while(!bIndexDone)
     657             :                 {
     658             :                     // edge found must be on the half-open range
     659             :                     // [0,fEdgeLength).
     660             :                     // Note that in theory, we cannot move beyond
     661             :                     // the last polygon point, since fDistance>=fLength
     662             :                     // is checked above. Unfortunately, with floating-
     663             :                     // point calculations, this case might happen.
     664             :                     // Handled by nIndex check below
     665         556 :                     if(nIndex < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength))
     666             :                     {
     667             :                         // go to next edge
     668         136 :                         fDistance -= fEdgeLength;
     669         136 :                         fEdgeLength = getEdgeLength(rCandidate, ++nIndex);
     670             :                     }
     671             :                     else
     672             :                     {
     673             :                         // it's on this edge, stop
     674         420 :                         bIndexDone = true;
     675             :                     }
     676             :                 }
     677             : 
     678             :                 // get the point using nIndex
     679         536 :                 aRetval = rCandidate.getB2DPoint(nIndex);
     680             : 
     681             :                 // if fDistance != 0.0, move that length on the edge. The edge
     682             :                 // length is in fEdgeLength.
     683         536 :                 if(!fTools::equalZero(fDistance))
     684             :                 {
     685         420 :                     if(fTools::moreOrEqual(fDistance, fEdgeLength))
     686             :                     {
     687             :                         // end point of chosen edge
     688           0 :                         const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
     689           0 :                         aRetval = rCandidate.getB2DPoint(nNextIndex);
     690             :                     }
     691         420 :                     else if(fTools::equalZero(fDistance))
     692             :                     {
     693             :                         // start point of chosen edge
     694           0 :                         aRetval = aRetval;
     695             :                     }
     696             :                     else
     697             :                     {
     698             :                         // inside edge
     699         420 :                         const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
     700         420 :                         const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
     701         420 :                         bool bDone(false);
     702             : 
     703             :                         // add calculated average value to the return value
     704         420 :                         if(rCandidate.areControlPointsUsed())
     705             :                         {
     706             :                             // get as bezier segment
     707             :                             const B2DCubicBezier aBezierSegment(
     708             :                                 aRetval, rCandidate.getNextControlPoint(nIndex),
     709           6 :                                 rCandidate.getPrevControlPoint(nNextIndex), aNextPoint);
     710             : 
     711           6 :                             if(aBezierSegment.isBezier())
     712             :                             {
     713             :                                 // use B2DCubicBezierHelper to bridge the non-linear gap between
     714             :                                 // length and bezier distances
     715           6 :                                 const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
     716           6 :                                 const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance));
     717             : 
     718           6 :                                 aRetval = aBezierSegment.interpolatePoint(fBezierDistance);
     719           6 :                                 bDone = true;
     720           6 :                             }
     721             :                         }
     722             : 
     723         420 :                         if(!bDone)
     724             :                         {
     725         414 :                             const double fRelativeInEdge(fDistance / fEdgeLength);
     726         414 :                             aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge);
     727         420 :                         }
     728             :                     }
     729             :                 }
     730             :             }
     731             : 
     732         536 :             return aRetval;
     733             :         }
     734             : 
     735           0 :         B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength)
     736             :         {
     737             :             // get length if not given
     738           0 :             if(fTools::equalZero(fLength))
     739             :             {
     740           0 :                 fLength = getLength(rCandidate);
     741             :             }
     742             : 
     743             :             // multiply fDistance with real length to get absolute position and
     744             :             // use getPositionAbsolute
     745           0 :             return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
     746             :         }
     747             : 
     748         386 :         B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
     749             :         {
     750         386 :             const sal_uInt32 nPointCount(rCandidate.count());
     751             : 
     752         386 :             if(nPointCount)
     753             :             {
     754             :                 // get length if not given
     755         386 :                 if(fTools::equalZero(fLength))
     756             :                 {
     757          64 :                     fLength = getLength(rCandidate);
     758             :                 }
     759             : 
     760             :                 // test and correct fFrom
     761         386 :                 if(fTools::less(fFrom, 0.0))
     762             :                 {
     763           0 :                     fFrom = 0.0;
     764             :                 }
     765             : 
     766             :                 // test and correct fTo
     767         386 :                 if(fTools::more(fTo, fLength))
     768             :                 {
     769           0 :                     fTo = fLength;
     770             :                 }
     771             : 
     772             :                 // test and correct relationship of fFrom, fTo
     773         386 :                 if(fTools::more(fFrom, fTo))
     774             :                 {
     775          64 :                     fFrom = fTo = (fFrom + fTo) / 2.0;
     776             :                 }
     777             : 
     778         386 :                 if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength))
     779             :                 {
     780             :                     // no change, result is the whole polygon
     781          52 :                     return rCandidate;
     782             :                 }
     783             :                 else
     784             :                 {
     785         334 :                     B2DPolygon aRetval;
     786         334 :                     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     787         334 :                     double fPositionOfStart(0.0);
     788         334 :                     bool bStartDone(false);
     789         334 :                     bool bEndDone(false);
     790             : 
     791         802 :                     for(sal_uInt32 a(0L); !(bStartDone && bEndDone) && a < nEdgeCount; a++)
     792             :                     {
     793         468 :                         const double fEdgeLength(getEdgeLength(rCandidate, a));
     794             : 
     795         468 :                         if(!bStartDone)
     796             :                         {
     797         364 :                             if(fTools::equalZero(fFrom))
     798             :                             {
     799         194 :                                 aRetval.append(rCandidate.getB2DPoint(a));
     800             : 
     801         194 :                                 if(rCandidate.areControlPointsUsed())
     802             :                                 {
     803           4 :                                     aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
     804             :                                 }
     805             : 
     806         194 :                                 bStartDone = true;
     807             :                             }
     808         170 :                             else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength))
     809             :                             {
     810             :                                 // calculate and add start point
     811         128 :                                 if(fTools::equalZero(fEdgeLength))
     812             :                                 {
     813           0 :                                     aRetval.append(rCandidate.getB2DPoint(a));
     814             : 
     815           0 :                                     if(rCandidate.areControlPointsUsed())
     816             :                                     {
     817           0 :                                         aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
     818             :                                     }
     819             :                                 }
     820             :                                 else
     821             :                                 {
     822         128 :                                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     823         128 :                                     const B2DPoint aStart(rCandidate.getB2DPoint(a));
     824         256 :                                     const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
     825         128 :                                     bool bDone(false);
     826             : 
     827         128 :                                     if(rCandidate.areControlPointsUsed())
     828             :                                     {
     829             :                                         const B2DCubicBezier aBezierSegment(
     830             :                                             aStart, rCandidate.getNextControlPoint(a),
     831           2 :                                             rCandidate.getPrevControlPoint(nNextIndex), aEnd);
     832             : 
     833           2 :                                         if(aBezierSegment.isBezier())
     834             :                                         {
     835             :                                             // use B2DCubicBezierHelper to bridge the non-linear gap between
     836             :                                             // length and bezier distances
     837           2 :                                             const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
     838           2 :                                             const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart));
     839           4 :                                             B2DCubicBezier aRight;
     840             : 
     841           2 :                                             aBezierSegment.split(fBezierDistance, 0, &aRight);
     842           2 :                                             aRetval.append(aRight.getStartPoint());
     843           2 :                                             aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA());
     844           4 :                                             bDone = true;
     845           2 :                                         }
     846             :                                     }
     847             : 
     848         128 :                                     if(!bDone)
     849             :                                     {
     850         126 :                                         const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength);
     851         126 :                                         aRetval.append(interpolate(aStart, aEnd, fRelValue));
     852         128 :                                     }
     853             :                                 }
     854             : 
     855         128 :                                 bStartDone = true;
     856             : 
     857             :                                 // if same point, end is done, too.
     858         128 :                                 if(fFrom == fTo)
     859             :                                 {
     860           0 :                                     bEndDone = true;
     861             :                                 }
     862             :                             }
     863             :                         }
     864             : 
     865         468 :                         if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength))
     866             :                         {
     867             :                             // calculate and add end point
     868         292 :                             if(fTools::equalZero(fEdgeLength))
     869             :                             {
     870           0 :                                 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     871           0 :                                 aRetval.append(rCandidate.getB2DPoint(nNextIndex));
     872             : 
     873           0 :                                 if(rCandidate.areControlPointsUsed())
     874             :                                 {
     875           0 :                                     aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
     876             :                                 }
     877             :                             }
     878             :                             else
     879             :                             {
     880         292 :                                 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     881         292 :                                 const B2DPoint aStart(rCandidate.getB2DPoint(a));
     882         584 :                                 const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
     883         292 :                                 bool bDone(false);
     884             : 
     885         292 :                                 if(rCandidate.areControlPointsUsed())
     886             :                                 {
     887             :                                     const B2DCubicBezier aBezierSegment(
     888             :                                         aStart, rCandidate.getNextControlPoint(a),
     889           4 :                                         rCandidate.getPrevControlPoint(nNextIndex), aEnd);
     890             : 
     891           4 :                                     if(aBezierSegment.isBezier())
     892             :                                     {
     893             :                                         // use B2DCubicBezierHelper to bridge the non-linear gap between
     894             :                                         // length and bezier distances
     895           4 :                                         const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
     896           4 :                                         const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart));
     897           8 :                                         B2DCubicBezier aLeft;
     898             : 
     899           4 :                                         aBezierSegment.split(fBezierDistance, &aLeft, 0);
     900           4 :                                         aRetval.append(aLeft.getEndPoint());
     901           4 :                                         aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB());
     902           8 :                                         bDone = true;
     903           4 :                                     }
     904             :                                 }
     905             : 
     906         292 :                                 if(!bDone)
     907             :                                 {
     908         288 :                                     const double fRelValue((fTo - fPositionOfStart) / fEdgeLength);
     909         288 :                                     aRetval.append(interpolate(aStart, aEnd, fRelValue));
     910         292 :                                 }
     911             :                             }
     912             : 
     913         292 :                             bEndDone = true;
     914             :                         }
     915             : 
     916         468 :                         if(!bEndDone)
     917             :                         {
     918         176 :                             if(bStartDone)
     919             :                             {
     920             :                                 // add segments end point
     921         134 :                                 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     922         134 :                                 aRetval.append(rCandidate.getB2DPoint(nNextIndex));
     923             : 
     924         134 :                                 if(rCandidate.areControlPointsUsed())
     925             :                                 {
     926          16 :                                     aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
     927          16 :                                     aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex));
     928             :                                 }
     929             :                             }
     930             : 
     931             :                             // increment fPositionOfStart
     932         176 :                             fPositionOfStart += fEdgeLength;
     933             :                         }
     934             :                     }
     935         334 :                     return aRetval;
     936             :                 }
     937             :             }
     938             :             else
     939             :             {
     940           0 :                 return rCandidate;
     941             :             }
     942             :         }
     943             : 
     944      396756 :         CutFlagValue findCut(
     945             :             const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
     946             :             const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
     947             :             CutFlagValue aCutFlags,
     948             :             double* pCut1, double* pCut2)
     949             :         {
     950      396756 :             CutFlagValue aRetval(CUTFLAG_NONE);
     951      396756 :             double fCut1(0.0);
     952      396756 :             double fCut2(0.0);
     953      396756 :             bool bFinished(!((bool)(aCutFlags & CUTFLAG_ALL)));
     954             : 
     955             :             // test for same points?
     956      396756 :             if(!bFinished
     957      396756 :                 && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END1))
     958       81666 :                 && (aCutFlags & (CUTFLAG_START2|CUTFLAG_END2)))
     959             :             {
     960             :                 // same startpoint?
     961       81666 :                 if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_START2)) == (CUTFLAG_START1|CUTFLAG_START2))
     962             :                 {
     963       81666 :                     if(rEdge1Start.equal(rEdge2Start))
     964             :                     {
     965           0 :                         bFinished = true;
     966           0 :                         aRetval = (CUTFLAG_START1|CUTFLAG_START2);
     967             :                     }
     968             :                 }
     969             : 
     970             :                 // same endpoint?
     971       81666 :                 if(!bFinished && (aCutFlags & (CUTFLAG_END1|CUTFLAG_END2)) == (CUTFLAG_END1|CUTFLAG_END2))
     972             :                 {
     973       81666 :                     const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
     974      163332 :                     const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
     975             : 
     976       81666 :                     if(aEnd1.equal(aEnd2))
     977             :                     {
     978           0 :                         bFinished = true;
     979           0 :                         aRetval = (CUTFLAG_END1|CUTFLAG_END2);
     980           0 :                         fCut1 = fCut2 = 1.0;
     981       81666 :                     }
     982             :                 }
     983             : 
     984             :                 // startpoint1 == endpoint2?
     985       81666 :                 if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END2)) == (CUTFLAG_START1|CUTFLAG_END2))
     986             :                 {
     987       81666 :                     const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
     988             : 
     989       81666 :                     if(rEdge1Start.equal(aEnd2))
     990             :                     {
     991           0 :                         bFinished = true;
     992           0 :                         aRetval = (CUTFLAG_START1|CUTFLAG_END2);
     993           0 :                         fCut1 = 0.0;
     994           0 :                         fCut2 = 1.0;
     995       81666 :                     }
     996             :                 }
     997             : 
     998             :                 // startpoint2 == endpoint1?
     999       81666 :                 if(!bFinished&& (aCutFlags & (CUTFLAG_START2|CUTFLAG_END1)) == (CUTFLAG_START2|CUTFLAG_END1))
    1000             :                 {
    1001       81666 :                     const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
    1002             : 
    1003       81666 :                     if(rEdge2Start.equal(aEnd1))
    1004             :                     {
    1005           0 :                         bFinished = true;
    1006           0 :                         aRetval = (CUTFLAG_START2|CUTFLAG_END1);
    1007           0 :                         fCut1 = 1.0;
    1008           0 :                         fCut2 = 0.0;
    1009       81666 :                     }
    1010             :                 }
    1011             :             }
    1012             : 
    1013      396756 :             if(!bFinished && (aCutFlags & CUTFLAG_LINE))
    1014             :             {
    1015      396756 :                 if(!bFinished && (aCutFlags & CUTFLAG_START1))
    1016             :                 {
    1017             :                     // start1 on line 2 ?
    1018       81666 :                     if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2))
    1019             :                     {
    1020        4254 :                         bFinished = true;
    1021        4254 :                         aRetval = (CUTFLAG_LINE|CUTFLAG_START1);
    1022             :                     }
    1023             :                 }
    1024             : 
    1025      396756 :                 if(!bFinished && (aCutFlags & CUTFLAG_START2))
    1026             :                 {
    1027             :                     // start2 on line 1 ?
    1028      186088 :                     if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1))
    1029             :                     {
    1030           0 :                         bFinished = true;
    1031           0 :                         aRetval = (CUTFLAG_LINE|CUTFLAG_START2);
    1032             :                     }
    1033             :                 }
    1034             : 
    1035      396756 :                 if(!bFinished && (aCutFlags & CUTFLAG_END1))
    1036             :                 {
    1037             :                     // end1 on line 2 ?
    1038       77412 :                     const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
    1039             : 
    1040       77412 :                     if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2))
    1041             :                     {
    1042           0 :                         bFinished = true;
    1043           0 :                         aRetval = (CUTFLAG_LINE|CUTFLAG_END1);
    1044       77412 :                     }
    1045             :                 }
    1046             : 
    1047      396756 :                 if(!bFinished && (aCutFlags & CUTFLAG_END2))
    1048             :                 {
    1049             :                     // end2 on line 1 ?
    1050      186088 :                     const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
    1051             : 
    1052      186088 :                     if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1))
    1053             :                     {
    1054           0 :                         bFinished = true;
    1055           0 :                         aRetval = (CUTFLAG_LINE|CUTFLAG_END2);
    1056      186088 :                     }
    1057             :                 }
    1058             : 
    1059      396756 :                 if(!bFinished)
    1060             :                 {
    1061             :                     // cut in line1, line2 ?
    1062      392502 :                     fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX());
    1063             : 
    1064      392502 :                     if(!fTools::equalZero(fCut1))
    1065             :                     {
    1066      367861 :                         fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX())
    1067      367861 :                             + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1;
    1068             : 
    1069      367861 :                         const double fZero(0.0);
    1070      367861 :                         const double fOne(1.0);
    1071             : 
    1072             :                         // inside parameter range edge1 AND fCut2 is calcable
    1073     1623261 :                         if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne)
    1074     1264373 :                             && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY())))
    1075             :                         {
    1076             :                             // take the mopre precise calculation of the two possible
    1077       80395 :                             if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY()))
    1078             :                             {
    1079       89070 :                                 fCut2 = (rEdge1Start.getX() + fCut1
    1080       89070 :                                     * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX();
    1081             :                             }
    1082             :                             else
    1083             :                             {
    1084       71720 :                                 fCut2 = (rEdge1Start.getY() + fCut1
    1085       71720 :                                     * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY();
    1086             :                             }
    1087             : 
    1088             :                             // inside parameter range edge2, too
    1089       80395 :                             if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
    1090             :                             {
    1091       22826 :                                 bFinished = true;
    1092       22826 :                                 aRetval = CUTFLAG_LINE;
    1093             :                             }
    1094             :                         }
    1095             :                     }
    1096             :                 }
    1097             :             }
    1098             : 
    1099             :             // copy values if wanted
    1100      396756 :             if(pCut1)
    1101             :             {
    1102      288080 :                 *pCut1 = fCut1;
    1103             :             }
    1104             : 
    1105      396756 :             if(pCut2)
    1106             :             {
    1107      206414 :                 *pCut2 = fCut2;
    1108             :             }
    1109             : 
    1110      396756 :             return aRetval;
    1111             :         }
    1112             : 
    1113     1356993 :         bool isPointOnEdge(
    1114             :             const B2DPoint& rPoint,
    1115             :             const B2DPoint& rEdgeStart,
    1116             :             const B2DVector& rEdgeDelta,
    1117             :             double* pCut)
    1118             :         {
    1119     1356993 :             bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX()));
    1120     1356993 :             bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY()));
    1121     1356993 :             const double fZero(0.0);
    1122     1356993 :             const double fOne(1.0);
    1123             : 
    1124     1356993 :             if(bDeltaXIsZero && bDeltaYIsZero)
    1125             :             {
    1126             :                 // no line, just a point
    1127         124 :                 return false;
    1128             :             }
    1129     1356869 :             else if(bDeltaXIsZero)
    1130             :             {
    1131             :                 // vertical line
    1132      236920 :                 if(fTools::equal(rPoint.getX(), rEdgeStart.getX()))
    1133             :                 {
    1134        8086 :                     double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
    1135             : 
    1136        8086 :                     if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
    1137             :                     {
    1138        4254 :                         if(pCut)
    1139             :                         {
    1140        4254 :                             *pCut = fValue;
    1141             :                         }
    1142             : 
    1143        4254 :                         return true;
    1144             :                     }
    1145             :                 }
    1146             :             }
    1147     1119949 :             else if(bDeltaYIsZero)
    1148             :             {
    1149             :                 // horizontal line
    1150      129786 :                 if(fTools::equal(rPoint.getY(), rEdgeStart.getY()))
    1151             :                 {
    1152           0 :                     double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
    1153             : 
    1154           0 :                     if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
    1155             :                     {
    1156           0 :                         if(pCut)
    1157             :                         {
    1158           0 :                             *pCut = fValue;
    1159             :                         }
    1160             : 
    1161           0 :                         return true;
    1162             :                     }
    1163             :                 }
    1164             :             }
    1165             :             else
    1166             :             {
    1167             :                 // any angle line
    1168      990163 :                 double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
    1169      990163 :                 double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
    1170             : 
    1171      990163 :                 if(fTools::equal(fTOne, fTTwo))
    1172             :                 {
    1173             :                     // same parameter representation, point is on line. Take
    1174             :                     // middle value for better results
    1175       55465 :                     double fValue = (fTOne + fTTwo) / 2.0;
    1176             : 
    1177       55465 :                     if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
    1178             :                     {
    1179             :                         // point is inside line bounds, too
    1180          38 :                         if(pCut)
    1181             :                         {
    1182           0 :                             *pCut = fValue;
    1183             :                         }
    1184             : 
    1185          38 :                         return true;
    1186             :                     }
    1187             :                 }
    1188             :             }
    1189             : 
    1190     1352577 :             return false;
    1191             :         }
    1192             : 
    1193        3260 :         void applyLineDashing(const B2DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength)
    1194             :         {
    1195        3260 :             const sal_uInt32 nPointCount(rCandidate.count());
    1196        3260 :             const sal_uInt32 nDotDashCount(rDotDashArray.size());
    1197             : 
    1198        3260 :             if(fTools::lessOrEqual(fDotDashLength, 0.0))
    1199             :             {
    1200          28 :                 fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
    1201             :             }
    1202             : 
    1203        3260 :             if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
    1204             :             {
    1205             :                 // clear targets
    1206        3260 :                 if(pLineTarget)
    1207             :                 {
    1208        3260 :                     pLineTarget->clear();
    1209             :                 }
    1210             : 
    1211        3260 :                 if(pGapTarget)
    1212             :                 {
    1213           0 :                     pGapTarget->clear();
    1214             :                 }
    1215             : 
    1216             :                 // prepare current edge's start
    1217        3260 :                 B2DCubicBezier aCurrentEdge;
    1218        3260 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
    1219        3260 :                 aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
    1220             : 
    1221             :                 // prepare DotDashArray iteration and the line/gap switching bool
    1222        3260 :                 sal_uInt32 nDotDashIndex(0);
    1223        3260 :                 bool bIsLine(true);
    1224        3260 :                 double fDotDashMovingLength(rDotDashArray[0]);
    1225        6520 :                 B2DPolygon aSnippet;
    1226             : 
    1227             :                 // iterate over all edges
    1228        7523 :                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
    1229             :                 {
    1230             :                     // update current edge (fill in C1, C2 and end point)
    1231        4263 :                     double fLastDotDashMovingLength(0.0);
    1232        4263 :                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    1233        4263 :                     aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
    1234        4263 :                     aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
    1235        4263 :                     aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
    1236             : 
    1237             :                     // check if we have a trivial bezier segment -> possible fallback to edge
    1238        4263 :                     aCurrentEdge.testAndSolveTrivialBezier();
    1239             : 
    1240        4263 :                     if(aCurrentEdge.isBezier())
    1241             :                     {
    1242             :                         // bezier segment
    1243         260 :                         const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
    1244         260 :                         const double fEdgeLength(aCubicBezierHelper.getLength());
    1245             : 
    1246         260 :                         if(!fTools::equalZero(fEdgeLength))
    1247             :                         {
    1248        2807 :                             while(fTools::less(fDotDashMovingLength, fEdgeLength))
    1249             :                             {
    1250             :                                 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
    1251        2287 :                                 const bool bHandleLine(bIsLine && pLineTarget);
    1252        2287 :                                 const bool bHandleGap(!bIsLine && pGapTarget);
    1253             : 
    1254        2287 :                                 if(bHandleLine || bHandleGap)
    1255             :                                 {
    1256        1141 :                                     const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
    1257        1141 :                                     const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
    1258        1141 :                                     B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
    1259             : 
    1260        1141 :                                     if(!aSnippet.count())
    1261             :                                     {
    1262         982 :                                         aSnippet.append(aBezierSnippet.getStartPoint());
    1263             :                                     }
    1264             : 
    1265        1141 :                                     aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
    1266             : 
    1267        1141 :                                     if(bHandleLine)
    1268             :                                     {
    1269        1141 :                                         pLineTarget->append(aSnippet);
    1270             :                                     }
    1271             :                                     else
    1272             :                                     {
    1273           0 :                                         pGapTarget->append(aSnippet);
    1274             :                                     }
    1275             : 
    1276        1141 :                                     aSnippet.clear();
    1277             :                                 }
    1278             : 
    1279             :                                 // prepare next DotDashArray step and flip line/gap flag
    1280        2287 :                                 fLastDotDashMovingLength = fDotDashMovingLength;
    1281        2287 :                                 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
    1282        2287 :                                 bIsLine = !bIsLine;
    1283             :                             }
    1284             : 
    1285             :                             // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
    1286         260 :                             const bool bHandleLine(bIsLine && pLineTarget);
    1287         260 :                             const bool bHandleGap(!bIsLine && pGapTarget);
    1288             : 
    1289         260 :                             if(bHandleLine || bHandleGap)
    1290             :                             {
    1291         191 :                                 B2DCubicBezier aRight;
    1292         191 :                                 const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
    1293             : 
    1294         191 :                                 aCurrentEdge.split(fBezierSplit, 0, &aRight);
    1295             : 
    1296         191 :                                 if(!aSnippet.count())
    1297             :                                 {
    1298         191 :                                     aSnippet.append(aRight.getStartPoint());
    1299             :                                 }
    1300             : 
    1301         191 :                                 aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
    1302             :                             }
    1303             : 
    1304             :                             // prepare move to next edge
    1305         260 :                             fDotDashMovingLength -= fEdgeLength;
    1306         260 :                         }
    1307             :                     }
    1308             :                     else
    1309             :                     {
    1310             :                         // simple edge
    1311        4003 :                         const double fEdgeLength(aCurrentEdge.getEdgeLength());
    1312             : 
    1313        4003 :                         if(!fTools::equalZero(fEdgeLength))
    1314             :                         {
    1315      262804 :                             while(fTools::less(fDotDashMovingLength, fEdgeLength))
    1316             :                             {
    1317             :                                 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
    1318      254924 :                                 const bool bHandleLine(bIsLine && pLineTarget);
    1319      254924 :                                 const bool bHandleGap(!bIsLine && pGapTarget);
    1320             : 
    1321      254924 :                                 if(bHandleLine || bHandleGap)
    1322             :                                 {
    1323      128468 :                                     if(!aSnippet.count())
    1324             :                                     {
    1325      128083 :                                         aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
    1326             :                                     }
    1327             : 
    1328      128468 :                                     aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
    1329             : 
    1330      128468 :                                     if(bHandleLine)
    1331             :                                     {
    1332      128468 :                                         pLineTarget->append(aSnippet);
    1333             :                                     }
    1334             :                                     else
    1335             :                                     {
    1336           0 :                                         pGapTarget->append(aSnippet);
    1337             :                                     }
    1338             : 
    1339      128468 :                                     aSnippet.clear();
    1340             :                                 }
    1341             : 
    1342             :                                 // prepare next DotDashArray step and flip line/gap flag
    1343      254924 :                                 fLastDotDashMovingLength = fDotDashMovingLength;
    1344      254924 :                                 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
    1345      254924 :                                 bIsLine = !bIsLine;
    1346             :                             }
    1347             : 
    1348             :                             // append snippet [fLastDotDashMovingLength, fEdgeLength]
    1349        3940 :                             const bool bHandleLine(bIsLine && pLineTarget);
    1350        3940 :                             const bool bHandleGap(!bIsLine && pGapTarget);
    1351             : 
    1352        3940 :                             if(bHandleLine || bHandleGap)
    1353             :                             {
    1354        1538 :                                 if(!aSnippet.count())
    1355             :                                 {
    1356        1538 :                                     aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
    1357             :                                 }
    1358             : 
    1359        1538 :                                 aSnippet.append(aCurrentEdge.getEndPoint());
    1360             :                             }
    1361             : 
    1362             :                             // prepare move to next edge
    1363        3940 :                             fDotDashMovingLength -= fEdgeLength;
    1364             :                         }
    1365             :                     }
    1366             : 
    1367             :                     // prepare next edge step (end point gets new start point)
    1368        4263 :                     aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
    1369             :                 }
    1370             : 
    1371             :                 // append last intermediate results (if exists)
    1372        3260 :                 if(aSnippet.count())
    1373             :                 {
    1374        1185 :                     if(bIsLine && pLineTarget)
    1375             :                     {
    1376        1185 :                         pLineTarget->append(aSnippet);
    1377             :                     }
    1378           0 :                     else if(!bIsLine && pGapTarget)
    1379             :                     {
    1380           0 :                         pGapTarget->append(aSnippet);
    1381             :                     }
    1382             :                 }
    1383             : 
    1384             :                 // check if start and end polygon may be merged
    1385        3260 :                 if(pLineTarget)
    1386             :                 {
    1387        3260 :                     const sal_uInt32 nCount(pLineTarget->count());
    1388             : 
    1389        3260 :                     if(nCount > 1)
    1390             :                     {
    1391             :                         // these polygons were created above, there exists none with less than two points,
    1392             :                         // thus dircet point access below is allowed
    1393        3192 :                         const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0));
    1394        6384 :                         B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1));
    1395             : 
    1396        3192 :                         if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
    1397             :                         {
    1398             :                             // start of first and end of last are the same -> merge them
    1399         159 :                             aLast.append(aFirst);
    1400         159 :                             aLast.removeDoublePoints();
    1401         159 :                             pLineTarget->setB2DPolygon(0, aLast);
    1402         159 :                             pLineTarget->remove(nCount - 1);
    1403        3192 :                         }
    1404             :                     }
    1405             :                 }
    1406             : 
    1407        3260 :                 if(pGapTarget)
    1408             :                 {
    1409           0 :                     const sal_uInt32 nCount(pGapTarget->count());
    1410             : 
    1411           0 :                     if(nCount > 1)
    1412             :                     {
    1413             :                         // these polygons were created above, there exists none with less than two points,
    1414             :                         // thus dircet point access below is allowed
    1415           0 :                         const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0));
    1416           0 :                         B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1));
    1417             : 
    1418           0 :                         if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
    1419             :                         {
    1420             :                             // start of first and end of last are the same -> merge them
    1421           0 :                             aLast.append(aFirst);
    1422           0 :                             aLast.removeDoublePoints();
    1423           0 :                             pGapTarget->setB2DPolygon(0, aLast);
    1424           0 :                             pGapTarget->remove(nCount - 1);
    1425           0 :                         }
    1426             :                     }
    1427        3260 :                 }
    1428             :             }
    1429             :             else
    1430             :             {
    1431             :                 // parameters make no sense, just add source to targets
    1432           0 :                 if(pLineTarget)
    1433             :                 {
    1434           0 :                     pLineTarget->append(rCandidate);
    1435             :                 }
    1436             : 
    1437           0 :                 if(pGapTarget)
    1438             :                 {
    1439           0 :                     pGapTarget->append(rCandidate);
    1440             :                 }
    1441             :             }
    1442        3260 :         }
    1443             : 
    1444             :         // test if point is inside epsilon-range around an edge defined
    1445             :         // by the two given points. Can be used for HitTesting. The epsilon-range
    1446             :         // is defined to be the rectangle centered to the given edge, using height
    1447             :         // 2 x fDistance, and the circle around both points with radius fDistance.
    1448          42 :         bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance)
    1449             :         {
    1450             :             // build edge vector
    1451          42 :             const B2DVector aEdge(rEdgeEnd - rEdgeStart);
    1452          42 :             bool bDoDistanceTestStart(false);
    1453          42 :             bool bDoDistanceTestEnd(false);
    1454             : 
    1455          42 :             if(aEdge.equalZero())
    1456             :             {
    1457             :                 // no edge, just a point. Do one of the distance tests.
    1458           0 :                 bDoDistanceTestStart = true;
    1459             :             }
    1460             :             else
    1461             :             {
    1462             :                 // edge has a length. Create perpendicular vector.
    1463          42 :                 const B2DVector aPerpend(getPerpendicular(aEdge));
    1464             :                 double fCut(
    1465          42 :                     (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX())
    1466          84 :                     + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) /
    1467          84 :                     (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY()));
    1468          42 :                 const double fZero(0.0);
    1469          42 :                 const double fOne(1.0);
    1470             : 
    1471          42 :                 if(fTools::less(fCut, fZero))
    1472             :                 {
    1473             :                     // left of rEdgeStart
    1474           0 :                     bDoDistanceTestStart = true;
    1475             :                 }
    1476          42 :                 else if(fTools::more(fCut, fOne))
    1477             :                 {
    1478             :                     // right of rEdgeEnd
    1479           4 :                     bDoDistanceTestEnd = true;
    1480             :                 }
    1481             :                 else
    1482             :                 {
    1483             :                     // inside line [0.0 .. 1.0]
    1484          38 :                     const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
    1485          76 :                     const B2DVector aDelta(rTestPosition - aCutPoint);
    1486          38 :                     const double fDistanceSquare(aDelta.scalar(aDelta));
    1487             : 
    1488          38 :                     if(fDistanceSquare <= fDistance * fDistance)
    1489             :                     {
    1490           6 :                         return true;
    1491             :                     }
    1492             :                     else
    1493             :                     {
    1494          32 :                         return false;
    1495          38 :                     }
    1496           4 :                 }
    1497             :             }
    1498             : 
    1499           4 :             if(bDoDistanceTestStart)
    1500             :             {
    1501           0 :                 const B2DVector aDelta(rTestPosition - rEdgeStart);
    1502           0 :                 const double fDistanceSquare(aDelta.scalar(aDelta));
    1503             : 
    1504           0 :                 if(fDistanceSquare <= fDistance * fDistance)
    1505             :                 {
    1506           0 :                     return true;
    1507           0 :                 }
    1508             :             }
    1509           4 :             else if(bDoDistanceTestEnd)
    1510             :             {
    1511           4 :                 const B2DVector aDelta(rTestPosition - rEdgeEnd);
    1512           4 :                 const double fDistanceSquare(aDelta.scalar(aDelta));
    1513             : 
    1514           4 :                 if(fDistanceSquare <= fDistance * fDistance)
    1515             :                 {
    1516           0 :                     return true;
    1517           4 :                 }
    1518             :             }
    1519             : 
    1520           4 :             return false;
    1521             :         }
    1522             : 
    1523             :         // test if point is inside epsilon-range around the given Polygon. Can be used
    1524             :         // for HitTesting. The epsilon-range is defined to be the tube around the polygon
    1525             :         // with distance fDistance and rounded edges (start and end point).
    1526          14 :         bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
    1527             :         {
    1528             :             // force to non-bezier polygon
    1529          14 :             const B2DPolygon aCandidate(rCandidate.getDefaultAdaptiveSubdivision());
    1530          14 :             const sal_uInt32 nPointCount(aCandidate.count());
    1531             : 
    1532          14 :             if(nPointCount)
    1533             :             {
    1534          14 :                 const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
    1535          14 :                 B2DPoint aCurrent(aCandidate.getB2DPoint(0));
    1536             : 
    1537          14 :                 if(nEdgeCount)
    1538             :                 {
    1539             :                     // edges
    1540          50 :                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
    1541             :                     {
    1542          42 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    1543          42 :                         const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
    1544             : 
    1545          42 :                         if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
    1546             :                         {
    1547           6 :                             return true;
    1548             :                         }
    1549             : 
    1550             :                         // prepare next step
    1551          36 :                         aCurrent = aNext;
    1552          36 :                     }
    1553             :                 }
    1554             :                 else
    1555             :                 {
    1556             :                     // no edges, but points -> not closed. Check single point. Just
    1557             :                     // use isInEpsilonRange with twice the same point, it handles those well
    1558           0 :                     if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
    1559             :                     {
    1560           0 :                         return true;
    1561             :                     }
    1562           8 :                 }
    1563             :             }
    1564             : 
    1565           8 :             return false;
    1566             :         }
    1567             : 
    1568       18765 :         B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY )
    1569             :         {
    1570       18765 :             const double fZero(0.0);
    1571       18765 :             const double fOne(1.0);
    1572             : 
    1573             :             // crop to useful values
    1574       18765 :             if(fTools::less(fRadiusX, fZero))
    1575             :             {
    1576           0 :                 fRadiusX = fZero;
    1577             :             }
    1578       18765 :             else if(fTools::more(fRadiusX, fOne))
    1579             :             {
    1580           0 :                 fRadiusX = fOne;
    1581             :             }
    1582             : 
    1583       18765 :             if(fTools::less(fRadiusY, fZero))
    1584             :             {
    1585           0 :                 fRadiusY = fZero;
    1586             :             }
    1587       18765 :             else if(fTools::more(fRadiusY, fOne))
    1588             :             {
    1589           0 :                 fRadiusY = fOne;
    1590             :             }
    1591             : 
    1592       18765 :             if(fZero == fRadiusX || fZero == fRadiusY)
    1593             :             {
    1594       18737 :                 B2DPolygon aRetval;
    1595             : 
    1596             :                 // at least in one direction no radius, use rectangle.
    1597             :                 // Do not use createPolygonFromRect() here since original
    1598             :                 // creator (historical reasons) still creates a start point at the
    1599             :                 // bottom center, so do the same here to get the same line patterns.
    1600             :                 // Due to this the order of points is different, too.
    1601       37474 :                 const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
    1602       18737 :                 aRetval.append(aBottomCenter);
    1603             : 
    1604       18737 :                 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
    1605       18737 :                 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
    1606       18737 :                 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
    1607       18737 :                 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
    1608             : 
    1609             :                 // close
    1610       18737 :                 aRetval.setClosed( true );
    1611             : 
    1612       37474 :                 return aRetval;
    1613             :             }
    1614          28 :             else if(fOne == fRadiusX && fOne == fRadiusY)
    1615             :             {
    1616             :                 // in both directions full radius, use ellipse
    1617           0 :                 const B2DPoint aCenter(rRect.getCenter());
    1618           0 :                 const double fRectRadiusX(rRect.getWidth() / 2.0);
    1619           0 :                 const double fRectRadiusY(rRect.getHeight() / 2.0);
    1620             : 
    1621           0 :                 return createPolygonFromEllipse( aCenter, fRectRadiusX, fRectRadiusY );
    1622             :             }
    1623             :             else
    1624             :             {
    1625          28 :                 B2DPolygon aRetval;
    1626          28 :                 const double fBowX((rRect.getWidth() / 2.0) * fRadiusX);
    1627          28 :                 const double fBowY((rRect.getHeight() / 2.0) * fRadiusY);
    1628          28 :                 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
    1629             : 
    1630             :                 // create start point at bottom center
    1631          28 :                 if(fOne != fRadiusX)
    1632             :                 {
    1633          28 :                     const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
    1634          28 :                     aRetval.append(aBottomCenter);
    1635             :                 }
    1636             : 
    1637             :                 // create first bow
    1638             :                 {
    1639          28 :                     const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY());
    1640          56 :                     const B2DPoint aStart(aBottomRight + B2DPoint(-fBowX, 0.0));
    1641          56 :                     const B2DPoint aStop(aBottomRight + B2DPoint(0.0, -fBowY));
    1642          28 :                     aRetval.append(aStart);
    1643          56 :                     aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop);
    1644             :                 }
    1645             : 
    1646             :                 // create second bow
    1647             :                 {
    1648          28 :                     const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY());
    1649          56 :                     const B2DPoint aStart(aTopRight + B2DPoint(0.0, fBowY));
    1650          56 :                     const B2DPoint aStop(aTopRight + B2DPoint(-fBowX, 0.0));
    1651          28 :                     aRetval.append(aStart);
    1652          56 :                     aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop);
    1653             :                 }
    1654             : 
    1655             :                 // create third bow
    1656             :                 {
    1657          28 :                     const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY());
    1658          56 :                     const B2DPoint aStart(aTopLeft + B2DPoint(fBowX, 0.0));
    1659          56 :                     const B2DPoint aStop(aTopLeft + B2DPoint(0.0, fBowY));
    1660          28 :                     aRetval.append(aStart);
    1661          56 :                     aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop);
    1662             :                 }
    1663             : 
    1664             :                 // create forth bow
    1665             :                 {
    1666          28 :                     const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY());
    1667          56 :                     const B2DPoint aStart(aBottomLeft + B2DPoint(0.0, -fBowY));
    1668          56 :                     const B2DPoint aStop(aBottomLeft + B2DPoint(fBowX, 0.0));
    1669          28 :                     aRetval.append(aStart);
    1670          56 :                     aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop);
    1671             :                 }
    1672             : 
    1673             :                 // close
    1674          28 :                 aRetval.setClosed( true );
    1675             : 
    1676             :                 // remove double created points if there are extreme radii envolved
    1677          28 :                 if(fOne == fRadiusX || fOne == fRadiusY)
    1678             :                 {
    1679           4 :                     aRetval.removeDoublePoints();
    1680             :                 }
    1681             : 
    1682          28 :                 return aRetval;
    1683             :             }
    1684             :         }
    1685             : 
    1686     1582070 :         B2DPolygon createPolygonFromRect( const B2DRectangle& rRect )
    1687             :         {
    1688     1582070 :             B2DPolygon aRetval;
    1689             : 
    1690     1582070 :             aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
    1691     1582070 :             aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
    1692     1582070 :             aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
    1693     1582070 :             aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
    1694             : 
    1695             :             // close
    1696     1582070 :             aRetval.setClosed( true );
    1697             : 
    1698     1582070 :             return aRetval;
    1699             :         }
    1700             : 
    1701             :         namespace
    1702             :         {
    1703             :             struct theUnitPolygon :
    1704             :                 public rtl::StaticWithInit<B2DPolygon, theUnitPolygon>
    1705             :             {
    1706          59 :                 B2DPolygon operator () ()
    1707             :                 {
    1708          59 :                     B2DPolygon aRetval;
    1709             : 
    1710          59 :                     aRetval.append( B2DPoint( 0.0, 0.0 ) );
    1711          59 :                     aRetval.append( B2DPoint( 1.0, 0.0 ) );
    1712          59 :                     aRetval.append( B2DPoint( 1.0, 1.0 ) );
    1713          59 :                     aRetval.append( B2DPoint( 0.0, 1.0 ) );
    1714             : 
    1715             :                     // close
    1716          59 :                     aRetval.setClosed( true );
    1717             : 
    1718          59 :                     return aRetval;
    1719             :                 }
    1720             :             };
    1721             :         }
    1722             : 
    1723        6493 :         B2DPolygon createUnitPolygon()
    1724             :         {
    1725        6493 :             return theUnitPolygon::get();
    1726             :         }
    1727             : 
    1728         277 :         B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius )
    1729             :         {
    1730         277 :             return createPolygonFromEllipse( rCenter, fRadius, fRadius );
    1731             :         }
    1732             : 
    1733          21 :         B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant)
    1734             :         {
    1735          21 :             B2DPolygon aUnitCircle;
    1736          21 :             const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
    1737          21 :             const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
    1738          42 :             const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
    1739             : 
    1740          42 :             B2DPoint aPoint(1.0, 0.0);
    1741          42 :             B2DPoint aForward(1.0, fScaledKappa);
    1742          42 :             B2DPoint aBackward(1.0, -fScaledKappa);
    1743             : 
    1744          21 :             if(0 != nStartQuadrant)
    1745             :             {
    1746          14 :                 const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4)));
    1747          14 :                 aPoint *= aQuadrantMatrix;
    1748          14 :                 aBackward *= aQuadrantMatrix;
    1749          14 :                 aForward *= aQuadrantMatrix;
    1750             :             }
    1751             : 
    1752          21 :             aUnitCircle.append(aPoint);
    1753             : 
    1754         273 :             for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++)
    1755             :             {
    1756         252 :                 aPoint *= aRotateMatrix;
    1757         252 :                 aBackward *= aRotateMatrix;
    1758         252 :                 aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint);
    1759         252 :                 aForward *= aRotateMatrix;
    1760             :             }
    1761             : 
    1762          21 :             aUnitCircle.setClosed(true);
    1763          21 :             aUnitCircle.removeDoublePoints();
    1764             : 
    1765          42 :             return aUnitCircle;
    1766             :         }
    1767             : 
    1768             :         namespace
    1769             :         {
    1770             :             struct theUnitHalfCircle :
    1771             :                 public rtl::StaticWithInit<B2DPolygon, theUnitHalfCircle>
    1772             :             {
    1773           1 :                 B2DPolygon operator()()
    1774             :                 {
    1775           1 :                     B2DPolygon aUnitHalfCircle;
    1776           1 :                     const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
    1777           1 :                     const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
    1778           2 :                     const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
    1779           2 :                     B2DPoint aPoint(1.0, 0.0);
    1780           2 :                     B2DPoint aForward(1.0, fScaledKappa);
    1781           2 :                     B2DPoint aBackward(1.0, -fScaledKappa);
    1782             : 
    1783           1 :                     aUnitHalfCircle.append(aPoint);
    1784             : 
    1785           7 :                     for(sal_uInt32 a(0); a < STEPSPERQUARTER * 2; a++)
    1786             :                     {
    1787           6 :                         aPoint *= aRotateMatrix;
    1788           6 :                         aBackward *= aRotateMatrix;
    1789           6 :                         aUnitHalfCircle.appendBezierSegment(aForward, aBackward, aPoint);
    1790           6 :                         aForward *= aRotateMatrix;
    1791             :                     }
    1792           2 :                     return aUnitHalfCircle;
    1793             :                 }
    1794             :             };
    1795             :         }
    1796             : 
    1797          18 :         B2DPolygon createHalfUnitCircle()
    1798             :         {
    1799          18 :             return theUnitHalfCircle::get();
    1800             :         }
    1801             : 
    1802             :         namespace
    1803             :         {
    1804             :             struct theUnitCircleStartQuadrantOne :
    1805             :                 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantOne>
    1806             :             {
    1807          14 :                 B2DPolygon operator()() { return impCreateUnitCircle(1); }
    1808             :             };
    1809             : 
    1810             :             struct theUnitCircleStartQuadrantTwo :
    1811             :                 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantTwo>
    1812             :             {
    1813           0 :                 B2DPolygon operator()() { return impCreateUnitCircle(2); }
    1814             :             };
    1815             : 
    1816             :             struct theUnitCircleStartQuadrantThree :
    1817             :                 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantThree>
    1818             :             {
    1819           0 :                 B2DPolygon operator()() { return impCreateUnitCircle(3); }
    1820             :             };
    1821             : 
    1822             :             struct theUnitCircleStartQuadrantZero :
    1823             :                 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantZero>
    1824             :             {
    1825           7 :                 B2DPolygon operator()() { return impCreateUnitCircle(0); }
    1826             :             };
    1827             :         }
    1828             : 
    1829        3867 :         B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
    1830             :         {
    1831        3867 :             switch(nStartQuadrant % 4)
    1832             :             {
    1833             :                 case 1 :
    1834         165 :                     return theUnitCircleStartQuadrantOne::get();
    1835             : 
    1836             :                 case 2 :
    1837           0 :                     return theUnitCircleStartQuadrantTwo::get();
    1838             : 
    1839             :                 case 3 :
    1840           0 :                     return theUnitCircleStartQuadrantThree::get();
    1841             : 
    1842             :                 default : // case 0 :
    1843        3702 :                     return theUnitCircleStartQuadrantZero::get();
    1844             :             }
    1845             :         }
    1846             : 
    1847         375 :         B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
    1848             :         {
    1849         375 :             B2DPolygon aRetval(createPolygonFromUnitCircle());
    1850         750 :             const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
    1851             : 
    1852         375 :             aRetval.transform(aMatrix);
    1853             : 
    1854         750 :             return aRetval;
    1855             :         }
    1856             : 
    1857       13560 :         B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
    1858             :         {
    1859       13560 :             B2DPolygon aRetval;
    1860             : 
    1861             :             // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI
    1862             :             // falls back to 0.0 to ensure a unique definition
    1863       13560 :             if(fTools::less(fStart, 0.0))
    1864             :             {
    1865           0 :                 fStart = 0.0;
    1866             :             }
    1867             : 
    1868       13560 :             if(fTools::moreOrEqual(fStart, F_2PI))
    1869             :             {
    1870           0 :                 fStart = 0.0;
    1871             :             }
    1872             : 
    1873       13560 :             if(fTools::less(fEnd, 0.0))
    1874             :             {
    1875           0 :                 fEnd = 0.0;
    1876             :             }
    1877             : 
    1878       13560 :             if(fTools::moreOrEqual(fEnd, F_2PI))
    1879             :             {
    1880           0 :                 fEnd = 0.0;
    1881             :             }
    1882             : 
    1883       13560 :             if(fTools::equal(fStart, fEnd))
    1884             :             {
    1885             :                 // same start and end angle, add single point
    1886           0 :                 aRetval.append(B2DPoint(cos(fStart), sin(fStart)));
    1887             :             }
    1888             :             else
    1889             :             {
    1890       13560 :                 const sal_uInt32 nSegments(STEPSPERQUARTER * 4);
    1891       13560 :                 const double fAnglePerSegment(F_PI2 / STEPSPERQUARTER);
    1892       13560 :                 const sal_uInt32 nStartSegment(sal_uInt32(fStart / fAnglePerSegment) % nSegments);
    1893       13560 :                 const sal_uInt32 nEndSegment(sal_uInt32(fEnd / fAnglePerSegment) % nSegments);
    1894       13560 :                 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
    1895       13560 :                 const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
    1896             : 
    1897       13560 :                 B2DPoint aSegStart(cos(fStart), sin(fStart));
    1898       13560 :                 aRetval.append(aSegStart);
    1899             : 
    1900       13560 :                 if(nStartSegment == nEndSegment && fTools::more(fEnd, fStart))
    1901             :                 {
    1902             :                     // start and end in one sector and in the right order, create in one segment
    1903        9570 :                     const B2DPoint aSegEnd(cos(fEnd), sin(fEnd));
    1904        9570 :                     const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment));
    1905             : 
    1906             :                     aRetval.appendBezierSegment(
    1907       19140 :                         aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
    1908       19140 :                         aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
    1909       38280 :                         aSegEnd);
    1910             :                 }
    1911             :                 else
    1912             :                 {
    1913        3990 :                     double fSegEndRad((nStartSegment + 1) * fAnglePerSegment);
    1914        3990 :                     double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment));
    1915        3990 :                     B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad));
    1916             : 
    1917             :                     aRetval.appendBezierSegment(
    1918        7980 :                         aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
    1919        7980 :                         aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
    1920       11970 :                         aSegEnd);
    1921             : 
    1922        3990 :                     sal_uInt32 nSegment((nStartSegment + 1) % nSegments);
    1923        3990 :                     aSegStart = aSegEnd;
    1924             : 
    1925       10510 :                     while(nSegment != nEndSegment)
    1926             :                     {
    1927             :                         // No end in this sector, add full sector.
    1928        2530 :                         fSegEndRad = (nSegment + 1) * fAnglePerSegment;
    1929        2530 :                         aSegEnd = B2DPoint(cos(fSegEndRad), sin(fSegEndRad));
    1930             : 
    1931             :                         aRetval.appendBezierSegment(
    1932        5060 :                             aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa),
    1933        5060 :                             aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa),
    1934        7590 :                             aSegEnd);
    1935             : 
    1936        2530 :                         nSegment = (nSegment + 1) % nSegments;
    1937        2530 :                         aSegStart = aSegEnd;
    1938             :                     }
    1939             : 
    1940             :                     // End in this sector
    1941        3990 :                     const double fSegStartRad(nSegment * fAnglePerSegment);
    1942        3990 :                     fFactor = fScaledKappa * ((fEnd - fSegStartRad) / fAnglePerSegment);
    1943        3990 :                     aSegEnd = B2DPoint(cos(fEnd), sin(fEnd));
    1944             : 
    1945             :                     aRetval.appendBezierSegment(
    1946        7980 :                         aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
    1947        7980 :                         aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
    1948       15960 :                         aSegEnd);
    1949       13560 :                 }
    1950             :             }
    1951             : 
    1952             :             // remove double points between segments created by segmented creation
    1953       13560 :             aRetval.removeDoublePoints();
    1954             : 
    1955       13560 :             return aRetval;
    1956             :         }
    1957             : 
    1958       13262 :         B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd )
    1959             :         {
    1960       13262 :             B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd));
    1961       26524 :             const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
    1962             : 
    1963       13262 :             aRetval.transform(aMatrix);
    1964             : 
    1965       26524 :             return aRetval;
    1966             :         }
    1967             : 
    1968         468 :         bool hasNeutralPoints(const B2DPolygon& rCandidate)
    1969             :         {
    1970             :             OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)");
    1971         468 :             const sal_uInt32 nPointCount(rCandidate.count());
    1972             : 
    1973         468 :             if(nPointCount > 2L)
    1974             :             {
    1975         456 :                 B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
    1976         838 :                 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
    1977             : 
    1978        2582 :                 for(sal_uInt32 a(0L); a < nPointCount; a++)
    1979             :                 {
    1980        2200 :                     const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
    1981        4326 :                     const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
    1982        4326 :                     const B2DVector aNextVec(aNextPoint - aCurrPoint);
    1983        2200 :                     const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
    1984             : 
    1985        2200 :                     if(ORIENTATION_NEUTRAL == aOrientation)
    1986             :                     {
    1987             :                         // current has neutral orientation
    1988          74 :                         return true;
    1989             :                     }
    1990             :                     else
    1991             :                     {
    1992             :                         // prepare next
    1993        2126 :                         aPrevPoint = aCurrPoint;
    1994        2126 :                         aCurrPoint = aNextPoint;
    1995             :                     }
    1996        2508 :                 }
    1997             :             }
    1998             : 
    1999         394 :             return false;
    2000             :         }
    2001             : 
    2002         468 :         B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate)
    2003             :         {
    2004         468 :             if(hasNeutralPoints(rCandidate))
    2005             :             {
    2006          74 :                 const sal_uInt32 nPointCount(rCandidate.count());
    2007          74 :                 B2DPolygon aRetval;
    2008         148 :                 B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
    2009         148 :                 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
    2010             : 
    2011         682 :                 for(sal_uInt32 a(0L); a < nPointCount; a++)
    2012             :                 {
    2013         608 :                     const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
    2014        1216 :                     const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
    2015        1216 :                     const B2DVector aNextVec(aNextPoint - aCurrPoint);
    2016         608 :                     const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
    2017             : 
    2018         608 :                     if(ORIENTATION_NEUTRAL == aOrientation)
    2019             :                     {
    2020             :                         // current has neutral orientation, leave it out and prepare next
    2021         296 :                         aCurrPoint = aNextPoint;
    2022             :                     }
    2023             :                     else
    2024             :                     {
    2025             :                         // add current point
    2026         312 :                         aRetval.append(aCurrPoint);
    2027             : 
    2028             :                         // prepare next
    2029         312 :                         aPrevPoint = aCurrPoint;
    2030         312 :                         aCurrPoint = aNextPoint;
    2031             :                     }
    2032         608 :                 }
    2033             : 
    2034         160 :                 while(aRetval.count() && ORIENTATION_NEUTRAL == getOrientationForIndex(aRetval, 0L))
    2035             :                 {
    2036          12 :                     aRetval.remove(0L);
    2037             :                 }
    2038             : 
    2039             :                 // copy closed state
    2040          74 :                 aRetval.setClosed(rCandidate.isClosed());
    2041             : 
    2042         148 :                 return aRetval;
    2043             :             }
    2044             :             else
    2045             :             {
    2046         394 :                 return rCandidate;
    2047             :             }
    2048             :         }
    2049             : 
    2050           0 :         bool isConvex(const B2DPolygon& rCandidate)
    2051             :         {
    2052             :             OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)");
    2053           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2054             : 
    2055           0 :             if(nPointCount > 2L)
    2056             :             {
    2057           0 :                 const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
    2058           0 :                 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
    2059           0 :                 B2DVector aCurrVec(aPrevPoint - aCurrPoint);
    2060           0 :                 B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL);
    2061             : 
    2062           0 :                 for(sal_uInt32 a(0L); a < nPointCount; a++)
    2063             :                 {
    2064           0 :                     const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
    2065           0 :                     const B2DVector aNextVec(aNextPoint - aCurrPoint);
    2066           0 :                     const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec));
    2067             : 
    2068           0 :                     if(ORIENTATION_NEUTRAL == aOrientation)
    2069             :                     {
    2070             :                         // set start value, maybe neutral again
    2071           0 :                         aOrientation = aCurrentOrientation;
    2072             :                     }
    2073             :                     else
    2074             :                     {
    2075           0 :                         if(ORIENTATION_NEUTRAL != aCurrentOrientation && aCurrentOrientation != aOrientation)
    2076             :                         {
    2077             :                             // different orientations found, that's it
    2078           0 :                             return false;
    2079             :                         }
    2080             :                     }
    2081             : 
    2082             :                     // prepare next
    2083           0 :                     aCurrPoint = aNextPoint;
    2084           0 :                     aCurrVec = -aNextVec;
    2085           0 :                 }
    2086             :             }
    2087             : 
    2088           0 :             return true;
    2089             :         }
    2090             : 
    2091          74 :         B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
    2092             :         {
    2093             :             OSL_ENSURE(nIndex < rCandidate.count(), "getOrientationForIndex: index out of range (!)");
    2094          74 :             const B2DPoint aPrev(rCandidate.getB2DPoint(getIndexOfPredecessor(nIndex, rCandidate)));
    2095         148 :             const B2DPoint aCurr(rCandidate.getB2DPoint(nIndex));
    2096         148 :             const B2DPoint aNext(rCandidate.getB2DPoint(getIndexOfSuccessor(nIndex, rCandidate)));
    2097         148 :             const B2DVector aBack(aPrev - aCurr);
    2098         148 :             const B2DVector aForw(aNext - aCurr);
    2099             : 
    2100         148 :             return getOrientation(aForw, aBack);
    2101             :         }
    2102             : 
    2103     3811384 :         bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints)
    2104             :         {
    2105     3811384 :             if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
    2106             :             {
    2107             :                 // candidate is in epsilon around start or end -> inside
    2108         362 :                 return bWithPoints;
    2109             :             }
    2110     3811022 :             else if(rStart.equal(rEnd))
    2111             :             {
    2112             :                 // start and end are equal, but candidate is outside their epsilon -> outside
    2113       21509 :                 return false;
    2114             :             }
    2115             :             else
    2116             :             {
    2117     3789513 :                 const B2DVector aEdgeVector(rEnd - rStart);
    2118     7579026 :                 const B2DVector aTestVector(rCandidate - rStart);
    2119             : 
    2120     3789513 :                 if(areParallel(aEdgeVector, aTestVector))
    2121             :                 {
    2122        1123 :                     const double fZero(0.0);
    2123        1123 :                     const double fOne(1.0);
    2124        1123 :                     const double fParamTestOnCurr(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())
    2125         509 :                         ? aTestVector.getX() / aEdgeVector.getX()
    2126        1632 :                         : aTestVector.getY() / aEdgeVector.getY());
    2127             : 
    2128        1123 :                     if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
    2129             :                     {
    2130           0 :                         return true;
    2131             :                     }
    2132             :                 }
    2133             : 
    2134     7579026 :                 return false;
    2135             :             }
    2136             :         }
    2137             : 
    2138       33158 :         bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints)
    2139             :         {
    2140       33158 :             const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
    2141       33158 :             const sal_uInt32 nPointCount(aCandidate.count());
    2142             : 
    2143       33158 :             if(nPointCount > 1L)
    2144             :             {
    2145       33158 :                 const sal_uInt32 nLoopCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
    2146       33158 :                 B2DPoint aCurrentPoint(aCandidate.getB2DPoint(0L));
    2147             : 
    2148     3844180 :                 for(sal_uInt32 a(0L); a < nLoopCount; a++)
    2149             :                 {
    2150     3811384 :                     const B2DPoint aNextPoint(aCandidate.getB2DPoint((a + 1L) % nPointCount));
    2151             : 
    2152     3811384 :                     if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
    2153             :                     {
    2154         362 :                         return true;
    2155             :                     }
    2156             : 
    2157     3811022 :                     aCurrentPoint = aNextPoint;
    2158     3843818 :                 }
    2159             :             }
    2160           0 :             else if(nPointCount && bWithPoints)
    2161             :             {
    2162           0 :                 return rPoint.equal(aCandidate.getB2DPoint(0L));
    2163             :             }
    2164             : 
    2165       32796 :             return false;
    2166             :         }
    2167             : 
    2168           0 :         bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder)
    2169             :         {
    2170           0 :             if(arePointsOnSameSideOfLine(rA, rB, rC, rCandidate, bWithBorder))
    2171             :             {
    2172           0 :                 if(arePointsOnSameSideOfLine(rB, rC, rA, rCandidate, bWithBorder))
    2173             :                 {
    2174           0 :                     if(arePointsOnSameSideOfLine(rC, rA, rB, rCandidate, bWithBorder))
    2175             :                     {
    2176           0 :                         return true;
    2177             :                     }
    2178             :                 }
    2179             :             }
    2180             : 
    2181           0 :             return false;
    2182             :         }
    2183             : 
    2184           0 :         bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine)
    2185             :         {
    2186           0 :             const B2DVector aLineVector(rEnd - rStart);
    2187           0 :             const B2DVector aVectorToA(rEnd - rCandidateA);
    2188           0 :             const double fCrossA(aLineVector.cross(aVectorToA));
    2189             : 
    2190           0 :             if(fTools::equalZero(fCrossA))
    2191             :             {
    2192             :                 // one point on the line
    2193           0 :                 return bWithLine;
    2194             :             }
    2195             : 
    2196           0 :             const B2DVector aVectorToB(rEnd - rCandidateB);
    2197           0 :             const double fCrossB(aLineVector.cross(aVectorToB));
    2198             : 
    2199           0 :             if(fTools::equalZero(fCrossB))
    2200             :             {
    2201             :                 // one point on the line
    2202           0 :                 return bWithLine;
    2203             :             }
    2204             : 
    2205             :             // return true if they both have the same sign
    2206           0 :             return ((fCrossA > 0.0) == (fCrossB > 0.0));
    2207             :         }
    2208             : 
    2209           0 :         void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget)
    2210             :         {
    2211           0 :             const sal_uInt32 nCount(rCandidate.count());
    2212             : 
    2213           0 :             if(nCount > 2L)
    2214             :             {
    2215           0 :                 const B2DPoint aStart(rCandidate.getB2DPoint(0L));
    2216           0 :                 B2DPoint aLast(rCandidate.getB2DPoint(1L));
    2217             : 
    2218           0 :                 for(sal_uInt32 a(2L); a < nCount; a++)
    2219             :                 {
    2220           0 :                     const B2DPoint aCurrent(rCandidate.getB2DPoint(a));
    2221           0 :                     rTarget.append(aStart);
    2222           0 :                     rTarget.append(aLast);
    2223           0 :                     rTarget.append(aCurrent);
    2224             : 
    2225             :                     // prepare next
    2226           0 :                     aLast = aCurrent;
    2227           0 :                 }
    2228             :             }
    2229           0 :         }
    2230             : 
    2231             :         namespace
    2232             :         {
    2233             :             /// return 0 for input of 0, -1 for negative and 1 for positive input
    2234       90240 :             inline int lcl_sgn( const double n )
    2235             :             {
    2236       90240 :                 return n == 0.0 ? 0 : 1 - 2*int(rtl::math::isSignBitSet(n));
    2237             :             }
    2238             :         }
    2239             : 
    2240       11682 :         bool isRectangle( const B2DPolygon& rPoly )
    2241             :         {
    2242             :             // polygon must be closed to resemble a rect, and contain
    2243             :             // at least four points.
    2244       35044 :             if( !rPoly.isClosed() ||
    2245       23360 :                 rPoly.count() < 4 ||
    2246       11678 :                 rPoly.areControlPointsUsed() )
    2247             :             {
    2248         172 :                 return false;
    2249             :             }
    2250             : 
    2251             :             // number of 90 degree turns the polygon has taken
    2252       11510 :             int nNumTurns(0);
    2253             : 
    2254       11510 :             int  nVerticalEdgeType=0;
    2255       11510 :             int  nHorizontalEdgeType=0;
    2256       11510 :             bool bNullVertex(true);
    2257       11510 :             bool bCWPolygon(false);  // when true, polygon is CW
    2258             :                                      // oriented, when false, CCW
    2259       11510 :             bool bOrientationSet(false); // when false, polygon
    2260             :                                          // orientation has not yet
    2261             :                                          // been determined.
    2262             : 
    2263             :             // scan all _edges_ (which involves coming back to point 0
    2264             :             // for the last edge - thus the modulo operation below)
    2265       11510 :             const sal_Int32 nCount( rPoly.count() );
    2266       56276 :             for( sal_Int32 i=0; i<nCount; ++i )
    2267             :             {
    2268       45120 :                 const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) );
    2269       89754 :                 const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) );
    2270             : 
    2271             :                 // is 0 for zero direction vector, 1 for south edge and -1
    2272             :                 // for north edge (standard screen coordinate system)
    2273       45120 :                 int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) );
    2274             : 
    2275             :                 // is 0 for zero direction vector, 1 for east edge and -1
    2276             :                 // for west edge (standard screen coordinate system)
    2277       45120 :                 int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) );
    2278             : 
    2279       45120 :                 if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType )
    2280         352 :                     return false; // oblique edge - for sure no rect
    2281             : 
    2282       44768 :                 const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType );
    2283             : 
    2284             :                 // current vertex is equal to previous - just skip,
    2285             :                 // until we have a real edge
    2286       44768 :                 if( bCurrNullVertex )
    2287          26 :                     continue;
    2288             : 
    2289             :                 // if previous edge has two identical points, because
    2290             :                 // no previous edge direction was available, simply
    2291             :                 // take this first non-null edge as the start
    2292             :                 // direction. That's what will happen here, if
    2293             :                 // bNullVertex is false
    2294       44742 :                 if( !bNullVertex )
    2295             :                 {
    2296             :                     // 2D cross product - is 1 for CW and -1 for CCW turns
    2297       33582 :                     const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType -
    2298       33582 :                                              nVerticalEdgeType*nCurrHorizontalEdgeType );
    2299             : 
    2300       33582 :                     if( !nCrossProduct )
    2301         106 :                         continue; // no change in orientation -
    2302             :                                   // collinear edges - just go on
    2303             : 
    2304             :                     // if polygon orientation is not set, we'll
    2305             :                     // determine it now
    2306       33476 :                     if( !bOrientationSet )
    2307             :                     {
    2308       11158 :                         bCWPolygon = nCrossProduct == 1;
    2309       11158 :                         bOrientationSet = true;
    2310             :                     }
    2311             :                     else
    2312             :                     {
    2313             :                         // if current turn orientation is not equal
    2314             :                         // initial orientation, this is not a
    2315             :                         // rectangle (as rectangles have consistent
    2316             :                         // orientation).
    2317       22318 :                         if( (nCrossProduct == 1) != bCWPolygon )
    2318           2 :                             return false;
    2319             :                     }
    2320             : 
    2321       33474 :                     ++nNumTurns;
    2322             : 
    2323             :                     // More than four 90 degree turns are an
    2324             :                     // indication that this must not be a rectangle.
    2325       33474 :                     if( nNumTurns > 4 )
    2326           0 :                         return false;
    2327             :                 }
    2328             : 
    2329             :                 // store current state for the next turn
    2330       44634 :                 nVerticalEdgeType   = nCurrVerticalEdgeType;
    2331       44634 :                 nHorizontalEdgeType = nCurrHorizontalEdgeType;
    2332       44634 :                 bNullVertex         = false; // won't reach this line,
    2333             :                                              // if bCurrNullVertex is
    2334             :                                              // true - see above
    2335       44634 :             }
    2336             : 
    2337       11156 :             return true;
    2338             :         }
    2339             : 
    2340       21668 :         B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate)
    2341             :         {
    2342       21668 :             if(rCandidate.areControlPointsUsed())
    2343             :             {
    2344             :                 // call myself recursively with subdivided input
    2345           0 :                 const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
    2346           0 :                 return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate);
    2347             :             }
    2348             :             else
    2349             :             {
    2350       21668 :                 B3DPolygon aRetval;
    2351             : 
    2352       92376 :                 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
    2353             :                 {
    2354       70708 :                     B2DPoint aPoint(rCandidate.getB2DPoint(a));
    2355       70708 :                     aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate));
    2356       70708 :                 }
    2357             : 
    2358             :                 // copy closed state
    2359       21668 :                 aRetval.setClosed(rCandidate.isClosed());
    2360             : 
    2361       21668 :                 return aRetval;
    2362             :             }
    2363             :         }
    2364             : 
    2365        4700 :         B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat)
    2366             :         {
    2367        4700 :             B2DPolygon aRetval;
    2368        4700 :             const sal_uInt32 nCount(rCandidate.count());
    2369        4700 :             const bool bIsIdentity(rMat.isIdentity());
    2370             : 
    2371       36972 :             for(sal_uInt32 a(0L); a < nCount; a++)
    2372             :             {
    2373       32272 :                 B3DPoint aCandidate(rCandidate.getB3DPoint(a));
    2374             : 
    2375       32272 :                 if(!bIsIdentity)
    2376             :                 {
    2377       15360 :                     aCandidate *= rMat;
    2378             :                 }
    2379             : 
    2380       32272 :                 aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY()));
    2381       32272 :             }
    2382             : 
    2383             :             // copy closed state
    2384        4700 :             aRetval.setClosed(rCandidate.isClosed());
    2385             : 
    2386        4700 :             return aRetval;
    2387             :         }
    2388             : 
    2389           0 :         double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut)
    2390             :         {
    2391           0 :             if(rPointA.equal(rPointB))
    2392             :             {
    2393           0 :                 rCut = 0.0;
    2394           0 :                 const B2DVector aVector(rTestPoint - rPointA);
    2395           0 :                 return aVector.getLength();
    2396             :             }
    2397             :             else
    2398             :             {
    2399             :                 // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
    2400           0 :                 const B2DVector aVector1(rPointB - rPointA);
    2401           0 :                 const B2DVector aVector2(rTestPoint - rPointA);
    2402           0 :                 const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY()));
    2403           0 :                 const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY()));
    2404           0 :                 const double fCut(fDividend / fDivisor);
    2405             : 
    2406           0 :                 if(fCut < 0.0)
    2407             :                 {
    2408             :                     // not in line range, get distance to PointA
    2409           0 :                     rCut = 0.0;
    2410           0 :                     return aVector2.getLength();
    2411             :                 }
    2412           0 :                 else if(fCut > 1.0)
    2413             :                 {
    2414             :                     // not in line range, get distance to PointB
    2415           0 :                     rCut = 1.0;
    2416           0 :                     const B2DVector aVector(rTestPoint - rPointB);
    2417           0 :                     return aVector.getLength();
    2418             :                 }
    2419             :                 else
    2420             :                 {
    2421             :                     // in line range
    2422           0 :                     const B2DPoint aCutPoint(rPointA + fCut * aVector1);
    2423           0 :                     const B2DVector aVector(rTestPoint - aCutPoint);
    2424           0 :                     rCut = fCut;
    2425           0 :                     return aVector.getLength();
    2426           0 :                 }
    2427             :             }
    2428             :         }
    2429             : 
    2430           0 :         double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut)
    2431             :         {
    2432           0 :             double fRetval(DBL_MAX);
    2433           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2434             : 
    2435           0 :             if(nPointCount > 1L)
    2436             :             {
    2437           0 :                 const double fZero(0.0);
    2438           0 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
    2439           0 :                 B2DCubicBezier aBezier;
    2440           0 :                 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
    2441             : 
    2442           0 :                 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
    2443             :                 {
    2444           0 :                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    2445           0 :                     aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
    2446             :                     double fEdgeDist;
    2447           0 :                     double fNewCut(0.0);
    2448           0 :                     bool bEdgeIsCurve(false);
    2449             : 
    2450           0 :                     if(rCandidate.areControlPointsUsed())
    2451             :                     {
    2452           0 :                         aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
    2453           0 :                         aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
    2454           0 :                         aBezier.testAndSolveTrivialBezier();
    2455           0 :                         bEdgeIsCurve = aBezier.isBezier();
    2456             :                     }
    2457             : 
    2458           0 :                     if(bEdgeIsCurve)
    2459             :                     {
    2460           0 :                         fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut);
    2461             :                     }
    2462             :                     else
    2463             :                     {
    2464           0 :                         fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut);
    2465             :                     }
    2466             : 
    2467           0 :                     if(DBL_MAX == fRetval || fEdgeDist < fRetval)
    2468             :                     {
    2469           0 :                         fRetval = fEdgeDist;
    2470           0 :                         rEdgeIndex = a;
    2471           0 :                         rCut = fNewCut;
    2472             : 
    2473           0 :                         if(fTools::equal(fRetval, fZero))
    2474             :                         {
    2475             :                             // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
    2476           0 :                             fRetval = 0.0;
    2477           0 :                             break;
    2478             :                         }
    2479             :                     }
    2480             : 
    2481             :                     // prepare next step
    2482           0 :                     aBezier.setStartPoint(aBezier.getEndPoint());
    2483             :                 }
    2484             : 
    2485           0 :                 if(1.0 == rCut)
    2486             :                 {
    2487             :                     // correct rEdgeIndex when not last point
    2488           0 :                     if(rCandidate.isClosed())
    2489             :                     {
    2490           0 :                         rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate);
    2491           0 :                         rCut = 0.0;
    2492             :                     }
    2493             :                     else
    2494             :                     {
    2495           0 :                         if(rEdgeIndex != nEdgeCount - 1L)
    2496             :                         {
    2497           0 :                             rEdgeIndex++;
    2498           0 :                             rCut = 0.0;
    2499             :                         }
    2500             :                     }
    2501           0 :                 }
    2502             :             }
    2503             : 
    2504           0 :             return fRetval;
    2505             :         }
    2506             : 
    2507           0 :         B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
    2508             :         {
    2509           0 :             if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight()))
    2510             :             {
    2511           0 :                 return rCandidate;
    2512             :             }
    2513             :             else
    2514             :             {
    2515           0 :                 const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth());
    2516           0 :                 const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight());
    2517           0 :                 const double fOneMinusRelativeX(1.0 - fRelativeX);
    2518           0 :                 const double fOneMinusRelativeY(1.0 - fRelativeY);
    2519           0 :                 const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) +
    2520           0 :                     fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX()));
    2521           0 :                 const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) +
    2522           0 :                     fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY()));
    2523             : 
    2524           0 :                 return B2DPoint(fNewX, fNewY);
    2525             :             }
    2526             :         }
    2527             : 
    2528           0 :         B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
    2529             :         {
    2530           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2531             : 
    2532           0 :             if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight())
    2533             :             {
    2534           0 :                 B2DPolygon aRetval;
    2535             : 
    2536           0 :                 for(sal_uInt32 a(0L); a < nPointCount; a++)
    2537             :                 {
    2538           0 :                     aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
    2539             : 
    2540           0 :                     if(rCandidate.areControlPointsUsed())
    2541             :                     {
    2542           0 :                         if(!rCandidate.getPrevControlPoint(a).equalZero())
    2543             :                         {
    2544           0 :                             aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
    2545             :                         }
    2546             : 
    2547           0 :                         if(!rCandidate.getNextControlPoint(a).equalZero())
    2548             :                         {
    2549           0 :                             aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
    2550             :                         }
    2551             :                     }
    2552             :                 }
    2553             : 
    2554           0 :                 aRetval.setClosed(rCandidate.isClosed());
    2555           0 :                 return aRetval;
    2556             :             }
    2557             :             else
    2558             :             {
    2559           0 :                 return rCandidate;
    2560             :             }
    2561             :         }
    2562             : 
    2563          40 :         B2DPolygon expandToCurve(const B2DPolygon& rCandidate)
    2564             :         {
    2565          40 :             B2DPolygon aRetval(rCandidate);
    2566             : 
    2567         284 :             for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
    2568             :             {
    2569         244 :                 expandToCurveInPoint(aRetval, a);
    2570             :             }
    2571             : 
    2572          40 :             return aRetval;
    2573             :         }
    2574             : 
    2575         244 :         bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex)
    2576             :         {
    2577             :             OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)");
    2578         244 :             bool bRetval(false);
    2579         244 :             const sal_uInt32 nPointCount(rCandidate.count());
    2580             : 
    2581         244 :             if(nPointCount)
    2582             :             {
    2583             :                 // predecessor
    2584         244 :                 if(!rCandidate.isPrevControlPointUsed(nIndex))
    2585             :                 {
    2586         100 :                     if(!rCandidate.isClosed() && 0 == nIndex)
    2587             :                     {
    2588             :                         // do not create previous vector for start point of open polygon
    2589             :                     }
    2590             :                     else
    2591             :                     {
    2592          94 :                         const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
    2593          94 :                         rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
    2594          94 :                         bRetval = true;
    2595             :                     }
    2596             :                 }
    2597             : 
    2598             :                 // successor
    2599         244 :                 if(!rCandidate.isNextControlPointUsed(nIndex))
    2600             :                 {
    2601         100 :                     if(!rCandidate.isClosed() && nIndex + 1 == nPointCount)
    2602             :                     {
    2603             :                         // do not create next vector for end point of open polygon
    2604             :                     }
    2605             :                     else
    2606             :                     {
    2607          94 :                         const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
    2608          94 :                         rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
    2609          94 :                         bRetval = true;
    2610             :                     }
    2611             :                 }
    2612             :             }
    2613             : 
    2614         244 :             return bRetval;
    2615             :         }
    2616             : 
    2617           0 :         bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity)
    2618             :         {
    2619             :             OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)");
    2620           0 :             bool bRetval(false);
    2621           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2622             : 
    2623           0 :             if(nPointCount)
    2624             :             {
    2625           0 :                 const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex));
    2626             : 
    2627           0 :                 switch(eContinuity)
    2628             :                 {
    2629             :                     case CONTINUITY_NONE :
    2630             :                     {
    2631           0 :                         if(rCandidate.isPrevControlPointUsed(nIndex))
    2632             :                         {
    2633           0 :                             if(!rCandidate.isClosed() && 0 == nIndex)
    2634             :                             {
    2635             :                                 // remove existing previous vector for start point of open polygon
    2636           0 :                                 rCandidate.resetPrevControlPoint(nIndex);
    2637             :                             }
    2638             :                             else
    2639             :                             {
    2640           0 :                                 const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
    2641           0 :                                 rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
    2642             :                             }
    2643             : 
    2644           0 :                             bRetval = true;
    2645             :                         }
    2646             : 
    2647           0 :                         if(rCandidate.isNextControlPointUsed(nIndex))
    2648             :                         {
    2649           0 :                             if(!rCandidate.isClosed() && nIndex == nPointCount + 1)
    2650             :                             {
    2651             :                                 // remove next vector for end point of open polygon
    2652           0 :                                 rCandidate.resetNextControlPoint(nIndex);
    2653             :                             }
    2654             :                             else
    2655             :                             {
    2656           0 :                                 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
    2657           0 :                                 rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
    2658             :                             }
    2659             : 
    2660           0 :                             bRetval = true;
    2661             :                         }
    2662             : 
    2663           0 :                         break;
    2664             :                     }
    2665             :                     case CONTINUITY_C1 :
    2666             :                     {
    2667           0 :                         if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
    2668             :                         {
    2669             :                             // lengths both exist since both are used
    2670           0 :                             B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
    2671           0 :                             B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
    2672           0 :                             const double fLenPrev(aVectorPrev.getLength());
    2673           0 :                             const double fLenNext(aVectorNext.getLength());
    2674           0 :                             aVectorPrev.normalize();
    2675           0 :                             aVectorNext.normalize();
    2676           0 :                             const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
    2677             : 
    2678           0 :                             if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
    2679             :                             {
    2680             :                                 // parallel and opposite direction; check length
    2681           0 :                                 if(fTools::equal(fLenPrev, fLenNext))
    2682             :                                 {
    2683             :                                     // this would be even C2, but we want C1. Use the lengths of the corresponding edges.
    2684           0 :                                     const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
    2685           0 :                                     const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
    2686           0 :                                     const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
    2687           0 :                                     const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
    2688             : 
    2689             :                                     rCandidate.setControlPoints(nIndex,
    2690           0 :                                         aCurrentPoint + (aVectorPrev * fLenPrevEdge),
    2691           0 :                                         aCurrentPoint + (aVectorNext * fLenNextEdge));
    2692           0 :                                     bRetval = true;
    2693             :                                 }
    2694             :                             }
    2695             :                             else
    2696             :                             {
    2697             :                                 // not parallel or same direction, set vectors and length
    2698           0 :                                 const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
    2699             : 
    2700           0 :                                 if(ORIENTATION_POSITIVE == aOrientation)
    2701             :                                 {
    2702             :                                     rCandidate.setControlPoints(nIndex,
    2703           0 :                                         aCurrentPoint - (aNormalizedPerpendicular * fLenPrev),
    2704           0 :                                         aCurrentPoint + (aNormalizedPerpendicular * fLenNext));
    2705             :                                 }
    2706             :                                 else
    2707             :                                 {
    2708             :                                     rCandidate.setControlPoints(nIndex,
    2709           0 :                                         aCurrentPoint + (aNormalizedPerpendicular * fLenPrev),
    2710           0 :                                         aCurrentPoint - (aNormalizedPerpendicular * fLenNext));
    2711             :                                 }
    2712             : 
    2713           0 :                                 bRetval = true;
    2714           0 :                             }
    2715             :                         }
    2716           0 :                         break;
    2717             :                     }
    2718             :                     case CONTINUITY_C2 :
    2719             :                     {
    2720           0 :                         if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
    2721             :                         {
    2722             :                             // lengths both exist since both are used
    2723           0 :                             B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
    2724           0 :                             B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
    2725           0 :                             const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0);
    2726           0 :                             aVectorPrev.normalize();
    2727           0 :                             aVectorNext.normalize();
    2728           0 :                             const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
    2729             : 
    2730           0 :                             if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
    2731             :                             {
    2732             :                                 // parallel and opposite direction; set length. Use one direction for better numerical correctness
    2733           0 :                                 const B2DVector aScaledDirection(aVectorPrev * fCommonLength);
    2734             : 
    2735             :                                 rCandidate.setControlPoints(nIndex,
    2736           0 :                                     aCurrentPoint + aScaledDirection,
    2737           0 :                                     aCurrentPoint - aScaledDirection);
    2738             :                             }
    2739             :                             else
    2740             :                             {
    2741             :                                 // not parallel or same direction, set vectors and length
    2742           0 :                                 const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
    2743           0 :                                 const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength);
    2744             : 
    2745           0 :                                 if(ORIENTATION_POSITIVE == aOrientation)
    2746             :                                 {
    2747             :                                     rCandidate.setControlPoints(nIndex,
    2748           0 :                                         aCurrentPoint - aPerpendicular,
    2749           0 :                                         aCurrentPoint + aPerpendicular);
    2750             :                                 }
    2751             :                                 else
    2752             :                                 {
    2753             :                                     rCandidate.setControlPoints(nIndex,
    2754           0 :                                         aCurrentPoint + aPerpendicular,
    2755           0 :                                         aCurrentPoint - aPerpendicular);
    2756           0 :                                 }
    2757             :                             }
    2758             : 
    2759           0 :                             bRetval = true;
    2760             :                         }
    2761           0 :                         break;
    2762             :                     }
    2763           0 :                 }
    2764             :             }
    2765             : 
    2766           0 :             return bRetval;
    2767             :         }
    2768             : 
    2769         126 :         B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue)
    2770             :         {
    2771         126 :             if(0.0 != fValue)
    2772             :             {
    2773         126 :                 if(rCandidate.areControlPointsUsed())
    2774             :                 {
    2775             :                     // call myself recursively with subdivided input
    2776           0 :                     const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
    2777           0 :                     return growInNormalDirection(aCandidate, fValue);
    2778             :                 }
    2779             :                 else
    2780             :                 {
    2781         126 :                     B2DPolygon aRetval;
    2782         126 :                     const sal_uInt32 nPointCount(rCandidate.count());
    2783             : 
    2784         126 :                     if(nPointCount)
    2785             :                     {
    2786         126 :                         B2DPoint aPrev(rCandidate.getB2DPoint(nPointCount - 1L));
    2787         252 :                         B2DPoint aCurrent(rCandidate.getB2DPoint(0L));
    2788             : 
    2789         504 :                         for(sal_uInt32 a(0L); a < nPointCount; a++)
    2790             :                         {
    2791         378 :                             const B2DPoint aNext(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
    2792         756 :                             const B2DVector aBack(aPrev - aCurrent);
    2793         756 :                             const B2DVector aForw(aNext - aCurrent);
    2794         756 :                             const B2DVector aPerpBack(getNormalizedPerpendicular(aBack));
    2795         756 :                             const B2DVector aPerpForw(getNormalizedPerpendicular(aForw));
    2796         756 :                             B2DVector aDirection(aPerpBack - aPerpForw);
    2797         378 :                             aDirection.normalize();
    2798         378 :                             aDirection *= fValue;
    2799         378 :                             aRetval.append(aCurrent + aDirection);
    2800             : 
    2801             :                             // prepare next step
    2802         378 :                             aPrev = aCurrent;
    2803         378 :                             aCurrent = aNext;
    2804         504 :                         }
    2805             :                     }
    2806             : 
    2807             :                     // copy closed state
    2808         126 :                     aRetval.setClosed(rCandidate.isClosed());
    2809             : 
    2810         126 :                     return aRetval;
    2811             :                 }
    2812             :             }
    2813             :             else
    2814             :             {
    2815           0 :                 return rCandidate;
    2816             :             }
    2817             :         }
    2818             : 
    2819           0 :         B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments)
    2820             :         {
    2821           0 :             B2DPolygon aRetval;
    2822           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2823             : 
    2824           0 :             if(nPointCount && nSegments)
    2825             :             {
    2826             :                 // get current segment count
    2827           0 :                 const sal_uInt32 nSegmentCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
    2828             : 
    2829           0 :                 if(nSegmentCount == nSegments)
    2830             :                 {
    2831           0 :                     aRetval = rCandidate;
    2832             :                 }
    2833             :                 else
    2834             :                 {
    2835           0 :                     const double fLength(getLength(rCandidate));
    2836           0 :                     const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nSegments : nSegments + 1L);
    2837             : 
    2838           0 :                     for(sal_uInt32 a(0L); a < nLoopCount; a++)
    2839             :                     {
    2840           0 :                         const double fRelativePos((double)a / (double)nSegments); // 0.0 .. 1.0
    2841           0 :                         const B2DPoint aNewPoint(getPositionRelative(rCandidate, fRelativePos, fLength));
    2842           0 :                         aRetval.append(aNewPoint);
    2843           0 :                     }
    2844             : 
    2845             :                     // copy closed flag
    2846           0 :                     aRetval.setClosed(rCandidate.isClosed());
    2847             :                 }
    2848             :             }
    2849             : 
    2850           0 :             return aRetval;
    2851             :         }
    2852             : 
    2853           0 :         B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t)
    2854             :         {
    2855             :             OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolygon interpolate: Different geometry (!)");
    2856             : 
    2857           0 :             if(fTools::lessOrEqual(t, 0.0) || rOld1 == rOld2)
    2858             :             {
    2859           0 :                 return rOld1;
    2860             :             }
    2861           0 :             else if(fTools::moreOrEqual(t, 1.0))
    2862             :             {
    2863           0 :                 return rOld2;
    2864             :             }
    2865             :             else
    2866             :             {
    2867           0 :                 B2DPolygon aRetval;
    2868           0 :                 const bool bInterpolateVectors(rOld1.areControlPointsUsed() || rOld2.areControlPointsUsed());
    2869           0 :                 aRetval.setClosed(rOld1.isClosed() && rOld2.isClosed());
    2870             : 
    2871           0 :                 for(sal_uInt32 a(0L); a < rOld1.count(); a++)
    2872             :                 {
    2873           0 :                     aRetval.append(interpolate(rOld1.getB2DPoint(a), rOld2.getB2DPoint(a), t));
    2874             : 
    2875           0 :                     if(bInterpolateVectors)
    2876             :                     {
    2877           0 :                         aRetval.setPrevControlPoint(a, interpolate(rOld1.getPrevControlPoint(a), rOld2.getPrevControlPoint(a), t));
    2878           0 :                         aRetval.setNextControlPoint(a, interpolate(rOld1.getNextControlPoint(a), rOld2.getNextControlPoint(a), t));
    2879             :                     }
    2880             :                 }
    2881             : 
    2882           0 :                 return aRetval;
    2883             :             }
    2884             :         }
    2885             : 
    2886             :         // #i76891#
    2887       32075 :         B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate)
    2888             :         {
    2889       32075 :             const sal_uInt32 nPointCount(rCandidate.count());
    2890             : 
    2891       32075 :             if(nPointCount && rCandidate.areControlPointsUsed())
    2892             :             {
    2893             :                 // prepare loop
    2894        3840 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
    2895        3840 :                 B2DPolygon aRetval;
    2896        7680 :                 B2DCubicBezier aBezier;
    2897        3840 :                 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
    2898             : 
    2899             :                 // try to avoid costly reallocations
    2900        3840 :                 aRetval.reserve( nEdgeCount+1);
    2901             : 
    2902             :                 // add start point
    2903        3840 :                 aRetval.append(aBezier.getStartPoint());
    2904             : 
    2905       41951 :                 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
    2906             :                 {
    2907             :                     // get values for edge
    2908       38111 :                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    2909       38111 :                     aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
    2910       38111 :                     aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
    2911       38111 :                     aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
    2912       38111 :                     aBezier.testAndSolveTrivialBezier();
    2913             : 
    2914             :                     // still bezier?
    2915       38111 :                     if(aBezier.isBezier())
    2916             :                     {
    2917             :                         // add edge with control vectors
    2918       20067 :                         aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint());
    2919             :                     }
    2920             :                     else
    2921             :                     {
    2922             :                         // add edge
    2923       18044 :                         aRetval.append(aBezier.getEndPoint());
    2924             :                     }
    2925             : 
    2926             :                     // next point
    2927       38111 :                     aBezier.setStartPoint(aBezier.getEndPoint());
    2928             :                 }
    2929             : 
    2930        3840 :                 if(rCandidate.isClosed())
    2931             :                 {
    2932             :                     // set closed flag, rescue control point and correct last double point
    2933        3425 :                     closeWithGeometryChange(aRetval);
    2934             :                 }
    2935             : 
    2936        7680 :                 return aRetval;
    2937             :             }
    2938             :             else
    2939             :             {
    2940       28235 :                 return rCandidate;
    2941             :             }
    2942             :         }
    2943             : 
    2944             :         // makes the given indexed point the new polygon start point. To do that, the points in the
    2945             :         // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
    2946             :         // an assertion will be triggered
    2947           0 :         B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint)
    2948             :         {
    2949           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    2950             : 
    2951           0 :             if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount)
    2952             :             {
    2953             :                 OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)");
    2954           0 :                 B2DPolygon aRetval;
    2955             : 
    2956           0 :                 for(sal_uInt32 a(0); a < nPointCount; a++)
    2957             :                 {
    2958           0 :                     const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount);
    2959           0 :                     aRetval.append(rCandidate.getB2DPoint(nSourceIndex));
    2960             : 
    2961           0 :                     if(rCandidate.areControlPointsUsed())
    2962             :                     {
    2963           0 :                         aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex));
    2964           0 :                         aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex));
    2965             :                     }
    2966             :                 }
    2967             : 
    2968           0 :                 return aRetval;
    2969             :             }
    2970             : 
    2971           0 :             return rCandidate;
    2972             :         }
    2973             : 
    2974           0 :         B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart, double fEnd)
    2975             :         {
    2976           0 :             B2DPolygon aRetval;
    2977             : 
    2978           0 :             if(fLength < 0.0)
    2979             :             {
    2980           0 :                 fLength = 0.0;
    2981             :             }
    2982             : 
    2983           0 :             if(!fTools::equalZero(fLength))
    2984             :             {
    2985           0 :                 if(fStart < 0.0)
    2986             :                 {
    2987           0 :                     fStart = 0.0;
    2988             :                 }
    2989             : 
    2990           0 :                 if(fEnd < 0.0)
    2991             :                 {
    2992           0 :                     fEnd = 0.0;
    2993             :                 }
    2994             : 
    2995           0 :                 if(fEnd < fStart)
    2996             :                 {
    2997           0 :                     fEnd = fStart;
    2998             :                 }
    2999             : 
    3000             :                 // iterate and consume pieces with fLength. First subdivide to reduce input to line segments
    3001           0 :                 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
    3002           0 :                 const sal_uInt32 nPointCount(aCandidate.count());
    3003             : 
    3004           0 :                 if(nPointCount > 1)
    3005             :                 {
    3006           0 :                     const bool bEndActive(!fTools::equalZero(fEnd));
    3007           0 :                     const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1);
    3008           0 :                     B2DPoint aCurrent(aCandidate.getB2DPoint(0));
    3009           0 :                     double fPositionInEdge(fStart);
    3010           0 :                     double fAbsolutePosition(fStart);
    3011             : 
    3012           0 :                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
    3013             :                     {
    3014           0 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    3015           0 :                         const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
    3016           0 :                         const B2DVector aEdge(aNext - aCurrent);
    3017           0 :                         double fEdgeLength(aEdge.getLength());
    3018             : 
    3019           0 :                         if(!fTools::equalZero(fEdgeLength))
    3020             :                         {
    3021           0 :                             while(fTools::less(fPositionInEdge, fEdgeLength))
    3022             :                             {
    3023             :                                 // move position on edge forward as long as on edge
    3024           0 :                                 const double fScalar(fPositionInEdge / fEdgeLength);
    3025           0 :                                 aRetval.append(aCurrent + (aEdge * fScalar));
    3026           0 :                                 fPositionInEdge += fLength;
    3027             : 
    3028           0 :                                 if(bEndActive)
    3029             :                                 {
    3030           0 :                                     fAbsolutePosition += fLength;
    3031             : 
    3032           0 :                                     if(fTools::more(fAbsolutePosition, fEnd))
    3033             :                                     {
    3034           0 :                                         break;
    3035             :                                     }
    3036             :                                 }
    3037             :                             }
    3038             : 
    3039             :                             // subtract length of current edge
    3040           0 :                             fPositionInEdge -= fEdgeLength;
    3041             :                         }
    3042             : 
    3043           0 :                         if(bEndActive && fTools::more(fAbsolutePosition, fEnd))
    3044             :                         {
    3045           0 :                             break;
    3046             :                         }
    3047             : 
    3048             :                         // prepare next step
    3049           0 :                         aCurrent = aNext;
    3050           0 :                     }
    3051             : 
    3052             :                     // keep closed state
    3053           0 :                     aRetval.setClosed(aCandidate.isClosed());
    3054             :                 }
    3055             :                 else
    3056             :                 {
    3057             :                     // source polygon has only one point, return unchanged
    3058           0 :                     aRetval = aCandidate;
    3059           0 :                 }
    3060             :             }
    3061             : 
    3062           0 :             return aRetval;
    3063             :         }
    3064             : 
    3065           0 :         B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight)
    3066             :         {
    3067           0 :             B2DPolygon aRetval;
    3068             : 
    3069           0 :             if(fWaveWidth < 0.0)
    3070             :             {
    3071           0 :                 fWaveWidth = 0.0;
    3072             :             }
    3073             : 
    3074           0 :             if(fWaveHeight < 0.0)
    3075             :             {
    3076           0 :                 fWaveHeight = 0.0;
    3077             :             }
    3078             : 
    3079           0 :             const bool bHasWidth(!fTools::equalZero(fWaveWidth));
    3080             : 
    3081           0 :             if(bHasWidth)
    3082             :             {
    3083           0 :                 const bool bHasHeight(!fTools::equalZero(fWaveHeight));
    3084           0 :                 if(bHasHeight)
    3085             :                 {
    3086             :                     // width and height, create waveline. First subdivide to reduce input to line segments
    3087             :                     // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it
    3088             :                     // may be added here again using the original last point from rCandidate. It may
    3089             :                     // also be the case that rCandidate was closed. To simplify things it is handled here
    3090             :                     // as if it was opened.
    3091             :                     // Result from createEdgesOfGivenLength contains no curved segments, handle as straight
    3092             :                     // edges.
    3093           0 :                     const B2DPolygon aEqualLenghEdges(createEdgesOfGivenLength(rCandidate, fWaveWidth));
    3094           0 :                     const sal_uInt32 nPointCount(aEqualLenghEdges.count());
    3095             : 
    3096           0 :                     if(nPointCount > 1)
    3097             :                     {
    3098             :                         // iterate over straight edges, add start point
    3099           0 :                         B2DPoint aCurrent(aEqualLenghEdges.getB2DPoint(0));
    3100           0 :                         aRetval.append(aCurrent);
    3101             : 
    3102           0 :                         for(sal_uInt32 a(0); a < nPointCount - 1; a++)
    3103             :                         {
    3104           0 :                             const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    3105           0 :                             const B2DPoint aNext(aEqualLenghEdges.getB2DPoint(nNextIndex));
    3106           0 :                             const B2DVector aEdge(aNext - aCurrent);
    3107           0 :                             const B2DVector aPerpendicular(getNormalizedPerpendicular(aEdge));
    3108           0 :                             const B2DVector aControlOffset((aEdge * 0.467308) - (aPerpendicular * fWaveHeight));
    3109             : 
    3110             :                             // add curve segment
    3111             :                             aRetval.appendBezierSegment(
    3112           0 :                                 aCurrent + aControlOffset,
    3113           0 :                                 aNext - aControlOffset,
    3114           0 :                                 aNext);
    3115             : 
    3116             :                             // prepare next step
    3117           0 :                             aCurrent = aNext;
    3118           0 :                         }
    3119           0 :                     }
    3120             :                 }
    3121             :                 else
    3122             :                 {
    3123             :                     // width but no height -> return original polygon
    3124           0 :                     aRetval = rCandidate;
    3125             :                 }
    3126             :             }
    3127             :             else
    3128             :             {
    3129             :                 // no width -> no waveline, stay empty and return
    3130             :             }
    3131             : 
    3132           0 :             return aRetval;
    3133             :         }
    3134             : 
    3135             :         // snap points of horizontal or vertical edges to discrete values
    3136           0 :         B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate)
    3137             :         {
    3138           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    3139             : 
    3140           0 :             if(nPointCount > 1)
    3141             :             {
    3142             :                 // Start by copying the source polygon to get a writeable copy. The closed state is
    3143             :                 // copied by aRetval's initialisation, too, so no need to copy it in this method
    3144           0 :                 B2DPolygon aRetval(rCandidate);
    3145             : 
    3146             :                 // prepare geometry data. Get rounded from original
    3147           0 :                 B2ITuple aPrevTuple(basegfx::fround(rCandidate.getB2DPoint(nPointCount - 1)));
    3148           0 :                 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0));
    3149           0 :                 B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
    3150             : 
    3151             :                 // loop over all points. This will also snap the implicit closing edge
    3152             :                 // even when not closed, but that's no problem here
    3153           0 :                 for(sal_uInt32 a(0); a < nPointCount; a++)
    3154             :                 {
    3155             :                     // get next point. Get rounded from original
    3156           0 :                     const bool bLastRun(a + 1 == nPointCount);
    3157           0 :                     const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
    3158           0 :                     const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
    3159           0 :                     const B2ITuple aNextTuple(basegfx::fround(aNextPoint));
    3160             : 
    3161             :                     // get the states
    3162           0 :                     const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
    3163           0 :                     const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
    3164           0 :                     const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
    3165           0 :                     const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
    3166           0 :                     const bool bSnapX(bPrevVertical || bNextVertical);
    3167           0 :                     const bool bSnapY(bPrevHorizontal || bNextHorizontal);
    3168             : 
    3169           0 :                     if(bSnapX || bSnapY)
    3170             :                     {
    3171             :                         const B2DPoint aSnappedPoint(
    3172           0 :                             bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
    3173           0 :                             bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
    3174             : 
    3175           0 :                         aRetval.setB2DPoint(a, aSnappedPoint);
    3176             :                     }
    3177             : 
    3178             :                     // prepare next point
    3179           0 :                     if(!bLastRun)
    3180             :                     {
    3181           0 :                         aPrevTuple = aCurrTuple;
    3182           0 :                         aCurrPoint = aNextPoint;
    3183           0 :                         aCurrTuple = aNextTuple;
    3184             :                     }
    3185           0 :                 }
    3186             : 
    3187           0 :                 return aRetval;
    3188             :             }
    3189             :             else
    3190             :             {
    3191           0 :                 return rCandidate;
    3192             :             }
    3193             :         }
    3194             : 
    3195           0 :         bool containsOnlyHorizontalAndVerticalEdges(const B2DPolygon& rCandidate)
    3196             :         {
    3197           0 :             if(rCandidate.areControlPointsUsed())
    3198             :             {
    3199           0 :                 return false;
    3200             :             }
    3201             : 
    3202           0 :             const sal_uInt32 nPointCount(rCandidate.count());
    3203             : 
    3204           0 :             if(nPointCount < 2)
    3205             :             {
    3206           0 :                 return true;
    3207             :             }
    3208             : 
    3209           0 :             const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount + 1 : nPointCount);
    3210           0 :             basegfx::B2DPoint aLast(rCandidate.getB2DPoint(0));
    3211             : 
    3212           0 :             for(sal_uInt32 a(1); a < nEdgeCount; a++)
    3213             :             {
    3214           0 :                 const sal_uInt32 nNextIndex(a % nPointCount);
    3215           0 :                 const basegfx::B2DPoint aCurrent(rCandidate.getB2DPoint(nNextIndex));
    3216             : 
    3217           0 :                 if(!basegfx::fTools::equal(aLast.getX(), aCurrent.getX()) && !basegfx::fTools::equal(aLast.getY(), aCurrent.getY()))
    3218             :                 {
    3219           0 :                     return false;
    3220             :                 }
    3221             : 
    3222           0 :                 aLast = aCurrent;
    3223           0 :             }
    3224             : 
    3225           0 :             return true;
    3226             :         }
    3227             : 
    3228           0 :         B2DVector getTangentEnteringPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
    3229             :         {
    3230           0 :             B2DVector aRetval(0.0, 0.0);
    3231           0 :             const sal_uInt32 nCount(rCandidate.count());
    3232             : 
    3233           0 :             if(nIndex >= nCount)
    3234             :             {
    3235             :                 // out of range
    3236           0 :                 return aRetval;
    3237             :             }
    3238             : 
    3239             :             // start immediately at prev point compared to nIndex
    3240           0 :             const bool bClosed(rCandidate.isClosed());
    3241           0 :             sal_uInt32 nPrev(bClosed ? (nIndex + nCount - 1) % nCount : nIndex ? nIndex - 1 : nIndex);
    3242             : 
    3243           0 :             if(nPrev == nIndex)
    3244             :             {
    3245             :                 // no previous, done
    3246           0 :                 return aRetval;
    3247             :             }
    3248             : 
    3249           0 :             B2DCubicBezier aSegment;
    3250             : 
    3251             :             // go backward in the polygon; if closed, maximal back to start index (nIndex); if not closed,
    3252             :             // until zero. Use nIndex as stop criteria
    3253           0 :             while(nPrev != nIndex)
    3254             :             {
    3255             :                 // get BezierSegment and tangent at the *end* of segment
    3256           0 :                 rCandidate.getBezierSegment(nPrev, aSegment);
    3257           0 :                 aRetval = aSegment.getTangent(1.0);
    3258             : 
    3259           0 :                 if(!aRetval.equalZero())
    3260             :                 {
    3261             :                     // if we have a tangent, return it
    3262           0 :                     return aRetval;
    3263             :                 }
    3264             : 
    3265             :                 // prepare index before checked one
    3266           0 :                 nPrev = bClosed ? (nPrev + nCount - 1) % nCount : nPrev ? nPrev - 1 : nIndex;
    3267             :             }
    3268             : 
    3269           0 :             return aRetval;
    3270             :         }
    3271             : 
    3272           0 :         B2DVector getTangentLeavingPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
    3273             :         {
    3274           0 :             B2DVector aRetval(0.0, 0.0);
    3275           0 :             const sal_uInt32 nCount(rCandidate.count());
    3276             : 
    3277           0 :             if(nIndex >= nCount)
    3278             :             {
    3279             :                 // out of range
    3280           0 :                 return aRetval;
    3281             :             }
    3282             : 
    3283             :             // start at nIndex
    3284           0 :             const bool bClosed(rCandidate.isClosed());
    3285           0 :             sal_uInt32 nCurrent(nIndex);
    3286           0 :             B2DCubicBezier aSegment;
    3287             : 
    3288             :             // go forward; if closed, do this until once around and back at start index (nIndex); if not
    3289             :             // closed, until last point (nCount - 1). Use nIndex as stop criteria
    3290           0 :             do
    3291             :             {
    3292             :                 // get BezierSegment and tangent at the *beginning* of segment
    3293           0 :                 rCandidate.getBezierSegment(nCurrent, aSegment);
    3294           0 :                 aRetval = aSegment.getTangent(0.0);
    3295             : 
    3296           0 :                 if(!aRetval.equalZero())
    3297             :                 {
    3298             :                     // if we have a tangent, return it
    3299           0 :                     return aRetval;
    3300             :                 }
    3301             : 
    3302             :                 // prepare next index
    3303           0 :                 nCurrent = bClosed ? (nCurrent + 1) % nCount : nCurrent + 1 < nCount ? nCurrent + 1 : nIndex;
    3304             :             }
    3305             :             while(nCurrent != nIndex);
    3306             : 
    3307           0 :             return aRetval;
    3308             :         }
    3309             : 
    3310             :         // converters for com::sun::star::drawing::PointSequence
    3311             : 
    3312           2 :         B2DPolygon UnoPointSequenceToB2DPolygon(
    3313             :             const com::sun::star::drawing::PointSequence& rPointSequenceSource,
    3314             :             bool bCheckClosed)
    3315             :         {
    3316           2 :             B2DPolygon aRetval;
    3317           2 :             const sal_uInt32 nLength(rPointSequenceSource.getLength());
    3318             : 
    3319           2 :             if(nLength)
    3320             :             {
    3321           2 :                 aRetval.reserve(nLength);
    3322           2 :                 const com::sun::star::awt::Point* pArray = rPointSequenceSource.getConstArray();
    3323           2 :                 const com::sun::star::awt::Point* pArrayEnd = pArray + rPointSequenceSource.getLength();
    3324             : 
    3325          12 :                 for(;pArray != pArrayEnd; pArray++)
    3326             :                 {
    3327          10 :                     aRetval.append(B2DPoint(pArray->X, pArray->Y));
    3328             :                 }
    3329             : 
    3330           2 :                 if(bCheckClosed)
    3331             :                 {
    3332             :                     // check for closed state flag
    3333           2 :                     tools::checkClosed(aRetval);
    3334             :                 }
    3335             :             }
    3336             : 
    3337           2 :             return aRetval;
    3338             :         }
    3339             : 
    3340         862 :         void B2DPolygonToUnoPointSequence(
    3341             :             const B2DPolygon& rPolygon,
    3342             :             com::sun::star::drawing::PointSequence& rPointSequenceRetval)
    3343             :         {
    3344         862 :             B2DPolygon aPolygon(rPolygon);
    3345             : 
    3346         862 :             if(aPolygon.areControlPointsUsed())
    3347             :             {
    3348             :                 OSL_ENSURE(false, "B2DPolygonToUnoPointSequence: Source contains bezier segments, wrong UNO API data type may be used (!)");
    3349           0 :                 aPolygon = aPolygon.getDefaultAdaptiveSubdivision();
    3350             :             }
    3351             : 
    3352         862 :             const sal_uInt32 nPointCount(aPolygon.count());
    3353             : 
    3354         862 :             if(nPointCount)
    3355             :             {
    3356             :                 // Take closed state into account, the API polygon still uses the old closed definition
    3357             :                 // with last/first point are identical (cannot hold information about open polygons with identical
    3358             :                 // first and last point, though)
    3359         862 :                 const bool bIsClosed(aPolygon.isClosed());
    3360             : 
    3361         862 :                 rPointSequenceRetval.realloc(bIsClosed ? nPointCount + 1 : nPointCount);
    3362         862 :                 com::sun::star::awt::Point* pSequence = rPointSequenceRetval.getArray();
    3363             : 
    3364        6258 :                 for(sal_uInt32 b(0); b < nPointCount; b++)
    3365             :                 {
    3366        5396 :                     const B2DPoint aPoint(aPolygon.getB2DPoint(b));
    3367        5396 :                     const com::sun::star::awt::Point aAPIPoint(fround(aPoint.getX()), fround(aPoint.getY()));
    3368             : 
    3369        5396 :                     *pSequence = aAPIPoint;
    3370        5396 :                     pSequence++;
    3371        5396 :                 }
    3372             : 
    3373             :                 // copy first point if closed
    3374         862 :                 if(bIsClosed)
    3375             :                 {
    3376         708 :                     *pSequence = *rPointSequenceRetval.getArray();
    3377             :                 }
    3378             :             }
    3379             :             else
    3380             :             {
    3381           0 :                 rPointSequenceRetval.realloc(0);
    3382         862 :             }
    3383         862 :         }
    3384             : 
    3385             :         // converters for com::sun::star::drawing::PointSequence and
    3386             :         // com::sun::star::drawing::FlagSequence to B2DPolygon (curved polygons)
    3387             : 
    3388          26 :         B2DPolygon UnoPolygonBezierCoordsToB2DPolygon(
    3389             :             const com::sun::star::drawing::PointSequence& rPointSequenceSource,
    3390             :             const com::sun::star::drawing::FlagSequence& rFlagSequenceSource,
    3391             :             bool bCheckClosed)
    3392             :         {
    3393          26 :             const sal_uInt32 nCount((sal_uInt32)rPointSequenceSource.getLength());
    3394             :             OSL_ENSURE(nCount == (sal_uInt32)rFlagSequenceSource.getLength(),
    3395             :                 "UnoPolygonBezierCoordsToB2DPolygon: Unequal count of Points and Flags (!)");
    3396             : 
    3397             :             // prepare new polygon
    3398          26 :             B2DPolygon aRetval;
    3399          26 :             const com::sun::star::awt::Point* pPointSequence = rPointSequenceSource.getConstArray();
    3400          26 :             const com::sun::star::drawing::PolygonFlags* pFlagSequence = rFlagSequenceSource.getConstArray();
    3401             : 
    3402             :             // get first point and flag
    3403          52 :             B2DPoint aNewCoordinatePair(pPointSequence->X, pPointSequence->Y); pPointSequence++;
    3404          26 :             com::sun::star::drawing::PolygonFlags ePolygonFlag(*pFlagSequence); pFlagSequence++;
    3405          52 :             B2DPoint aControlA;
    3406          52 :             B2DPoint aControlB;
    3407             : 
    3408             :             // first point is not allowed to be a control point
    3409             :             OSL_ENSURE(com::sun::star::drawing::PolygonFlags_CONTROL != ePolygonFlag,
    3410             :                 "UnoPolygonBezierCoordsToB2DPolygon: Start point is a control point, illegal input polygon (!)");
    3411             : 
    3412             :             // add first point as start point
    3413          26 :             aRetval.append(aNewCoordinatePair);
    3414             : 
    3415         130 :             for(sal_uInt32 b(1); b < nCount;)
    3416             :             {
    3417             :                 // prepare loop
    3418          78 :                 bool bControlA(false);
    3419          78 :                 bool bControlB(false);
    3420             : 
    3421             :                 // get next point and flag
    3422          78 :                 aNewCoordinatePair = B2DPoint(pPointSequence->X, pPointSequence->Y);
    3423          78 :                 ePolygonFlag = *pFlagSequence;
    3424          78 :                 pPointSequence++; pFlagSequence++; b++;
    3425             : 
    3426          78 :                 if(b < nCount && com::sun::star::drawing::PolygonFlags_CONTROL == ePolygonFlag)
    3427             :                 {
    3428           0 :                     aControlA = aNewCoordinatePair;
    3429           0 :                     bControlA = true;
    3430             : 
    3431             :                     // get next point and flag
    3432           0 :                     aNewCoordinatePair = B2DPoint(pPointSequence->X, pPointSequence->Y);
    3433           0 :                     ePolygonFlag = *pFlagSequence;
    3434           0 :                     pPointSequence++; pFlagSequence++; b++;
    3435             :                 }
    3436             : 
    3437          78 :                 if(b < nCount && com::sun::star::drawing::PolygonFlags_CONTROL == ePolygonFlag)
    3438             :                 {
    3439           0 :                     aControlB = aNewCoordinatePair;
    3440           0 :                     bControlB = true;
    3441             : 
    3442             :                     // get next point and flag
    3443           0 :                     aNewCoordinatePair = B2DPoint(pPointSequence->X, pPointSequence->Y);
    3444           0 :                     ePolygonFlag = *pFlagSequence;
    3445           0 :                     pPointSequence++; pFlagSequence++; b++;
    3446             :                 }
    3447             : 
    3448             :                 // two or no control points are consumed, another one would be an error.
    3449             :                 // It's also an error if only one control point was read
    3450             :                 SAL_WARN_IF(com::sun::star::drawing::PolygonFlags_CONTROL == ePolygonFlag || bControlA != bControlB,
    3451             :                     "basegfx", "UnoPolygonBezierCoordsToB2DPolygon: Illegal source polygon (!)");
    3452             : 
    3453             :                 // the previous writes used the B2DPolyPoygon -> tools::PolyPolygon converter
    3454             :                 // which did not create minimal PolyPolygons, but created all control points
    3455             :                 // as null vectors (identical points). Because of the former P(CA)(CB)-norm of
    3456             :                 // B2DPolygon and it's unused sign of being the zero-vector and CA and CB being
    3457             :                 // relative to P, an empty edge was exported as P == CA == CB. Luckily, the new
    3458             :                 // export format can be read without errors by the old OOo-versions, so we need only
    3459             :                 // to correct here at read and do not need to export a wrong but compatible version
    3460             :                 // for the future.
    3461         234 :                 if(bControlA
    3462           0 :                     && aControlA.equal(aControlB)
    3463         156 :                     && aControlA.equal(aRetval.getB2DPoint(aRetval.count() - 1)))
    3464             :                 {
    3465           0 :                     bControlA = false;
    3466           0 :                     bControlB = false;
    3467             :                 }
    3468             : 
    3469          78 :                 if(bControlA)
    3470             :                 {
    3471             :                     // add bezier edge
    3472           0 :                     aRetval.appendBezierSegment(aControlA, aControlB, aNewCoordinatePair);
    3473             :                 }
    3474             :                 else
    3475             :                 {
    3476             :                     // add edge
    3477          78 :                     aRetval.append(aNewCoordinatePair);
    3478             :                 }
    3479             :             }
    3480             : 
    3481             :             // #i72807# API import uses old line start/end-equal definition for closed,
    3482             :             // so we need to correct this to closed state here
    3483          26 :             if(bCheckClosed)
    3484             :             {
    3485          26 :                 checkClosed(aRetval);
    3486             :             }
    3487             : 
    3488          52 :             return aRetval;
    3489             :         }
    3490             : 
    3491         944 :         void B2DPolygonToUnoPolygonBezierCoords(
    3492             :             const B2DPolygon& rPolygon,
    3493             :             com::sun::star::drawing::PointSequence& rPointSequenceRetval,
    3494             :             com::sun::star::drawing::FlagSequence& rFlagSequenceRetval)
    3495             :         {
    3496         944 :             const sal_uInt32 nPointCount(rPolygon.count());
    3497             : 
    3498         944 :             if(nPointCount)
    3499             :             {
    3500         944 :                 const bool bCurve(rPolygon.areControlPointsUsed());
    3501         944 :                 const bool bClosed(rPolygon.isClosed());
    3502             : 
    3503         944 :                 if(bCurve)
    3504             :                 {
    3505             :                     // calculate target point count
    3506         630 :                     const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
    3507             : 
    3508         630 :                     if(nLoopCount)
    3509             :                     {
    3510             :                         // prepare target data. The real needed number of target points (and flags)
    3511             :                         // could only be calculated by using two loops, so use dynamic memory
    3512         630 :                         std::vector< com::sun::star::awt::Point > aCollectPoints;
    3513        1260 :                         std::vector< com::sun::star::drawing::PolygonFlags > aCollectFlags;
    3514             : 
    3515             :                         // reserve maximum creatable points
    3516         630 :                         const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1);
    3517         630 :                         aCollectPoints.reserve(nMaxTargetCount);
    3518         630 :                         aCollectFlags.reserve(nMaxTargetCount);
    3519             : 
    3520             :                         // prepare current bezier segment by setting start point
    3521        1260 :                         B2DCubicBezier aBezierSegment;
    3522         630 :                         aBezierSegment.setStartPoint(rPolygon.getB2DPoint(0));
    3523             : 
    3524        6804 :                         for(sal_uInt32 a(0); a < nLoopCount; a++)
    3525             :                         {
    3526             :                             // add current point (always) and remember StartPointIndex for evtl. later corrections
    3527        6174 :                             const sal_uInt32 nStartPointIndex(aCollectPoints.size());
    3528             :                             aCollectPoints.push_back(
    3529             :                                 com::sun::star::awt::Point(
    3530       12348 :                                     fround(aBezierSegment.getStartPoint().getX()),
    3531       18522 :                                     fround(aBezierSegment.getStartPoint().getY())));
    3532        6174 :                             aCollectFlags.push_back(com::sun::star::drawing::PolygonFlags_NORMAL);
    3533             : 
    3534             :                             // prepare next segment
    3535        6174 :                             const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    3536        6174 :                             aBezierSegment.setEndPoint(rPolygon.getB2DPoint(nNextIndex));
    3537        6174 :                             aBezierSegment.setControlPointA(rPolygon.getNextControlPoint(a));
    3538        6174 :                             aBezierSegment.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex));
    3539             : 
    3540        6174 :                             if(aBezierSegment.isBezier())
    3541             :                             {
    3542             :                                 // if bezier is used, add always two control points due to the old schema
    3543             :                                 aCollectPoints.push_back(
    3544             :                                     com::sun::star::awt::Point(
    3545        8596 :                                         fround(aBezierSegment.getControlPointA().getX()),
    3546       12894 :                                         fround(aBezierSegment.getControlPointA().getY())));
    3547        4298 :                                 aCollectFlags.push_back(com::sun::star::drawing::PolygonFlags_CONTROL);
    3548             : 
    3549             :                                 aCollectPoints.push_back(
    3550             :                                     com::sun::star::awt::Point(
    3551        8596 :                                         fround(aBezierSegment.getControlPointB().getX()),
    3552       12894 :                                         fround(aBezierSegment.getControlPointB().getY())));
    3553        4298 :                                 aCollectFlags.push_back(com::sun::star::drawing::PolygonFlags_CONTROL);
    3554             :                             }
    3555             : 
    3556             :                             // test continuity with previous control point to set flag value
    3557        6174 :                             if(aBezierSegment.getControlPointA() != aBezierSegment.getStartPoint() && (bClosed || a))
    3558             :                             {
    3559        4098 :                                 const B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a));
    3560             : 
    3561        4098 :                                 if(CONTINUITY_C1 == eCont)
    3562             :                                 {
    3563         570 :                                     aCollectFlags[nStartPointIndex] = com::sun::star::drawing::PolygonFlags_SMOOTH;
    3564             :                                 }
    3565        3528 :                                 else if(CONTINUITY_C2 == eCont)
    3566             :                                 {
    3567        1166 :                                     aCollectFlags[nStartPointIndex] = com::sun::star::drawing::PolygonFlags_SYMMETRIC;
    3568             :                                 }
    3569             :                             }
    3570             : 
    3571             :                             // prepare next loop
    3572        6174 :                             aBezierSegment.setStartPoint(aBezierSegment.getEndPoint());
    3573             :                         }
    3574             : 
    3575         630 :                         if(bClosed)
    3576             :                         {
    3577             :                             // add first point again as closing point due to old definition
    3578         542 :                             aCollectPoints.push_back(aCollectPoints[0]);
    3579         542 :                             aCollectFlags.push_back(com::sun::star::drawing::PolygonFlags_NORMAL);
    3580             :                         }
    3581             :                         else
    3582             :                         {
    3583             :                             // add last point as closing point
    3584          88 :                             const B2DPoint aClosingPoint(rPolygon.getB2DPoint(nPointCount - 1L));
    3585             :                             aCollectPoints.push_back(
    3586             :                                 com::sun::star::awt::Point(
    3587          88 :                                     fround(aClosingPoint.getX()),
    3588         176 :                                     fround(aClosingPoint.getY())));
    3589          88 :                             aCollectFlags.push_back(com::sun::star::drawing::PolygonFlags_NORMAL);
    3590             :                         }
    3591             : 
    3592             :                         // copy collected data to target arrays
    3593         630 :                         const sal_uInt32 nTargetCount(aCollectPoints.size());
    3594             :                         OSL_ENSURE(nTargetCount == aCollectFlags.size(), "Unequal Point and Flag count (!)");
    3595             : 
    3596         630 :                         rPointSequenceRetval.realloc((sal_Int32)nTargetCount);
    3597         630 :                         rFlagSequenceRetval.realloc((sal_Int32)nTargetCount);
    3598         630 :                         com::sun::star::awt::Point* pPointSequence = rPointSequenceRetval.getArray();
    3599         630 :                         com::sun::star::drawing::PolygonFlags* pFlagSequence = rFlagSequenceRetval.getArray();
    3600             : 
    3601       16030 :                         for(sal_uInt32 a(0); a < nTargetCount; a++)
    3602             :                         {
    3603       15400 :                             *pPointSequence = aCollectPoints[a];
    3604       15400 :                             *pFlagSequence = aCollectFlags[a];
    3605       15400 :                             pPointSequence++;
    3606       15400 :                             pFlagSequence++;
    3607         630 :                         }
    3608             :                     }
    3609             :                 }
    3610             :                 else
    3611             :                 {
    3612             :                     // straightforward point list creation
    3613         314 :                     const sal_uInt32 nTargetCount(nPointCount + (bClosed ? 1 : 0));
    3614             : 
    3615         314 :                     rPointSequenceRetval.realloc((sal_Int32)nTargetCount);
    3616         314 :                     rFlagSequenceRetval.realloc((sal_Int32)nTargetCount);
    3617             : 
    3618         314 :                     com::sun::star::awt::Point* pPointSequence = rPointSequenceRetval.getArray();
    3619         314 :                     com::sun::star::drawing::PolygonFlags* pFlagSequence = rFlagSequenceRetval.getArray();
    3620             : 
    3621        1736 :                     for(sal_uInt32 a(0); a < nPointCount; a++)
    3622             :                     {
    3623        1422 :                         const B2DPoint aB2DPoint(rPolygon.getB2DPoint(a));
    3624             :                         const com::sun::star::awt::Point aAPIPoint(
    3625        1422 :                             fround(aB2DPoint.getX()),
    3626        2844 :                             fround(aB2DPoint.getY()));
    3627             : 
    3628        1422 :                         *pPointSequence = aAPIPoint;
    3629        1422 :                         *pFlagSequence = com::sun::star::drawing::PolygonFlags_NORMAL;
    3630        1422 :                         pPointSequence++;
    3631        1422 :                         pFlagSequence++;
    3632        1422 :                     }
    3633             : 
    3634         314 :                     if(bClosed)
    3635             :                     {
    3636             :                         // add first point as closing point
    3637         234 :                         *pPointSequence = *rPointSequenceRetval.getConstArray();
    3638         234 :                         *pFlagSequence = com::sun::star::drawing::PolygonFlags_NORMAL;
    3639             :                     }
    3640             :                 }
    3641             :             }
    3642             :             else
    3643             :             {
    3644           0 :                 rPointSequenceRetval.realloc(0);
    3645           0 :                 rFlagSequenceRetval.realloc(0);
    3646             :             }
    3647         944 :         }
    3648             : 
    3649             :     } // end of namespace tools
    3650             : } // end of namespace basegfx
    3651             : 
    3652             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10