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