LCOV - code coverage report
Current view: top level - basegfx/source/polygon - b2dtrapezoid.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 277 446 62.1 %
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/polygon/b2dtrapezoid.hxx>
      21             : #include <basegfx/range/b1drange.hxx>
      22             : #include <basegfx/polygon/b2dpolygontools.hxx>
      23             : 
      24             : #include <osl/diagnose.h>
      25             : 
      26             : #include <list>
      27             : 
      28             : namespace basegfx
      29             : {
      30             :     namespace trapezoidhelper
      31             :     {
      32             : 
      33             :         // helper class to hold a simple ege. This is only used for horizontal edges
      34             :         // currently, thus the YPositions will be equal. I did not create a special
      35             :         // class for this since holdingthe pointers is more effective and also can be
      36             :         // used as baseclass for the traversing edges
      37             : 
      38             :         class TrDeSimpleEdge
      39             :         {
      40             :         protected:
      41             :             // pointers to start and end point
      42             :             const B2DPoint*     mpStart;
      43             :             const B2DPoint*     mpEnd;
      44             : 
      45             :         public:
      46             :             // constructor
      47       33935 :             TrDeSimpleEdge(
      48             :                 const B2DPoint* pStart,
      49             :                 const B2DPoint* pEnd)
      50             :             :   mpStart(pStart),
      51       33935 :                 mpEnd(pEnd)
      52             :             {
      53       33935 :             }
      54             : 
      55             :             // data read access
      56    26636061 :             const B2DPoint& getStart() const { return *mpStart; }
      57     2889606 :             const B2DPoint& getEnd() const { return *mpEnd; }
      58             :         };
      59             : 
      60             :         // define vector of simple edges
      61             : 
      62             :         typedef ::std::vector< TrDeSimpleEdge > TrDeSimpleEdges;
      63             : 
      64             :         // helper class for holding a traversing edge. It will always have some
      65             :         // distance in YPos. The slope (in a numerically useful form, see comments) is
      66             :         // hold and used in SortValue to allow sorting traversing edges by Y, X and slope
      67             :         // (in that order)
      68             : 
      69             :         class TrDeEdgeEntry : public TrDeSimpleEdge
      70             :         {
      71             :         private:
      72             :             // the slope in a numerical useful form for sorting
      73             :             sal_uInt32          mnSortValue;
      74             : 
      75             :         public:
      76             :             // convenience data read access
      77      371109 :             double getDeltaX() const { return mpEnd->getX() - mpStart->getX(); }
      78      371109 :             double getDeltaY() const { return mpEnd->getY() - mpStart->getY(); }
      79             : 
      80             :             // convenience data read access. SortValue is created on demand since
      81             :             // it is not always used
      82       56528 :             sal_uInt32 getSortValue() const
      83             :             {
      84       56528 :                 if(0 != mnSortValue)
      85       56210 :                     return mnSortValue;
      86             : 
      87             :                 // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
      88             :                 // sal_uInt32 range for maximum precision
      89         318 :                 const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32 / F_PI));
      90             : 
      91             :                 // convert to sal_uInt32 value
      92         318 :                 const_cast< TrDeEdgeEntry* >(this)->mnSortValue = sal_uInt32(fRadiant);
      93             : 
      94         318 :                 return mnSortValue;
      95             :             }
      96             : 
      97             :             // constructor. SortValue can be given when known, use zero otherwise
      98       33934 :             TrDeEdgeEntry(
      99             :                 const B2DPoint* pStart,
     100             :                 const B2DPoint* pEnd,
     101             :                 sal_uInt32 nSortValue = 0)
     102             :             :   TrDeSimpleEdge(pStart, pEnd),
     103       33934 :                 mnSortValue(nSortValue)
     104             :             {
     105             :                 // force traversal of deltaY downward
     106       33934 :                 if(mpEnd->getY() < mpStart->getY())
     107             :                 {
     108         162 :                     std::swap(mpStart, mpEnd);
     109             :                 }
     110             : 
     111             :                 // no horizontal edges allowed, all neeed to traverse vertically
     112             :                 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
     113       33934 :             }
     114             : 
     115             :             // data write access to StartPoint
     116           0 :             void setStart( const B2DPoint* pNewStart)
     117             :             {
     118             :                 OSL_ENSURE(0 != pNewStart, "No null pointer allowed here (!)");
     119             : 
     120           0 :                 if(mpStart != pNewStart)
     121             :                 {
     122           0 :                     mpStart = pNewStart;
     123             : 
     124             :                     // no horizontal edges allowed, all neeed to traverse vertivally
     125             :                     OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
     126             :                 }
     127           0 :             }
     128             : 
     129             :             // data write access to EndPoint
     130       33616 :             void setEnd( const B2DPoint* pNewEnd)
     131             :             {
     132             :                 OSL_ENSURE(0 != pNewEnd, "No null pointer allowed here (!)");
     133             : 
     134       33616 :                 if(mpEnd != pNewEnd)
     135             :                 {
     136       33616 :                     mpEnd = pNewEnd;
     137             : 
     138             :                     // no horizontal edges allowed, all neeed to traverse vertivally
     139             :                     OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
     140             :                 }
     141       33616 :             }
     142             : 
     143             :             // operator for sort support. Sort by Y, X and slope (in that order)
     144     4181467 :             bool operator<(const TrDeEdgeEntry& rComp) const
     145             :             {
     146     4181467 :                 if(fTools::equal(getStart().getY(), rComp.getStart().getY()))
     147             :                 {
     148       32079 :                     if(fTools::equal(getStart().getX(), rComp.getStart().getX()))
     149             :                     {
     150             :                         // when start points are equal, use the direction the edge is pointing
     151             :                         // to. That value is created on demand and derived from atan2 in the
     152             :                         // range ]0.0 .. pi[ (without extremas, we always have a deltaY in this
     153             :                         // class) and scaled to sal_uInt32 range for best precision. 0 means no angle,
     154             :                         // while SAL_MAX_UINT32 means pi. Thus, the higher the value, the more left
     155             :                         // the edge traverses.
     156       11456 :                         return (getSortValue() > rComp.getSortValue());
     157             :                     }
     158             :                     else
     159             :                     {
     160       20623 :                         return fTools::less(getStart().getX(), rComp.getStart().getX());
     161             :                     }
     162             :                 }
     163             :                 else
     164             :                 {
     165     4149388 :                     return fTools::less(getStart().getY(), rComp.getStart().getY());
     166             :                 }
     167             :             }
     168             : 
     169             :             // method for cut support
     170      154774 :             B2DPoint getCutPointForGivenY(double fGivenY)
     171             :             {
     172             :                 // Calculate cut point locally (do not use interpolate) since it is numerically
     173             :                 // necessary to guarantee the new, equal Y-coordinate
     174      154774 :                 const double fFactor((fGivenY - getStart().getY()) / getDeltaY());
     175      154774 :                 const double fDeltaXNew(fFactor * getDeltaX());
     176             : 
     177      154774 :                 return B2DPoint(getStart().getX() + fDeltaXNew, fGivenY);
     178             :             }
     179             :         };
     180             : 
     181             :         // define double linked list of edges (for fast random insert)
     182             : 
     183             :         typedef ::std::list< TrDeEdgeEntry > TrDeEdgeEntries;
     184             : 
     185             :     } // end of anonymous namespace
     186             : } // end of namespace basegfx
     187             : 
     188             : namespace basegfx
     189             : {
     190             :     namespace trapezoidhelper
     191             :     {
     192             :         // FIXME: templatize this and use it for TrDeEdgeEntries too ...
     193             : 
     194             :         /// Class to allow efficient allocation and release of B2DPoints
     195             :         class PointBlockAllocator
     196             :         {
     197             :             static const size_t nBlockSize = 32;
     198             :             size_t nCurPoint;
     199             :             B2DPoint *mpPointBase;
     200             :             /// Special case the first allocation to avoid it.
     201             :             B2DPoint maFirstStackBlock[nBlockSize];
     202             :             std::vector< B2DPoint * > maBlocks;
     203             :         public:
     204           1 :             PointBlockAllocator() :
     205             :                 nCurPoint( nBlockSize ),
     206           1 :                 mpPointBase( maFirstStackBlock )
     207             :             {
     208           1 :             }
     209             : 
     210           1 :             ~PointBlockAllocator()
     211          33 :             {
     212         705 :                 while(maBlocks.size() > 0)
     213             :                 {
     214         703 :                     delete [] maBlocks.back();
     215         703 :                     maBlocks.pop_back();
     216             :                 }
     217          33 :             }
     218             : 
     219       22496 :             B2DPoint *allocatePoint()
     220             :             {
     221       22496 :                 if(nCurPoint >= nBlockSize)
     222             :                 {
     223         703 :                     mpPointBase = new B2DPoint[nBlockSize];
     224         703 :                     maBlocks.push_back(mpPointBase);
     225         703 :                     nCurPoint = 0;
     226             :                 }
     227       22496 :                 return mpPointBase + nCurPoint++;
     228             :             }
     229             : 
     230       22496 :             B2DPoint *allocatePoint(const B2DTuple &rPoint)
     231             :             {
     232       22496 :                 B2DPoint *pPoint = allocatePoint();
     233       22496 :                 *pPoint = rPoint;
     234       22496 :                 return pPoint;
     235             :             }
     236             : 
     237             :             /// This is a very uncommon case but why not ...
     238           0 :             void freeIfLast(B2DPoint *pPoint)
     239             :             {
     240             :                 // just re-use the last point if we can.
     241           0 :                 if ( nCurPoint > 0 && pPoint == mpPointBase + nCurPoint - 1 )
     242           0 :                     nCurPoint--;
     243           0 :             }
     244             :         };
     245             : 
     246             :         // helper class to handle the complete trapezoid subdivision of a PolyPolygon
     247           1 :         class TrapezoidSubdivider
     248             :         {
     249             :         private:
     250             :             // local data
     251             :             sal_uInt32                  mnInitialEdgeEntryCount;
     252             :             TrDeEdgeEntries             maTrDeEdgeEntries;
     253             :             ::std::vector< B2DPoint >   maPoints;
     254             :             /// new points allocated for cuts
     255             :             PointBlockAllocator         maNewPoints;
     256             : 
     257       33616 :             void addEdgeSorted(
     258             :                 TrDeEdgeEntries::iterator aCurrent,
     259             :                 const TrDeEdgeEntry& rNewEdge)
     260             :             {
     261             :                 // Loop while new entry is bigger, use operator<
     262     4212795 :                 while(aCurrent != maTrDeEdgeEntries.end() && (*aCurrent) < rNewEdge)
     263             :                 {
     264     4145563 :                     ++aCurrent;
     265             :                 }
     266             : 
     267             :                 // Insert before first which is smaller or equal or at end
     268       33616 :                 maTrDeEdgeEntries.insert(aCurrent, rNewEdge);
     269       33616 :             }
     270             : 
     271       33639 :             bool splitEdgeAtGivenPoint(
     272             :                 TrDeEdgeEntries::reference aEdge,
     273             :                 const B2DPoint& rCutPoint,
     274             :                 TrDeEdgeEntries::iterator aCurrent)
     275             :             {
     276             :                 // do not create edges without deltaY: do not split when start is identical
     277       33639 :                 if(aEdge.getStart().equal(rCutPoint))
     278             :                 {
     279           6 :                     return false;
     280             :                 }
     281             : 
     282             :                 // do not create edges without deltaY: do not split when end is identical
     283       33633 :                 if(aEdge.getEnd().equal(rCutPoint))
     284             :                 {
     285          17 :                     return false;
     286             :                 }
     287             : 
     288       33616 :                 const double fOldDeltaYStart(rCutPoint.getY() - aEdge.getStart().getY());
     289             : 
     290       33616 :                 if(fTools::lessOrEqual(fOldDeltaYStart, 0.0))
     291             :                 {
     292             :                     // do not split: the resulting edge would be horizontal
     293             :                     // correct it to new start point
     294           0 :                     aEdge.setStart(&rCutPoint);
     295           0 :                     return false;
     296             :                 }
     297             : 
     298       33616 :                 const double fNewDeltaYStart(aEdge.getEnd().getY() - rCutPoint.getY());
     299             : 
     300       33616 :                 if(fTools::lessOrEqual(fNewDeltaYStart, 0.0))
     301             :                 {
     302             :                     // do not split: the resulting edge would be horizontal
     303             :                     // correct it to new end point
     304           0 :                     aEdge.setEnd(&rCutPoint);
     305           0 :                     return false;
     306             :                 }
     307             : 
     308             :                 // Create new entry
     309             :                 const TrDeEdgeEntry aNewEdge(
     310             :                     &rCutPoint,
     311       33616 :                     &aEdge.getEnd(),
     312       67232 :                     aEdge.getSortValue());
     313             : 
     314             :                 // Correct old entry
     315       33616 :                 aEdge.setEnd(&rCutPoint);
     316             : 
     317             :                 // Insert sorted (to avoid new sort)
     318       33616 :                 addEdgeSorted(aCurrent, aNewEdge);
     319             : 
     320       33616 :                 return true;
     321             :             }
     322             : 
     323      119497 :             bool testAndCorrectEdgeIntersection(
     324             :                 TrDeEdgeEntries::reference aEdgeA,
     325             :                 TrDeEdgeEntries::reference aEdgeB,
     326             :                 TrDeEdgeEntries::iterator aCurrent)
     327             :             {
     328             :                 // Exclude simple cases: same start or end point
     329      119497 :                 if(aEdgeA.getStart().equal(aEdgeB.getStart()))
     330             :                 {
     331           0 :                     return false;
     332             :                 }
     333             : 
     334      119497 :                 if(aEdgeA.getStart().equal(aEdgeB.getEnd()))
     335             :                 {
     336           0 :                     return false;
     337             :                 }
     338             : 
     339      119497 :                 if(aEdgeA.getEnd().equal(aEdgeB.getStart()))
     340             :                 {
     341           0 :                     return false;
     342             :                 }
     343             : 
     344      119497 :                 if(aEdgeA.getEnd().equal(aEdgeB.getEnd()))
     345             :                 {
     346       11475 :                     return false;
     347             :                 }
     348             : 
     349             :                 // Exclude simple cases: one of the edges has no length anymore
     350      108022 :                 if(aEdgeA.getStart().equal(aEdgeA.getEnd()))
     351             :                 {
     352           0 :                     return false;
     353             :                 }
     354             : 
     355      108022 :                 if(aEdgeB.getStart().equal(aEdgeB.getEnd()))
     356             :                 {
     357           0 :                     return false;
     358             :                 }
     359             : 
     360             :                 // check if one point is on the other edge (a touch, not a cut)
     361      108022 :                 const B2DVector aDeltaB(aEdgeB.getDeltaX(), aEdgeB.getDeltaY());
     362             : 
     363      108022 :                 if(tools::isPointOnEdge(aEdgeA.getStart(), aEdgeB.getStart(), aDeltaB))
     364             :                 {
     365           0 :                     return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getStart(), aCurrent);
     366             :                 }
     367             : 
     368      108022 :                 if(tools::isPointOnEdge(aEdgeA.getEnd(), aEdgeB.getStart(), aDeltaB))
     369             :                 {
     370          27 :                     return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getEnd(), aCurrent);
     371             :                 }
     372             : 
     373      215990 :                 const B2DVector aDeltaA(aEdgeA.getDeltaX(), aEdgeA.getDeltaY());
     374             : 
     375      107995 :                 if(tools::isPointOnEdge(aEdgeB.getStart(), aEdgeA.getStart(), aDeltaA))
     376             :                 {
     377           0 :                     return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getStart(), aCurrent);
     378             :                 }
     379             : 
     380      107995 :                 if(tools::isPointOnEdge(aEdgeB.getEnd(), aEdgeA.getStart(), aDeltaA))
     381             :                 {
     382           4 :                     return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getEnd(), aCurrent);
     383             :                 }
     384             : 
     385             :                 // check for cut inside edges. Use both t-values to choose the more precise
     386             :                 // one later
     387      107991 :                 double fCutA(0.0);
     388      107991 :                 double fCutB(0.0);
     389             : 
     390      107991 :                 if(tools::findCut(
     391      107991 :                     aEdgeA.getStart(), aDeltaA,
     392      107991 :                     aEdgeB.getStart(), aDeltaB,
     393             :                     CutFlagValue::LINE,
     394             :                     &fCutA,
     395      107991 :                     &fCutB) != CutFlagValue::NONE)
     396             :                 {
     397             :                     // use a simple metric (length criteria) for choosing the numerically
     398             :                     // better cut
     399       11112 :                     const double fSimpleLengthA(aDeltaA.getX() + aDeltaA.getY());
     400       11112 :                     const double fSimpleLengthB(aDeltaB.getX() + aDeltaB.getY());
     401       11112 :                     const bool bAIsLonger(fSimpleLengthA > fSimpleLengthB);
     402             :                     B2DPoint* pNewPoint = bAIsLonger
     403       31914 :                         ? maNewPoints.allocatePoint(aEdgeA.getStart() + (fCutA * aDeltaA))
     404       29158 :                         : maNewPoints.allocatePoint(aEdgeB.getStart() + (fCutB * aDeltaB));
     405             : 
     406             :                     // try to split both edges
     407       11112 :                     bool bRetval = splitEdgeAtGivenPoint(aEdgeA, *pNewPoint, aCurrent);
     408       11112 :                     bRetval |= splitEdgeAtGivenPoint(aEdgeB, *pNewPoint, aCurrent);
     409             : 
     410       11112 :                     if(!bRetval)
     411           0 :                         maNewPoints.freeIfLast(pNewPoint);
     412             : 
     413       11112 :                     return bRetval;
     414             :                 }
     415             : 
     416      204901 :                 return false;
     417             :             }
     418             : 
     419           1 :             void solveHorizontalEdges(TrDeSimpleEdges& rTrDeSimpleEdges)
     420             :             {
     421           1 :                 if(!rTrDeSimpleEdges.empty() && !maTrDeEdgeEntries.empty())
     422             :                 {
     423             :                     // there were horizontal edges. These can be excluded, but
     424             :                     // cuts with other edges need to be solved and added before
     425             :                     // ignoring them
     426           2 :                     for(size_t a = 0; a < rTrDeSimpleEdges.size(); a++)
     427             :                     {
     428             :                         // get horizontal edge as candidate; prepare its range and fixed Y
     429           1 :                         const TrDeSimpleEdge& rHorEdge = rTrDeSimpleEdges[a];
     430           1 :                         const B1DRange aRange(rHorEdge.getStart().getX(), rHorEdge.getEnd().getX());
     431           1 :                         const double fFixedY(rHorEdge.getStart().getY());
     432             : 
     433             :                         // loop over traversing edges
     434           1 :                         TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
     435             : 
     436         632 :                         do
     437             :                         {
     438             :                             // get compare edge
     439         316 :                             TrDeEdgeEntries::reference aCompare(*aCurrent++);
     440             : 
     441         316 :                             if(fTools::lessOrEqual(aCompare.getEnd().getY(), fFixedY))
     442             :                             {
     443             :                                 // edge ends above horizontal edge, continue
     444         488 :                                 continue;
     445             :                             }
     446             : 
     447          72 :                             if(fTools::moreOrEqual(aCompare.getStart().getY(), fFixedY))
     448             :                             {
     449             :                                 // edge starts below horizontal edge, continue
     450           0 :                                 continue;
     451             :                             }
     452             : 
     453             :                             // vertical overlap, get horizontal range
     454          72 :                             const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
     455             : 
     456          72 :                             if(aRange.overlaps(aCompareRange))
     457             :                             {
     458             :                                 // possible cut, get cut point
     459          71 :                                 const B2DPoint aSplit(aCompare.getCutPointForGivenY(fFixedY));
     460             : 
     461         355 :                                 if(fTools::more(aSplit.getX(), aRange.getMinimum())
     462         355 :                                     && fTools::less(aSplit.getX(), aRange.getMaximum()))
     463             :                                 {
     464             :                                     // cut is in XRange of horizontal edge, potentially needed cut
     465          61 :                                     B2DPoint* pNewPoint = maNewPoints.allocatePoint(aSplit);
     466             : 
     467          61 :                                     if(!splitEdgeAtGivenPoint(aCompare, *pNewPoint, aCurrent))
     468             :                                     {
     469           0 :                                         maNewPoints.freeIfLast(pNewPoint);
     470             :                                     }
     471          71 :                                 }
     472             :                             }
     473             :                         }
     474        1264 :                         while(aCurrent != maTrDeEdgeEntries.end()
     475        1264 :                             && fTools::less(aCurrent->getStart().getY(), fFixedY));
     476             :                     }
     477             :                 }
     478           1 :             }
     479             : 
     480             :         public:
     481           1 :             explicit TrapezoidSubdivider(
     482             :                 const B2DPolyPolygon& rSourcePolyPolygon)
     483             :             :   mnInitialEdgeEntryCount(0),
     484             :                 maTrDeEdgeEntries(),
     485             :                 maPoints(),
     486           1 :                 maNewPoints()
     487             :             {
     488           1 :                 B2DPolyPolygon aSource(rSourcePolyPolygon);
     489           1 :                 const sal_uInt32 nPolygonCount(rSourcePolyPolygon.count());
     490           2 :                 TrDeSimpleEdges aTrDeSimpleEdges;
     491           1 :                 sal_uInt32 a(0), b(0);
     492           1 :                 sal_uInt32 nAllPointCount(0);
     493             : 
     494             :                 // ensure there are no curves used
     495           1 :                 if(aSource.areControlPointsUsed())
     496             :                 {
     497           0 :                     aSource = aSource.getDefaultAdaptiveSubdivision();
     498             :                 }
     499             : 
     500           3 :                 for(a = 0; a < nPolygonCount; a++)
     501             :                 {
     502             :                     // 1st run: count points
     503           2 :                     const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
     504           2 :                     const sal_uInt32 nCount(aPolygonCandidate.count());
     505             : 
     506           2 :                     if(nCount > 2)
     507             :                     {
     508           1 :                         nAllPointCount += nCount;
     509             :                     }
     510           2 :                 }
     511             : 
     512           1 :                 if(nAllPointCount)
     513             :                 {
     514             :                     // reserve needed points. CAUTION: maPoints size is NOT to be changed anymore
     515             :                     // after 2nd loop since pointers to it are used in the edges
     516           1 :                     maPoints.reserve(nAllPointCount);
     517             : 
     518           3 :                     for(a = 0; a < nPolygonCount; a++)
     519             :                     {
     520             :                         // 2nd run: add points
     521           2 :                         const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
     522           2 :                         const sal_uInt32 nCount(aPolygonCandidate.count());
     523             : 
     524           2 :                         if(nCount > 2)
     525             :                         {
     526         321 :                             for(b = 0; b < nCount; b++)
     527             :                             {
     528         320 :                                 maPoints.push_back(aPolygonCandidate.getB2DPoint(b));
     529             :                             }
     530             :                         }
     531           2 :                     }
     532             : 
     533             :                     // Moved the edge construction to a 3rd run: doing it in the 2nd run is
     534             :                     // possible(and i used it), but requires a working vector::reserve()
     535             :                     // implementation, else the vector will be reallocated and the pointers
     536             :                     // in the edges may be wrong. Security first here.
     537           1 :                     sal_uInt32 nStartIndex(0);
     538             : 
     539           3 :                     for(a = 0; a < nPolygonCount; a++)
     540             :                     {
     541           2 :                         const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
     542           2 :                         const sal_uInt32 nCount(aPolygonCandidate.count());
     543             : 
     544           2 :                         if(nCount > 2)
     545             :                         {
     546             :                             // get the last point of the current polygon
     547           1 :                             B2DPoint* pPrev(&maPoints[nCount + nStartIndex - 1]);
     548             : 
     549         321 :                             for(b = 0; b < nCount; b++)
     550             :                             {
     551             :                                 // get next point
     552         320 :                                 B2DPoint* pCurr(&maPoints[nStartIndex++]);
     553             : 
     554         320 :                                 if(fTools::equal(pPrev->getY(), pCurr->getY()))
     555             :                                 {
     556             :                                     // horizontal edge, check for single point
     557           2 :                                     if(!fTools::equal(pPrev->getX(), pCurr->getX()))
     558             :                                     {
     559             :                                         // X-order not needed, just add
     560           1 :                                         aTrDeSimpleEdges.push_back(TrDeSimpleEdge(pPrev, pCurr));
     561             : 
     562           1 :                                         const double fMiddle((pPrev->getY() + pCurr->getY()) * 0.5);
     563           1 :                                         pPrev->setY(fMiddle);
     564           1 :                                         pCurr->setY(fMiddle);
     565             :                                     }
     566             :                                 }
     567             :                                 else
     568             :                                 {
     569             :                                     // vertical edge. Positive Y-direction is guaranteed by the
     570             :                                     // TrDeEdgeEntry constructor
     571         318 :                                     maTrDeEdgeEntries.push_back(TrDeEdgeEntry(pPrev, pCurr, 0));
     572         318 :                                     mnInitialEdgeEntryCount++;
     573             :                                 }
     574             : 
     575             :                                 // prepare next step
     576         320 :                                 pPrev = pCurr;
     577             :                             }
     578             :                         }
     579           2 :                     }
     580             :                 }
     581             : 
     582           1 :                 if(!maTrDeEdgeEntries.empty())
     583             :                 {
     584             :                     // single and initial sort of traversing edges
     585           1 :                     maTrDeEdgeEntries.sort();
     586             : 
     587             :                     // solve horizontal edges if there are any detected
     588           1 :                     solveHorizontalEdges(aTrDeSimpleEdges);
     589           1 :                 }
     590           1 :             }
     591             : 
     592           1 :             void Subdivide(B2DTrapezoidVector& ro_Result)
     593             :             {
     594             :                 // This is the central subdivider. The strategy is to use the first two entries
     595             :                 // from the traversing edges as a potential trapezoid and do the needed corrections
     596             :                 // and adaptions on the way.
     597             : 
     598             :                 // There always must be two edges with the same YStart value: When adding the polygons
     599             :                 // in the constructor, there is always a topmost point from which two edges start; when
     600             :                 // the topmost is an edge, there is a start and end of this edge from which two edges
     601             :                 // start. All cases have two edges with same StartY (QED).
     602             : 
     603             :                 // Based on this these edges get corrected when:
     604             :                 // - one is longer than the other
     605             :                 // - they intersect
     606             :                 // - they intersect with other edges
     607             :                 // - another edge starts inside the thought trapezoid
     608             : 
     609             :                 // All this cases again produce a valid state so that the first two edges have a common
     610             :                 // Ystart again. Some cases lead to a restart of the process, some allow consuming the
     611             :                 // edges and create the intended trapezoid.
     612             : 
     613             :                 // Be careful when doing chages here: It is essential to keep all possible paths
     614             :                 // in valid states and to be numerically correct. This is especially needed e.g.
     615             :                 // by using fTools::equal(..) in the more robust small-value incarnation.
     616           1 :                 B1DRange aLeftRange;
     617           1 :                 B1DRange aRightRange;
     618             : 
     619           1 :                 if(!maTrDeEdgeEntries.empty())
     620             :                 {
     621             :                     // measuring shows that the relation between edges and created trapezoids is
     622             :                     // mostly in the 1:1 range, thus reserve as much trapezoids as edges exist. Do
     623             :                     // not use maTrDeEdgeEntries.size() since that may be a non-constant time
     624             :                     // operation for Lists. Instead, use mnInitialEdgeEntryCount which will contain
     625             :                     // the roughly counted adds to the List
     626           1 :                     ro_Result.reserve(ro_Result.size() + mnInitialEdgeEntryCount);
     627             :                 }
     628             : 
     629       28172 :                 while(!maTrDeEdgeEntries.empty())
     630             :                 {
     631             :                     // Prepare current operator and get first edge
     632       28170 :                     TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
     633       28170 :                     TrDeEdgeEntries::reference aLeft(*aCurrent++);
     634             : 
     635       28170 :                     if(aCurrent == maTrDeEdgeEntries.end())
     636             :                     {
     637             :                         // Should not happen: No 2nd edge; consume the single edge
     638             :                         // to not have an endless loop and start next. During development
     639             :                         // i constantly had breakpoints here, so i am sure enough to add an
     640             :                         // assertion here
     641             :                         OSL_FAIL("Trapezoid decomposer in illegal state (!)");
     642           0 :                         maTrDeEdgeEntries.pop_front();
     643       11267 :                         continue;
     644             :                     }
     645             : 
     646             :                     // get second edge
     647       28170 :                     TrDeEdgeEntries::reference aRight(*aCurrent++);
     648             : 
     649       28170 :                     if(!fTools::equal(aLeft.getStart().getY(), aRight.getStart().getY()))
     650             :                     {
     651             :                         // Should not happen: We have a 2nd edge, but YStart is on another
     652             :                         // line; consume the single edge to not have an endless loop and start
     653             :                         // next. During development i constantly had breakpoints here, so i am
     654             :                         // sure enough to add an assertion here
     655             :                         OSL_FAIL("Trapezoid decomposer in illegal state (!)");
     656           0 :                         maTrDeEdgeEntries.pop_front();
     657           0 :                         continue;
     658             :                     }
     659             : 
     660             :                     // aLeft and aRight build a thought trapezoid now. They have a common
     661             :                     // start line (same Y for start points). Potentially, one of the edges
     662             :                     // is longer than the other. It is only needed to look at the shorter
     663             :                     // length which build the potential trapezoid. To do so, get the end points
     664             :                     // locally and adapt the evtl. longer one. Use only aLeftEnd and aRightEnd
     665             :                     // from here on, not the aLeft.getEnd() or aRight.getEnd() accesses.
     666       28170 :                     B2DPoint aLeftEnd(aLeft.getEnd());
     667       45073 :                     B2DPoint aRightEnd(aRight.getEnd());
     668             : 
     669             :                     // check if end points are on the same line. If yes, no adaption
     670             :                     // needs to be prepared. Also remember which one actually is longer.
     671       28170 :                     const bool bEndOnSameLine(fTools::equal(aLeftEnd.getY(), aRightEnd.getY()));
     672       28170 :                     bool bLeftIsLonger(false);
     673             : 
     674       28170 :                     if(!bEndOnSameLine)
     675             :                     {
     676             :                         // check which edge is longer and correct accordingly
     677       21899 :                         bLeftIsLonger = fTools::more(aLeftEnd.getY(), aRightEnd.getY());
     678             : 
     679       21899 :                         if(bLeftIsLonger)
     680             :                         {
     681       10038 :                             aLeftEnd = aLeft.getCutPointForGivenY(aRightEnd.getY());
     682             :                         }
     683             :                         else
     684             :                         {
     685       11861 :                             aRightEnd = aRight.getCutPointForGivenY(aLeftEnd.getY());
     686             :                         }
     687             :                     }
     688             : 
     689             :                     // check for same start and end points
     690       28170 :                     const bool bSameStartPoint(aLeft.getStart().equal(aRight.getStart()));
     691       28170 :                     const bool bSameEndPoint(aLeftEnd.equal(aRightEnd));
     692             : 
     693             :                     // check the simple case that the edges form a 'blind' edge (deadend)
     694       28170 :                     if(bSameStartPoint && bSameEndPoint)
     695             :                     {
     696             :                         // correct the longer edge if prepared
     697          64 :                         if(!bEndOnSameLine)
     698             :                         {
     699          21 :                             if(bLeftIsLonger)
     700             :                             {
     701           5 :                                 B2DPoint* pNewPoint = maNewPoints.allocatePoint(aLeftEnd);
     702             : 
     703           5 :                                 if(!splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
     704             :                                 {
     705           0 :                                     maNewPoints.freeIfLast(pNewPoint);
     706             :                                 }
     707             :                             }
     708             :                             else
     709             :                             {
     710          16 :                                 B2DPoint* pNewPoint = maNewPoints.allocatePoint(aRightEnd);
     711             : 
     712          16 :                                 if(!splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
     713             :                                 {
     714           0 :                                     maNewPoints.freeIfLast(pNewPoint);
     715             :                                 }
     716             :                             }
     717             :                         }
     718             : 
     719             :                         // consume both edges and start next run
     720          64 :                         maTrDeEdgeEntries.pop_front();
     721          64 :                         maTrDeEdgeEntries.pop_front();
     722             : 
     723          64 :                         continue;
     724             :                     }
     725             : 
     726             :                     // check if the edges self-intersect. This can only happen when
     727             :                     // start and end point are different
     728       28106 :                     bool bRangesSet(false);
     729             : 
     730       28106 :                     if(!(bSameStartPoint || bSameEndPoint))
     731             :                     {
     732             :                         // get XRanges of edges
     733       13215 :                         aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
     734       13215 :                         aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
     735       13215 :                         bRangesSet = true;
     736             : 
     737             :                         // use fast range test first
     738       13215 :                         if(aLeftRange.overlaps(aRightRange))
     739             :                         {
     740             :                             // real cut test and correction. If correction was needed,
     741             :                             // start new run
     742        5829 :                             if(testAndCorrectEdgeIntersection(aLeft, aRight, aCurrent))
     743             :                             {
     744        3314 :                                 continue;
     745             :                             }
     746             :                         }
     747             :                     }
     748             : 
     749             :                     // now we need to check if there are intersections with other edges
     750             :                     // or if other edges start inside the candidate trapezoid
     751       99168 :                     if(aCurrent != maTrDeEdgeEntries.end()
     752       99168 :                         && fTools::less(aCurrent->getStart().getY(), aLeftEnd.getY()))
     753             :                     {
     754             :                         // get XRanges of edges
     755       24126 :                         if(!bRangesSet)
     756             :                         {
     757       14270 :                             aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
     758       14270 :                             aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
     759             :                         }
     760             : 
     761             :                         // build full XRange for fast check
     762       24126 :                         B1DRange aAllRange(aLeftRange);
     763       24126 :                         aAllRange.expand(aRightRange);
     764             : 
     765             :                         // prepare loop iterator; aCurrent needs to stay unchanged for
     766             :                         // possibly sorted insertions of new EdgeNodes. Also prepare stop flag
     767       24126 :                         TrDeEdgeEntries::iterator aLoop(aCurrent);
     768       24126 :                         bool bDone(false);
     769             : 
     770     3669275 :                         do
     771             :                         {
     772             :                             // get compare edge and its XRange
     773     1830693 :                             TrDeEdgeEntries::reference aCompare(*aLoop++);
     774             : 
     775             :                             // avoid edges using the same start point as one of
     776             :                             // the edges. These can neither have their start point
     777             :                             // in the thought trapezoid nor cut with one of the edges
     778     1830693 :                             if(aCompare.getStart().equal(aRight.getStart()))
     779             :                             {
     780        8761 :                                 continue;
     781             :                             }
     782             : 
     783             :                             // get compare XRange
     784     1821932 :                             const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
     785             : 
     786             :                             // use fast range test first
     787     1821932 :                             if(aAllRange.overlaps(aCompareRange))
     788             :                             {
     789             :                                 // check for start point inside thought trapezoid
     790       69366 :                                 if(fTools::more(aCompare.getStart().getY(), aLeft.getStart().getY()))
     791             :                                 {
     792             :                                     // calculate the two possible split points at compare's Y
     793       66402 :                                     const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aCompare.getStart().getY()));
     794      132804 :                                     const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aCompare.getStart().getY()));
     795             : 
     796             :                                     // check for start point of aCompare being inside thought
     797             :                                     // trapezoid
     798      101705 :                                     if(aCompare.getStart().getX() >= aSplitLeft.getX() &&
     799       35303 :                                         aCompare.getStart().getX() <= aSplitRight.getX())
     800             :                                     {
     801             :                                         // is inside, correct and restart loop
     802          60 :                                         B2DPoint* pNewLeft = maNewPoints.allocatePoint(aSplitLeft);
     803             : 
     804          60 :                                         if(splitEdgeAtGivenPoint(aLeft, *pNewLeft, aCurrent))
     805             :                                         {
     806          60 :                                             bDone = true;
     807             :                                         }
     808             :                                         else
     809             :                                         {
     810           0 :                                             maNewPoints.freeIfLast(pNewLeft);
     811             :                                         }
     812             : 
     813          60 :                                         B2DPoint* pNewRight = maNewPoints.allocatePoint(aSplitRight);
     814             : 
     815          60 :                                         if(splitEdgeAtGivenPoint(aRight, *pNewRight, aCurrent))
     816             :                                         {
     817          60 :                                             bDone = true;
     818             :                                         }
     819             :                                         else
     820             :                                         {
     821           0 :                                             maNewPoints.freeIfLast(pNewRight);
     822             :                                         }
     823       66402 :                                     }
     824             :                                 }
     825             : 
     826       69366 :                                 if(!bDone && aLeftRange.overlaps(aCompareRange))
     827             :                                 {
     828             :                                     // test for concrete cut of compare edge with left edge
     829       59499 :                                     bDone = testAndCorrectEdgeIntersection(aLeft, aCompare, aCurrent);
     830             :                                 }
     831             : 
     832       69366 :                                 if(!bDone && aRightRange.overlaps(aCompareRange))
     833             :                                 {
     834             :                                     // test for concrete cut of compare edge with Right edge
     835       54169 :                                     bDone = testAndCorrectEdgeIntersection(aRight, aCompare, aCurrent);
     836             :                                 }
     837             :                             }
     838             :                         }
     839     1830693 :                         while(!bDone
     840     5476301 :                             && aLoop != maTrDeEdgeEntries.end()
     841     7314875 :                             && fTools::less(aLoop->getStart().getY(), aLeftEnd.getY()));
     842             : 
     843       24126 :                         if(bDone)
     844             :                         {
     845             :                             // something needed to be changed; start next loop
     846        7889 :                             continue;
     847             :                         }
     848             :                     }
     849             : 
     850             :                     // when we get here, the intended trapezoid can be used. It needs to
     851             :                     // be corrected possibly (if prepared); but this is no reason not to
     852             :                     // use it in the same loop iteration
     853       16903 :                     if(!bEndOnSameLine)
     854             :                     {
     855       11182 :                         if(bLeftIsLonger)
     856             :                         {
     857        5531 :                             B2DPoint* pNewPoint = maNewPoints.allocatePoint(aLeftEnd);
     858             : 
     859        5531 :                             if(!splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
     860             :                             {
     861           0 :                                 maNewPoints.freeIfLast(pNewPoint);
     862             :                             }
     863             :                         }
     864             :                         else
     865             :                         {
     866        5651 :                             B2DPoint* pNewPoint = maNewPoints.allocatePoint(aRightEnd);
     867             : 
     868        5651 :                             if(!splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
     869             :                             {
     870           0 :                                 maNewPoints.freeIfLast(pNewPoint);
     871             :                             }
     872             :                         }
     873             :                     }
     874             : 
     875             :                     // the two edges start at the same Y, they use the same DeltaY, they
     876             :                     // do not cut themselves and not any other edge in range. Create a
     877             :                     // B2DTrapezoid and consume both edges
     878             :                     ro_Result.push_back(
     879             :                         B2DTrapezoid(
     880       16903 :                             aLeft.getStart().getX(),
     881       16903 :                             aRight.getStart().getX(),
     882       16903 :                             aLeft.getStart().getY(),
     883       16903 :                             aLeftEnd.getX(),
     884       16903 :                             aRightEnd.getX(),
     885      101418 :                             aLeftEnd.getY()));
     886             : 
     887       16903 :                     maTrDeEdgeEntries.pop_front();
     888       16903 :                     maTrDeEdgeEntries.pop_front();
     889       16903 :                 }
     890           1 :             }
     891             :         };
     892             :     } // end of anonymous namespace
     893             : } // end of namespace basegfx
     894             : 
     895             : namespace basegfx
     896             : {
     897       16903 :     B2DTrapezoid::B2DTrapezoid(
     898             :         const double& rfTopXLeft,
     899             :         const double& rfTopXRight,
     900             :         const double& rfTopY,
     901             :         const double& rfBottomXLeft,
     902             :         const double& rfBottomXRight,
     903             :         const double& rfBottomY)
     904             :     :   mfTopXLeft(rfTopXLeft),
     905             :         mfTopXRight(rfTopXRight),
     906             :         mfTopY(rfTopY),
     907             :         mfBottomXLeft(rfBottomXLeft),
     908             :         mfBottomXRight(rfBottomXRight),
     909       16903 :         mfBottomY(rfBottomY)
     910             :     {
     911             :         // guarantee mfTopXRight >= mfTopXLeft
     912       16903 :         if(mfTopXLeft > mfTopXRight)
     913             :         {
     914           7 :             std::swap(mfTopXLeft, mfTopXRight);
     915             :         }
     916             : 
     917             :         // guarantee mfBottomXRight >= mfBottomXLeft
     918       16903 :         if(mfBottomXLeft > mfBottomXRight)
     919             :         {
     920         212 :             std::swap(mfBottomXLeft, mfBottomXRight);
     921             :         }
     922             : 
     923             :         // guarantee mfBottomY >= mfTopY
     924       16903 :         if(mfTopY > mfBottomY)
     925             :         {
     926           0 :             std::swap(mfTopY, mfBottomY);
     927           0 :             std::swap(mfTopXLeft, mfBottomXLeft);
     928           0 :             std::swap(mfTopXRight, mfBottomXRight);
     929             :         }
     930       16903 :     }
     931             : 
     932           0 :     B2DPolygon B2DTrapezoid::getB2DPolygon() const
     933             :     {
     934           0 :         B2DPolygon aRetval;
     935             : 
     936           0 :         aRetval.append(B2DPoint(getTopXLeft(), getTopY()));
     937           0 :         aRetval.append(B2DPoint(getTopXRight(), getTopY()));
     938           0 :         aRetval.append(B2DPoint(getBottomXRight(), getBottomY()));
     939           0 :         aRetval.append(B2DPoint(getBottomXLeft(), getBottomY()));
     940           0 :         aRetval.setClosed(true);
     941             : 
     942           0 :         return aRetval;
     943             :     }
     944             : } // end of namespace basegfx
     945             : 
     946             : namespace basegfx
     947             : {
     948             :     namespace tools
     949             :     {
     950             :         // convert Source tools::PolyPolygon to trapezoids
     951           1 :         void trapezoidSubdivide(B2DTrapezoidVector& ro_Result, const B2DPolyPolygon& rSourcePolyPolygon)
     952             :         {
     953           1 :             trapezoidhelper::TrapezoidSubdivider aTrapezoidSubdivider(rSourcePolyPolygon);
     954             : 
     955           1 :             aTrapezoidSubdivider.Subdivide(ro_Result);
     956           1 :         }
     957             : 
     958           0 :         void createLineTrapezoidFromEdge(
     959             :             B2DTrapezoidVector& ro_Result,
     960             :             const B2DPoint& rPointA,
     961             :             const B2DPoint& rPointB,
     962             :             double fLineWidth)
     963             :         {
     964           0 :             if(fTools::lessOrEqual(fLineWidth, 0.0))
     965             :             {
     966             :                 // no line witdh
     967           0 :                 return;
     968             :             }
     969             : 
     970           0 :             if(rPointA.equal(rPointB))
     971             :             {
     972             :                 // points are equal, no edge
     973           0 :                 return;
     974             :             }
     975             : 
     976           0 :             const double fHalfLineWidth(0.5 * fLineWidth);
     977             : 
     978           0 :             if(fTools::equal(rPointA.getX(), rPointB.getX()))
     979             :             {
     980             :                 // vertical line
     981           0 :                 const double fLeftX(rPointA.getX() - fHalfLineWidth);
     982           0 :                 const double fRightX(rPointA.getX() + fHalfLineWidth);
     983             : 
     984             :                 ro_Result.push_back(
     985             :                     B2DTrapezoid(
     986             :                         fLeftX,
     987             :                         fRightX,
     988           0 :                         std::min(rPointA.getY(), rPointB.getY()),
     989             :                         fLeftX,
     990             :                         fRightX,
     991           0 :                         std::max(rPointA.getY(), rPointB.getY())));
     992             :             }
     993           0 :             else if(fTools::equal(rPointA.getY(), rPointB.getY()))
     994             :             {
     995             :                 // horizontal line
     996           0 :                 const double fLeftX(std::min(rPointA.getX(), rPointB.getX()));
     997           0 :                 const double fRightX(std::max(rPointA.getX(), rPointB.getX()));
     998             : 
     999             :                 ro_Result.push_back(
    1000             :                     B2DTrapezoid(
    1001             :                         fLeftX,
    1002             :                         fRightX,
    1003           0 :                         rPointA.getY() - fHalfLineWidth,
    1004             :                         fLeftX,
    1005             :                         fRightX,
    1006           0 :                         rPointA.getY() + fHalfLineWidth));
    1007             :             }
    1008             :             else
    1009             :             {
    1010             :                 // diagonal line
    1011             :                 // create perpendicular vector
    1012           0 :                 const B2DVector aDelta(rPointB - rPointA);
    1013           0 :                 B2DVector aPerpendicular(-aDelta.getY(), aDelta.getX());
    1014           0 :                 aPerpendicular.setLength(fHalfLineWidth);
    1015             : 
    1016             :                 // create StartLow, StartHigh, EndLow and EndHigh
    1017           0 :                 const B2DPoint aStartLow(rPointA + aPerpendicular);
    1018           0 :                 const B2DPoint aStartHigh(rPointA - aPerpendicular);
    1019           0 :                 const B2DPoint aEndHigh(rPointB - aPerpendicular);
    1020           0 :                 const B2DPoint aEndLow(rPointB + aPerpendicular);
    1021             : 
    1022             :                 // create EdgeEntries
    1023           0 :                 basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries;
    1024             : 
    1025           0 :                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow, &aStartHigh, 0));
    1026           0 :                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh, &aEndHigh, 0));
    1027           0 :                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh, &aEndLow, 0));
    1028           0 :                 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow, &aStartLow, 0));
    1029           0 :                 aTrDeEdgeEntries.sort();
    1030             : 
    1031             :                 // here we know we have exactly four edges, and they do not cut, touch or
    1032             :                 // intersect. This makes processing much easier. Get the first two as start
    1033             :                 // edges for the thought trapezoid
    1034           0 :                 basegfx::trapezoidhelper::TrDeEdgeEntries::iterator aCurrent(aTrDeEdgeEntries.begin());
    1035           0 :                 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft(*aCurrent++);
    1036           0 :                 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight(*aCurrent++);
    1037           0 :                 const bool bEndOnSameLine(fTools::equal(aLeft.getEnd().getY(), aRight.getEnd().getY()));
    1038             : 
    1039           0 :                 if(bEndOnSameLine)
    1040             :                 {
    1041             :                     // create two triangle trapezoids
    1042             :                     ro_Result.push_back(
    1043             :                         B2DTrapezoid(
    1044           0 :                             aLeft.getStart().getX(),
    1045           0 :                             aRight.getStart().getX(),
    1046           0 :                             aLeft.getStart().getY(),
    1047           0 :                             aLeft.getEnd().getX(),
    1048           0 :                             aRight.getEnd().getX(),
    1049           0 :                             aLeft.getEnd().getY()));
    1050             : 
    1051           0 :                     basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
    1052           0 :                     basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
    1053             : 
    1054             :                     ro_Result.push_back(
    1055             :                         B2DTrapezoid(
    1056           0 :                             aLeft2.getStart().getX(),
    1057           0 :                             aRight2.getStart().getX(),
    1058           0 :                             aLeft2.getStart().getY(),
    1059           0 :                             aLeft2.getEnd().getX(),
    1060           0 :                             aRight2.getEnd().getX(),
    1061           0 :                             aLeft2.getEnd().getY()));
    1062             :                 }
    1063             :                 else
    1064             :                 {
    1065             :                     // create three trapezoids. Check which edge is longer and
    1066             :                     // correct accordingly
    1067           0 :                     const bool bLeftIsLonger(fTools::more(aLeft.getEnd().getY(), aRight.getEnd().getY()));
    1068             : 
    1069           0 :                     if(bLeftIsLonger)
    1070             :                     {
    1071           0 :                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
    1072           0 :                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
    1073           0 :                         const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aRight.getEnd().getY()));
    1074           0 :                         const B2DPoint aSplitRight(aRight2.getCutPointForGivenY(aLeft.getEnd().getY()));
    1075             : 
    1076             :                         ro_Result.push_back(
    1077             :                             B2DTrapezoid(
    1078           0 :                                 aLeft.getStart().getX(),
    1079           0 :                                 aRight.getStart().getX(),
    1080           0 :                                 aLeft.getStart().getY(),
    1081           0 :                                 aSplitLeft.getX(),
    1082           0 :                                 aRight.getEnd().getX(),
    1083           0 :                                 aRight.getEnd().getY()));
    1084             : 
    1085             :                         ro_Result.push_back(
    1086             :                             B2DTrapezoid(
    1087           0 :                                 aSplitLeft.getX(),
    1088           0 :                                 aRight.getEnd().getX(),
    1089           0 :                                 aRight.getEnd().getY(),
    1090           0 :                                 aLeft2.getStart().getX(),
    1091           0 :                                 aSplitRight.getX(),
    1092           0 :                                 aLeft2.getStart().getY()));
    1093             : 
    1094             :                         ro_Result.push_back(
    1095             :                             B2DTrapezoid(
    1096           0 :                                 aLeft2.getStart().getX(),
    1097           0 :                                 aSplitRight.getX(),
    1098           0 :                                 aLeft2.getStart().getY(),
    1099           0 :                                 aLeft2.getEnd().getX(),
    1100           0 :                                 aRight2.getEnd().getX(),
    1101           0 :                                 aLeft2.getEnd().getY()));
    1102             :                     }
    1103             :                     else
    1104             :                     {
    1105           0 :                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
    1106           0 :                         basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
    1107           0 :                         const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aLeft.getEnd().getY()));
    1108           0 :                         const B2DPoint aSplitLeft(aLeft2.getCutPointForGivenY(aRight.getEnd().getY()));
    1109             : 
    1110             :                         ro_Result.push_back(
    1111             :                             B2DTrapezoid(
    1112           0 :                                 aLeft.getStart().getX(),
    1113           0 :                                 aRight.getStart().getX(),
    1114           0 :                                 aLeft.getStart().getY(),
    1115           0 :                                 aLeft.getEnd().getX(),
    1116           0 :                                 aSplitRight.getX(),
    1117           0 :                                 aLeft.getEnd().getY()));
    1118             : 
    1119             :                         ro_Result.push_back(
    1120             :                             B2DTrapezoid(
    1121           0 :                                 aLeft.getEnd().getX(),
    1122           0 :                                 aSplitRight.getX(),
    1123           0 :                                 aLeft.getEnd().getY(),
    1124           0 :                                 aSplitLeft.getX(),
    1125           0 :                                 aRight.getEnd().getX(),
    1126           0 :                                 aRight2.getStart().getY()));
    1127             : 
    1128             :                         ro_Result.push_back(
    1129             :                             B2DTrapezoid(
    1130           0 :                                 aSplitLeft.getX(),
    1131           0 :                                 aRight.getEnd().getX(),
    1132           0 :                                 aRight2.getStart().getY(),
    1133           0 :                                 aLeft2.getEnd().getX(),
    1134           0 :                                 aRight2.getEnd().getX(),
    1135           0 :                                 aLeft2.getEnd().getY()));
    1136             :                     }
    1137           0 :                 }
    1138             :             }
    1139             :         }
    1140             : 
    1141           0 :         void createLineTrapezoidFromB2DPolygon(
    1142             :             B2DTrapezoidVector& ro_Result,
    1143             :             const B2DPolygon& rPolygon,
    1144             :             double fLineWidth)
    1145             :         {
    1146           0 :             if(fTools::lessOrEqual(fLineWidth, 0.0))
    1147             :             {
    1148           0 :                 return;
    1149             :             }
    1150             : 
    1151             :             // ensure there are no curves used
    1152           0 :             B2DPolygon aSource(rPolygon);
    1153             : 
    1154           0 :             if(aSource.areControlPointsUsed())
    1155             :             {
    1156           0 :             const double fPrecisionFactor = 0.25;
    1157           0 :                 aSource = adaptiveSubdivideByDistance( aSource, fLineWidth * fPrecisionFactor );
    1158             :             }
    1159             : 
    1160           0 :             const sal_uInt32 nPointCount(aSource.count());
    1161             : 
    1162           0 :             if(!nPointCount)
    1163             :             {
    1164           0 :                 return;
    1165             :             }
    1166             : 
    1167           0 :             const sal_uInt32 nEdgeCount(aSource.isClosed() ? nPointCount : nPointCount - 1);
    1168           0 :             B2DPoint aCurrent(aSource.getB2DPoint(0));
    1169             : 
    1170           0 :             ro_Result.reserve(ro_Result.size() + (3 * nEdgeCount));
    1171             : 
    1172           0 :             for(sal_uInt32 a(0); a < nEdgeCount; a++)
    1173             :             {
    1174           0 :                 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
    1175           0 :                 const B2DPoint aNext(aSource.getB2DPoint(nNextIndex));
    1176             : 
    1177           0 :                 createLineTrapezoidFromEdge(ro_Result, aCurrent, aNext, fLineWidth);
    1178           0 :                 aCurrent = aNext;
    1179           0 :             }
    1180             :         }
    1181             : 
    1182           0 :         void createLineTrapezoidFromB2DPolyPolygon(
    1183             :             B2DTrapezoidVector& ro_Result,
    1184             :             const B2DPolyPolygon& rPolyPolygon,
    1185             :             double fLineWidth)
    1186             :         {
    1187           0 :             if(fTools::lessOrEqual(fLineWidth, 0.0))
    1188             :             {
    1189           0 :                 return;
    1190             :             }
    1191             : 
    1192             :             // ensure there are no curves used
    1193           0 :             B2DPolyPolygon aSource(rPolyPolygon);
    1194             : 
    1195           0 :             if(aSource.areControlPointsUsed())
    1196             :             {
    1197           0 :                 aSource = aSource.getDefaultAdaptiveSubdivision();
    1198             :             }
    1199             : 
    1200           0 :             const sal_uInt32 nCount(aSource.count());
    1201             : 
    1202           0 :             if(!nCount)
    1203             :             {
    1204           0 :                 return;
    1205             :             }
    1206             : 
    1207           0 :             for(sal_uInt32 a(0); a < nCount; a++)
    1208             :             {
    1209             :                 createLineTrapezoidFromB2DPolygon(
    1210             :                     ro_Result,
    1211             :                     aSource.getB2DPolygon(a),
    1212           0 :                     fLineWidth);
    1213           0 :             }
    1214             :         }
    1215             : 
    1216             :     } // end of namespace tools
    1217             : } // end of namespace basegfx
    1218             : 
    1219             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11