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