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