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