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