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