LCOV - code coverage report
Current view: top level - basegfx/source/polygon - b2dlinegeometry.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 115 270 42.6 %
Date: 2012-08-25 Functions: 5 7 71.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 127 483 26.3 %

           Branch data     Line data    Source code
       1                 :            : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 :            : /*************************************************************************
       3                 :            :  *
       4                 :            :  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       5                 :            :  *
       6                 :            :  * Copyright 2000, 2010 Oracle and/or its affiliates.
       7                 :            :  *
       8                 :            :  * OpenOffice.org - a multi-platform office productivity suite
       9                 :            :  *
      10                 :            :  * This file is part of OpenOffice.org.
      11                 :            :  *
      12                 :            :  * OpenOffice.org is free software: you can redistribute it and/or modify
      13                 :            :  * it under the terms of the GNU Lesser General Public License version 3
      14                 :            :  * only, as published by the Free Software Foundation.
      15                 :            :  *
      16                 :            :  * OpenOffice.org is distributed in the hope that it will be useful,
      17                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19                 :            :  * GNU Lesser General Public License version 3 for more details
      20                 :            :  * (a copy is included in the LICENSE file that accompanied this code).
      21                 :            :  *
      22                 :            :  * You should have received a copy of the GNU Lesser General Public License
      23                 :            :  * version 3 along with OpenOffice.org.  If not, see
      24                 :            :  * <http://www.openoffice.org/license.html>
      25                 :            :  * for a copy of the LGPLv3 License.
      26                 :            :  *
      27                 :            :  ************************************************************************/
      28                 :            : 
      29                 :            : #include <cstdio>
      30                 :            : #include <osl/diagnose.h>
      31                 :            : #include <basegfx/polygon/b2dlinegeometry.hxx>
      32                 :            : #include <basegfx/point/b2dpoint.hxx>
      33                 :            : #include <basegfx/vector/b2dvector.hxx>
      34                 :            : #include <basegfx/polygon/b2dpolygontools.hxx>
      35                 :            : #include <basegfx/polygon/b2dpolypolygontools.hxx>
      36                 :            : #include <basegfx/range/b2drange.hxx>
      37                 :            : #include <basegfx/matrix/b2dhommatrix.hxx>
      38                 :            : #include <basegfx/curve/b2dcubicbezier.hxx>
      39                 :            : #include <basegfx/matrix/b2dhommatrixtools.hxx>
      40                 :            : 
      41                 :            : //////////////////////////////////////////////////////////////////////////////
      42                 :            : 
      43                 :            : namespace basegfx
      44                 :            : {
      45                 :            :     namespace tools
      46                 :            :     {
      47                 :         15 :         B2DPolyPolygon createAreaGeometryForLineStartEnd(
      48                 :            :             const B2DPolygon& rCandidate,
      49                 :            :             const B2DPolyPolygon& rArrow,
      50                 :            :             bool bStart,
      51                 :            :             double fWidth,
      52                 :            :             double fCandidateLength,
      53                 :            :             double fDockingPosition, // 0->top, 1->bottom
      54                 :            :             double* pConsumedLength)
      55                 :            :         {
      56                 :         15 :             B2DPolyPolygon aRetval;
      57                 :            :             OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
      58                 :            :             OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
      59                 :            :             OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
      60                 :            :             OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
      61                 :            :                 "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
      62                 :            : 
      63         [ -  + ]:         15 :             if(fWidth < 0.0)
      64                 :            :             {
      65                 :          0 :                 fWidth = -fWidth;
      66                 :            :             }
      67                 :            : 
      68 [ +  - ][ +  - ]:         15 :             if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
      69                 :            :             {
      70         [ -  + ]:         15 :                 if(fDockingPosition < 0.0)
      71                 :            :                 {
      72                 :          0 :                     fDockingPosition = 0.0;
      73                 :            :                 }
      74         [ -  + ]:         15 :                 else if(fDockingPosition > 1.0)
      75                 :            :                 {
      76                 :          0 :                     fDockingPosition = 1.0;
      77                 :            :                 }
      78                 :            : 
      79                 :            :                 // init return value from arrow
      80         [ +  - ]:         15 :                 aRetval.append(rArrow);
      81                 :            : 
      82                 :            :                 // get size of the arrow
      83         [ +  - ]:         15 :                 const B2DRange aArrowSize(getRange(rArrow));
      84                 :            : 
      85                 :            :                 // build ArrowTransform; center in X, align with axis in Y
      86                 :            :                 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
      87 [ +  - ][ +  - ]:         15 :                     -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
                 [ +  - ]
      88                 :            : 
      89                 :            :                 // scale to target size
      90         [ +  - ]:         15 :                 const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
      91         [ +  - ]:         15 :                 aArrowTransform.scale(fArrowScale, fArrowScale);
      92                 :            : 
      93                 :            :                 // get arrow size in Y
      94 [ +  - ][ +  - ]:         15 :                 B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
      95         [ +  - ]:         15 :                 aUpperCenter *= aArrowTransform;
      96         [ +  - ]:         15 :                 const double fArrowYLength(B2DVector(aUpperCenter).getLength());
      97                 :            : 
      98                 :            :                 // move arrow to have docking position centered
      99         [ +  - ]:         15 :                 aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
     100                 :            : 
     101                 :            :                 // prepare polygon length
     102         [ -  + ]:         15 :                 if(fTools::equalZero(fCandidateLength))
     103                 :            :                 {
     104         [ #  # ]:          0 :                     fCandidateLength = getLength(rCandidate);
     105                 :            :                 }
     106                 :            : 
     107                 :            :                 // get the polygon vector we want to plant this arrow on
     108                 :         15 :                 const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
     109 [ +  + ][ +  - ]:         15 :                 const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
                 [ +  - ]
     110                 :            :                 const B2DVector aTail(getPositionAbsolute(rCandidate,
     111 [ +  - ][ +  + ]:         15 :                     (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
     112                 :            : 
     113                 :            :                 // from that vector, take the needed rotation and add rotate for arrow to transformation
     114                 :         15 :                 const B2DVector aTargetDirection(aHead - aTail);
     115                 :         15 :                 const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
     116                 :            : 
     117                 :            :                 // rotate around docking position
     118         [ +  - ]:         15 :                 aArrowTransform.rotate(fRotation);
     119                 :            : 
     120                 :            :                 // move arrow docking position to polygon head
     121         [ +  - ]:         15 :                 aArrowTransform.translate(aHead.getX(), aHead.getY());
     122                 :            : 
     123                 :            :                 // transform retval and close
     124         [ +  - ]:         15 :                 aRetval.transform(aArrowTransform);
     125         [ +  - ]:         15 :                 aRetval.setClosed(true);
     126                 :            : 
     127                 :            :                 // if pConsumedLength is asked for, fill it
     128         [ +  + ]:         15 :                 if(pConsumedLength)
     129                 :            :                 {
     130                 :         10 :                     *pConsumedLength = fConsumedLength;
     131         [ +  - ]:         15 :                 }
     132                 :            :             }
     133                 :            : 
     134                 :         15 :             return aRetval;
     135                 :            :         }
     136                 :            :     } // end of namespace tools
     137                 :            : } // end of namespace basegfx
     138                 :            : 
     139                 :            : //////////////////////////////////////////////////////////////////////////////
     140                 :            : 
     141                 :            : namespace basegfx
     142                 :            : {
     143                 :            :     // anonymus namespace for local helpers
     144                 :            :     namespace
     145                 :            :     {
     146                 :          0 :         bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
     147                 :            :         {
     148                 :            :             // isBezier() is true, already tested by caller
     149                 :          0 :             const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
     150                 :            : 
     151 [ #  # ][ #  # ]:          0 :             if(aEdge.equalZero())
     152                 :            :             {
     153                 :            :                 // start and end point the same, but control vectors used -> baloon curve loop
     154                 :            :                 // is not a simple edge
     155                 :          0 :                 return false;
     156                 :            :             }
     157                 :            : 
     158                 :            :             // get tangentA and scalar with edge
     159         [ #  # ]:          0 :             const B2DVector aTangentA(rCandidate.getTangent(0.0));
     160         [ #  # ]:          0 :             const double fScalarAE(aEdge.scalar(aTangentA));
     161                 :            : 
     162         [ #  # ]:          0 :             if(fTools::lessOrEqual(fScalarAE, 0.0))
     163                 :            :             {
     164                 :            :                 // angle between TangentA and Edge is bigger or equal 90 degrees
     165                 :          0 :                 return false;
     166                 :            :             }
     167                 :            : 
     168                 :            :             // get self-scalars for E and A
     169         [ #  # ]:          0 :             const double fScalarE(aEdge.scalar(aEdge));
     170         [ #  # ]:          0 :             const double fScalarA(aTangentA.scalar(aTangentA));
     171                 :          0 :             const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
     172                 :            : 
     173         [ #  # ]:          0 :             if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
     174                 :            :             {
     175                 :            :                 // length of TangentA is more than fMaxPartOfEdge of length of edge
     176                 :          0 :                 return false;
     177                 :            :             }
     178                 :            : 
     179         [ #  # ]:          0 :             if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
     180                 :            :             {
     181                 :            :                 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
     182                 :          0 :                 return false;
     183                 :            :             }
     184                 :            : 
     185                 :            :             // get tangentB and scalar with edge
     186         [ #  # ]:          0 :             const B2DVector aTangentB(rCandidate.getTangent(1.0));
     187         [ #  # ]:          0 :             const double fScalarBE(aEdge.scalar(aTangentB));
     188                 :            : 
     189         [ #  # ]:          0 :             if(fTools::lessOrEqual(fScalarBE, 0.0))
     190                 :            :             {
     191                 :            :                 // angle between TangentB and Edge is bigger or equal 90 degrees
     192                 :          0 :                 return false;
     193                 :            :             }
     194                 :            : 
     195                 :            :             // get self-scalar for B
     196         [ #  # ]:          0 :             const double fScalarB(aTangentB.scalar(aTangentB));
     197                 :            : 
     198         [ #  # ]:          0 :             if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
     199                 :            :             {
     200                 :            :                 // length of TangentB is more than fMaxPartOfEdge of length of edge
     201                 :          0 :                 return false;
     202                 :            :             }
     203                 :            : 
     204         [ #  # ]:          0 :             if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
     205                 :            :             {
     206                 :            :                 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
     207                 :          0 :                 return false;
     208                 :            :             }
     209                 :            : 
     210                 :          0 :             return true;
     211                 :            :         }
     212                 :            : 
     213                 :          0 :         void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
     214                 :            :         {
     215 [ #  # ][ #  # ]:          0 :             if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
                 [ #  # ]
     216                 :            :             {
     217         [ #  # ]:          0 :                 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
     218                 :            :             }
     219                 :            :             else
     220                 :            :             {
     221 [ #  # ][ #  # ]:          0 :                 B2DCubicBezier aLeft, aRight;
     222         [ #  # ]:          0 :                 rCandidate.split(0.5, &aLeft, &aRight);
     223                 :            : 
     224         [ #  # ]:          0 :                 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
     225 [ #  # ][ #  # ]:          0 :                 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
                 [ #  # ]
     226                 :            :             }
     227                 :          0 :         }
     228                 :            : 
     229                 :       9861 :         B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
     230                 :            :         {
     231                 :       9861 :             const sal_uInt32 nPointCount(rCandidate.count());
     232                 :            : 
     233 [ #  # ][ -  + ]:       9861 :             if(rCandidate.areControlPointsUsed() && nPointCount)
                 [ -  + ]
     234                 :            :             {
     235 [ #  # ][ #  # ]:          0 :                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
     236         [ #  # ]:          0 :                 B2DPolygon aRetval;
     237         [ #  # ]:          0 :                 B2DCubicBezier aEdge;
     238                 :            : 
     239                 :            :                 // prepare edge for loop
     240         [ #  # ]:          0 :                 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
     241         [ #  # ]:          0 :                 aRetval.append(aEdge.getStartPoint());
     242                 :            : 
     243         [ #  # ]:          0 :                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
     244                 :            :                 {
     245                 :            :                     // fill B2DCubicBezier
     246                 :          0 :                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     247         [ #  # ]:          0 :                     aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
     248         [ #  # ]:          0 :                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
     249         [ #  # ]:          0 :                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
     250                 :            : 
     251                 :            :                     // get rid of unnecessary bezier segments
     252         [ #  # ]:          0 :                     aEdge.testAndSolveTrivialBezier();
     253                 :            : 
     254 [ #  # ][ #  # ]:          0 :                     if(aEdge.isBezier())
     255                 :            :                     {
     256                 :            :                         // before splitting recursively with internal simple criteria, use
     257                 :            :                         // ExtremumPosFinder to remove those
     258         [ #  # ]:          0 :                         ::std::vector< double > aExtremumPositions;
     259                 :            : 
     260         [ #  # ]:          0 :                         aExtremumPositions.reserve(4);
     261         [ #  # ]:          0 :                         aEdge.getAllExtremumPositions(aExtremumPositions);
     262                 :            : 
     263                 :          0 :                         const sal_uInt32 nCount(aExtremumPositions.size());
     264                 :            : 
     265         [ #  # ]:          0 :                         if(nCount)
     266                 :            :                         {
     267         [ #  # ]:          0 :                             if(nCount > 1)
     268                 :            :                             {
     269                 :            :                                 // create order from left to right
     270         [ #  # ]:          0 :                                 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
     271                 :            :                             }
     272                 :            : 
     273         [ #  # ]:          0 :                             for(sal_uInt32 b(0); b < nCount;)
     274                 :            :                             {
     275                 :            :                                 // split aEdge at next split pos
     276         [ #  # ]:          0 :                                 B2DCubicBezier aLeft;
     277         [ #  # ]:          0 :                                 const double fSplitPos(aExtremumPositions[b++]);
     278                 :            : 
     279         [ #  # ]:          0 :                                 aEdge.split(fSplitPos, &aLeft, &aEdge);
     280         [ #  # ]:          0 :                                 aLeft.testAndSolveTrivialBezier();
     281                 :            : 
     282                 :            :                                 // consume left part
     283 [ #  # ][ #  # ]:          0 :                                 if(aLeft.isBezier())
     284                 :            :                                 {
     285         [ #  # ]:          0 :                                     impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
     286                 :            :                                 }
     287                 :            :                                 else
     288                 :            :                                 {
     289         [ #  # ]:          0 :                                     aRetval.append(aLeft.getEndPoint());
     290                 :            :                                 }
     291                 :            : 
     292         [ #  # ]:          0 :                                 if(b < nCount)
     293                 :            :                                 {
     294                 :            :                                     // correct the remaining split positions to fit to shortened aEdge
     295                 :          0 :                                     const double fScaleFactor(1.0 / (1.0 - fSplitPos));
     296                 :            : 
     297         [ #  # ]:          0 :                                     for(sal_uInt32 c(b); c < nCount; c++)
     298                 :            :                                     {
     299 [ #  # ][ #  # ]:          0 :                                         aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
     300                 :            :                                     }
     301                 :            :                                 }
     302         [ #  # ]:          0 :                             }
     303                 :            : 
     304                 :            :                             // test the shortened rest of aEdge
     305         [ #  # ]:          0 :                             aEdge.testAndSolveTrivialBezier();
     306                 :            : 
     307                 :            :                             // consume right part
     308 [ #  # ][ #  # ]:          0 :                             if(aEdge.isBezier())
     309                 :            :                             {
     310         [ #  # ]:          0 :                                 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
     311                 :            :                             }
     312                 :            :                             else
     313                 :            :                             {
     314         [ #  # ]:          0 :                                 aRetval.append(aEdge.getEndPoint());
     315                 :            :                             }
     316                 :            :                         }
     317                 :            :                         else
     318                 :            :                         {
     319         [ #  # ]:          0 :                             impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
     320                 :          0 :                         }
     321                 :            :                     }
     322                 :            :                     else
     323                 :            :                     {
     324                 :            :                         // straight edge, add point
     325         [ #  # ]:          0 :                         aRetval.append(aEdge.getEndPoint());
     326                 :            :                     }
     327                 :            : 
     328                 :            :                     // prepare edge for next step
     329                 :          0 :                     aEdge.setStartPoint(aEdge.getEndPoint());
     330                 :            :                 }
     331                 :            : 
     332                 :            :                 // copy closed flag and check for double points
     333 [ #  # ][ #  # ]:          0 :                 aRetval.setClosed(rCandidate.isClosed());
     334         [ #  # ]:          0 :                 aRetval.removeDoublePoints();
     335                 :            : 
     336 [ #  # ][ #  # ]:          0 :                 return aRetval;
                 [ #  # ]
     337                 :            :             }
     338                 :            :             else
     339                 :            :             {
     340                 :       9861 :                 return rCandidate;
     341                 :            :             }
     342                 :            :         }
     343                 :            : 
     344                 :      10729 :         B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
     345                 :            :         {
     346                 :            :             // create polygon for edge
     347                 :            :             // Unfortunately, while it would be geometrically correct to not add
     348                 :            :             // the in-between points EdgeEnd and EdgeStart, it leads to rounding
     349                 :            :             // errors when converting to integer polygon coordinates for painting
     350         [ -  + ]:      10729 :             if(rEdge.isBezier())
     351                 :            :             {
     352                 :            :                 // prepare target and data common for upper and lower
     353         [ #  # ]:          0 :                 B2DPolygon aBezierPolygon;
     354                 :          0 :                 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
     355         [ #  # ]:          0 :                 const double fEdgeLength(aPureEdgeVector.getLength());
     356                 :          0 :                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
     357         [ #  # ]:          0 :                 const B2DVector aTangentA(rEdge.getTangent(0.0));
     358         [ #  # ]:          0 :                 const B2DVector aTangentB(rEdge.getTangent(1.0));
     359                 :            : 
     360                 :            :                 // create upper edge.
     361                 :            :                 {
     362                 :            :                     // create displacement vectors and check if they cut
     363         [ #  # ]:          0 :                     const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
     364         [ #  # ]:          0 :                     const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
     365                 :          0 :                     double fCut(0.0);
     366                 :            :                     const tools::CutFlagValue aCut(tools::findCut(
     367                 :            :                         rEdge.getStartPoint(), aPerpendStart,
     368                 :            :                         rEdge.getEndPoint(), aPerpendEnd,
     369         [ #  # ]:          0 :                         CUTFLAG_ALL, &fCut));
     370                 :            : 
     371         [ #  # ]:          0 :                     if(CUTFLAG_NONE != aCut)
     372                 :            :                     {
     373                 :            :                         // calculate cut point and add
     374                 :          0 :                         const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
     375         [ #  # ]:          0 :                         aBezierPolygon.append(aCutPoint);
     376                 :            :                     }
     377                 :            :                     else
     378                 :            :                     {
     379                 :            :                         // create scaled bezier segment
     380                 :          0 :                         const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
     381                 :          0 :                         const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
     382                 :          0 :                         const B2DVector aEdge(aEnd - aStart);
     383         [ #  # ]:          0 :                         const double fLength(aEdge.getLength());
     384         [ #  # ]:          0 :                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
     385                 :          0 :                         const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
     386                 :          0 :                         const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
     387                 :            : 
     388         [ #  # ]:          0 :                         aBezierPolygon.append(aStart);
     389         [ #  # ]:          0 :                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
     390                 :          0 :                     }
     391                 :            :                 }
     392                 :            : 
     393                 :            :                 // append original in-between point
     394         [ #  # ]:          0 :                 aBezierPolygon.append(rEdge.getEndPoint());
     395                 :            : 
     396                 :            :                 // create lower edge.
     397                 :            :                 {
     398                 :            :                     // create displacement vectors and check if they cut
     399         [ #  # ]:          0 :                     const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
     400         [ #  # ]:          0 :                     const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
     401                 :          0 :                     double fCut(0.0);
     402                 :            :                     const tools::CutFlagValue aCut(tools::findCut(
     403                 :            :                         rEdge.getEndPoint(), aPerpendEnd,
     404                 :            :                         rEdge.getStartPoint(), aPerpendStart,
     405         [ #  # ]:          0 :                         CUTFLAG_ALL, &fCut));
     406                 :            : 
     407         [ #  # ]:          0 :                     if(CUTFLAG_NONE != aCut)
     408                 :            :                     {
     409                 :            :                         // calculate cut point and add
     410                 :          0 :                         const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
     411         [ #  # ]:          0 :                         aBezierPolygon.append(aCutPoint);
     412                 :            :                     }
     413                 :            :                     else
     414                 :            :                     {
     415                 :            :                         // create scaled bezier segment
     416                 :          0 :                         const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
     417                 :          0 :                         const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
     418                 :          0 :                         const B2DVector aEdge(aEnd - aStart);
     419         [ #  # ]:          0 :                         const double fLength(aEdge.getLength());
     420         [ #  # ]:          0 :                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
     421                 :          0 :                         const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
     422                 :          0 :                         const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
     423                 :            : 
     424         [ #  # ]:          0 :                         aBezierPolygon.append(aStart);
     425         [ #  # ]:          0 :                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
     426                 :          0 :                     }
     427                 :            :                 }
     428                 :            : 
     429                 :            :                 // append original in-between point
     430         [ #  # ]:          0 :                 aBezierPolygon.append(rEdge.getStartPoint());
     431                 :            : 
     432                 :            :                 // close and return
     433         [ #  # ]:          0 :                 aBezierPolygon.setClosed(true);
     434 [ #  # ][ #  # ]:          0 :                 return aBezierPolygon;
     435                 :            :             }
     436                 :            :             else
     437                 :            :             {
     438                 :            :                 // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
     439                 :            :                 // full-length edge vector to have numerically exactly the same results as in the
     440                 :            :                 // createAreaGeometryForJoin implementation
     441                 :      10729 :                 const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
     442         [ +  - ]:      10729 :                 const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
     443         [ +  - ]:      10729 :                 B2DPolygon aEdgePolygon;
     444                 :            : 
     445                 :            :                 // create upper edge
     446         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
     447         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
     448                 :            : 
     449                 :            :                 // append original in-between point
     450         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getEndPoint());
     451                 :            : 
     452                 :            :                 // create lower edge
     453         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
     454         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
     455                 :            : 
     456                 :            :                 // append original in-between point
     457         [ +  - ]:      10729 :                 aEdgePolygon.append(rEdge.getStartPoint());
     458                 :            : 
     459                 :            :                 // close and return
     460         [ +  - ]:      10729 :                 aEdgePolygon.setClosed(true);
     461 [ +  - ][ +  - ]:      10729 :                 return aEdgePolygon;
     462                 :            :             }
     463                 :            :         }
     464                 :            : 
     465                 :        868 :         B2DPolygon createAreaGeometryForJoin(
     466                 :            :             const B2DVector& rTangentPrev,
     467                 :            :             const B2DVector& rTangentEdge,
     468                 :            :             const B2DVector& rPerpendPrev,
     469                 :            :             const B2DVector& rPerpendEdge,
     470                 :            :             const B2DPoint& rPoint,
     471                 :            :             double fHalfLineWidth,
     472                 :            :             B2DLineJoin eJoin,
     473                 :            :             double fMiterMinimumAngle)
     474                 :            :         {
     475                 :            :             OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
     476                 :            :             OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
     477                 :            : 
     478                 :            :             // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
     479         [ +  - ]:        868 :             B2DPolygon aEdgePolygon;
     480                 :        868 :             const B2DPoint aStartPoint(rPoint + rPerpendPrev);
     481                 :        868 :             const B2DPoint aEndPoint(rPoint + rPerpendEdge);
     482                 :            : 
     483                 :            :             // test if for Miter, the angle is too small and the fallback
     484                 :            :             // to bevel needs to be used
     485         [ -  + ]:        868 :             if(B2DLINEJOIN_MITER == eJoin)
     486                 :            :             {
     487         [ #  # ]:          0 :                 const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
     488                 :            : 
     489         [ #  # ]:          0 :                 if((F_PI - fAngle) < fMiterMinimumAngle)
     490                 :            :                 {
     491                 :            :                     // fallback to bevel
     492                 :          0 :                     eJoin = B2DLINEJOIN_BEVEL;
     493                 :            :                 }
     494                 :            :             }
     495                 :            : 
     496      [ -  +  - ]:        868 :             switch(eJoin)
     497                 :            :             {
     498                 :            :                 case B2DLINEJOIN_MITER :
     499                 :            :                 {
     500         [ #  # ]:          0 :                     aEdgePolygon.append(aEndPoint);
     501         [ #  # ]:          0 :                     aEdgePolygon.append(rPoint);
     502         [ #  # ]:          0 :                     aEdgePolygon.append(aStartPoint);
     503                 :            : 
     504                 :            :                     // Look for the cut point between start point along rTangentPrev and
     505                 :            :                     // end point along rTangentEdge. -rTangentEdge should be used, but since
     506                 :            :                     // the cut value is used for interpolating along the first edge, the negation
     507                 :            :                     // is not needed since the same fCut will be found on the first edge.
     508                 :            :                     // If it exists, insert it to complete the mitered fill polygon.
     509                 :          0 :                     double fCutPos(0.0);
     510         [ #  # ]:          0 :                     tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
     511                 :            : 
     512         [ #  # ]:          0 :                     if(0.0 != fCutPos)
     513                 :            :                     {
     514                 :          0 :                         const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
     515         [ #  # ]:          0 :                         aEdgePolygon.append(aCutPoint);
     516                 :            :                     }
     517                 :            : 
     518                 :            :                     break;
     519                 :            :                 }
     520                 :            :                 case B2DLINEJOIN_ROUND :
     521                 :            :                 {
     522                 :            :                     // use tooling to add needed EllipseSegment
     523                 :        868 :                     double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
     524                 :        868 :                     double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
     525                 :            : 
     526                 :            :                     // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
     527         [ +  + ]:        868 :                     if(fAngleStart < 0.0)
     528                 :            :                     {
     529                 :        486 :                         fAngleStart += F_2PI;
     530                 :            :                     }
     531                 :            : 
     532         [ +  + ]:        868 :                     if(fAngleEnd < 0.0)
     533                 :            :                     {
     534                 :        486 :                         fAngleEnd += F_2PI;
     535                 :            :                     }
     536                 :            : 
     537         [ +  - ]:        868 :                     const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
     538                 :            : 
     539 [ +  - ][ +  - ]:        868 :                     if(aBow.count() > 1)
     540                 :            :                     {
     541                 :            :                         // #i101491#
     542                 :            :                         // use the original start/end positions; the ones from bow creation may be numerically
     543                 :            :                         // different due to their different creation. To guarantee good merging quality with edges
     544                 :            :                         // and edge roundings (and to reduce point count)
     545         [ +  - ]:        868 :                         aEdgePolygon = aBow;
     546         [ +  - ]:        868 :                         aEdgePolygon.setB2DPoint(0, aStartPoint);
     547 [ +  - ][ +  - ]:        868 :                         aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
     548         [ +  - ]:        868 :                         aEdgePolygon.append(rPoint);
     549                 :            : 
     550                 :            :                         break;
     551                 :            :                     }
     552                 :            :                     else
     553                 :            :                     {
     554                 :            :                         // wanted fall-through to default
     555 [ +  - ][ -  + ]:        868 :                     }
     556                 :            :                 }
     557                 :            :                 default: // B2DLINEJOIN_BEVEL
     558                 :            :                 {
     559         [ #  # ]:          0 :                     aEdgePolygon.append(aEndPoint);
     560         [ #  # ]:          0 :                     aEdgePolygon.append(rPoint);
     561         [ #  # ]:          0 :                     aEdgePolygon.append(aStartPoint);
     562                 :            : 
     563                 :          0 :                     break;
     564                 :            :                 }
     565                 :            :             }
     566                 :            : 
     567                 :            :             // create last polygon part for edge
     568         [ +  - ]:        868 :             aEdgePolygon.setClosed(true);
     569                 :            : 
     570                 :        868 :             return aEdgePolygon;
     571                 :            :         }
     572                 :            :     } // end of anonymus namespace
     573                 :            : 
     574                 :            :     namespace tools
     575                 :            :     {
     576                 :       9861 :         B2DPolyPolygon createAreaGeometry(
     577                 :            :             const B2DPolygon& rCandidate,
     578                 :            :             double fHalfLineWidth,
     579                 :            :             B2DLineJoin eJoin,
     580                 :            :             double fMaxAllowedAngle,
     581                 :            :             double fMaxPartOfEdge,
     582                 :            :             double fMiterMinimumAngle)
     583                 :            :         {
     584         [ -  + ]:       9861 :             if(fMaxAllowedAngle > F_PI2)
     585                 :            :             {
     586                 :          0 :                 fMaxAllowedAngle = F_PI2;
     587                 :            :             }
     588         [ -  + ]:       9861 :             else if(fMaxAllowedAngle < 0.01 * F_PI2)
     589                 :            :             {
     590                 :          0 :                 fMaxAllowedAngle = 0.01 * F_PI2;
     591                 :            :             }
     592                 :            : 
     593         [ -  + ]:       9861 :             if(fMaxPartOfEdge > 1.0)
     594                 :            :             {
     595                 :          0 :                 fMaxPartOfEdge = 1.0;
     596                 :            :             }
     597         [ -  + ]:       9861 :             else if(fMaxPartOfEdge < 0.01)
     598                 :            :             {
     599                 :          0 :                 fMaxPartOfEdge = 0.01;
     600                 :            :             }
     601                 :            : 
     602         [ -  + ]:       9861 :             if(fMiterMinimumAngle > F_PI)
     603                 :            :             {
     604                 :          0 :                 fMiterMinimumAngle = F_PI;
     605                 :            :             }
     606         [ -  + ]:       9861 :             else if(fMiterMinimumAngle < 0.01 * F_PI)
     607                 :            :             {
     608                 :          0 :                 fMiterMinimumAngle = 0.01 * F_PI;
     609                 :            :             }
     610                 :            : 
     611         [ +  - ]:       9861 :             B2DPolygon aCandidate(rCandidate);
     612                 :       9861 :             const double fMaxCos(cos(fMaxAllowedAngle));
     613                 :            : 
     614         [ +  - ]:       9861 :             aCandidate.removeDoublePoints();
     615 [ +  - ][ +  - ]:       9861 :             aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
                 [ +  - ]
     616                 :            : 
     617         [ +  - ]:       9861 :             const sal_uInt32 nPointCount(aCandidate.count());
     618                 :            : 
     619         [ +  - ]:       9861 :             if(nPointCount)
     620                 :            :             {
     621         [ +  - ]:       9861 :                 B2DPolyPolygon aRetval;
     622         [ +  - ]:       9861 :                 const bool bIsClosed(aCandidate.isClosed());
     623         [ -  + ]:       9861 :                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
     624                 :            : 
     625         [ +  - ]:       9861 :                 if(nEdgeCount)
     626                 :            :                 {
     627         [ +  - ]:       9861 :                     B2DCubicBezier aEdge;
     628         [ +  - ]:       9861 :                     B2DCubicBezier aPrev;
     629                 :            : 
     630                 :       9861 :                     const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
     631                 :            :                     // prepare edge
     632         [ +  - ]:       9861 :                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
     633                 :            : 
     634 [ #  # ][ -  + ]:       9861 :                     if(bIsClosed && bEventuallyCreateLineJoin)
     635                 :            :                     {
     636                 :            :                         // prepare previous edge
     637                 :          0 :                         const sal_uInt32 nPrevIndex(nPointCount - 1);
     638         [ #  # ]:          0 :                         aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
     639         [ #  # ]:          0 :                         aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
     640         [ #  # ]:          0 :                         aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
     641                 :          0 :                         aPrev.setEndPoint(aEdge.getStartPoint());
     642                 :            :                     }
     643                 :            : 
     644         [ +  + ]:      20590 :                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
     645                 :            :                     {
     646                 :            :                         // fill current Edge
     647                 :      10729 :                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
     648         [ +  - ]:      10729 :                         aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
     649         [ +  - ]:      10729 :                         aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
     650         [ +  - ]:      10729 :                         aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
     651                 :            : 
     652                 :            :                         // check and create linejoin
     653 [ +  - ][ +  + ]:      10729 :                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
                 [ +  + ]
     654                 :            :                         {
     655         [ +  - ]:        868 :                             const B2DVector aTangentPrev(aPrev.getTangent(1.0));
     656         [ +  - ]:        868 :                             const B2DVector aTangentEdge(aEdge.getTangent(0.0));
     657         [ +  - ]:        868 :                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
     658                 :            : 
     659         [ -  + ]:        868 :                             if(ORIENTATION_NEUTRAL == aOrientation)
     660                 :            :                             {
     661                 :            :                                 // they are parallell or empty; if they are both not zero and point
     662                 :            :                                 // in opposite direction, a half-circle is needed
     663 [ #  # ][ #  # ]:          0 :                                 if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
         [ #  # ][ #  # ]
                 [ #  # ]
     664                 :            :                                 {
     665         [ #  # ]:          0 :                                     const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
     666                 :            : 
     667         [ #  # ]:          0 :                                     if(fTools::equal(fAngle, F_PI))
     668                 :            :                                     {
     669                 :            :                                         // for half-circle production, fallback to positive
     670                 :            :                                         // orientation
     671                 :          0 :                                         aOrientation = ORIENTATION_POSITIVE;
     672                 :            :                                     }
     673                 :            :                                 }
     674                 :            :                             }
     675                 :            : 
     676         [ +  + ]:        868 :                             if(ORIENTATION_POSITIVE == aOrientation)
     677                 :            :                             {
     678         [ +  - ]:        486 :                                 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
     679         [ +  - ]:        486 :                                 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
     680                 :            : 
     681                 :            :                                 aRetval.append(createAreaGeometryForJoin(
     682                 :            :                                     aTangentPrev, aTangentEdge,
     683                 :            :                                     aPerpendPrev, aPerpendEdge,
     684                 :            :                                     aEdge.getStartPoint(), fHalfLineWidth,
     685 [ +  - ][ +  - ]:        486 :                                     eJoin, fMiterMinimumAngle));
                 [ +  - ]
     686                 :            :                             }
     687         [ +  - ]:        382 :                             else if(ORIENTATION_NEGATIVE == aOrientation)
     688                 :            :                             {
     689         [ +  - ]:        382 :                                 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
     690         [ +  - ]:        382 :                                 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
     691                 :            : 
     692                 :            :                                 aRetval.append(createAreaGeometryForJoin(
     693                 :            :                                     aTangentEdge, aTangentPrev,
     694                 :            :                                     aPerpendEdge, aPerpendPrev,
     695                 :            :                                     aEdge.getStartPoint(), fHalfLineWidth,
     696 [ +  - ][ +  - ]:        382 :                                     eJoin, fMiterMinimumAngle));
                 [ +  - ]
     697                 :        868 :                             }
     698                 :            :                         }
     699                 :            : 
     700                 :            :                         // create geometry for edge
     701 [ +  - ][ +  - ]:      10729 :                         aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
                 [ +  - ]
     702                 :            : 
     703                 :            :                         // prepare next step
     704         [ +  + ]:      10729 :                         if(bEventuallyCreateLineJoin)
     705                 :            :                         {
     706         [ +  - ]:       1540 :                             aPrev = aEdge;
     707                 :            :                         }
     708                 :            : 
     709                 :      10729 :                         aEdge.setStartPoint(aEdge.getEndPoint());
     710 [ +  - ][ +  - ]:       9861 :                     }
     711                 :            :                 }
     712                 :            : 
     713 [ +  - ][ +  - ]:       9861 :                 return aRetval;
     714                 :            :             }
     715                 :            :             else
     716                 :            :             {
     717         [ #  # ]:          0 :                 return B2DPolyPolygon(rCandidate);
     718         [ +  - ]:       9861 :             }
     719                 :            :         }
     720                 :            :     } // end of namespace tools
     721                 :            : } // end of namespace basegfx
     722                 :            : 
     723                 :            : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10