LCOV - code coverage report
Current view: top level - basegfx/source/curve - b2dcubicbezier.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 287 425 67.5 %
Date: 2015-06-13 12:38:46 Functions: 23 29 79.3 %
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/curve/b2dcubicbezier.hxx>
      21             : #include <basegfx/vector/b2dvector.hxx>
      22             : #include <basegfx/polygon/b2dpolygon.hxx>
      23             : #include <basegfx/numeric/ftools.hxx>
      24             : 
      25             : #include <osl/diagnose.h>
      26             : 
      27             : #include <limits>
      28             : 
      29             : // #i37443#
      30             : #define FACTOR_FOR_UNSHARPEN    (1.6)
      31             : #ifdef DBG_UTIL
      32             : static double fMultFactUnsharpen = FACTOR_FOR_UNSHARPEN;
      33             : #endif
      34             : 
      35             : namespace basegfx
      36             : {
      37             :     namespace
      38             :     {
      39      139823 :         void ImpSubDivAngle(
      40             :             const B2DPoint& rfPA,           // start point
      41             :             const B2DPoint& rfEA,           // edge on A
      42             :             const B2DPoint& rfEB,           // edge on B
      43             :             const B2DPoint& rfPB,           // end point
      44             :             B2DPolygon& rTarget,            // target polygon
      45             :             double fAngleBound,             // angle bound in [0.0 .. 2PI]
      46             :             bool bAllowUnsharpen,           // #i37443# allow the criteria to get unsharp in recursions
      47             :             sal_uInt16 nMaxRecursionDepth)  // endless loop protection
      48             :         {
      49      139823 :             if(nMaxRecursionDepth)
      50             :             {
      51             :                 // do angle test
      52      139823 :                 B2DVector aLeft(rfEA - rfPA);
      53      279646 :                 B2DVector aRight(rfEB - rfPB);
      54             : 
      55             :                 // #i72104#
      56      139823 :                 if(aLeft.equalZero())
      57             :                 {
      58          42 :                     aLeft = rfEB - rfPA;
      59             :                 }
      60             : 
      61      139823 :                 if(aRight.equalZero())
      62             :                 {
      63          12 :                     aRight = rfEA - rfPB;
      64             :                 }
      65             : 
      66      139823 :                 const double fCurrentAngle(aLeft.angle(aRight));
      67             : 
      68      139823 :                 if(fabs(fCurrentAngle) > (F_PI - fAngleBound))
      69             :                 {
      70             :                     // end recursion
      71       78828 :                     nMaxRecursionDepth = 0;
      72             :                 }
      73             :                 else
      74             :                 {
      75       60995 :                     if(bAllowUnsharpen)
      76             :                     {
      77             :                         // #i37443# unsharpen criteria
      78             : #ifdef DBG_UTIL
      79             :                         fAngleBound *= fMultFactUnsharpen;
      80             : #else
      81       60995 :                         fAngleBound *= FACTOR_FOR_UNSHARPEN;
      82             : #endif
      83             :                     }
      84      139823 :                 }
      85             :             }
      86             : 
      87      139823 :             if(nMaxRecursionDepth)
      88             :             {
      89             :                 // divide at 0.5
      90       60995 :                 const B2DPoint aS1L(average(rfPA, rfEA));
      91      121990 :                 const B2DPoint aS1C(average(rfEA, rfEB));
      92      121990 :                 const B2DPoint aS1R(average(rfEB, rfPB));
      93      121990 :                 const B2DPoint aS2L(average(aS1L, aS1C));
      94      121990 :                 const B2DPoint aS2R(average(aS1C, aS1R));
      95      121990 :                 const B2DPoint aS3C(average(aS2L, aS2R));
      96             : 
      97             :                 // left recursion
      98       60995 :                 ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
      99             : 
     100             :                 // right recursion
     101      121990 :                 ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
     102             :             }
     103             :             else
     104             :             {
     105       78828 :                 rTarget.append(rfPB);
     106             :             }
     107      139823 :         }
     108             : 
     109        9320 :         void ImpSubDivAngleStart(
     110             :             const B2DPoint& rfPA,           // start point
     111             :             const B2DPoint& rfEA,           // edge on A
     112             :             const B2DPoint& rfEB,           // edge on B
     113             :             const B2DPoint& rfPB,           // end point
     114             :             B2DPolygon& rTarget,            // target polygon
     115             :             const double& rfAngleBound,     // angle bound in [0.0 .. 2PI]
     116             :             bool bAllowUnsharpen)           // #i37443# allow the criteria to get unsharp in recursions
     117             :         {
     118        9320 :             sal_uInt16 nMaxRecursionDepth(8);
     119        9320 :             const B2DVector aLeft(rfEA - rfPA);
     120       18640 :             const B2DVector aRight(rfEB - rfPB);
     121        9320 :             bool bLeftEqualZero(aLeft.equalZero());
     122        9320 :             bool bRightEqualZero(aRight.equalZero());
     123        9320 :             bool bAllParallel(false);
     124             : 
     125        9320 :             if(bLeftEqualZero && bRightEqualZero)
     126             :             {
     127           0 :                 nMaxRecursionDepth = 0;
     128             :             }
     129             :             else
     130             :             {
     131        9320 :                 const B2DVector aBase(rfPB - rfPA);
     132        9320 :                 const bool bBaseEqualZero(aBase.equalZero()); // #i72104#
     133             : 
     134        9320 :                 if(!bBaseEqualZero)
     135             :                 {
     136        9320 :                     const bool bLeftParallel(bLeftEqualZero || areParallel(aLeft, aBase));
     137        9320 :                     const bool bRightParallel(bRightEqualZero || areParallel(aRight, aBase));
     138             : 
     139        9320 :                     if(bLeftParallel && bRightParallel)
     140             :                     {
     141           0 :                         bAllParallel = true;
     142             : 
     143           0 :                         if(!bLeftEqualZero)
     144             :                         {
     145             :                             double fFactor;
     146             : 
     147           0 :                             if(fabs(aBase.getX()) > fabs(aBase.getY()))
     148             :                             {
     149           0 :                                 fFactor = aLeft.getX() / aBase.getX();
     150             :                             }
     151             :                             else
     152             :                             {
     153           0 :                                 fFactor = aLeft.getY() / aBase.getY();
     154             :                             }
     155             : 
     156           0 :                             if(fFactor >= 0.0 && fFactor <= 1.0)
     157             :                             {
     158           0 :                                 bLeftEqualZero = true;
     159             :                             }
     160             :                         }
     161             : 
     162           0 :                         if(!bRightEqualZero)
     163             :                         {
     164             :                             double fFactor;
     165             : 
     166           0 :                             if(fabs(aBase.getX()) > fabs(aBase.getY()))
     167             :                             {
     168           0 :                                 fFactor = aRight.getX() / -aBase.getX();
     169             :                             }
     170             :                             else
     171             :                             {
     172           0 :                                 fFactor = aRight.getY() / -aBase.getY();
     173             :                             }
     174             : 
     175           0 :                             if(fFactor >= 0.0 && fFactor <= 1.0)
     176             :                             {
     177           0 :                                 bRightEqualZero = true;
     178             :                             }
     179             :                         }
     180             : 
     181           0 :                         if(bLeftEqualZero && bRightEqualZero)
     182             :                         {
     183           0 :                             nMaxRecursionDepth = 0;
     184             :                         }
     185             :                     }
     186        9320 :                 }
     187             :             }
     188             : 
     189        9320 :             if(nMaxRecursionDepth)
     190             :             {
     191             :                 // divide at 0.5 ad test both edges for angle criteria
     192        9320 :                 const B2DPoint aS1L(average(rfPA, rfEA));
     193       18640 :                 const B2DPoint aS1C(average(rfEA, rfEB));
     194       18640 :                 const B2DPoint aS1R(average(rfEB, rfPB));
     195       18640 :                 const B2DPoint aS2L(average(aS1L, aS1C));
     196       18640 :                 const B2DPoint aS2R(average(aS1C, aS1R));
     197       18640 :                 const B2DPoint aS3C(average(aS2L, aS2R));
     198             : 
     199             :                 // test left
     200        9320 :                 bool bAngleIsSmallerLeft(bAllParallel && bLeftEqualZero);
     201        9320 :                 if(!bAngleIsSmallerLeft)
     202             :                 {
     203        9320 :                     const B2DVector aLeftLeft(bLeftEqualZero ? aS2L - aS1L : aS1L - rfPA); // #i72104#
     204       18640 :                     const B2DVector aRightLeft(aS2L - aS3C);
     205        9320 :                     const double fCurrentAngleLeft(aLeftLeft.angle(aRightLeft));
     206       18640 :                     bAngleIsSmallerLeft = (fabs(fCurrentAngleLeft) > (F_PI - rfAngleBound));
     207             :                 }
     208             : 
     209             :                 // test right
     210        9320 :                 bool bAngleIsSmallerRight(bAllParallel && bRightEqualZero);
     211        9320 :                 if(!bAngleIsSmallerRight)
     212             :                 {
     213        9320 :                     const B2DVector aLeftRight(aS2R - aS3C);
     214       18640 :                     const B2DVector aRightRight(bRightEqualZero ? aS2R - aS1R : aS1R - rfPB); // #i72104#
     215        9320 :                     const double fCurrentAngleRight(aLeftRight.angle(aRightRight));
     216       18640 :                     bAngleIsSmallerRight = (fabs(fCurrentAngleRight) > (F_PI - rfAngleBound));
     217             :                 }
     218             : 
     219        9320 :                 if(bAngleIsSmallerLeft && bAngleIsSmallerRight)
     220             :                 {
     221             :                     // no recursion necessary at all
     222          73 :                     nMaxRecursionDepth = 0;
     223             :                 }
     224             :                 else
     225             :                 {
     226             :                     // left
     227        9247 :                     if(bAngleIsSmallerLeft)
     228             :                     {
     229         303 :                         rTarget.append(aS3C);
     230             :                     }
     231             :                     else
     232             :                     {
     233        8944 :                         ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
     234             :                     }
     235             : 
     236             :                     // right
     237        9247 :                     if(bAngleIsSmallerRight)
     238             :                     {
     239         358 :                         rTarget.append(rfPB);
     240             :                     }
     241             :                     else
     242             :                     {
     243        8889 :                         ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
     244             :                     }
     245        9320 :                 }
     246             :             }
     247             : 
     248        9320 :             if(!nMaxRecursionDepth)
     249             :             {
     250          73 :                 rTarget.append(rfPB);
     251        9320 :             }
     252        9320 :         }
     253             : 
     254           0 :         void ImpSubDivDistance(
     255             :             const B2DPoint& rfPA,           // start point
     256             :             const B2DPoint& rfEA,           // edge on A
     257             :             const B2DPoint& rfEB,           // edge on B
     258             :             const B2DPoint& rfPB,           // end point
     259             :             B2DPolygon& rTarget,            // target polygon
     260             :             double fDistanceBound2,         // quadratic distance criteria
     261             :             double fLastDistanceError2,     // the last quadratic distance error
     262             :             sal_uInt16 nMaxRecursionDepth)  // endless loop protection
     263             :         {
     264           0 :             if(nMaxRecursionDepth)
     265             :             {
     266             :                 // decide if another recursion is needed. If not, set
     267             :                 // nMaxRecursionDepth to zero
     268             : 
     269             :                 // Perform bezier flatness test (lecture notes from R. Schaback,
     270             :                 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
     271             : 
     272             :                 // ||P(t) - L(t)|| <= max     ||b_j - b_0 - j/n(b_n - b_0)||
     273             :                 //                    0<=j<=n
     274             : 
     275             :                 // What is calculated here is an upper bound to the distance from
     276             :                 // a line through b_0 and b_3 (rfPA and P4 in our notation) and the
     277             :                 // curve. We can drop 0 and n from the running indices, since the
     278             :                 // argument of max becomes zero for those cases.
     279           0 :                 const double fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX()));
     280           0 :                 const double fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY()));
     281           0 :                 const double fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX()));
     282           0 :                 const double fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY()));
     283           0 :                 const double fDistanceError2(::std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y));
     284             : 
     285             :                 // stop if error measure does not improve anymore. This is a
     286             :                 // safety guard against floating point inaccuracies.
     287             :                 // stop if distance from line is guaranteed to be bounded by d
     288           0 :                 const bool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2);
     289             : 
     290           0 :                 if(bFurtherDivision)
     291             :                 {
     292             :                     // remember last error value
     293           0 :                     fLastDistanceError2 = fDistanceError2;
     294             :                 }
     295             :                 else
     296             :                 {
     297             :                     // stop recustion
     298           0 :                     nMaxRecursionDepth = 0;
     299             :                 }
     300             :             }
     301             : 
     302           0 :             if(nMaxRecursionDepth)
     303             :             {
     304             :                 // divide at 0.5
     305           0 :                 const B2DPoint aS1L(average(rfPA, rfEA));
     306           0 :                 const B2DPoint aS1C(average(rfEA, rfEB));
     307           0 :                 const B2DPoint aS1R(average(rfEB, rfPB));
     308           0 :                 const B2DPoint aS2L(average(aS1L, aS1C));
     309           0 :                 const B2DPoint aS2R(average(aS1C, aS1R));
     310           0 :                 const B2DPoint aS3C(average(aS2L, aS2R));
     311             : 
     312             :                 // left recursion
     313           0 :                 ImpSubDivDistance(rfPA, aS1L, aS2L, aS3C, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
     314             : 
     315             :                 // right recursion
     316           0 :                 ImpSubDivDistance(aS3C, aS2R, aS1R, rfPB, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
     317             :             }
     318             :             else
     319             :             {
     320           0 :                 rTarget.append(rfPB);
     321             :             }
     322           0 :         }
     323             :     } // end of anonymous namespace
     324             : } // end of namespace basegfx
     325             : 
     326             : namespace basegfx
     327             : {
     328           0 :     B2DCubicBezier::B2DCubicBezier(const B2DCubicBezier& rBezier)
     329             :     :   maStartPoint(rBezier.maStartPoint),
     330             :         maEndPoint(rBezier.maEndPoint),
     331             :         maControlPointA(rBezier.maControlPointA),
     332           0 :         maControlPointB(rBezier.maControlPointB)
     333             :     {
     334           0 :     }
     335             : 
     336      114365 :     B2DCubicBezier::B2DCubicBezier()
     337             :     {
     338      114365 :     }
     339             : 
     340       30722 :     B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd)
     341             :     :   maStartPoint(rStart),
     342             :         maEndPoint(rEnd),
     343             :         maControlPointA(rControlPointA),
     344       30722 :         maControlPointB(rControlPointB)
     345             :     {
     346       30722 :     }
     347             : 
     348      145087 :     B2DCubicBezier::~B2DCubicBezier()
     349             :     {
     350      145087 :     }
     351             : 
     352             :     // assignment operator
     353       15478 :     B2DCubicBezier& B2DCubicBezier::operator=(const B2DCubicBezier& rBezier)
     354             :     {
     355       15478 :         maStartPoint = rBezier.maStartPoint;
     356       15478 :         maEndPoint = rBezier.maEndPoint;
     357       15478 :         maControlPointA = rBezier.maControlPointA;
     358       15478 :         maControlPointB = rBezier.maControlPointB;
     359             : 
     360       15478 :         return *this;
     361             :     }
     362             : 
     363             :     // compare operators
     364           0 :     bool B2DCubicBezier::operator==(const B2DCubicBezier& rBezier) const
     365             :     {
     366             :         return (
     367           0 :             maStartPoint == rBezier.maStartPoint
     368           0 :             && maEndPoint == rBezier.maEndPoint
     369           0 :             && maControlPointA == rBezier.maControlPointA
     370           0 :             && maControlPointB == rBezier.maControlPointB
     371           0 :         );
     372             :     }
     373             : 
     374           0 :     bool B2DCubicBezier::operator!=(const B2DCubicBezier& rBezier) const
     375             :     {
     376             :         return (
     377           0 :             maStartPoint != rBezier.maStartPoint
     378           0 :             || maEndPoint != rBezier.maEndPoint
     379           0 :             || maControlPointA != rBezier.maControlPointA
     380           0 :             || maControlPointB != rBezier.maControlPointB
     381           0 :         );
     382             :     }
     383             : 
     384        2655 :     bool B2DCubicBezier::equal(const B2DCubicBezier& rBezier) const
     385             :     {
     386             :         return (
     387        2655 :             maStartPoint.equal(rBezier.maStartPoint)
     388        2655 :             && maEndPoint.equal(rBezier.maEndPoint)
     389         441 :             && maControlPointA.equal(rBezier.maControlPointA)
     390        3030 :             && maControlPointB.equal(rBezier.maControlPointB)
     391        2655 :         );
     392             :     }
     393             : 
     394             :     // test if vectors are used
     395     3471989 :     bool B2DCubicBezier::isBezier() const
     396             :     {
     397     3471989 :         if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
     398             :         {
     399     3134723 :             return true;
     400             :         }
     401             : 
     402      337266 :         return false;
     403             :     }
     404             : 
     405      839925 :     void B2DCubicBezier::testAndSolveTrivialBezier()
     406             :     {
     407      839925 :         if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
     408             :         {
     409      382918 :             const B2DVector aEdge(maEndPoint - maStartPoint);
     410             : 
     411             :             // controls parallel to edge can be trivial. No edge -> not parallel -> control can
     412             :             // still not be trivial (e.g. ballon loop)
     413      382918 :             if(!aEdge.equalZero())
     414             :             {
     415             :                 // get control vectors
     416      382742 :                 const B2DVector aVecA(maControlPointA - maStartPoint);
     417      765484 :                 const B2DVector aVecB(maControlPointB - maEndPoint);
     418             : 
     419             :                 // check if trivial per se
     420      382742 :                 bool bAIsTrivial(aVecA.equalZero());
     421      382742 :                 bool bBIsTrivial(aVecB.equalZero());
     422             : 
     423             :                 // #i102241# prepare inverse edge length to normalize cross values;
     424             :                 // else the small compare value used in fTools::equalZero
     425             :                 // will be length dependent and this detection will work as less
     426             :                 // precise as longer the edge is. In principle, the length of the control
     427             :                 // vector would need to be used too, but to be trivial it is assumed to
     428             :                 // be of roughly equal length to the edge, so edge length can be used
     429             :                 // for both. Only needed when one of both is not trivial per se.
     430      396697 :                 const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial
     431             :                     ? 1.0
     432      765484 :                     : 1.0 / aEdge.getLength());
     433             : 
     434             :                 // if A is not zero, check if it could be
     435      382742 :                 if(!bAIsTrivial)
     436             :                 {
     437             :                     // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what
     438             :                     // we need here with the precision we need
     439      368787 :                     const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength);
     440             : 
     441      368787 :                     if(fTools::equalZero(fCross))
     442             :                     {
     443             :                         // get scale to edge. Use bigger distance for numeric quality
     444      110452 :                         const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
     445        2398 :                             ? aVecA.getX() / aEdge.getX()
     446      112850 :                             : aVecA.getY() / aEdge.getY());
     447             : 
     448             :                         // relative end point of vector in edge range?
     449      110452 :                         if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0))
     450             :                         {
     451      110420 :                             bAIsTrivial = true;
     452             :                         }
     453             :                     }
     454             :                 }
     455             : 
     456             :                 // if B is not zero, check if it could be, but only if A is already trivial;
     457             :                 // else solve to trivial will not be possible for whole edge
     458      382742 :                 if(bAIsTrivial && !bBIsTrivial)
     459             :                 {
     460             :                     // parallel to edge? Check aVecB, aEdge
     461      122784 :                     const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength);
     462             : 
     463      122784 :                     if(fTools::equalZero(fCross))
     464             :                     {
     465             :                         // get scale to edge. Use bigger distance for numeric quality
     466      110571 :                         const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
     467        2497 :                             ? aVecB.getX() / aEdge.getX()
     468      113068 :                             : aVecB.getY() / aEdge.getY());
     469             : 
     470             :                         // end point of vector in edge range? Caution: controlB is directed AGAINST edge
     471      110571 :                         if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0))
     472             :                         {
     473      110571 :                             bBIsTrivial = true;
     474             :                         }
     475             :                     }
     476             :                 }
     477             : 
     478             :                 // if both are/can be reduced, do it.
     479             :                 // Not possible if only one is/can be reduced (!)
     480      382742 :                 if(bAIsTrivial && bBIsTrivial)
     481             :                 {
     482      112162 :                     maControlPointA = maStartPoint;
     483      112162 :                     maControlPointB = maEndPoint;
     484      382742 :                 }
     485      382918 :             }
     486             :         }
     487      839925 :     }
     488             : 
     489             :     namespace {
     490         667 :         double impGetLength(const B2DCubicBezier& rEdge, double fDeviation, sal_uInt32 nRecursionWatch)
     491             :         {
     492         667 :             const double fEdgeLength(rEdge.getEdgeLength());
     493         667 :             const double fControlPolygonLength(rEdge.getControlPolygonLength());
     494         667 :             const double fCurrentDeviation(fTools::equalZero(fControlPolygonLength) ? 0.0 : 1.0 - (fEdgeLength / fControlPolygonLength));
     495             : 
     496         667 :             if(!nRecursionWatch || fTools:: lessOrEqual(fCurrentDeviation, fDeviation))
     497             :             {
     498         348 :                 return (fEdgeLength + fControlPolygonLength) * 0.5;
     499             :             }
     500             :             else
     501             :             {
     502         638 :                 B2DCubicBezier aLeft, aRight;
     503         319 :                 const double fNewDeviation(fDeviation * 0.5);
     504         319 :                 const sal_uInt32 nNewRecursionWatch(nRecursionWatch - 1);
     505             : 
     506         319 :                 rEdge.split(0.5, &aLeft, &aRight);
     507             : 
     508         319 :                 return impGetLength(aLeft, fNewDeviation, nNewRecursionWatch)
     509         638 :                     + impGetLength(aRight, fNewDeviation, nNewRecursionWatch);
     510             :             }
     511             :         }
     512             :     }
     513             : 
     514          29 :     double B2DCubicBezier::getLength(double fDeviation) const
     515             :     {
     516          29 :         if(isBezier())
     517             :         {
     518          29 :             if(fDeviation < 0.00000001)
     519             :             {
     520           0 :                 fDeviation = 0.00000001;
     521             :             }
     522             : 
     523          29 :             return impGetLength(*this, fDeviation, 6);
     524             :         }
     525             :         else
     526             :         {
     527           0 :             return B2DVector(getEndPoint() - getStartPoint()).getLength();
     528             :         }
     529             :     }
     530             : 
     531        4489 :     double B2DCubicBezier::getEdgeLength() const
     532             :     {
     533        4489 :         const B2DVector aEdge(maEndPoint - maStartPoint);
     534        4489 :         return aEdge.getLength();
     535             :     }
     536             : 
     537         667 :     double B2DCubicBezier::getControlPolygonLength() const
     538             :     {
     539         667 :         const B2DVector aVectorA(maControlPointA - maStartPoint);
     540        1334 :         const B2DVector aVectorB(maEndPoint - maControlPointB);
     541             : 
     542         667 :         if(!aVectorA.equalZero() || !aVectorB.equalZero())
     543             :         {
     544         667 :             const B2DVector aTop(maControlPointB - maControlPointA);
     545         667 :             return (aVectorA.getLength() + aVectorB.getLength() + aTop.getLength());
     546             :         }
     547             :         else
     548             :         {
     549           0 :             return getEdgeLength();
     550         667 :         }
     551             :     }
     552             : 
     553        9320 :     void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const
     554             :     {
     555        9320 :         if(isBezier())
     556             :         {
     557             :             // use support method #i37443# and allow unsharpen the criteria
     558        9320 :             ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, fAngleBound * F_PI180, bAllowUnsharpen);
     559             :         }
     560             :         else
     561             :         {
     562           0 :             rTarget.append(getEndPoint());
     563             :         }
     564        9320 :     }
     565             : 
     566       35507 :     B2DVector B2DCubicBezier::getTangent(double t) const
     567             :     {
     568       35507 :         if(fTools::lessOrEqual(t, 0.0))
     569             :         {
     570             :             // tangent in start point
     571       17991 :             B2DVector aTangent(getControlPointA() - getStartPoint());
     572             : 
     573       17991 :             if(!aTangent.equalZero())
     574             :             {
     575        3605 :                 return aTangent;
     576             :             }
     577             : 
     578             :             // start point and control vector are the same, fallback
     579             :             // to implicit start vector to control point B
     580       14386 :             aTangent = (getControlPointB() - getStartPoint()) * 0.3;
     581             : 
     582       14386 :             if(!aTangent.equalZero())
     583             :             {
     584       13975 :                 return aTangent;
     585             :             }
     586             : 
     587             :             // not a bezier at all, return edge vector
     588         411 :             return (getEndPoint() - getStartPoint()) * 0.3;
     589             :         }
     590       17516 :         else if(fTools::moreOrEqual(t, 1.0))
     591             :         {
     592             :             // tangent in end point
     593       17516 :             B2DVector aTangent(getEndPoint() - getControlPointB());
     594             : 
     595       17516 :             if(!aTangent.equalZero())
     596             :             {
     597        3299 :                 return aTangent;
     598             :             }
     599             : 
     600             :             // end point and control vector are the same, fallback
     601             :             // to implicit start vector from control point A
     602       14217 :             aTangent = (getEndPoint() - getControlPointA()) * 0.3;
     603             : 
     604       14217 :             if(!aTangent.equalZero())
     605             :             {
     606       13806 :                 return aTangent;
     607             :             }
     608             : 
     609             :             // not a bezier at all, return edge vector
     610         411 :             return (getEndPoint() - getStartPoint()) * 0.3;
     611             :         }
     612             :         else
     613             :         {
     614             :             // t is in ]0.0 .. 1.0[. Split and extract
     615           0 :             B2DCubicBezier aRight;
     616           0 :             split(t, 0, &aRight);
     617             : 
     618           0 :             return aRight.getControlPointA() - aRight.getStartPoint();
     619             :         }
     620             :     }
     621             : 
     622             :     // #i37443# adaptive subdivide by nCount subdivisions
     623       64840 :     void B2DCubicBezier::adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const
     624             :     {
     625       64840 :         const double fLenFact(1.0 / static_cast< double >(nCount + 1));
     626             : 
     627     2767690 :         for(sal_uInt32 a(1); a <= nCount; a++)
     628             :         {
     629     2702850 :             const double fPos(static_cast< double >(a) * fLenFact);
     630     2702850 :             rTarget.append(interpolatePoint(fPos));
     631             :         }
     632             : 
     633       64840 :         rTarget.append(getEndPoint());
     634       64840 :     }
     635             : 
     636             :     // adaptive subdivide by distance
     637           0 :     void B2DCubicBezier::adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const
     638             :     {
     639           0 :         if(isBezier())
     640             :         {
     641             :             ImpSubDivDistance(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget,
     642           0 :                 fDistanceBound * fDistanceBound, ::std::numeric_limits<double>::max(), 30);
     643             :         }
     644             :         else
     645             :         {
     646           0 :             rTarget.append(getEndPoint());
     647             :         }
     648           0 :     }
     649             : 
     650     2744810 :     B2DPoint B2DCubicBezier::interpolatePoint(double t) const
     651             :     {
     652             :         OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)");
     653             : 
     654     2744810 :         if(isBezier())
     655             :         {
     656     2735632 :             const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
     657     5471264 :             const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
     658     5471264 :             const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
     659     5471264 :             const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
     660     5471264 :             const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
     661             : 
     662     5471264 :             return interpolate(aS2L, aS2R, t);
     663             :         }
     664             :         else
     665             :         {
     666        9178 :             return interpolate(maStartPoint, maEndPoint, t);
     667             :         }
     668             :     }
     669             : 
     670           0 :     double B2DCubicBezier::getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const
     671             :     {
     672           0 :         const sal_uInt32 nInitialDivisions(3L);
     673           0 :         B2DPolygon aInitialPolygon;
     674             : 
     675             :         // as start make a fix division, creates nInitialDivisions + 2L points
     676           0 :         aInitialPolygon.append(getStartPoint());
     677           0 :         adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions);
     678             : 
     679             :         // now look for the closest point
     680           0 :         const sal_uInt32 nPointCount(aInitialPolygon.count());
     681           0 :         B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0L));
     682           0 :         double fQuadDist(aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY());
     683             :         double fNewQuadDist;
     684           0 :         sal_uInt32 nSmallestIndex(0L);
     685             : 
     686           0 :         for(sal_uInt32 a(1L); a < nPointCount; a++)
     687             :         {
     688           0 :             aVector = B2DVector(rTestPoint - aInitialPolygon.getB2DPoint(a));
     689           0 :             fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
     690             : 
     691           0 :             if(fNewQuadDist < fQuadDist)
     692             :             {
     693           0 :                 fQuadDist = fNewQuadDist;
     694           0 :                 nSmallestIndex = a;
     695             :             }
     696             :         }
     697             : 
     698             :         // look right and left for even smaller distances
     699           0 :         double fStepValue(1.0 / (double)((nPointCount - 1L) * 2L)); // half the edge step width
     700           0 :         double fPosition((double)nSmallestIndex / (double)(nPointCount - 1L));
     701           0 :         bool bDone(false);
     702             : 
     703           0 :         while(!bDone)
     704             :         {
     705           0 :             if(!bDone)
     706             :             {
     707             :                 // test left
     708           0 :                 double fPosLeft(fPosition - fStepValue);
     709             : 
     710           0 :                 if(fPosLeft < 0.0)
     711             :                 {
     712           0 :                     fPosLeft = 0.0;
     713           0 :                     aVector = B2DVector(rTestPoint - maStartPoint);
     714             :                 }
     715             :                 else
     716             :                 {
     717           0 :                     aVector = B2DVector(rTestPoint - interpolatePoint(fPosLeft));
     718             :                 }
     719             : 
     720           0 :                 fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
     721             : 
     722           0 :                 if(fTools::less(fNewQuadDist, fQuadDist))
     723             :                 {
     724           0 :                     fQuadDist = fNewQuadDist;
     725           0 :                     fPosition = fPosLeft;
     726             :                 }
     727             :                 else
     728             :                 {
     729             :                     // test right
     730           0 :                     double fPosRight(fPosition + fStepValue);
     731             : 
     732           0 :                     if(fPosRight > 1.0)
     733             :                     {
     734           0 :                         fPosRight = 1.0;
     735           0 :                         aVector = B2DVector(rTestPoint - maEndPoint);
     736             :                     }
     737             :                     else
     738             :                     {
     739           0 :                         aVector = B2DVector(rTestPoint - interpolatePoint(fPosRight));
     740             :                     }
     741             : 
     742           0 :                     fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
     743             : 
     744           0 :                     if(fTools::less(fNewQuadDist, fQuadDist))
     745             :                     {
     746           0 :                         fQuadDist = fNewQuadDist;
     747           0 :                         fPosition = fPosRight;
     748             :                     }
     749             :                     else
     750             :                     {
     751             :                         // not less left or right, done
     752           0 :                         bDone = true;
     753             :                     }
     754             :                 }
     755             :             }
     756             : 
     757           0 :             if(0.0 == fPosition || 1.0 == fPosition)
     758             :             {
     759             :                 // if we are completely left or right, we are done
     760           0 :                 bDone = true;
     761             :             }
     762             : 
     763           0 :             if(!bDone)
     764             :             {
     765             :                 // prepare next step value
     766           0 :                 fStepValue /= 2.0;
     767             :             }
     768             :         }
     769             : 
     770           0 :         rCut = fPosition;
     771           0 :         return sqrt(fQuadDist);
     772             :     }
     773             : 
     774        3704 :     void B2DCubicBezier::split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const
     775             :     {
     776             :         OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::split: Access out of range (!)");
     777             : 
     778        3704 :         if(!pBezierA && !pBezierB)
     779             :         {
     780        3704 :             return;
     781             :         }
     782             : 
     783        3704 :         if(isBezier())
     784             :         {
     785        3704 :             const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
     786        7408 :             const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
     787        7408 :             const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
     788        7408 :             const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
     789        7408 :             const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
     790        7408 :             const B2DPoint aS3C(interpolate(aS2L, aS2R, t));
     791             : 
     792        3704 :             if(pBezierA)
     793             :             {
     794        2557 :                 pBezierA->setStartPoint(maStartPoint);
     795        2557 :                 pBezierA->setEndPoint(aS3C);
     796        2557 :                 pBezierA->setControlPointA(aS1L);
     797        2557 :                 pBezierA->setControlPointB(aS2L);
     798             :             }
     799             : 
     800        3704 :             if(pBezierB)
     801             :             {
     802        2561 :                 pBezierB->setStartPoint(aS3C);
     803        2561 :                 pBezierB->setEndPoint(maEndPoint);
     804        2561 :                 pBezierB->setControlPointA(aS2R);
     805        2561 :                 pBezierB->setControlPointB(aS1R);
     806        3704 :             }
     807             :         }
     808             :         else
     809             :         {
     810           0 :             const B2DPoint aSplit(interpolate(maStartPoint, maEndPoint, t));
     811             : 
     812           0 :             if(pBezierA)
     813             :             {
     814           0 :                 pBezierA->setStartPoint(maStartPoint);
     815           0 :                 pBezierA->setEndPoint(aSplit);
     816           0 :                 pBezierA->setControlPointA(maStartPoint);
     817           0 :                 pBezierA->setControlPointB(aSplit);
     818             :             }
     819             : 
     820           0 :             if(pBezierB)
     821             :             {
     822           0 :                 pBezierB->setStartPoint(aSplit);
     823           0 :                 pBezierB->setEndPoint(maEndPoint);
     824           0 :                 pBezierB->setControlPointA(aSplit);
     825           0 :                 pBezierB->setControlPointB(maEndPoint);
     826           0 :             }
     827             :         }
     828             :     }
     829             : 
     830        1141 :     B2DCubicBezier B2DCubicBezier::snippet(double fStart, double fEnd) const
     831             :     {
     832        1141 :         B2DCubicBezier aRetval;
     833             : 
     834        1141 :         if(fTools::more(fStart, 1.0))
     835             :         {
     836           0 :             fStart = 1.0;
     837             :         }
     838        1141 :         else if(fTools::less(fStart, 0.0))
     839             :         {
     840           0 :             fStart = 0.0;
     841             :         }
     842             : 
     843        1141 :         if(fTools::more(fEnd, 1.0))
     844             :         {
     845           0 :             fEnd = 1.0;
     846             :         }
     847        1141 :         else if(fTools::less(fEnd, 0.0))
     848             :         {
     849           0 :             fEnd = 0.0;
     850             :         }
     851             : 
     852        1141 :         if(fEnd <= fStart)
     853             :         {
     854             :             // empty or NULL, create single point at center
     855           0 :             const double fSplit((fEnd + fStart) * 0.5);
     856           0 :             const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit));
     857           0 :             aRetval.setStartPoint(aPoint);
     858           0 :             aRetval.setEndPoint(aPoint);
     859           0 :             aRetval.setControlPointA(aPoint);
     860           0 :             aRetval.setControlPointB(aPoint);
     861             :         }
     862             :         else
     863             :         {
     864        1141 :             if(isBezier())
     865             :             {
     866             :                 // copy bezier; cut off right, then cut off left. Do not forget to
     867             :                 // adapt cut value when both cuts happen
     868        1141 :                 const bool bEndIsOne(fTools::equal(fEnd, 1.0));
     869        1141 :                 const bool bStartIsZero(fTools::equalZero(fStart));
     870        1141 :                 aRetval = *this;
     871             : 
     872        1141 :                 if(!bEndIsOne)
     873             :                 {
     874        1141 :                     aRetval.split(fEnd, &aRetval, 0);
     875             : 
     876        1141 :                     if(!bStartIsZero)
     877             :                     {
     878         955 :                         fStart /= fEnd;
     879             :                     }
     880             :                 }
     881             : 
     882        1141 :                 if(!bStartIsZero)
     883             :                 {
     884         955 :                     aRetval.split(fStart, 0, &aRetval);
     885             :                 }
     886             :             }
     887             :             else
     888             :             {
     889             :                 // no bezier, create simple edge
     890           0 :                 const B2DPoint aPointA(interpolate(getStartPoint(), getEndPoint(), fStart));
     891           0 :                 const B2DPoint aPointB(interpolate(getStartPoint(), getEndPoint(), fEnd));
     892           0 :                 aRetval.setStartPoint(aPointA);
     893           0 :                 aRetval.setEndPoint(aPointB);
     894           0 :                 aRetval.setControlPointA(aPointA);
     895           0 :                 aRetval.setControlPointB(aPointB);
     896             :             }
     897             :         }
     898             : 
     899        1141 :         return aRetval;
     900             :     }
     901             : 
     902      907039 :     B2DRange B2DCubicBezier::getRange() const
     903             :     {
     904      907039 :         B2DRange aRetval(maStartPoint, maEndPoint);
     905             : 
     906      907039 :         aRetval.expand(maControlPointA);
     907      907039 :         aRetval.expand(maControlPointB);
     908             : 
     909      907039 :         return aRetval;
     910             :     }
     911             : 
     912       13115 :     bool B2DCubicBezier::getMinimumExtremumPosition(double& rfResult) const
     913             :     {
     914       13115 :         ::std::vector< double > aAllResults;
     915             : 
     916       13115 :         aAllResults.reserve(4);
     917       13115 :         getAllExtremumPositions(aAllResults);
     918             : 
     919       13115 :         const sal_uInt32 nCount(aAllResults.size());
     920             : 
     921       13115 :         if(!nCount)
     922             :         {
     923       10865 :             return false;
     924             :         }
     925        2250 :         else if(1 == nCount)
     926             :         {
     927        2197 :             rfResult = aAllResults[0];
     928        2197 :             return true;
     929             :         }
     930             :         else
     931             :         {
     932          53 :             rfResult = *(::std::min_element(aAllResults.begin(), aAllResults.end()));
     933          53 :             return true;
     934       13115 :         }
     935             :     }
     936             : 
     937             :     namespace
     938             :     {
     939      157959 :         inline void impCheckExtremumResult(double fCandidate, ::std::vector< double >& rResult)
     940             :         {
     941             :             // check for range ]0.0 .. 1.0[ with excluding 1.0 and 0.0 clearly
     942             :             // by using the equalZero test, NOT ::more or ::less which will use the
     943             :             // ApproxEqual() which is too exact here
     944      157959 :             if(fCandidate > 0.0 && !fTools::equalZero(fCandidate))
     945             :             {
     946       94301 :                 if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0))
     947             :                 {
     948       32299 :                     rResult.push_back(fCandidate);
     949             :                 }
     950             :             }
     951      157959 :         }
     952             :     }
     953             : 
     954       43892 :     void B2DCubicBezier::getAllExtremumPositions(::std::vector< double >& rResults) const
     955             :     {
     956       43892 :         rResults.clear();
     957             : 
     958             :         // calculate the x-extrema parameters by zeroing first x-derivative
     959             :         // of the cubic bezier's parametric formula, which results in a
     960             :         // quadratic equation: dBezier/dt = t*t*fAX - 2*t*fBX + fCX
     961       43892 :         const B2DPoint aControlDiff( maControlPointA - maControlPointB );
     962       43892 :         double fCX = maControlPointA.getX() - maStartPoint.getX();
     963       43892 :         const double fBX = fCX + aControlDiff.getX();
     964       43892 :         const double fAX = 3 * aControlDiff.getX() + (maEndPoint.getX() - maStartPoint.getX());
     965             : 
     966       43892 :         if(fTools::equalZero(fCX))
     967             :         {
     968             :             // detect fCX equal zero and truncate to real zero value in that case
     969        3828 :             fCX = 0.0;
     970             :         }
     971             : 
     972       43892 :         if( !fTools::equalZero(fAX) )
     973             :         {
     974             :             // derivative is polynomial of order 2 => use binomial formula
     975       39776 :             const double fD = fBX*fBX - fAX*fCX;
     976       39776 :             if( fD >= 0.0 )
     977             :             {
     978       37560 :                 const double fS = sqrt(fD);
     979             :                 // calculate both roots (avoiding a numerically unstable subtraction)
     980       37560 :                 const double fQ = fBX + ((fBX >= 0) ? +fS : -fS);
     981       37560 :                 impCheckExtremumResult(fQ / fAX, rResults);
     982       37560 :                 if( !fTools::equalZero(fS) ) // ignore root multiplicity
     983       37431 :                     impCheckExtremumResult(fCX / fQ, rResults);
     984             :             }
     985             :         }
     986        4116 :         else if( !fTools::equalZero(fBX) )
     987             :         {
     988             :             // derivative is polynomial of order 1 => one extrema
     989        3472 :             impCheckExtremumResult(fCX / (2 * fBX), rResults);
     990             :         }
     991             : 
     992             :         // calculate the y-extrema parameters by zeroing first y-derivative
     993       43892 :         double fCY = maControlPointA.getY() - maStartPoint.getY();
     994       43892 :         const double fBY = fCY + aControlDiff.getY();
     995       43892 :         const double fAY = 3 * aControlDiff.getY() + (maEndPoint.getY() - maStartPoint.getY());
     996             : 
     997       43892 :         if(fTools::equalZero(fCY))
     998             :         {
     999             :             // detect fCY equal zero and truncate to real zero value in that case
    1000        4852 :             fCY = 0.0;
    1001             :         }
    1002             : 
    1003       43892 :         if( !fTools::equalZero(fAY) )
    1004             :         {
    1005             :             // derivative is polynomial of order 2 => use binomial formula
    1006       39850 :             const double fD = fBY*fBY - fAY*fCY;
    1007       39850 :             if( fD >= 0.0 )
    1008             :             {
    1009       38173 :                 const double fS = sqrt(fD);
    1010             :                 // calculate both roots (avoiding a numerically unstable subtraction)
    1011       38173 :                 const double fQ = fBY + ((fBY >= 0) ? +fS : -fS);
    1012       38173 :                 impCheckExtremumResult(fQ / fAY, rResults);
    1013       38173 :                 if( !fTools::equalZero(fS) ) // ignore root multiplicity
    1014       38053 :                     impCheckExtremumResult(fCY / fQ, rResults);
    1015             :             }
    1016             :         }
    1017        4042 :         else if( !fTools::equalZero(fBY) )
    1018             :         {
    1019             :             // derivative is polynomial of order 1 => one extrema
    1020        3270 :             impCheckExtremumResult(fCY / (2 * fBY), rResults);
    1021       43892 :         }
    1022       43892 :     }
    1023             : 
    1024             : } // end of namespace basegfx
    1025             : 
    1026             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11