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