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