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