fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / basegfx / source / polygon / b2dpolygontools.cxx
blob24affdfb84c9b3fd72b7dbbd595819d4b6b965e0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
36 #include <numeric>
37 #include <limits>
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)
48 //////////////////////////////////////////////////////////////////////////////
50 namespace basegfx
52 namespace tools
54 void openWithGeometryChange(B2DPolygon& rCandidate)
56 if(rCandidate.isClosed())
58 if(rCandidate.count())
60 rCandidate.append(rCandidate.getB2DPoint(0));
62 if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0))
64 rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0));
65 rCandidate.resetPrevControlPoint(0);
69 rCandidate.setClosed(false);
73 void closeWithGeometryChange(B2DPolygon& rCandidate)
75 if(!rCandidate.isClosed())
77 while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
79 if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1))
81 rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1));
84 rCandidate.remove(rCandidate.count() - 1);
87 rCandidate.setClosed(true);
91 void checkClosed(B2DPolygon& rCandidate)
93 // #i80172# Removed unnecessary assertion
94 // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");
96 if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
98 closeWithGeometryChange(rCandidate);
102 // Get successor and predecessor indices. Returning the same index means there
103 // is none. Same for successor.
104 sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
106 OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
108 if(nIndex)
110 return nIndex - 1L;
112 else if(rCandidate.count())
114 return rCandidate.count() - 1L;
116 else
118 return nIndex;
122 sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
124 OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
126 if(nIndex + 1L < rCandidate.count())
128 return nIndex + 1L;
130 else if(nIndex + 1L == rCandidate.count())
132 return 0L;
134 else
136 return nIndex;
140 B2VectorOrientation getOrientation(const B2DPolygon& rCandidate)
142 B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
144 if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed())
146 const double fSignedArea(getSignedArea(rCandidate));
148 if(fTools::equalZero(fSignedArea))
150 // ORIENTATION_NEUTRAL, already set
152 if(fSignedArea > 0.0)
154 eRetval = ORIENTATION_POSITIVE;
156 else if(fSignedArea < 0.0)
158 eRetval = ORIENTATION_NEGATIVE;
162 return eRetval;
165 B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
167 return rCandidate.getContinuityInPoint(nIndex);
170 B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound)
172 if(rCandidate.areControlPointsUsed())
174 const sal_uInt32 nPointCount(rCandidate.count());
175 B2DPolygon aRetval;
177 if(nPointCount)
179 // prepare edge-oriented loop
180 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
181 B2DCubicBezier aBezier;
182 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
184 // perf: try to avoid too many realloctions by guessing the result's pointcount
185 aRetval.reserve(nPointCount*4);
187 // add start point (always)
188 aRetval.append(aBezier.getStartPoint());
190 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
192 // get next and control points
193 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
194 aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
195 aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
196 aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
197 aBezier.testAndSolveTrivialBezier();
199 if(aBezier.isBezier())
201 // add curved edge and generate DistanceBound
202 double fBound(0.0);
204 if(0.0 == fDistanceBound)
206 // If not set, use B2DCubicBezier functionality to guess a rough value
207 const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0);
209 // take 1/100th of the rough curve length
210 fBound = fRoughLength * 0.01;
212 else
214 // use given bound value
215 fBound = fDistanceBound;
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 if(fBound < 0.01)
222 fBound = 0.01;
225 // call adaptive subdivide which adds edges to aRetval accordingly
226 aBezier.adaptiveSubdivideByDistance(aRetval, fBound);
228 else
230 // add non-curved edge
231 aRetval.append(aBezier.getEndPoint());
234 // prepare next step
235 aBezier.setStartPoint(aBezier.getEndPoint());
238 if(rCandidate.isClosed())
240 // set closed flag and correct last point (which is added double now).
241 closeWithGeometryChange(aRetval);
245 return aRetval;
247 else
249 return rCandidate;
253 B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound)
255 if(rCandidate.areControlPointsUsed())
257 const sal_uInt32 nPointCount(rCandidate.count());
258 B2DPolygon aRetval;
260 if(nPointCount)
262 // prepare edge-oriented loop
263 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
264 B2DCubicBezier aBezier;
265 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
267 // perf: try to avoid too many realloctions by guessing the result's pointcount
268 aRetval.reserve(nPointCount*4);
270 // add start point (always)
271 aRetval.append(aBezier.getStartPoint());
273 // #i37443# prepare convenient AngleBound if none was given
274 if(0.0 == fAngleBound)
276 #ifdef DBG_UTIL
277 fAngleBound = fAngleBoundStartValue;
278 #else
279 fAngleBound = ANGLE_BOUND_START_VALUE;
280 #endif
282 else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE))
284 fAngleBound = 0.1;
287 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
289 // get next and control points
290 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
291 aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
292 aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
293 aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
294 aBezier.testAndSolveTrivialBezier();
296 if(aBezier.isBezier())
298 // call adaptive subdivide
299 aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true);
301 else
303 // add non-curved edge
304 aRetval.append(aBezier.getEndPoint());
307 // prepare next step
308 aBezier.setStartPoint(aBezier.getEndPoint());
311 if(rCandidate.isClosed())
313 // set closed flag and correct last point (which is added double now).
314 closeWithGeometryChange(aRetval);
318 return aRetval;
320 else
322 return rCandidate;
326 B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount)
328 if(rCandidate.areControlPointsUsed())
330 const sal_uInt32 nPointCount(rCandidate.count());
331 B2DPolygon aRetval;
333 if(nPointCount)
335 // prepare edge-oriented loop
336 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
337 B2DCubicBezier aBezier;
338 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
340 // perf: try to avoid too many realloctions by guessing the result's pointcount
341 aRetval.reserve(nPointCount*4);
343 // add start point (always)
344 aRetval.append(aBezier.getStartPoint());
346 // #i37443# prepare convenient count if none was given
347 if(0L == nCount)
349 nCount = COUNT_SUBDIVIDE_DEFAULT;
352 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
354 // get next and control points
355 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
356 aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
357 aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
358 aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
359 aBezier.testAndSolveTrivialBezier();
361 if(aBezier.isBezier())
363 // call adaptive subdivide
364 aBezier.adaptiveSubdivideByCount(aRetval, nCount);
366 else
368 // add non-curved edge
369 aRetval.append(aBezier.getEndPoint());
372 // prepare next step
373 aBezier.setStartPoint(aBezier.getEndPoint());
376 if(rCandidate.isClosed())
378 // set closed flag and correct last point (which is added double now).
379 closeWithGeometryChange(aRetval);
383 return aRetval;
385 else
387 return rCandidate;
391 bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
393 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
395 if(bWithBorder && isPointOnPolygon(aCandidate, rPoint, true))
397 return true;
399 else
401 bool bRetval(false);
402 const sal_uInt32 nPointCount(aCandidate.count());
404 if(nPointCount)
406 B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1L));
408 for(sal_uInt32 a(0L); a < nPointCount; a++)
410 const B2DPoint aPreviousPoint(aCurrentPoint);
411 aCurrentPoint = aCandidate.getB2DPoint(a);
413 // cross-over in Y?
414 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
415 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
417 if(bCompYA != bCompYB)
419 // cross-over in X?
420 const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
421 const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
423 if(bCompXA == bCompXB)
425 if(bCompXA)
427 bRetval = !bRetval;
430 else
432 const double fCompare(
433 aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
434 (aPreviousPoint.getX() - aCurrentPoint.getX()) /
435 (aPreviousPoint.getY() - aCurrentPoint.getY()));
437 if(fTools::more(fCompare, rPoint.getX()))
439 bRetval = !bRetval;
446 return bRetval;
450 bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder)
452 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
453 const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon);
454 const sal_uInt32 nPointCount(aPolygon.count());
456 for(sal_uInt32 a(0L); a < nPointCount; a++)
458 const B2DPoint aTestPoint(aPolygon.getB2DPoint(a));
460 if(!isInside(aCandidate, aTestPoint, bWithBorder))
462 return false;
466 return true;
469 B2DRange getRange(const B2DPolygon& rCandidate)
471 // changed to use internally buffered version at B2DPolygon
472 return rCandidate.getB2DRange();
475 double getSignedArea(const B2DPolygon& rCandidate)
477 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
478 double fRetval(0.0);
479 const sal_uInt32 nPointCount(aCandidate.count());
481 if(nPointCount > 2)
483 for(sal_uInt32 a(0L); a < nPointCount; a++)
485 const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L));
486 const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a));
488 fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
489 fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
492 fRetval /= 2.0;
494 // correct to zero if small enough. Also test the quadratic
495 // of the result since the precision is near quadratic due to
496 // the algorithm
497 if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval))
499 fRetval = 0.0;
503 return fRetval;
506 double getArea(const B2DPolygon& rCandidate)
508 double fRetval(0.0);
510 if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
512 fRetval = getSignedArea(rCandidate);
513 const double fZero(0.0);
515 if(fTools::less(fRetval, fZero))
517 fRetval = -fRetval;
521 return fRetval;
524 double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
526 const sal_uInt32 nPointCount(rCandidate.count());
527 OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)");
528 double fRetval(0.0);
530 if(nPointCount)
532 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
534 if(rCandidate.areControlPointsUsed())
536 B2DCubicBezier aEdge;
538 aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex));
539 aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex));
540 aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
541 aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
543 fRetval = aEdge.getLength();
545 else
547 const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex));
548 const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
550 fRetval = B2DVector(aNext - aCurrent).getLength();
554 return fRetval;
557 double getLength(const B2DPolygon& rCandidate)
559 double fRetval(0.0);
560 const sal_uInt32 nPointCount(rCandidate.count());
562 if(nPointCount)
564 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
566 if(rCandidate.areControlPointsUsed())
568 B2DCubicBezier aEdge;
569 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
571 for(sal_uInt32 a(0); a < nEdgeCount; a++)
573 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
574 aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
575 aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
576 aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
578 fRetval += aEdge.getLength();
579 aEdge.setStartPoint(aEdge.getEndPoint());
582 else
584 B2DPoint aCurrent(rCandidate.getB2DPoint(0));
586 for(sal_uInt32 a(0); a < nEdgeCount; a++)
588 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
589 const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
591 fRetval += B2DVector(aNext - aCurrent).getLength();
592 aCurrent = aNext;
597 return fRetval;
600 B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength)
602 B2DPoint aRetval;
603 const sal_uInt32 nPointCount(rCandidate.count());
605 if( 1L == nPointCount )
607 // only one point (i.e. no edge) - simply take that point
608 aRetval = rCandidate.getB2DPoint(0);
610 else if(nPointCount > 1L)
612 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
613 sal_uInt32 nIndex(0L);
614 bool bIndexDone(false);
616 // get length if not given
617 if(fTools::equalZero(fLength))
619 fLength = getLength(rCandidate);
622 if(fTools::less(fDistance, 0.0))
624 // handle fDistance < 0.0
625 if(rCandidate.isClosed())
627 // if fDistance < 0.0 increment with multiple of fLength
628 sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
629 fDistance += double(nCount + 1L) * fLength;
631 else
633 // crop to polygon start
634 fDistance = 0.0;
635 bIndexDone = true;
638 else if(fTools::moreOrEqual(fDistance, fLength))
640 // handle fDistance >= fLength
641 if(rCandidate.isClosed())
643 // if fDistance >= fLength decrement with multiple of fLength
644 sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
645 fDistance -= (double)(nCount) * fLength;
647 else
649 // crop to polygon end
650 fDistance = 0.0;
651 nIndex = nEdgeCount;
652 bIndexDone = true;
656 // look for correct index. fDistance is now [0.0 .. fLength[
657 double fEdgeLength(getEdgeLength(rCandidate, nIndex));
659 while(!bIndexDone)
661 // edge found must be on the half-open range
662 // [0,fEdgeLength).
663 // Note that in theory, we cannot move beyond
664 // the last polygon point, since fDistance>=fLength
665 // is checked above. Unfortunately, with floating-
666 // point calculations, this case might happen.
667 // Handled by nIndex check below
668 if(nIndex < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength))
670 // go to next edge
671 fDistance -= fEdgeLength;
672 fEdgeLength = getEdgeLength(rCandidate, ++nIndex);
674 else
676 // it's on this edge, stop
677 bIndexDone = true;
681 // get the point using nIndex
682 aRetval = rCandidate.getB2DPoint(nIndex);
684 // if fDistance != 0.0, move that length on the edge. The edge
685 // length is in fEdgeLength.
686 if(!fTools::equalZero(fDistance))
688 if(fTools::moreOrEqual(fDistance, fEdgeLength))
690 // end point of choosen edge
691 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
692 aRetval = rCandidate.getB2DPoint(nNextIndex);
694 else if(fTools::equalZero(fDistance))
696 // start point of choosen edge
697 aRetval = aRetval;
699 else
701 // inside edge
702 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
703 const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
704 bool bDone(false);
706 // add calculated average value to the return value
707 if(rCandidate.areControlPointsUsed())
709 // get as bezier segment
710 const B2DCubicBezier aBezierSegment(
711 aRetval, rCandidate.getNextControlPoint(nIndex),
712 rCandidate.getPrevControlPoint(nNextIndex), aNextPoint);
714 if(aBezierSegment.isBezier())
716 // use B2DCubicBezierHelper to bridge the non-linear gap between
717 // length and bezier distances
718 const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
719 const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance));
721 aRetval = aBezierSegment.interpolatePoint(fBezierDistance);
722 bDone = true;
726 if(!bDone)
728 const double fRelativeInEdge(fDistance / fEdgeLength);
729 aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge);
735 return aRetval;
738 B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength)
740 // get length if not given
741 if(fTools::equalZero(fLength))
743 fLength = getLength(rCandidate);
746 // multiply fDistance with real length to get absolute position and
747 // use getPositionAbsolute
748 return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
751 B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
753 const sal_uInt32 nPointCount(rCandidate.count());
755 if(nPointCount)
757 // get length if not given
758 if(fTools::equalZero(fLength))
760 fLength = getLength(rCandidate);
763 // test and correct fFrom
764 if(fTools::less(fFrom, 0.0))
766 fFrom = 0.0;
769 // test and correct fTo
770 if(fTools::more(fTo, fLength))
772 fTo = fLength;
775 // test and correct relationship of fFrom, fTo
776 if(fTools::more(fFrom, fTo))
778 fFrom = fTo = (fFrom + fTo) / 2.0;
781 if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength))
783 // no change, result is the whole polygon
784 return rCandidate;
786 else
788 B2DPolygon aRetval;
789 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
790 double fPositionOfStart(0.0);
791 bool bStartDone(false);
792 bool bEndDone(false);
794 for(sal_uInt32 a(0L); !(bStartDone && bEndDone) && a < nEdgeCount; a++)
796 const double fEdgeLength(getEdgeLength(rCandidate, a));
798 if(!bStartDone)
800 if(fTools::equalZero(fFrom))
802 aRetval.append(rCandidate.getB2DPoint(a));
804 if(rCandidate.areControlPointsUsed())
806 aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
809 bStartDone = true;
811 else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength))
813 // calculate and add start point
814 if(fTools::equalZero(fEdgeLength))
816 aRetval.append(rCandidate.getB2DPoint(a));
818 if(rCandidate.areControlPointsUsed())
820 aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
823 else
825 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
826 const B2DPoint aStart(rCandidate.getB2DPoint(a));
827 const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
828 bool bDone(false);
830 if(rCandidate.areControlPointsUsed())
832 const B2DCubicBezier aBezierSegment(
833 aStart, rCandidate.getNextControlPoint(a),
834 rCandidate.getPrevControlPoint(nNextIndex), aEnd);
836 if(aBezierSegment.isBezier())
838 // use B2DCubicBezierHelper to bridge the non-linear gap between
839 // length and bezier distances
840 const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
841 const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart));
842 B2DCubicBezier aRight;
844 aBezierSegment.split(fBezierDistance, 0, &aRight);
845 aRetval.append(aRight.getStartPoint());
846 aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA());
847 bDone = true;
851 if(!bDone)
853 const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength);
854 aRetval.append(interpolate(aStart, aEnd, fRelValue));
858 bStartDone = true;
860 // if same point, end is done, too.
861 if(fFrom == fTo)
863 bEndDone = true;
868 if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength))
870 // calculate and add end point
871 if(fTools::equalZero(fEdgeLength))
873 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
874 aRetval.append(rCandidate.getB2DPoint(nNextIndex));
876 if(rCandidate.areControlPointsUsed())
878 aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
881 else
883 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
884 const B2DPoint aStart(rCandidate.getB2DPoint(a));
885 const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
886 bool bDone(false);
888 if(rCandidate.areControlPointsUsed())
890 const B2DCubicBezier aBezierSegment(
891 aStart, rCandidate.getNextControlPoint(a),
892 rCandidate.getPrevControlPoint(nNextIndex), aEnd);
894 if(aBezierSegment.isBezier())
896 // use B2DCubicBezierHelper to bridge the non-linear gap between
897 // length and bezier distances
898 const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
899 const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart));
900 B2DCubicBezier aLeft;
902 aBezierSegment.split(fBezierDistance, &aLeft, 0);
903 aRetval.append(aLeft.getEndPoint());
904 aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB());
905 bDone = true;
909 if(!bDone)
911 const double fRelValue((fTo - fPositionOfStart) / fEdgeLength);
912 aRetval.append(interpolate(aStart, aEnd, fRelValue));
916 bEndDone = true;
919 if(!bEndDone)
921 if(bStartDone)
923 // add segments end point
924 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
925 aRetval.append(rCandidate.getB2DPoint(nNextIndex));
927 if(rCandidate.areControlPointsUsed())
929 aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
930 aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex));
934 // increment fPositionOfStart
935 fPositionOfStart += fEdgeLength;
938 return aRetval;
941 else
943 return rCandidate;
947 CutFlagValue findCut(
948 const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
949 const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
950 CutFlagValue aCutFlags,
951 double* pCut1, double* pCut2)
953 CutFlagValue aRetval(CUTFLAG_NONE);
954 double fCut1(0.0);
955 double fCut2(0.0);
956 bool bFinished(!((bool)(aCutFlags & CUTFLAG_ALL)));
958 // test for same points?
959 if(!bFinished
960 && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END1))
961 && (aCutFlags & (CUTFLAG_START2|CUTFLAG_END2)))
963 // same startpoint?
964 if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_START2)) == (CUTFLAG_START1|CUTFLAG_START2))
966 if(rEdge1Start.equal(rEdge2Start))
968 bFinished = true;
969 aRetval = (CUTFLAG_START1|CUTFLAG_START2);
973 // same endpoint?
974 if(!bFinished && (aCutFlags & (CUTFLAG_END1|CUTFLAG_END2)) == (CUTFLAG_END1|CUTFLAG_END2))
976 const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
977 const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
979 if(aEnd1.equal(aEnd2))
981 bFinished = true;
982 aRetval = (CUTFLAG_END1|CUTFLAG_END2);
983 fCut1 = fCut2 = 1.0;
987 // startpoint1 == endpoint2?
988 if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END2)) == (CUTFLAG_START1|CUTFLAG_END2))
990 const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
992 if(rEdge1Start.equal(aEnd2))
994 bFinished = true;
995 aRetval = (CUTFLAG_START1|CUTFLAG_END2);
996 fCut1 = 0.0;
997 fCut2 = 1.0;
1001 // startpoint2 == endpoint1?
1002 if(!bFinished&& (aCutFlags & (CUTFLAG_START2|CUTFLAG_END1)) == (CUTFLAG_START2|CUTFLAG_END1))
1004 const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
1006 if(rEdge2Start.equal(aEnd1))
1008 bFinished = true;
1009 aRetval = (CUTFLAG_START2|CUTFLAG_END1);
1010 fCut1 = 1.0;
1011 fCut2 = 0.0;
1016 if(!bFinished && (aCutFlags & CUTFLAG_LINE))
1018 if(!bFinished && (aCutFlags & CUTFLAG_START1))
1020 // start1 on line 2 ?
1021 if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2))
1023 bFinished = true;
1024 aRetval = (CUTFLAG_LINE|CUTFLAG_START1);
1028 if(!bFinished && (aCutFlags & CUTFLAG_START2))
1030 // start2 on line 1 ?
1031 if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1))
1033 bFinished = true;
1034 aRetval = (CUTFLAG_LINE|CUTFLAG_START2);
1038 if(!bFinished && (aCutFlags & CUTFLAG_END1))
1040 // end1 on line 2 ?
1041 const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
1043 if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2))
1045 bFinished = true;
1046 aRetval = (CUTFLAG_LINE|CUTFLAG_END1);
1050 if(!bFinished && (aCutFlags & CUTFLAG_END2))
1052 // end2 on line 1 ?
1053 const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
1055 if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1))
1057 bFinished = true;
1058 aRetval = (CUTFLAG_LINE|CUTFLAG_END2);
1062 if(!bFinished)
1064 // cut in line1, line2 ?
1065 fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX());
1067 if(!fTools::equalZero(fCut1))
1069 fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX())
1070 + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1;
1072 const double fZero(0.0);
1073 const double fOne(1.0);
1075 // inside parameter range edge1 AND fCut2 is calcable
1076 if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne)
1077 && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY())))
1079 // take the mopre precise calculation of the two possible
1080 if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY()))
1082 fCut2 = (rEdge1Start.getX() + fCut1
1083 * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX();
1085 else
1087 fCut2 = (rEdge1Start.getY() + fCut1
1088 * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY();
1091 // inside parameter range edge2, too
1092 if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
1094 bFinished = true;
1095 aRetval = CUTFLAG_LINE;
1102 // copy values if wanted
1103 if(pCut1)
1105 *pCut1 = fCut1;
1108 if(pCut2)
1110 *pCut2 = fCut2;
1113 return aRetval;
1116 bool isPointOnEdge(
1117 const B2DPoint& rPoint,
1118 const B2DPoint& rEdgeStart,
1119 const B2DVector& rEdgeDelta,
1120 double* pCut)
1122 bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX()));
1123 bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY()));
1124 const double fZero(0.0);
1125 const double fOne(1.0);
1127 if(bDeltaXIsZero && bDeltaYIsZero)
1129 // no line, just a point
1130 return false;
1132 else if(bDeltaXIsZero)
1134 // vertical line
1135 if(fTools::equal(rPoint.getX(), rEdgeStart.getX()))
1137 double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
1139 if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
1141 if(pCut)
1143 *pCut = fValue;
1146 return true;
1150 else if(bDeltaYIsZero)
1152 // horizontal line
1153 if(fTools::equal(rPoint.getY(), rEdgeStart.getY()))
1155 double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
1157 if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
1159 if(pCut)
1161 *pCut = fValue;
1164 return true;
1168 else
1170 // any angle line
1171 double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
1172 double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
1174 if(fTools::equal(fTOne, fTTwo))
1176 // same parameter representation, point is on line. Take
1177 // middle value for better results
1178 double fValue = (fTOne + fTTwo) / 2.0;
1180 if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
1182 // point is inside line bounds, too
1183 if(pCut)
1185 *pCut = fValue;
1188 return true;
1193 return false;
1196 void applyLineDashing(const B2DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength)
1198 const sal_uInt32 nPointCount(rCandidate.count());
1199 const sal_uInt32 nDotDashCount(rDotDashArray.size());
1201 if(fTools::lessOrEqual(fDotDashLength, 0.0))
1203 fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
1206 if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
1208 // clear targets
1209 if(pLineTarget)
1211 pLineTarget->clear();
1214 if(pGapTarget)
1216 pGapTarget->clear();
1219 // prepare current edge's start
1220 B2DCubicBezier aCurrentEdge;
1221 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
1222 aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
1224 // prepare DotDashArray iteration and the line/gap switching bool
1225 sal_uInt32 nDotDashIndex(0);
1226 bool bIsLine(true);
1227 double fDotDashMovingLength(rDotDashArray[0]);
1228 B2DPolygon aSnippet;
1230 // iterate over all edges
1231 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1233 // update current edge (fill in C1, C2 and end point)
1234 double fLastDotDashMovingLength(0.0);
1235 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1236 aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
1237 aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
1238 aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
1240 // check if we have a trivial bezier segment -> possible fallback to edge
1241 aCurrentEdge.testAndSolveTrivialBezier();
1243 if(aCurrentEdge.isBezier())
1245 // bezier segment
1246 const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
1247 const double fEdgeLength(aCubicBezierHelper.getLength());
1249 if(!fTools::equalZero(fEdgeLength))
1251 while(fTools::less(fDotDashMovingLength, fEdgeLength))
1253 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1254 const bool bHandleLine(bIsLine && pLineTarget);
1255 const bool bHandleGap(!bIsLine && pGapTarget);
1257 if(bHandleLine || bHandleGap)
1259 const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
1260 const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
1261 B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
1263 if(!aSnippet.count())
1265 aSnippet.append(aBezierSnippet.getStartPoint());
1268 aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
1270 if(bHandleLine)
1272 pLineTarget->append(aSnippet);
1274 else
1276 pGapTarget->append(aSnippet);
1279 aSnippet.clear();
1282 // prepare next DotDashArray step and flip line/gap flag
1283 fLastDotDashMovingLength = fDotDashMovingLength;
1284 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
1285 bIsLine = !bIsLine;
1288 // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
1289 const bool bHandleLine(bIsLine && pLineTarget);
1290 const bool bHandleGap(!bIsLine && pGapTarget);
1292 if(bHandleLine || bHandleGap)
1294 B2DCubicBezier aRight;
1295 const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
1297 aCurrentEdge.split(fBezierSplit, 0, &aRight);
1299 if(!aSnippet.count())
1301 aSnippet.append(aRight.getStartPoint());
1304 aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
1307 // prepare move to next edge
1308 fDotDashMovingLength -= fEdgeLength;
1311 else
1313 // simple edge
1314 const double fEdgeLength(aCurrentEdge.getEdgeLength());
1316 if(!fTools::equalZero(fEdgeLength))
1318 while(fTools::less(fDotDashMovingLength, fEdgeLength))
1320 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1321 const bool bHandleLine(bIsLine && pLineTarget);
1322 const bool bHandleGap(!bIsLine && pGapTarget);
1324 if(bHandleLine || bHandleGap)
1326 if(!aSnippet.count())
1328 aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
1331 aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
1333 if(bHandleLine)
1335 pLineTarget->append(aSnippet);
1337 else
1339 pGapTarget->append(aSnippet);
1342 aSnippet.clear();
1345 // prepare next DotDashArray step and flip line/gap flag
1346 fLastDotDashMovingLength = fDotDashMovingLength;
1347 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
1348 bIsLine = !bIsLine;
1351 // append snippet [fLastDotDashMovingLength, fEdgeLength]
1352 const bool bHandleLine(bIsLine && pLineTarget);
1353 const bool bHandleGap(!bIsLine && pGapTarget);
1355 if(bHandleLine || bHandleGap)
1357 if(!aSnippet.count())
1359 aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
1362 aSnippet.append(aCurrentEdge.getEndPoint());
1365 // prepare move to next edge
1366 fDotDashMovingLength -= fEdgeLength;
1370 // prepare next edge step (end point gets new start point)
1371 aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
1374 // append last intermediate results (if exists)
1375 if(aSnippet.count())
1377 if(bIsLine && pLineTarget)
1379 pLineTarget->append(aSnippet);
1381 else if(!bIsLine && pGapTarget)
1383 pGapTarget->append(aSnippet);
1387 // check if start and end polygon may be merged
1388 if(pLineTarget)
1390 const sal_uInt32 nCount(pLineTarget->count());
1392 if(nCount > 1)
1394 // these polygons were created above, there exists none with less than two points,
1395 // thus dircet point access below is allowed
1396 const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0));
1397 B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1));
1399 if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
1401 // start of first and end of last are the same -> merge them
1402 aLast.append(aFirst);
1403 aLast.removeDoublePoints();
1404 pLineTarget->setB2DPolygon(0, aLast);
1405 pLineTarget->remove(nCount - 1);
1410 if(pGapTarget)
1412 const sal_uInt32 nCount(pGapTarget->count());
1414 if(nCount > 1)
1416 // these polygons were created above, there exists none with less than two points,
1417 // thus dircet point access below is allowed
1418 const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0));
1419 B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1));
1421 if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
1423 // start of first and end of last are the same -> merge them
1424 aLast.append(aFirst);
1425 aLast.removeDoublePoints();
1426 pGapTarget->setB2DPolygon(0, aLast);
1427 pGapTarget->remove(nCount - 1);
1432 else
1434 // parameters make no sense, just add source to targets
1435 if(pLineTarget)
1437 pLineTarget->append(rCandidate);
1440 if(pGapTarget)
1442 pGapTarget->append(rCandidate);
1447 // test if point is inside epsilon-range around an edge defined
1448 // by the two given points. Can be used for HitTesting. The epsilon-range
1449 // is defined to be the rectangle centered to the given edge, using height
1450 // 2 x fDistance, and the circle around both points with radius fDistance.
1451 bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance)
1453 // build edge vector
1454 const B2DVector aEdge(rEdgeEnd - rEdgeStart);
1455 bool bDoDistanceTestStart(false);
1456 bool bDoDistanceTestEnd(false);
1458 if(aEdge.equalZero())
1460 // no edge, just a point. Do one of the distance tests.
1461 bDoDistanceTestStart = true;
1463 else
1465 // edge has a length. Create perpendicular vector.
1466 const B2DVector aPerpend(getPerpendicular(aEdge));
1467 double fCut(
1468 (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX())
1469 + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) /
1470 (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY()));
1471 const double fZero(0.0);
1472 const double fOne(1.0);
1474 if(fTools::less(fCut, fZero))
1476 // left of rEdgeStart
1477 bDoDistanceTestStart = true;
1479 else if(fTools::more(fCut, fOne))
1481 // right of rEdgeEnd
1482 bDoDistanceTestEnd = true;
1484 else
1486 // inside line [0.0 .. 1.0]
1487 const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
1488 const B2DVector aDelta(rTestPosition - aCutPoint);
1489 const double fDistanceSquare(aDelta.scalar(aDelta));
1491 if(fDistanceSquare <= fDistance * fDistance)
1493 return true;
1495 else
1497 return false;
1502 if(bDoDistanceTestStart)
1504 const B2DVector aDelta(rTestPosition - rEdgeStart);
1505 const double fDistanceSquare(aDelta.scalar(aDelta));
1507 if(fDistanceSquare <= fDistance * fDistance)
1509 return true;
1512 else if(bDoDistanceTestEnd)
1514 const B2DVector aDelta(rTestPosition - rEdgeEnd);
1515 const double fDistanceSquare(aDelta.scalar(aDelta));
1517 if(fDistanceSquare <= fDistance * fDistance)
1519 return true;
1523 return false;
1526 // test if point is inside epsilon-range around the given Polygon. Can be used
1527 // for HitTesting. The epsilon-range is defined to be the tube around the polygon
1528 // with distance fDistance and rounded edges (start and end point).
1529 bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
1531 // force to non-bezier polygon
1532 const B2DPolygon aCandidate(rCandidate.getDefaultAdaptiveSubdivision());
1533 const sal_uInt32 nPointCount(aCandidate.count());
1535 if(nPointCount)
1537 const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
1538 B2DPoint aCurrent(aCandidate.getB2DPoint(0));
1540 if(nEdgeCount)
1542 // edges
1543 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1545 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1546 const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
1548 if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
1550 return true;
1553 // prepare next step
1554 aCurrent = aNext;
1557 else
1559 // no edges, but points -> not closed. Check single point. Just
1560 // use isInEpsilonRange with twice the same point, it handles those well
1561 if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
1563 return true;
1568 return false;
1571 B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY )
1573 const double fZero(0.0);
1574 const double fOne(1.0);
1576 // crop to useful values
1577 if(fTools::less(fRadiusX, fZero))
1579 fRadiusX = fZero;
1581 else if(fTools::more(fRadiusX, fOne))
1583 fRadiusX = fOne;
1586 if(fTools::less(fRadiusY, fZero))
1588 fRadiusY = fZero;
1590 else if(fTools::more(fRadiusY, fOne))
1592 fRadiusY = fOne;
1595 if(fZero == fRadiusX || fZero == fRadiusY)
1597 B2DPolygon aRetval;
1599 // at least in one direction no radius, use rectangle.
1600 // Do not use createPolygonFromRect() here since original
1601 // creator (historical reasons) still creates a start point at the
1602 // bottom center, so do the same here to get the same line patterns.
1603 // Due to this the order of points is different, too.
1604 const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
1605 aRetval.append(aBottomCenter);
1607 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
1608 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
1609 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
1610 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
1612 // close
1613 aRetval.setClosed( true );
1615 return aRetval;
1617 else if(fOne == fRadiusX && fOne == fRadiusY)
1619 // in both directions full radius, use ellipse
1620 const B2DPoint aCenter(rRect.getCenter());
1621 const double fRectRadiusX(rRect.getWidth() / 2.0);
1622 const double fRectRadiusY(rRect.getHeight() / 2.0);
1624 return createPolygonFromEllipse( aCenter, fRectRadiusX, fRectRadiusY );
1626 else
1628 B2DPolygon aRetval;
1629 const double fBowX((rRect.getWidth() / 2.0) * fRadiusX);
1630 const double fBowY((rRect.getHeight() / 2.0) * fRadiusY);
1631 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1633 // create start point at bottom center
1634 if(fOne != fRadiusX)
1636 const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
1637 aRetval.append(aBottomCenter);
1640 // create first bow
1642 const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY());
1643 const B2DPoint aStart(aBottomRight + B2DPoint(-fBowX, 0.0));
1644 const B2DPoint aStop(aBottomRight + B2DPoint(0.0, -fBowY));
1645 aRetval.append(aStart);
1646 aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop);
1649 // create second bow
1651 const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY());
1652 const B2DPoint aStart(aTopRight + B2DPoint(0.0, fBowY));
1653 const B2DPoint aStop(aTopRight + B2DPoint(-fBowX, 0.0));
1654 aRetval.append(aStart);
1655 aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop);
1658 // create third bow
1660 const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY());
1661 const B2DPoint aStart(aTopLeft + B2DPoint(fBowX, 0.0));
1662 const B2DPoint aStop(aTopLeft + B2DPoint(0.0, fBowY));
1663 aRetval.append(aStart);
1664 aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop);
1667 // create forth bow
1669 const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY());
1670 const B2DPoint aStart(aBottomLeft + B2DPoint(0.0, -fBowY));
1671 const B2DPoint aStop(aBottomLeft + B2DPoint(fBowX, 0.0));
1672 aRetval.append(aStart);
1673 aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop);
1676 // close
1677 aRetval.setClosed( true );
1679 // remove double created points if there are extreme radii envolved
1680 if(fOne == fRadiusX || fOne == fRadiusY)
1682 aRetval.removeDoublePoints();
1685 return aRetval;
1689 B2DPolygon createPolygonFromRect( const B2DRectangle& rRect )
1691 B2DPolygon aRetval;
1693 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
1694 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
1695 aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
1696 aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
1698 // close
1699 aRetval.setClosed( true );
1701 return aRetval;
1704 namespace
1706 struct theUnitPolygon :
1707 public rtl::StaticWithInit<B2DPolygon, theUnitPolygon>
1709 B2DPolygon operator () ()
1711 B2DPolygon aRetval;
1713 aRetval.append( B2DPoint( 0.0, 0.0 ) );
1714 aRetval.append( B2DPoint( 1.0, 0.0 ) );
1715 aRetval.append( B2DPoint( 1.0, 1.0 ) );
1716 aRetval.append( B2DPoint( 0.0, 1.0 ) );
1718 // close
1719 aRetval.setClosed( true );
1721 return aRetval;
1726 B2DPolygon createUnitPolygon()
1728 return theUnitPolygon::get();
1731 B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius )
1733 return createPolygonFromEllipse( rCenter, fRadius, fRadius );
1736 B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant)
1738 B2DPolygon aUnitCircle;
1739 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1740 const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
1741 const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
1743 B2DPoint aPoint(1.0, 0.0);
1744 B2DPoint aForward(1.0, fScaledKappa);
1745 B2DPoint aBackward(1.0, -fScaledKappa);
1747 if(0 != nStartQuadrant)
1749 const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4)));
1750 aPoint *= aQuadrantMatrix;
1751 aBackward *= aQuadrantMatrix;
1752 aForward *= aQuadrantMatrix;
1755 aUnitCircle.append(aPoint);
1757 for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++)
1759 aPoint *= aRotateMatrix;
1760 aBackward *= aRotateMatrix;
1761 aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint);
1762 aForward *= aRotateMatrix;
1765 aUnitCircle.setClosed(true);
1766 aUnitCircle.removeDoublePoints();
1768 return aUnitCircle;
1771 namespace
1773 struct theUnitHalfCircle :
1774 public rtl::StaticWithInit<B2DPolygon, theUnitHalfCircle>
1776 B2DPolygon operator()()
1778 B2DPolygon aUnitHalfCircle;
1779 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1780 const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
1781 const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
1782 B2DPoint aPoint(1.0, 0.0);
1783 B2DPoint aForward(1.0, fScaledKappa);
1784 B2DPoint aBackward(1.0, -fScaledKappa);
1786 aUnitHalfCircle.append(aPoint);
1788 for(sal_uInt32 a(0); a < STEPSPERQUARTER * 2; a++)
1790 aPoint *= aRotateMatrix;
1791 aBackward *= aRotateMatrix;
1792 aUnitHalfCircle.appendBezierSegment(aForward, aBackward, aPoint);
1793 aForward *= aRotateMatrix;
1795 return aUnitHalfCircle;
1800 B2DPolygon createHalfUnitCircle()
1802 return theUnitHalfCircle::get();
1805 namespace
1807 struct theUnitCircleStartQuadrantOne :
1808 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantOne>
1810 B2DPolygon operator()() { return impCreateUnitCircle(1); }
1813 struct theUnitCircleStartQuadrantTwo :
1814 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantTwo>
1816 B2DPolygon operator()() { return impCreateUnitCircle(2); }
1819 struct theUnitCircleStartQuadrantThree :
1820 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantThree>
1822 B2DPolygon operator()() { return impCreateUnitCircle(3); }
1825 struct theUnitCircleStartQuadrantZero :
1826 public rtl::StaticWithInit<B2DPolygon, theUnitCircleStartQuadrantZero>
1828 B2DPolygon operator()() { return impCreateUnitCircle(0); }
1832 B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
1834 switch(nStartQuadrant % 4)
1836 case 1 :
1837 return theUnitCircleStartQuadrantOne::get();
1839 case 2 :
1840 return theUnitCircleStartQuadrantTwo::get();
1842 case 3 :
1843 return theUnitCircleStartQuadrantThree::get();
1845 default : // case 0 :
1846 return theUnitCircleStartQuadrantZero::get();
1850 B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
1852 B2DPolygon aRetval(createPolygonFromUnitCircle());
1853 const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
1855 aRetval.transform(aMatrix);
1857 return aRetval;
1860 B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
1862 B2DPolygon aRetval;
1864 // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI
1865 // falls back to 0.0 to ensure a unique definition
1866 if(fTools::less(fStart, 0.0))
1868 fStart = 0.0;
1871 if(fTools::moreOrEqual(fStart, F_2PI))
1873 fStart = 0.0;
1876 if(fTools::less(fEnd, 0.0))
1878 fEnd = 0.0;
1881 if(fTools::moreOrEqual(fEnd, F_2PI))
1883 fEnd = 0.0;
1886 if(fTools::equal(fStart, fEnd))
1888 // same start and end angle, add single point
1889 aRetval.append(B2DPoint(cos(fStart), sin(fStart)));
1891 else
1893 const sal_uInt32 nSegments(STEPSPERQUARTER * 4);
1894 const double fAnglePerSegment(F_PI2 / STEPSPERQUARTER);
1895 const sal_uInt32 nStartSegment(sal_uInt32(fStart / fAnglePerSegment) % nSegments);
1896 const sal_uInt32 nEndSegment(sal_uInt32(fEnd / fAnglePerSegment) % nSegments);
1897 const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1898 const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
1900 B2DPoint aSegStart(cos(fStart), sin(fStart));
1901 aRetval.append(aSegStart);
1903 if(nStartSegment == nEndSegment && fTools::more(fEnd, fStart))
1905 // start and end in one sector and in the right order, create in one segment
1906 const B2DPoint aSegEnd(cos(fEnd), sin(fEnd));
1907 const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment));
1909 aRetval.appendBezierSegment(
1910 aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1911 aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1912 aSegEnd);
1914 else
1916 double fSegEndRad((nStartSegment + 1) * fAnglePerSegment);
1917 double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment));
1918 B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad));
1920 aRetval.appendBezierSegment(
1921 aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1922 aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1923 aSegEnd);
1925 sal_uInt32 nSegment((nStartSegment + 1) % nSegments);
1926 aSegStart = aSegEnd;
1928 while(nSegment != nEndSegment)
1930 // No end in this sector, add full sector.
1931 fSegEndRad = (nSegment + 1) * fAnglePerSegment;
1932 aSegEnd = B2DPoint(cos(fSegEndRad), sin(fSegEndRad));
1934 aRetval.appendBezierSegment(
1935 aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa),
1936 aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa),
1937 aSegEnd);
1939 nSegment = (nSegment + 1) % nSegments;
1940 aSegStart = aSegEnd;
1943 // End in this sector
1944 const double fSegStartRad(nSegment * fAnglePerSegment);
1945 fFactor = fScaledKappa * ((fEnd - fSegStartRad) / fAnglePerSegment);
1946 aSegEnd = B2DPoint(cos(fEnd), sin(fEnd));
1948 aRetval.appendBezierSegment(
1949 aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1950 aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1951 aSegEnd);
1955 // remove double points between segments created by segmented creation
1956 aRetval.removeDoublePoints();
1958 return aRetval;
1961 B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd )
1963 B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd));
1964 const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
1966 aRetval.transform(aMatrix);
1968 return aRetval;
1971 bool hasNeutralPoints(const B2DPolygon& rCandidate)
1973 OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)");
1974 const sal_uInt32 nPointCount(rCandidate.count());
1976 if(nPointCount > 2L)
1978 B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
1979 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
1981 for(sal_uInt32 a(0L); a < nPointCount; a++)
1983 const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
1984 const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
1985 const B2DVector aNextVec(aNextPoint - aCurrPoint);
1986 const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
1988 if(ORIENTATION_NEUTRAL == aOrientation)
1990 // current has neutral orientation
1991 return true;
1993 else
1995 // prepare next
1996 aPrevPoint = aCurrPoint;
1997 aCurrPoint = aNextPoint;
2002 return false;
2005 B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate)
2007 if(hasNeutralPoints(rCandidate))
2009 const sal_uInt32 nPointCount(rCandidate.count());
2010 B2DPolygon aRetval;
2011 B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
2012 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
2014 for(sal_uInt32 a(0L); a < nPointCount; a++)
2016 const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
2017 const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
2018 const B2DVector aNextVec(aNextPoint - aCurrPoint);
2019 const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
2021 if(ORIENTATION_NEUTRAL == aOrientation)
2023 // current has neutral orientation, leave it out and prepare next
2024 aCurrPoint = aNextPoint;
2026 else
2028 // add current point
2029 aRetval.append(aCurrPoint);
2031 // prepare next
2032 aPrevPoint = aCurrPoint;
2033 aCurrPoint = aNextPoint;
2037 while(aRetval.count() && ORIENTATION_NEUTRAL == getOrientationForIndex(aRetval, 0L))
2039 aRetval.remove(0L);
2042 // copy closed state
2043 aRetval.setClosed(rCandidate.isClosed());
2045 return aRetval;
2047 else
2049 return rCandidate;
2053 bool isConvex(const B2DPolygon& rCandidate)
2055 OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)");
2056 const sal_uInt32 nPointCount(rCandidate.count());
2058 if(nPointCount > 2L)
2060 const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
2061 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
2062 B2DVector aCurrVec(aPrevPoint - aCurrPoint);
2063 B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL);
2065 for(sal_uInt32 a(0L); a < nPointCount; a++)
2067 const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
2068 const B2DVector aNextVec(aNextPoint - aCurrPoint);
2069 const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec));
2071 if(ORIENTATION_NEUTRAL == aOrientation)
2073 // set start value, maybe neutral again
2074 aOrientation = aCurrentOrientation;
2076 else
2078 if(ORIENTATION_NEUTRAL != aCurrentOrientation && aCurrentOrientation != aOrientation)
2080 // different orientations found, that's it
2081 return false;
2085 // prepare next
2086 aCurrPoint = aNextPoint;
2087 aCurrVec = -aNextVec;
2091 return true;
2094 B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
2096 OSL_ENSURE(nIndex < rCandidate.count(), "getOrientationForIndex: index out of range (!)");
2097 const B2DPoint aPrev(rCandidate.getB2DPoint(getIndexOfPredecessor(nIndex, rCandidate)));
2098 const B2DPoint aCurr(rCandidate.getB2DPoint(nIndex));
2099 const B2DPoint aNext(rCandidate.getB2DPoint(getIndexOfSuccessor(nIndex, rCandidate)));
2100 const B2DVector aBack(aPrev - aCurr);
2101 const B2DVector aForw(aNext - aCurr);
2103 return getOrientation(aForw, aBack);
2106 bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints)
2108 if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
2110 // candidate is in epsilon around start or end -> inside
2111 return bWithPoints;
2113 else if(rStart.equal(rEnd))
2115 // start and end are equal, but candidate is outside their epsilon -> outside
2116 return false;
2118 else
2120 const B2DVector aEdgeVector(rEnd - rStart);
2121 const B2DVector aTestVector(rCandidate - rStart);
2123 if(areParallel(aEdgeVector, aTestVector))
2125 const double fZero(0.0);
2126 const double fOne(1.0);
2127 const double fParamTestOnCurr(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())
2128 ? aTestVector.getX() / aEdgeVector.getX()
2129 : aTestVector.getY() / aEdgeVector.getY());
2131 if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
2133 return true;
2137 return false;
2141 bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints)
2143 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
2144 const sal_uInt32 nPointCount(aCandidate.count());
2146 if(nPointCount > 1L)
2148 const sal_uInt32 nLoopCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2149 B2DPoint aCurrentPoint(aCandidate.getB2DPoint(0L));
2151 for(sal_uInt32 a(0L); a < nLoopCount; a++)
2153 const B2DPoint aNextPoint(aCandidate.getB2DPoint((a + 1L) % nPointCount));
2155 if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
2157 return true;
2160 aCurrentPoint = aNextPoint;
2163 else if(nPointCount && bWithPoints)
2165 return rPoint.equal(aCandidate.getB2DPoint(0L));
2168 return false;
2171 bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder)
2173 if(arePointsOnSameSideOfLine(rA, rB, rC, rCandidate, bWithBorder))
2175 if(arePointsOnSameSideOfLine(rB, rC, rA, rCandidate, bWithBorder))
2177 if(arePointsOnSameSideOfLine(rC, rA, rB, rCandidate, bWithBorder))
2179 return true;
2184 return false;
2187 bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine)
2189 const B2DVector aLineVector(rEnd - rStart);
2190 const B2DVector aVectorToA(rEnd - rCandidateA);
2191 const double fCrossA(aLineVector.cross(aVectorToA));
2193 if(fTools::equalZero(fCrossA))
2195 // one point on the line
2196 return bWithLine;
2199 const B2DVector aVectorToB(rEnd - rCandidateB);
2200 const double fCrossB(aLineVector.cross(aVectorToB));
2202 if(fTools::equalZero(fCrossB))
2204 // one point on the line
2205 return bWithLine;
2208 // return true if they both have the same sign
2209 return ((fCrossA > 0.0) == (fCrossB > 0.0));
2212 void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget)
2214 const sal_uInt32 nCount(rCandidate.count());
2216 if(nCount > 2L)
2218 const B2DPoint aStart(rCandidate.getB2DPoint(0L));
2219 B2DPoint aLast(rCandidate.getB2DPoint(1L));
2221 for(sal_uInt32 a(2L); a < nCount; a++)
2223 const B2DPoint aCurrent(rCandidate.getB2DPoint(a));
2224 rTarget.append(aStart);
2225 rTarget.append(aLast);
2226 rTarget.append(aCurrent);
2228 // prepare next
2229 aLast = aCurrent;
2234 namespace
2236 /// return 0 for input of 0, -1 for negative and 1 for positive input
2237 inline int lcl_sgn( const double n )
2239 return n == 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n);
2243 bool isRectangle( const B2DPolygon& rPoly )
2245 // polygon must be closed to resemble a rect, and contain
2246 // at least four points.
2247 if( !rPoly.isClosed() ||
2248 rPoly.count() < 4 ||
2249 rPoly.areControlPointsUsed() )
2251 return false;
2254 // number of 90 degree turns the polygon has taken
2255 int nNumTurns(0);
2257 int nVerticalEdgeType=0;
2258 int nHorizontalEdgeType=0;
2259 bool bNullVertex(true);
2260 bool bCWPolygon(false); // when true, polygon is CW
2261 // oriented, when false, CCW
2262 bool bOrientationSet(false); // when false, polygon
2263 // orientation has not yet
2264 // been determined.
2266 // scan all _edges_ (which involves coming back to point 0
2267 // for the last edge - thus the modulo operation below)
2268 const sal_Int32 nCount( rPoly.count() );
2269 for( sal_Int32 i=0; i<nCount; ++i )
2271 const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) );
2272 const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) );
2274 // is 0 for zero direction vector, 1 for south edge and -1
2275 // for north edge (standard screen coordinate system)
2276 int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) );
2278 // is 0 for zero direction vector, 1 for east edge and -1
2279 // for west edge (standard screen coordinate system)
2280 int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) );
2282 if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType )
2283 return false; // oblique edge - for sure no rect
2285 const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType );
2287 // current vertex is equal to previous - just skip,
2288 // until we have a real edge
2289 if( bCurrNullVertex )
2290 continue;
2292 // if previous edge has two identical points, because
2293 // no previous edge direction was available, simply
2294 // take this first non-null edge as the start
2295 // direction. That's what will happen here, if
2296 // bNullVertex is false
2297 if( !bNullVertex )
2299 // 2D cross product - is 1 for CW and -1 for CCW turns
2300 const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType -
2301 nVerticalEdgeType*nCurrHorizontalEdgeType );
2303 if( !nCrossProduct )
2304 continue; // no change in orientation -
2305 // collinear edges - just go on
2307 // if polygon orientation is not set, we'll
2308 // determine it now
2309 if( !bOrientationSet )
2311 bCWPolygon = nCrossProduct == 1;
2312 bOrientationSet = true;
2314 else
2316 // if current turn orientation is not equal
2317 // initial orientation, this is not a
2318 // rectangle (as rectangles have consistent
2319 // orientation).
2320 if( (nCrossProduct == 1) != bCWPolygon )
2321 return false;
2324 ++nNumTurns;
2326 // More than four 90 degree turns are an
2327 // indication that this must not be a rectangle.
2328 if( nNumTurns > 4 )
2329 return false;
2332 // store current state for the next turn
2333 nVerticalEdgeType = nCurrVerticalEdgeType;
2334 nHorizontalEdgeType = nCurrHorizontalEdgeType;
2335 bNullVertex = false; // won't reach this line,
2336 // if bCurrNullVertex is
2337 // true - see above
2340 return true;
2343 B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate)
2345 if(rCandidate.areControlPointsUsed())
2347 // call myself recursively with subdivided input
2348 const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
2349 return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate);
2351 else
2353 B3DPolygon aRetval;
2355 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
2357 B2DPoint aPoint(rCandidate.getB2DPoint(a));
2358 aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate));
2361 // copy closed state
2362 aRetval.setClosed(rCandidate.isClosed());
2364 return aRetval;
2368 B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat)
2370 B2DPolygon aRetval;
2371 const sal_uInt32 nCount(rCandidate.count());
2372 const bool bIsIdentity(rMat.isIdentity());
2374 for(sal_uInt32 a(0L); a < nCount; a++)
2376 B3DPoint aCandidate(rCandidate.getB3DPoint(a));
2378 if(!bIsIdentity)
2380 aCandidate *= rMat;
2383 aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY()));
2386 // copy closed state
2387 aRetval.setClosed(rCandidate.isClosed());
2389 return aRetval;
2392 double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut)
2394 if(rPointA.equal(rPointB))
2396 rCut = 0.0;
2397 const B2DVector aVector(rTestPoint - rPointA);
2398 return aVector.getLength();
2400 else
2402 // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
2403 const B2DVector aVector1(rPointB - rPointA);
2404 const B2DVector aVector2(rTestPoint - rPointA);
2405 const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY()));
2406 const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY()));
2407 const double fCut(fDividend / fDivisor);
2409 if(fCut < 0.0)
2411 // not in line range, get distance to PointA
2412 rCut = 0.0;
2413 return aVector2.getLength();
2415 else if(fCut > 1.0)
2417 // not in line range, get distance to PointB
2418 rCut = 1.0;
2419 const B2DVector aVector(rTestPoint - rPointB);
2420 return aVector.getLength();
2422 else
2424 // in line range
2425 const B2DPoint aCutPoint(rPointA + fCut * aVector1);
2426 const B2DVector aVector(rTestPoint - aCutPoint);
2427 rCut = fCut;
2428 return aVector.getLength();
2433 double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut)
2435 double fRetval(DBL_MAX);
2436 const sal_uInt32 nPointCount(rCandidate.count());
2438 if(nPointCount > 1L)
2440 const double fZero(0.0);
2441 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2442 B2DCubicBezier aBezier;
2443 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
2445 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
2447 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
2448 aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
2449 double fEdgeDist;
2450 double fNewCut(0.0);
2451 bool bEdgeIsCurve(false);
2453 if(rCandidate.areControlPointsUsed())
2455 aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
2456 aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
2457 aBezier.testAndSolveTrivialBezier();
2458 bEdgeIsCurve = aBezier.isBezier();
2461 if(bEdgeIsCurve)
2463 fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut);
2465 else
2467 fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut);
2470 if(DBL_MAX == fRetval || fEdgeDist < fRetval)
2472 fRetval = fEdgeDist;
2473 rEdgeIndex = a;
2474 rCut = fNewCut;
2476 if(fTools::equal(fRetval, fZero))
2478 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
2479 fRetval = 0.0;
2480 break;
2484 // prepare next step
2485 aBezier.setStartPoint(aBezier.getEndPoint());
2488 if(1.0 == rCut)
2490 // correct rEdgeIndex when not last point
2491 if(rCandidate.isClosed())
2493 rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate);
2494 rCut = 0.0;
2496 else
2498 if(rEdgeIndex != nEdgeCount - 1L)
2500 rEdgeIndex++;
2501 rCut = 0.0;
2507 return fRetval;
2510 B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
2512 if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight()))
2514 return rCandidate;
2516 else
2518 const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth());
2519 const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight());
2520 const double fOneMinusRelativeX(1.0 - fRelativeX);
2521 const double fOneMinusRelativeY(1.0 - fRelativeY);
2522 const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) +
2523 fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX()));
2524 const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) +
2525 fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY()));
2527 return B2DPoint(fNewX, fNewY);
2531 B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
2533 const sal_uInt32 nPointCount(rCandidate.count());
2535 if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight())
2537 B2DPolygon aRetval;
2539 for(sal_uInt32 a(0L); a < nPointCount; a++)
2541 aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
2543 if(rCandidate.areControlPointsUsed())
2545 if(!rCandidate.getPrevControlPoint(a).equalZero())
2547 aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
2550 if(!rCandidate.getNextControlPoint(a).equalZero())
2552 aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
2557 aRetval.setClosed(rCandidate.isClosed());
2558 return aRetval;
2560 else
2562 return rCandidate;
2566 B2DPolygon expandToCurve(const B2DPolygon& rCandidate)
2568 B2DPolygon aRetval(rCandidate);
2570 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
2572 expandToCurveInPoint(aRetval, a);
2575 return aRetval;
2578 bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex)
2580 OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)");
2581 bool bRetval(false);
2582 const sal_uInt32 nPointCount(rCandidate.count());
2584 if(nPointCount)
2586 // predecessor
2587 if(!rCandidate.isPrevControlPointUsed(nIndex))
2589 if(!rCandidate.isClosed() && 0 == nIndex)
2591 // do not create previous vector for start point of open polygon
2593 else
2595 const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2596 rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
2597 bRetval = true;
2601 // successor
2602 if(!rCandidate.isNextControlPointUsed(nIndex))
2604 if(!rCandidate.isClosed() && nIndex + 1 == nPointCount)
2606 // do not create next vector for end point of open polygon
2608 else
2610 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2611 rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
2612 bRetval = true;
2617 return bRetval;
2620 bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity)
2622 OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)");
2623 bool bRetval(false);
2624 const sal_uInt32 nPointCount(rCandidate.count());
2626 if(nPointCount)
2628 const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex));
2630 switch(eContinuity)
2632 case CONTINUITY_NONE :
2634 if(rCandidate.isPrevControlPointUsed(nIndex))
2636 if(!rCandidate.isClosed() && 0 == nIndex)
2638 // remove existing previous vector for start point of open polygon
2639 rCandidate.resetPrevControlPoint(nIndex);
2641 else
2643 const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2644 rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
2647 bRetval = true;
2650 if(rCandidate.isNextControlPointUsed(nIndex))
2652 if(!rCandidate.isClosed() && nIndex == nPointCount + 1)
2654 // remove next vector for end point of open polygon
2655 rCandidate.resetNextControlPoint(nIndex);
2657 else
2659 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2660 rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
2663 bRetval = true;
2666 break;
2668 case CONTINUITY_C1 :
2670 if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
2672 // lengths both exist since both are used
2673 B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
2674 B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
2675 const double fLenPrev(aVectorPrev.getLength());
2676 const double fLenNext(aVectorNext.getLength());
2677 aVectorPrev.normalize();
2678 aVectorNext.normalize();
2679 const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
2681 if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
2683 // parallel and opposite direction; check length
2684 if(fTools::equal(fLenPrev, fLenNext))
2686 // this would be even C2, but we want C1. Use the lengths of the corresponding edges.
2687 const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2688 const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2689 const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
2690 const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
2692 rCandidate.setControlPoints(nIndex,
2693 aCurrentPoint + (aVectorPrev * fLenPrevEdge),
2694 aCurrentPoint + (aVectorNext * fLenNextEdge));
2695 bRetval = true;
2698 else
2700 // not parallel or same direction, set vectors and length
2701 const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
2703 if(ORIENTATION_POSITIVE == aOrientation)
2705 rCandidate.setControlPoints(nIndex,
2706 aCurrentPoint - (aNormalizedPerpendicular * fLenPrev),
2707 aCurrentPoint + (aNormalizedPerpendicular * fLenNext));
2709 else
2711 rCandidate.setControlPoints(nIndex,
2712 aCurrentPoint + (aNormalizedPerpendicular * fLenPrev),
2713 aCurrentPoint - (aNormalizedPerpendicular * fLenNext));
2716 bRetval = true;
2719 break;
2721 case CONTINUITY_C2 :
2723 if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
2725 // lengths both exist since both are used
2726 B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
2727 B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
2728 const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0);
2729 aVectorPrev.normalize();
2730 aVectorNext.normalize();
2731 const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
2733 if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
2735 // parallel and opposite direction; set length. Use one direction for better numerical correctness
2736 const B2DVector aScaledDirection(aVectorPrev * fCommonLength);
2738 rCandidate.setControlPoints(nIndex,
2739 aCurrentPoint + aScaledDirection,
2740 aCurrentPoint - aScaledDirection);
2742 else
2744 // not parallel or same direction, set vectors and length
2745 const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
2746 const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength);
2748 if(ORIENTATION_POSITIVE == aOrientation)
2750 rCandidate.setControlPoints(nIndex,
2751 aCurrentPoint - aPerpendicular,
2752 aCurrentPoint + aPerpendicular);
2754 else
2756 rCandidate.setControlPoints(nIndex,
2757 aCurrentPoint + aPerpendicular,
2758 aCurrentPoint - aPerpendicular);
2762 bRetval = true;
2764 break;
2769 return bRetval;
2772 B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue)
2774 if(0.0 != fValue)
2776 if(rCandidate.areControlPointsUsed())
2778 // call myself recursively with subdivided input
2779 const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
2780 return growInNormalDirection(aCandidate, fValue);
2782 else
2784 B2DPolygon aRetval;
2785 const sal_uInt32 nPointCount(rCandidate.count());
2787 if(nPointCount)
2789 B2DPoint aPrev(rCandidate.getB2DPoint(nPointCount - 1L));
2790 B2DPoint aCurrent(rCandidate.getB2DPoint(0L));
2792 for(sal_uInt32 a(0L); a < nPointCount; a++)
2794 const B2DPoint aNext(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
2795 const B2DVector aBack(aPrev - aCurrent);
2796 const B2DVector aForw(aNext - aCurrent);
2797 const B2DVector aPerpBack(getNormalizedPerpendicular(aBack));
2798 const B2DVector aPerpForw(getNormalizedPerpendicular(aForw));
2799 B2DVector aDirection(aPerpBack - aPerpForw);
2800 aDirection.normalize();
2801 aDirection *= fValue;
2802 aRetval.append(aCurrent + aDirection);
2804 // prepare next step
2805 aPrev = aCurrent;
2806 aCurrent = aNext;
2810 // copy closed state
2811 aRetval.setClosed(rCandidate.isClosed());
2813 return aRetval;
2816 else
2818 return rCandidate;
2822 B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments)
2824 B2DPolygon aRetval;
2825 const sal_uInt32 nPointCount(rCandidate.count());
2827 if(nPointCount && nSegments)
2829 // get current segment count
2830 const sal_uInt32 nSegmentCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2832 if(nSegmentCount == nSegments)
2834 aRetval = rCandidate;
2836 else
2838 const double fLength(getLength(rCandidate));
2839 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nSegments : nSegments + 1L);
2841 for(sal_uInt32 a(0L); a < nLoopCount; a++)
2843 const double fRelativePos((double)a / (double)nSegments); // 0.0 .. 1.0
2844 const B2DPoint aNewPoint(getPositionRelative(rCandidate, fRelativePos, fLength));
2845 aRetval.append(aNewPoint);
2848 // copy closed flag
2849 aRetval.setClosed(rCandidate.isClosed());
2853 return aRetval;
2856 B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t)
2858 OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolygon interpolate: Different geometry (!)");
2860 if(fTools::lessOrEqual(t, 0.0) || rOld1 == rOld2)
2862 return rOld1;
2864 else if(fTools::moreOrEqual(t, 1.0))
2866 return rOld2;
2868 else
2870 B2DPolygon aRetval;
2871 const bool bInterpolateVectors(rOld1.areControlPointsUsed() || rOld2.areControlPointsUsed());
2872 aRetval.setClosed(rOld1.isClosed() && rOld2.isClosed());
2874 for(sal_uInt32 a(0L); a < rOld1.count(); a++)
2876 aRetval.append(interpolate(rOld1.getB2DPoint(a), rOld2.getB2DPoint(a), t));
2878 if(bInterpolateVectors)
2880 aRetval.setPrevControlPoint(a, interpolate(rOld1.getPrevControlPoint(a), rOld2.getPrevControlPoint(a), t));
2881 aRetval.setNextControlPoint(a, interpolate(rOld1.getNextControlPoint(a), rOld2.getNextControlPoint(a), t));
2885 return aRetval;
2889 // #i76891#
2890 B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate)
2892 const sal_uInt32 nPointCount(rCandidate.count());
2894 if(nPointCount && rCandidate.areControlPointsUsed())
2896 // prepare loop
2897 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
2898 B2DPolygon aRetval;
2899 B2DCubicBezier aBezier;
2900 aBezier.setStartPoint(rCandidate.getB2DPoint(0));
2902 // try to avoid costly reallocations
2903 aRetval.reserve( nEdgeCount+1);
2905 // add start point
2906 aRetval.append(aBezier.getStartPoint());
2908 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
2910 // get values for edge
2911 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
2912 aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
2913 aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
2914 aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
2915 aBezier.testAndSolveTrivialBezier();
2917 // still bezier?
2918 if(aBezier.isBezier())
2920 // add edge with control vectors
2921 aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint());
2923 else
2925 // add edge
2926 aRetval.append(aBezier.getEndPoint());
2929 // next point
2930 aBezier.setStartPoint(aBezier.getEndPoint());
2933 if(rCandidate.isClosed())
2935 // set closed flag, rescue control point and correct last double point
2936 closeWithGeometryChange(aRetval);
2939 return aRetval;
2941 else
2943 return rCandidate;
2947 // makes the given indexed point the new polygon start point. To do that, the points in the
2948 // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
2949 // an assertion will be triggered
2950 B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint)
2952 const sal_uInt32 nPointCount(rCandidate.count());
2954 if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount)
2956 OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)");
2957 B2DPolygon aRetval;
2959 for(sal_uInt32 a(0); a < nPointCount; a++)
2961 const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount);
2962 aRetval.append(rCandidate.getB2DPoint(nSourceIndex));
2964 if(rCandidate.areControlPointsUsed())
2966 aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex));
2967 aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex));
2971 return aRetval;
2974 return rCandidate;
2977 B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart, double fEnd)
2979 B2DPolygon aRetval;
2981 if(fLength < 0.0)
2983 fLength = 0.0;
2986 if(!fTools::equalZero(fLength))
2988 if(fStart < 0.0)
2990 fStart = 0.0;
2993 if(fEnd < 0.0)
2995 fEnd = 0.0;
2998 if(fEnd < fStart)
3000 fEnd = fStart;
3003 // iterate and consume pieces with fLength. First subdivide to reduce input to line segments
3004 const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
3005 const sal_uInt32 nPointCount(aCandidate.count());
3007 if(nPointCount > 1)
3009 const bool bEndActive(!fTools::equalZero(fEnd));
3010 const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1);
3011 B2DPoint aCurrent(aCandidate.getB2DPoint(0));
3012 double fPositionInEdge(fStart);
3013 double fAbsolutePosition(fStart);
3015 for(sal_uInt32 a(0); a < nEdgeCount; a++)
3017 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
3018 const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
3019 const B2DVector aEdge(aNext - aCurrent);
3020 double fEdgeLength(aEdge.getLength());
3022 if(!fTools::equalZero(fEdgeLength))
3024 while(fTools::less(fPositionInEdge, fEdgeLength))
3026 // move position on edge forward as long as on edge
3027 const double fScalar(fPositionInEdge / fEdgeLength);
3028 aRetval.append(aCurrent + (aEdge * fScalar));
3029 fPositionInEdge += fLength;
3031 if(bEndActive)
3033 fAbsolutePosition += fLength;
3035 if(fTools::more(fAbsolutePosition, fEnd))
3037 break;
3042 // substract length of current edge
3043 fPositionInEdge -= fEdgeLength;
3046 if(bEndActive && fTools::more(fAbsolutePosition, fEnd))
3048 break;
3051 // prepare next step
3052 aCurrent = aNext;
3055 // keep closed state
3056 aRetval.setClosed(aCandidate.isClosed());
3058 else
3060 // source polygon has only one point, return unchanged
3061 aRetval = aCandidate;
3065 return aRetval;
3068 B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight)
3070 B2DPolygon aRetval;
3072 if(fWaveWidth < 0.0)
3074 fWaveWidth = 0.0;
3077 if(fWaveHeight < 0.0)
3079 fWaveHeight = 0.0;
3082 const bool bHasWidth(!fTools::equalZero(fWaveWidth));
3084 if(bHasWidth)
3086 const bool bHasHeight(!fTools::equalZero(fWaveHeight));
3087 if(bHasHeight)
3089 // width and height, create waveline. First subdivide to reduce input to line segments
3090 // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it
3091 // may be added here again using the original last point from rCandidate. It may
3092 // also be the case that rCandidate was closed. To simplify things it is handled here
3093 // as if it was opened.
3094 // Result from createEdgesOfGivenLength contains no curved segments, handle as straight
3095 // edges.
3096 const B2DPolygon aEqualLenghEdges(createEdgesOfGivenLength(rCandidate, fWaveWidth));
3097 const sal_uInt32 nPointCount(aEqualLenghEdges.count());
3099 if(nPointCount > 1)
3101 // iterate over straight edges, add start point
3102 B2DPoint aCurrent(aEqualLenghEdges.getB2DPoint(0));
3103 aRetval.append(aCurrent);
3105 for(sal_uInt32 a(0); a < nPointCount - 1; a++)
3107 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
3108 const B2DPoint aNext(aEqualLenghEdges.getB2DPoint(nNextIndex));
3109 const B2DVector aEdge(aNext - aCurrent);
3110 const B2DVector aPerpendicular(getNormalizedPerpendicular(aEdge));
3111 const B2DVector aControlOffset((aEdge * 0.467308) - (aPerpendicular * fWaveHeight));
3113 // add curve segment
3114 aRetval.appendBezierSegment(
3115 aCurrent + aControlOffset,
3116 aNext - aControlOffset,
3117 aNext);
3119 // prepare next step
3120 aCurrent = aNext;
3124 else
3126 // width but no height -> return original polygon
3127 aRetval = rCandidate;
3130 else
3132 // no width -> no waveline, stay empty and return
3135 return aRetval;
3138 // snap points of horizontal or vertical edges to discrete values
3139 B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate)
3141 const sal_uInt32 nPointCount(rCandidate.count());
3143 if(nPointCount > 1)
3145 // Start by copying the source polygon to get a writeable copy. The closed state is
3146 // copied by aRetval's initialisation, too, so no need to copy it in this method
3147 B2DPolygon aRetval(rCandidate);
3149 // prepare geometry data. Get rounded from original
3150 B2ITuple aPrevTuple(basegfx::fround(rCandidate.getB2DPoint(nPointCount - 1)));
3151 B2DPoint aCurrPoint(rCandidate.getB2DPoint(0));
3152 B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
3154 // loop over all points. This will also snap the implicit closing edge
3155 // even when not closed, but that's no problem here
3156 for(sal_uInt32 a(0); a < nPointCount; a++)
3158 // get next point. Get rounded from original
3159 const bool bLastRun(a + 1 == nPointCount);
3160 const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
3161 const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
3162 const B2ITuple aNextTuple(basegfx::fround(aNextPoint));
3164 // get the states
3165 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
3166 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
3167 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
3168 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
3169 const bool bSnapX(bPrevVertical || bNextVertical);
3170 const bool bSnapY(bPrevHorizontal || bNextHorizontal);
3172 if(bSnapX || bSnapY)
3174 const B2DPoint aSnappedPoint(
3175 bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
3176 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
3178 aRetval.setB2DPoint(a, aSnappedPoint);
3181 // prepare next point
3182 if(!bLastRun)
3184 aPrevTuple = aCurrTuple;
3185 aCurrPoint = aNextPoint;
3186 aCurrTuple = aNextTuple;
3190 return aRetval;
3192 else
3194 return rCandidate;
3198 } // end of namespace tools
3199 } // end of namespace basegfx
3201 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */