1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b2dpolygontools.cxx,v $
10 * $Revision: 1.29.4.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basegfx.hxx"
33 #include <basegfx/numeric/ftools.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 #include <osl/diagnose.h>
36 #include <rtl/math.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolypolygon.hxx>
40 #include <basegfx/range/b2drange.hxx>
41 #include <basegfx/curve/b2dcubicbezier.hxx>
42 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
43 #include <basegfx/point/b3dpoint.hxx>
44 #include <basegfx/matrix/b3dhommatrix.hxx>
45 #include <basegfx/matrix/b2dhommatrix.hxx>
46 #include <basegfx/curve/b2dbeziertools.hxx>
52 #define ANGLE_BOUND_START_VALUE (2.25)
53 #define ANGLE_BOUND_MINIMUM_VALUE (0.1)
54 #define COUNT_SUBDIVIDE_DEFAULT (4L)
56 static double fAngleBoundStartValue
= ANGLE_BOUND_START_VALUE
;
59 //////////////////////////////////////////////////////////////////////////////
65 void openWithGeometryChange(B2DPolygon
& rCandidate
)
67 if(rCandidate
.isClosed())
69 if(rCandidate
.count())
71 rCandidate
.append(rCandidate
.getB2DPoint(0));
73 if(rCandidate
.areControlPointsUsed() && rCandidate
.isPrevControlPointUsed(0))
75 rCandidate
.setPrevControlPoint(rCandidate
.count() - 1, rCandidate
.getPrevControlPoint(0));
76 rCandidate
.resetPrevControlPoint(0);
80 rCandidate
.setClosed(false);
84 void closeWithGeometryChange(B2DPolygon
& rCandidate
)
86 if(!rCandidate
.isClosed())
88 while(rCandidate
.count() > 1 && rCandidate
.getB2DPoint(0) == rCandidate
.getB2DPoint(rCandidate
.count() - 1))
90 if(rCandidate
.areControlPointsUsed() && rCandidate
.isPrevControlPointUsed(rCandidate
.count() - 1))
92 rCandidate
.setPrevControlPoint(0, rCandidate
.getPrevControlPoint(rCandidate
.count() - 1));
95 rCandidate
.remove(rCandidate
.count() - 1);
98 rCandidate
.setClosed(true);
102 void checkClosed(B2DPolygon
& rCandidate
)
104 // #i80172# Removed unnecessary assertion
105 // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");
107 if(rCandidate
.count() > 1 && rCandidate
.getB2DPoint(0) == rCandidate
.getB2DPoint(rCandidate
.count() - 1))
109 closeWithGeometryChange(rCandidate
);
113 // Get successor and predecessor indices. Returning the same index means there
114 // is none. Same for successor.
115 sal_uInt32
getIndexOfPredecessor(sal_uInt32 nIndex
, const B2DPolygon
& rCandidate
)
117 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
123 else if(rCandidate
.count())
125 return rCandidate
.count() - 1L;
133 sal_uInt32
getIndexOfSuccessor(sal_uInt32 nIndex
, const B2DPolygon
& rCandidate
)
135 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
137 if(nIndex
+ 1L < rCandidate
.count())
141 else if(nIndex
+ 1L == rCandidate
.count())
151 B2VectorOrientation
getOrientation(const B2DPolygon
& rCandidate
)
153 B2VectorOrientation
eRetval(ORIENTATION_NEUTRAL
);
155 if(rCandidate
.count() > 2L || rCandidate
.areControlPointsUsed())
157 const double fSignedArea(getSignedArea(rCandidate
));
159 if(fTools::equalZero(fSignedArea
))
161 // ORIENTATION_NEUTRAL, already set
163 if(fSignedArea
> 0.0)
165 eRetval
= ORIENTATION_POSITIVE
;
167 else if(fSignedArea
< 0.0)
169 eRetval
= ORIENTATION_NEGATIVE
;
176 B2VectorContinuity
getContinuityInPoint(const B2DPolygon
& rCandidate
, sal_uInt32 nIndex
)
178 return rCandidate
.getContinuityInPoint(nIndex
);
181 B2DPolygon
adaptiveSubdivideByDistance(const B2DPolygon
& rCandidate
, double fDistanceBound
)
183 if(rCandidate
.areControlPointsUsed())
185 const sal_uInt32
nPointCount(rCandidate
.count());
190 // prepare edge-oriented loop
191 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
192 B2DCubicBezier aBezier
;
193 aBezier
.setStartPoint(rCandidate
.getB2DPoint(0));
195 // add start point (always)
196 aRetval
.append(aBezier
.getStartPoint());
198 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
200 // get next and control points
201 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
202 aBezier
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
203 aBezier
.setControlPointA(rCandidate
.getNextControlPoint(a
));
204 aBezier
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
205 aBezier
.testAndSolveTrivialBezier();
207 if(aBezier
.isBezier())
209 // add curved edge and generate DistanceBound
212 if(0.0 == fDistanceBound
)
214 // If not set, use B2DCubicBezier functionality to guess a rough value
215 const double fRoughLength((aBezier
.getEdgeLength() + aBezier
.getControlPolygonLength()) / 2.0);
217 // take 1/100th of the rough curve length
218 fBound
= fRoughLength
* 0.01;
222 // use given bound value
223 fBound
= fDistanceBound
;
226 // make sure bound value is not too small. The base units are 1/100th mm, thus
227 // just make sure it's not smaller then 1/100th of that
233 // call adaptive subdivide which adds edges to aRetval accordingly
234 aBezier
.adaptiveSubdivideByDistance(aRetval
, fBound
);
238 // add non-curved edge
239 aRetval
.append(aBezier
.getEndPoint());
243 aBezier
.setStartPoint(aBezier
.getEndPoint());
246 if(rCandidate
.isClosed())
248 // set closed flag and correct last point (which is added double now).
249 closeWithGeometryChange(aRetval
);
261 B2DPolygon
adaptiveSubdivideByAngle(const B2DPolygon
& rCandidate
, double fAngleBound
)
263 if(rCandidate
.areControlPointsUsed())
265 const sal_uInt32
nPointCount(rCandidate
.count());
270 // prepare edge-oriented loop
271 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
272 B2DCubicBezier aBezier
;
273 aBezier
.setStartPoint(rCandidate
.getB2DPoint(0));
275 // add start point (always)
276 aRetval
.append(aBezier
.getStartPoint());
278 // #i37443# prepare convenient AngleBound if none was given
279 if(0.0 == fAngleBound
)
282 fAngleBound
= fAngleBoundStartValue
;
284 fAngleBound
= ANGLE_BOUND_START_VALUE
;
287 else if(fTools::less(fAngleBound
, ANGLE_BOUND_MINIMUM_VALUE
))
292 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
294 // get next and control points
295 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
296 aBezier
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
297 aBezier
.setControlPointA(rCandidate
.getNextControlPoint(a
));
298 aBezier
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
299 aBezier
.testAndSolveTrivialBezier();
301 if(aBezier
.isBezier())
303 // call adaptive subdivide
304 aBezier
.adaptiveSubdivideByAngle(aRetval
, fAngleBound
, true);
308 // add non-curved edge
309 aRetval
.append(aBezier
.getEndPoint());
313 aBezier
.setStartPoint(aBezier
.getEndPoint());
316 if(rCandidate
.isClosed())
318 // set closed flag and correct last point (which is added double now).
319 closeWithGeometryChange(aRetval
);
331 B2DPolygon
adaptiveSubdivideByCount(const B2DPolygon
& rCandidate
, sal_uInt32 nCount
)
333 if(rCandidate
.areControlPointsUsed())
335 const sal_uInt32
nPointCount(rCandidate
.count());
340 // prepare edge-oriented loop
341 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
342 B2DCubicBezier aBezier
;
343 aBezier
.setStartPoint(rCandidate
.getB2DPoint(0));
345 // add start point (always)
346 aRetval
.append(aBezier
.getStartPoint());
348 // #i37443# prepare convenient count if none was given
351 nCount
= COUNT_SUBDIVIDE_DEFAULT
;
354 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
356 // get next and control points
357 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
358 aBezier
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
359 aBezier
.setControlPointA(rCandidate
.getNextControlPoint(a
));
360 aBezier
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
361 aBezier
.testAndSolveTrivialBezier();
363 if(aBezier
.isBezier())
365 // call adaptive subdivide
366 aBezier
.adaptiveSubdivideByCount(aRetval
, nCount
);
370 // add non-curved edge
371 aRetval
.append(aBezier
.getEndPoint());
375 aBezier
.setStartPoint(aBezier
.getEndPoint());
378 if(rCandidate
.isClosed())
380 // set closed flag and correct last point (which is added double now).
381 closeWithGeometryChange(aRetval
);
393 bool isInside(const B2DPolygon
& rCandidate
, const B2DPoint
& rPoint
, bool bWithBorder
)
395 const B2DPolygon
aCandidate(rCandidate
.areControlPointsUsed() ? rCandidate
.getDefaultAdaptiveSubdivision() : rCandidate
);
397 if(bWithBorder
&& isPointOnPolygon(aCandidate
, rPoint
, true))
404 const sal_uInt32
nPointCount(aCandidate
.count());
408 B2DPoint
aCurrentPoint(aCandidate
.getB2DPoint(nPointCount
- 1L));
410 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
412 const B2DPoint
aPreviousPoint(aCurrentPoint
);
413 aCurrentPoint
= aCandidate
.getB2DPoint(a
);
416 const bool bCompYA(fTools::more(aPreviousPoint
.getY(), rPoint
.getY()));
417 const bool bCompYB(fTools::more(aCurrentPoint
.getY(), rPoint
.getY()));
419 if(bCompYA
!= bCompYB
)
422 const bool bCompXA(fTools::more(aPreviousPoint
.getX(), rPoint
.getX()));
423 const bool bCompXB(fTools::more(aCurrentPoint
.getX(), rPoint
.getX()));
425 if(bCompXA
== bCompXB
)
434 const double fCompare(
435 aCurrentPoint
.getX() - (aCurrentPoint
.getY() - rPoint
.getY()) *
436 (aPreviousPoint
.getX() - aCurrentPoint
.getX()) /
437 (aPreviousPoint
.getY() - aCurrentPoint
.getY()));
439 if(fTools::more(fCompare
, rPoint
.getX()))
452 bool isInside(const B2DPolygon
& rCandidate
, const B2DPolygon
& rPolygon
, bool bWithBorder
)
454 const B2DPolygon
aCandidate(rCandidate
.areControlPointsUsed() ? rCandidate
.getDefaultAdaptiveSubdivision() : rCandidate
);
455 const B2DPolygon
aPolygon(rPolygon
.areControlPointsUsed() ? rPolygon
.getDefaultAdaptiveSubdivision() : rPolygon
);
456 const sal_uInt32
nPointCount(aPolygon
.count());
458 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
460 const B2DPoint
aTestPoint(aPolygon
.getB2DPoint(a
));
462 if(!isInside(aCandidate
, aTestPoint
, bWithBorder
))
471 B2DRange
getRangeWithControlPoints(const B2DPolygon
& rCandidate
)
473 const sal_uInt32
nPointCount(rCandidate
.count());
478 const bool bControlPointsUsed(rCandidate
.areControlPointsUsed());
480 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
482 aRetval
.expand(rCandidate
.getB2DPoint(a
));
484 if(bControlPointsUsed
)
486 aRetval
.expand(rCandidate
.getNextControlPoint(a
));
487 aRetval
.expand(rCandidate
.getPrevControlPoint(a
));
495 B2DRange
getRange(const B2DPolygon
& rCandidate
)
497 // changed to use internally buffered version at B2DPolygon
498 return rCandidate
.getB2DRange();
501 double getSignedArea(const B2DPolygon
& rCandidate
)
503 const B2DPolygon
aCandidate(rCandidate
.areControlPointsUsed() ? rCandidate
.getDefaultAdaptiveSubdivision() : rCandidate
);
505 const sal_uInt32
nPointCount(aCandidate
.count());
509 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
511 const B2DPoint
aPreviousPoint(aCandidate
.getB2DPoint((!a
) ? nPointCount
- 1L : a
- 1L));
512 const B2DPoint
aCurrentPoint(aCandidate
.getB2DPoint(a
));
514 fRetval
+= aPreviousPoint
.getX() * aCurrentPoint
.getY();
515 fRetval
-= aPreviousPoint
.getY() * aCurrentPoint
.getX();
520 // correct to zero if small enough. Also test the quadratic
521 // of the result since the precision is near quadratic due to
523 if(fTools::equalZero(fRetval
) || fTools::equalZero(fRetval
* fRetval
))
532 double getArea(const B2DPolygon
& rCandidate
)
536 if(rCandidate
.count() > 2 || rCandidate
.areControlPointsUsed())
538 fRetval
= getSignedArea(rCandidate
);
539 const double fZero(0.0);
541 if(fTools::less(fRetval
, fZero
))
550 double getEdgeLength(const B2DPolygon
& rCandidate
, sal_uInt32 nIndex
)
552 const sal_uInt32
nPointCount(rCandidate
.count());
553 OSL_ENSURE(nIndex
< nPointCount
, "getEdgeLength: Access to polygon out of range (!)");
558 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
560 if(rCandidate
.areControlPointsUsed())
562 B2DCubicBezier aEdge
;
564 aEdge
.setStartPoint(rCandidate
.getB2DPoint(nIndex
));
565 aEdge
.setControlPointA(rCandidate
.getNextControlPoint(nIndex
));
566 aEdge
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
567 aEdge
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
569 fRetval
= aEdge
.getLength();
573 const B2DPoint
aCurrent(rCandidate
.getB2DPoint(nIndex
));
574 const B2DPoint
aNext(rCandidate
.getB2DPoint(nNextIndex
));
576 fRetval
= B2DVector(aNext
- aCurrent
).getLength();
583 double getLength(const B2DPolygon
& rCandidate
)
586 const sal_uInt32
nPointCount(rCandidate
.count());
590 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
592 if(rCandidate
.areControlPointsUsed())
594 B2DCubicBezier aEdge
;
595 aEdge
.setStartPoint(rCandidate
.getB2DPoint(0));
597 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
599 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
600 aEdge
.setControlPointA(rCandidate
.getNextControlPoint(a
));
601 aEdge
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
602 aEdge
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
604 fRetval
+= aEdge
.getLength();
605 aEdge
.setStartPoint(aEdge
.getEndPoint());
610 B2DPoint
aCurrent(rCandidate
.getB2DPoint(0));
612 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
614 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
615 const B2DPoint
aNext(rCandidate
.getB2DPoint(nNextIndex
));
617 fRetval
+= B2DVector(aNext
- aCurrent
).getLength();
626 B2DPoint
getPositionAbsolute(const B2DPolygon
& rCandidate
, double fDistance
, double fLength
)
629 const sal_uInt32
nPointCount(rCandidate
.count());
631 if( 1L == nPointCount
)
633 // only one point (i.e. no edge) - simply take that point
634 aRetval
= rCandidate
.getB2DPoint(0);
636 else if(nPointCount
> 1L)
638 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
639 sal_uInt32
nIndex(0L);
640 bool bIndexDone(false);
642 // get length if not given
643 if(fTools::equalZero(fLength
))
645 fLength
= getLength(rCandidate
);
648 if(fTools::less(fDistance
, 0.0))
650 // handle fDistance < 0.0
651 if(rCandidate
.isClosed())
653 // if fDistance < 0.0 increment with multiple of fLength
654 sal_uInt32
nCount(sal_uInt32(-fDistance
/ fLength
));
655 fDistance
+= double(nCount
+ 1L) * fLength
;
659 // crop to polygon start
664 else if(fTools::moreOrEqual(fDistance
, fLength
))
666 // handle fDistance >= fLength
667 if(rCandidate
.isClosed())
669 // if fDistance >= fLength decrement with multiple of fLength
670 sal_uInt32
nCount(sal_uInt32(fDistance
/ fLength
));
671 fDistance
-= (double)(nCount
) * fLength
;
675 // crop to polygon end
682 // look for correct index. fDistance is now [0.0 .. fLength[
683 double fEdgeLength(getEdgeLength(rCandidate
, nIndex
));
687 // edge found must be on the half-open range
689 // Note that in theory, we cannot move beyond
690 // the last polygon point, since fDistance>=fLength
691 // is checked above. Unfortunately, with floating-
692 // point calculations, this case might happen.
693 // Handled by nIndex check below
694 if(nIndex
< nEdgeCount
&& fTools::moreOrEqual(fDistance
, fEdgeLength
))
697 fDistance
-= fEdgeLength
;
698 fEdgeLength
= getEdgeLength(rCandidate
, ++nIndex
);
702 // it's on this edge, stop
707 // get the point using nIndex
708 aRetval
= rCandidate
.getB2DPoint(nIndex
);
710 // if fDistance != 0.0, move that length on the edge. The edge
711 // length is in fEdgeLength.
712 if(!fTools::equalZero(fDistance
))
714 if(fTools::moreOrEqual(fDistance
, fEdgeLength
))
716 // end point of choosen edge
717 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
718 aRetval
= rCandidate
.getB2DPoint(nNextIndex
);
720 else if(fTools::equalZero(fDistance
))
722 // start point of choosen edge
728 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
729 const B2DPoint
aNextPoint(rCandidate
.getB2DPoint(nNextIndex
));
732 // add calculated average value to the return value
733 if(rCandidate
.areControlPointsUsed())
735 // get as bezier segment
736 const B2DCubicBezier
aBezierSegment(
737 aRetval
, rCandidate
.getNextControlPoint(nIndex
),
738 rCandidate
.getPrevControlPoint(nNextIndex
), aNextPoint
);
740 if(aBezierSegment
.isBezier())
742 // use B2DCubicBezierHelper to bridge the non-linear gap between
743 // length and bezier distances
744 const B2DCubicBezierHelper
aBezierSegmentHelper(aBezierSegment
);
745 const double fBezierDistance(aBezierSegmentHelper
.distanceToRelative(fDistance
));
747 aRetval
= aBezierSegment
.interpolatePoint(fBezierDistance
);
754 const double fRelativeInEdge(fDistance
/ fEdgeLength
);
755 aRetval
= interpolate(aRetval
, aNextPoint
, fRelativeInEdge
);
764 B2DPoint
getPositionRelative(const B2DPolygon
& rCandidate
, double fDistance
, double fLength
)
766 // get length if not given
767 if(fTools::equalZero(fLength
))
769 fLength
= getLength(rCandidate
);
772 // multiply fDistance with real length to get absolute position and
773 // use getPositionAbsolute
774 return getPositionAbsolute(rCandidate
, fDistance
* fLength
, fLength
);
777 B2DPolygon
getSnippetAbsolute(const B2DPolygon
& rCandidate
, double fFrom
, double fTo
, double fLength
)
779 const sal_uInt32
nPointCount(rCandidate
.count());
783 // get length if not given
784 if(fTools::equalZero(fLength
))
786 fLength
= getLength(rCandidate
);
789 // test and correct fFrom
790 if(fTools::less(fFrom
, 0.0))
795 // test and correct fTo
796 if(fTools::more(fTo
, fLength
))
801 // test and correct relationship of fFrom, fTo
802 if(fTools::more(fFrom
, fTo
))
804 fFrom
= fTo
= (fFrom
+ fTo
) / 2.0;
807 if(fTools::equalZero(fFrom
) && fTools::equal(fTo
, fLength
))
809 // no change, result is the whole polygon
815 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
816 double fPositionOfStart(0.0);
817 bool bStartDone(false);
818 bool bEndDone(false);
820 for(sal_uInt32
a(0L); !(bStartDone
&& bEndDone
) && a
< nEdgeCount
; a
++)
822 const double fEdgeLength(getEdgeLength(rCandidate
, a
));
826 if(fTools::equalZero(fFrom
))
828 aRetval
.append(rCandidate
.getB2DPoint(a
));
830 if(rCandidate
.areControlPointsUsed())
832 aRetval
.setNextControlPoint(aRetval
.count() - 1, rCandidate
.getNextControlPoint(a
));
837 else if(fTools::moreOrEqual(fFrom
, fPositionOfStart
) && fTools::less(fFrom
, fPositionOfStart
+ fEdgeLength
))
839 // calculate and add start point
840 if(fTools::equalZero(fEdgeLength
))
842 aRetval
.append(rCandidate
.getB2DPoint(a
));
844 if(rCandidate
.areControlPointsUsed())
846 aRetval
.setNextControlPoint(aRetval
.count() - 1, rCandidate
.getNextControlPoint(a
));
851 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
852 const B2DPoint
aStart(rCandidate
.getB2DPoint(a
));
853 const B2DPoint
aEnd(rCandidate
.getB2DPoint(nNextIndex
));
856 if(rCandidate
.areControlPointsUsed())
858 const B2DCubicBezier
aBezierSegment(
859 aStart
, rCandidate
.getNextControlPoint(a
),
860 rCandidate
.getPrevControlPoint(nNextIndex
), aEnd
);
862 if(aBezierSegment
.isBezier())
864 // use B2DCubicBezierHelper to bridge the non-linear gap between
865 // length and bezier distances
866 const B2DCubicBezierHelper
aBezierSegmentHelper(aBezierSegment
);
867 const double fBezierDistance(aBezierSegmentHelper
.distanceToRelative(fFrom
- fPositionOfStart
));
868 B2DCubicBezier aRight
;
870 aBezierSegment
.split(fBezierDistance
, 0, &aRight
);
871 aRetval
.append(aRight
.getStartPoint());
872 aRetval
.setNextControlPoint(aRetval
.count() - 1, aRight
.getControlPointA());
879 const double fRelValue((fFrom
- fPositionOfStart
) / fEdgeLength
);
880 aRetval
.append(interpolate(aStart
, aEnd
, fRelValue
));
886 // if same point, end is done, too.
894 if(!bEndDone
&& fTools::moreOrEqual(fTo
, fPositionOfStart
) && fTools::less(fTo
, fPositionOfStart
+ fEdgeLength
))
896 // calculate and add end point
897 if(fTools::equalZero(fEdgeLength
))
899 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
900 aRetval
.append(rCandidate
.getB2DPoint(nNextIndex
));
902 if(rCandidate
.areControlPointsUsed())
904 aRetval
.setPrevControlPoint(aRetval
.count() - 1, rCandidate
.getPrevControlPoint(nNextIndex
));
909 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
910 const B2DPoint
aStart(rCandidate
.getB2DPoint(a
));
911 const B2DPoint
aEnd(rCandidate
.getB2DPoint(nNextIndex
));
914 if(rCandidate
.areControlPointsUsed())
916 const B2DCubicBezier
aBezierSegment(
917 aStart
, rCandidate
.getNextControlPoint(a
),
918 rCandidate
.getPrevControlPoint(nNextIndex
), aEnd
);
920 if(aBezierSegment
.isBezier())
922 // use B2DCubicBezierHelper to bridge the non-linear gap between
923 // length and bezier distances
924 const B2DCubicBezierHelper
aBezierSegmentHelper(aBezierSegment
);
925 const double fBezierDistance(aBezierSegmentHelper
.distanceToRelative(fTo
- fPositionOfStart
));
926 B2DCubicBezier aLeft
;
928 aBezierSegment
.split(fBezierDistance
, &aLeft
, 0);
929 aRetval
.append(aLeft
.getEndPoint());
930 aRetval
.setPrevControlPoint(aRetval
.count() - 1, aLeft
.getControlPointB());
937 const double fRelValue((fTo
- fPositionOfStart
) / fEdgeLength
);
938 aRetval
.append(interpolate(aStart
, aEnd
, fRelValue
));
949 // add segments end point
950 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
951 aRetval
.append(rCandidate
.getB2DPoint(nNextIndex
));
953 if(rCandidate
.areControlPointsUsed())
955 aRetval
.setPrevControlPoint(aRetval
.count() - 1, rCandidate
.getPrevControlPoint(nNextIndex
));
956 aRetval
.setNextControlPoint(aRetval
.count() - 1, rCandidate
.getNextControlPoint(nNextIndex
));
960 // increment fPositionOfStart
961 fPositionOfStart
+= fEdgeLength
;
973 B2DPolygon
getSnippetRelative(const B2DPolygon
& rCandidate
, double fFrom
, double fTo
, double fLength
)
975 // get length if not given
976 if(fTools::equalZero(fLength
))
978 fLength
= getLength(rCandidate
);
981 // multiply distances with real length to get absolute position and
982 // use getSnippetAbsolute
983 return getSnippetAbsolute(rCandidate
, fFrom
* fLength
, fTo
* fLength
, fLength
);
986 CutFlagValue
findCut(
987 const B2DPolygon
& rCandidate
,
988 sal_uInt32 nIndex1
, sal_uInt32 nIndex2
,
989 CutFlagValue aCutFlags
,
990 double* pCut1
, double* pCut2
)
992 CutFlagValue
aRetval(CUTFLAG_NONE
);
993 const sal_uInt32
nPointCount(rCandidate
.count());
995 if(nIndex1
< nPointCount
&& nIndex2
< nPointCount
&& nIndex1
!= nIndex2
)
997 sal_uInt32
nEnd1(getIndexOfSuccessor(nIndex1
, rCandidate
));
998 sal_uInt32
nEnd2(getIndexOfSuccessor(nIndex2
, rCandidate
));
1000 const B2DPoint
aStart1(rCandidate
.getB2DPoint(nIndex1
));
1001 const B2DPoint
aEnd1(rCandidate
.getB2DPoint(nEnd1
));
1002 const B2DVector
aVector1(aEnd1
- aStart1
);
1004 const B2DPoint
aStart2(rCandidate
.getB2DPoint(nIndex2
));
1005 const B2DPoint
aEnd2(rCandidate
.getB2DPoint(nEnd2
));
1006 const B2DVector
aVector2(aEnd2
- aStart2
);
1009 aStart1
, aVector1
, aStart2
, aVector2
,
1010 aCutFlags
, pCut1
, pCut2
);
1016 CutFlagValue
findCut(
1017 const B2DPolygon
& rCandidate1
, sal_uInt32 nIndex1
,
1018 const B2DPolygon
& rCandidate2
, sal_uInt32 nIndex2
,
1019 CutFlagValue aCutFlags
,
1020 double* pCut1
, double* pCut2
)
1022 CutFlagValue
aRetval(CUTFLAG_NONE
);
1023 const sal_uInt32
nPointCount1(rCandidate1
.count());
1024 const sal_uInt32
nPointCount2(rCandidate2
.count());
1026 if(nIndex1
< nPointCount1
&& nIndex2
< nPointCount2
)
1028 sal_uInt32
nEnd1(getIndexOfSuccessor(nIndex1
, rCandidate1
));
1029 sal_uInt32
nEnd2(getIndexOfSuccessor(nIndex2
, rCandidate2
));
1031 const B2DPoint
aStart1(rCandidate1
.getB2DPoint(nIndex1
));
1032 const B2DPoint
aEnd1(rCandidate1
.getB2DPoint(nEnd1
));
1033 const B2DVector
aVector1(aEnd1
- aStart1
);
1035 const B2DPoint
aStart2(rCandidate2
.getB2DPoint(nIndex2
));
1036 const B2DPoint
aEnd2(rCandidate2
.getB2DPoint(nEnd2
));
1037 const B2DVector
aVector2(aEnd2
- aStart2
);
1040 aStart1
, aVector1
, aStart2
, aVector2
,
1041 aCutFlags
, pCut1
, pCut2
);
1047 CutFlagValue
findCut(
1048 const B2DPoint
& rEdge1Start
, const B2DVector
& rEdge1Delta
,
1049 const B2DPoint
& rEdge2Start
, const B2DVector
& rEdge2Delta
,
1050 CutFlagValue aCutFlags
,
1051 double* pCut1
, double* pCut2
)
1053 CutFlagValue
aRetval(CUTFLAG_NONE
);
1056 bool bFinished(!((bool)(aCutFlags
& CUTFLAG_ALL
)));
1058 // test for same points?
1060 && (aCutFlags
& (CUTFLAG_START1
|CUTFLAG_END1
))
1061 && (aCutFlags
& (CUTFLAG_START2
|CUTFLAG_END2
)))
1064 if(!bFinished
&& (aCutFlags
& (CUTFLAG_START1
|CUTFLAG_START2
)) == (CUTFLAG_START1
|CUTFLAG_START2
))
1066 if(rEdge1Start
.equal(rEdge2Start
))
1069 aRetval
= (CUTFLAG_START1
|CUTFLAG_START2
);
1074 if(!bFinished
&& (aCutFlags
& (CUTFLAG_END1
|CUTFLAG_END2
)) == (CUTFLAG_END1
|CUTFLAG_END2
))
1076 const B2DPoint
aEnd1(rEdge1Start
+ rEdge1Delta
);
1077 const B2DPoint
aEnd2(rEdge2Start
+ rEdge2Delta
);
1079 if(aEnd1
.equal(aEnd2
))
1082 aRetval
= (CUTFLAG_END1
|CUTFLAG_END2
);
1083 fCut1
= fCut2
= 1.0;
1087 // startpoint1 == endpoint2?
1088 if(!bFinished
&& (aCutFlags
& (CUTFLAG_START1
|CUTFLAG_END2
)) == (CUTFLAG_START1
|CUTFLAG_END2
))
1090 const B2DPoint
aEnd2(rEdge2Start
+ rEdge2Delta
);
1092 if(rEdge1Start
.equal(aEnd2
))
1095 aRetval
= (CUTFLAG_START1
|CUTFLAG_END2
);
1101 // startpoint2 == endpoint1?
1102 if(!bFinished
&& (aCutFlags
& (CUTFLAG_START2
|CUTFLAG_END1
)) == (CUTFLAG_START2
|CUTFLAG_END1
))
1104 const B2DPoint
aEnd1(rEdge1Start
+ rEdge1Delta
);
1106 if(rEdge2Start
.equal(aEnd1
))
1109 aRetval
= (CUTFLAG_START2
|CUTFLAG_END1
);
1116 if(!bFinished
&& (aCutFlags
& CUTFLAG_LINE
))
1118 if(!bFinished
&& (aCutFlags
& CUTFLAG_START1
))
1120 // start1 on line 2 ?
1121 if(isPointOnEdge(rEdge1Start
, rEdge2Start
, rEdge2Delta
, &fCut2
))
1124 aRetval
= (CUTFLAG_LINE
|CUTFLAG_START1
);
1128 if(!bFinished
&& (aCutFlags
& CUTFLAG_START2
))
1130 // start2 on line 1 ?
1131 if(isPointOnEdge(rEdge2Start
, rEdge1Start
, rEdge1Delta
, &fCut1
))
1134 aRetval
= (CUTFLAG_LINE
|CUTFLAG_START2
);
1138 if(!bFinished
&& (aCutFlags
& CUTFLAG_END1
))
1141 const B2DPoint
aEnd1(rEdge1Start
+ rEdge1Delta
);
1143 if(isPointOnEdge(aEnd1
, rEdge2Start
, rEdge2Delta
, &fCut2
))
1146 aRetval
= (CUTFLAG_LINE
|CUTFLAG_END1
);
1150 if(!bFinished
&& (aCutFlags
& CUTFLAG_END2
))
1153 const B2DPoint
aEnd2(rEdge2Start
+ rEdge2Delta
);
1155 if(isPointOnEdge(aEnd2
, rEdge1Start
, rEdge1Delta
, &fCut1
))
1158 aRetval
= (CUTFLAG_LINE
|CUTFLAG_END2
);
1164 // cut in line1, line2 ?
1165 fCut1
= (rEdge1Delta
.getX() * rEdge2Delta
.getY()) - (rEdge1Delta
.getY() * rEdge2Delta
.getX());
1167 if(!fTools::equalZero(fCut1
))
1169 fCut1
= (rEdge2Delta
.getY() * (rEdge2Start
.getX() - rEdge1Start
.getX())
1170 + rEdge2Delta
.getX() * (rEdge1Start
.getY() - rEdge2Start
.getY())) / fCut1
;
1172 const double fZero(0.0);
1173 const double fOne(1.0);
1175 // inside parameter range edge1 AND fCut2 is calcable
1176 if(fTools::more(fCut1
, fZero
) && fTools::less(fCut1
, fOne
)
1177 && (!fTools::equalZero(rEdge2Delta
.getX()) || !fTools::equalZero(rEdge2Delta
.getY())))
1179 // take the mopre precise calculation of the two possible
1180 if(fabs(rEdge2Delta
.getX()) > fabs(rEdge2Delta
.getY()))
1182 fCut2
= (rEdge1Start
.getX() + fCut1
1183 * rEdge1Delta
.getX() - rEdge2Start
.getX()) / rEdge2Delta
.getX();
1187 fCut2
= (rEdge1Start
.getY() + fCut1
1188 * rEdge1Delta
.getY() - rEdge2Start
.getY()) / rEdge2Delta
.getY();
1191 // inside parameter range edge2, too
1192 if(fTools::more(fCut2
, fZero
) && fTools::less(fCut2
, fOne
))
1195 aRetval
= CUTFLAG_LINE
;
1202 // copy values if wanted
1217 const B2DPoint
& rPoint
,
1218 const B2DPoint
& rEdgeStart
,
1219 const B2DVector
& rEdgeDelta
,
1222 bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta
.getX()));
1223 bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta
.getY()));
1224 const double fZero(0.0);
1225 const double fOne(1.0);
1227 if(bDeltaXIsZero
&& bDeltaYIsZero
)
1229 // no line, just a point
1232 else if(bDeltaXIsZero
)
1235 if(fTools::equal(rPoint
.getX(), rEdgeStart
.getX()))
1237 double fValue
= (rPoint
.getY() - rEdgeStart
.getY()) / rEdgeDelta
.getY();
1239 if(fTools::more(fValue
, fZero
) && fTools::less(fValue
, fOne
))
1250 else if(bDeltaYIsZero
)
1253 if(fTools::equal(rPoint
.getY(), rEdgeStart
.getY()))
1255 double fValue
= (rPoint
.getX() - rEdgeStart
.getX()) / rEdgeDelta
.getX();
1257 if(fTools::more(fValue
, fZero
) && fTools::less(fValue
, fOne
))
1271 double fTOne
= (rPoint
.getX() - rEdgeStart
.getX()) / rEdgeDelta
.getX();
1272 double fTTwo
= (rPoint
.getY() - rEdgeStart
.getY()) / rEdgeDelta
.getY();
1274 if(fTools::equal(fTOne
, fTTwo
))
1276 // same parameter representation, point is on line. Take
1277 // middle value for better results
1278 double fValue
= (fTOne
+ fTTwo
) / 2.0;
1280 if(fTools::more(fValue
, fZero
) && fTools::less(fValue
, fOne
))
1282 // point is inside line bounds, too
1296 void applyLineDashing(const B2DPolygon
& rCandidate
, const ::std::vector
<double>& rDotDashArray
, B2DPolyPolygon
* pLineTarget
, B2DPolyPolygon
* pGapTarget
, double fDotDashLength
)
1298 const sal_uInt32
nPointCount(rCandidate
.count());
1299 const sal_uInt32
nDotDashCount(rDotDashArray
.size());
1301 if(fTools::lessOrEqual(fDotDashLength
, 0.0))
1303 fDotDashLength
= ::std::accumulate(rDotDashArray
.begin(), rDotDashArray
.end(), 0.0);
1306 if(fTools::more(fDotDashLength
, 0.0) && (pLineTarget
|| pGapTarget
) && nPointCount
)
1311 pLineTarget
->clear();
1316 pGapTarget
->clear();
1319 // prepare current edge's start
1320 B2DCubicBezier aCurrentEdge
;
1321 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
1322 aCurrentEdge
.setStartPoint(rCandidate
.getB2DPoint(0));
1324 // prepare DotDashArray iteration and the line/gap switching bool
1325 sal_uInt32
nDotDashIndex(0);
1327 double fDotDashMovingLength(rDotDashArray
[0]);
1328 B2DPolygon aSnippet
;
1330 // iterate over all edges
1331 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1333 // update current edge (fill in C1, C2 and end point)
1334 double fLastDotDashMovingLength(0.0);
1335 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
1336 aCurrentEdge
.setControlPointA(rCandidate
.getNextControlPoint(a
));
1337 aCurrentEdge
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
1338 aCurrentEdge
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
1340 // check if we have a trivial bezier segment -> possible fallback to edge
1341 aCurrentEdge
.testAndSolveTrivialBezier();
1343 if(aCurrentEdge
.isBezier())
1346 const B2DCubicBezierHelper
aCubicBezierHelper(aCurrentEdge
);
1347 const double fEdgeLength(aCubicBezierHelper
.getLength());
1349 if(!fTools::equalZero(fEdgeLength
))
1351 while(fTools::less(fDotDashMovingLength
, fEdgeLength
))
1353 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1354 const bool bHandleLine(bIsLine
&& pLineTarget
);
1355 const bool bHandleGap(!bIsLine
&& pGapTarget
);
1357 if(bHandleLine
|| bHandleGap
)
1359 const double fBezierSplitStart(aCubicBezierHelper
.distanceToRelative(fLastDotDashMovingLength
));
1360 const double fBezierSplitEnd(aCubicBezierHelper
.distanceToRelative(fDotDashMovingLength
));
1361 B2DCubicBezier
aBezierSnippet(aCurrentEdge
.snippet(fBezierSplitStart
, fBezierSplitEnd
));
1363 if(!aSnippet
.count())
1365 aSnippet
.append(aBezierSnippet
.getStartPoint());
1368 aSnippet
.appendBezierSegment(aBezierSnippet
.getControlPointA(), aBezierSnippet
.getControlPointB(), aBezierSnippet
.getEndPoint());
1372 pLineTarget
->append(aSnippet
);
1376 pGapTarget
->append(aSnippet
);
1382 // prepare next DotDashArray step and flip line/gap flag
1383 fLastDotDashMovingLength
= fDotDashMovingLength
;
1384 fDotDashMovingLength
+= rDotDashArray
[(++nDotDashIndex
) % nDotDashCount
];
1388 // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
1389 const bool bHandleLine(bIsLine
&& pLineTarget
);
1390 const bool bHandleGap(!bIsLine
&& pGapTarget
);
1392 if(bHandleLine
|| bHandleGap
)
1394 B2DCubicBezier aRight
;
1395 const double fBezierSplit(aCubicBezierHelper
.distanceToRelative(fLastDotDashMovingLength
));
1397 aCurrentEdge
.split(fBezierSplit
, 0, &aRight
);
1399 if(!aSnippet
.count())
1401 aSnippet
.append(aRight
.getStartPoint());
1404 aSnippet
.appendBezierSegment(aRight
.getControlPointA(), aRight
.getControlPointB(), aRight
.getEndPoint());
1407 // prepare move to next edge
1408 fDotDashMovingLength
-= fEdgeLength
;
1414 const double fEdgeLength(aCurrentEdge
.getEdgeLength());
1416 if(!fTools::equalZero(fEdgeLength
))
1418 while(fTools::less(fDotDashMovingLength
, fEdgeLength
))
1420 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1421 const bool bHandleLine(bIsLine
&& pLineTarget
);
1422 const bool bHandleGap(!bIsLine
&& pGapTarget
);
1424 if(bHandleLine
|| bHandleGap
)
1426 if(!aSnippet
.count())
1428 aSnippet
.append(interpolate(aCurrentEdge
.getStartPoint(), aCurrentEdge
.getEndPoint(), fLastDotDashMovingLength
/ fEdgeLength
));
1431 aSnippet
.append(interpolate(aCurrentEdge
.getStartPoint(), aCurrentEdge
.getEndPoint(), fDotDashMovingLength
/ fEdgeLength
));
1435 pLineTarget
->append(aSnippet
);
1439 pGapTarget
->append(aSnippet
);
1445 // prepare next DotDashArray step and flip line/gap flag
1446 fLastDotDashMovingLength
= fDotDashMovingLength
;
1447 fDotDashMovingLength
+= rDotDashArray
[(++nDotDashIndex
) % nDotDashCount
];
1451 // append snippet [fLastDotDashMovingLength, fEdgeLength]
1452 const bool bHandleLine(bIsLine
&& pLineTarget
);
1453 const bool bHandleGap(!bIsLine
&& pGapTarget
);
1455 if(bHandleLine
|| bHandleGap
)
1457 if(!aSnippet
.count())
1459 aSnippet
.append(interpolate(aCurrentEdge
.getStartPoint(), aCurrentEdge
.getEndPoint(), fLastDotDashMovingLength
/ fEdgeLength
));
1462 aSnippet
.append(aCurrentEdge
.getEndPoint());
1465 // prepare move to next edge
1466 fDotDashMovingLength
-= fEdgeLength
;
1470 // prepare next edge step (end point gets new start point)
1471 aCurrentEdge
.setStartPoint(aCurrentEdge
.getEndPoint());
1474 // append last intermediate results (if exists)
1475 if(aSnippet
.count())
1477 if(bIsLine
&& pLineTarget
)
1479 pLineTarget
->append(aSnippet
);
1481 else if(!bIsLine
&& pGapTarget
)
1483 pGapTarget
->append(aSnippet
);
1487 // check if start and end polygon may be merged
1490 const sal_uInt32
nCount(pLineTarget
->count());
1494 // these polygons were created above, there exists none with less than two points,
1495 // thus dircet point access below is allowed
1496 const B2DPolygon
aFirst(pLineTarget
->getB2DPolygon(0));
1497 B2DPolygon
aLast(pLineTarget
->getB2DPolygon(nCount
- 1));
1499 if(aFirst
.getB2DPoint(0).equal(aLast
.getB2DPoint(aLast
.count() - 1)))
1501 // start of first and end of last are the same -> merge them
1502 aLast
.append(aFirst
);
1503 aLast
.removeDoublePoints();
1504 pLineTarget
->setB2DPolygon(0, aLast
);
1505 pLineTarget
->remove(nCount
- 1);
1512 const sal_uInt32
nCount(pGapTarget
->count());
1516 // these polygons were created above, there exists none with less than two points,
1517 // thus dircet point access below is allowed
1518 const B2DPolygon
aFirst(pGapTarget
->getB2DPolygon(0));
1519 B2DPolygon
aLast(pGapTarget
->getB2DPolygon(nCount
- 1));
1521 if(aFirst
.getB2DPoint(0).equal(aLast
.getB2DPoint(aLast
.count() - 1)))
1523 // start of first and end of last are the same -> merge them
1524 aLast
.append(aFirst
);
1525 aLast
.removeDoublePoints();
1526 pGapTarget
->setB2DPolygon(0, aLast
);
1527 pGapTarget
->remove(nCount
- 1);
1534 // parameters make no sense, just add source to targets
1537 pLineTarget
->append(rCandidate
);
1542 pGapTarget
->append(rCandidate
);
1547 // test if point is inside epsilon-range around an edge defined
1548 // by the two given points. Can be used for HitTesting. The epsilon-range
1549 // is defined to be the rectangle centered to the given edge, using height
1550 // 2 x fDistance, and the circle around both points with radius fDistance.
1551 bool isInEpsilonRange(const B2DPoint
& rEdgeStart
, const B2DPoint
& rEdgeEnd
, const B2DPoint
& rTestPosition
, double fDistance
)
1553 // build edge vector
1554 const B2DVector
aEdge(rEdgeEnd
- rEdgeStart
);
1555 bool bDoDistanceTestStart(false);
1556 bool bDoDistanceTestEnd(false);
1558 if(aEdge
.equalZero())
1560 // no edge, just a point. Do one of the distance tests.
1561 bDoDistanceTestStart
= true;
1565 // edge has a length. Create perpendicular vector.
1566 const B2DVector
aPerpend(getPerpendicular(aEdge
));
1568 (aPerpend
.getY() * (rTestPosition
.getX() - rEdgeStart
.getX())
1569 + aPerpend
.getX() * (rEdgeStart
.getY() - rTestPosition
.getY())) /
1570 (aEdge
.getX() * aEdge
.getX() + aEdge
.getY() * aEdge
.getY()));
1571 const double fZero(0.0);
1572 const double fOne(1.0);
1574 if(fTools::less(fCut
, fZero
))
1576 // left of rEdgeStart
1577 bDoDistanceTestStart
= true;
1579 else if(fTools::more(fCut
, fOne
))
1581 // right of rEdgeEnd
1582 bDoDistanceTestEnd
= true;
1586 // inside line [0.0 .. 1.0]
1587 const B2DPoint
aCutPoint(interpolate(rEdgeStart
, rEdgeEnd
, fCut
));
1588 const B2DVector
aDelta(rTestPosition
- aCutPoint
);
1589 const double fDistanceSquare(aDelta
.scalar(aDelta
));
1591 if(fDistanceSquare
<= fDistance
* fDistance
)
1602 if(bDoDistanceTestStart
)
1604 const B2DVector
aDelta(rTestPosition
- rEdgeStart
);
1605 const double fDistanceSquare(aDelta
.scalar(aDelta
));
1607 if(fDistanceSquare
<= fDistance
* fDistance
)
1612 else if(bDoDistanceTestEnd
)
1614 const B2DVector
aDelta(rTestPosition
- rEdgeEnd
);
1615 const double fDistanceSquare(aDelta
.scalar(aDelta
));
1617 if(fDistanceSquare
<= fDistance
* fDistance
)
1626 // test if point is inside epsilon-range around the given Polygon. Can be used
1627 // for HitTesting. The epsilon-range is defined to be the tube around the polygon
1628 // with distance fDistance and rounded edges (start and end point).
1629 bool isInEpsilonRange(const B2DPolygon
& rCandidate
, const B2DPoint
& rTestPosition
, double fDistance
)
1631 // force to non-bezier polygon
1632 const B2DPolygon
aCandidate(rCandidate
.getDefaultAdaptiveSubdivision());
1633 const sal_uInt32
nPointCount(aCandidate
.count());
1637 const sal_uInt32
nEdgeCount(aCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
1638 B2DPoint
aCurrent(aCandidate
.getB2DPoint(0));
1643 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1645 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
1646 const B2DPoint
aNext(aCandidate
.getB2DPoint(nNextIndex
));
1648 if(isInEpsilonRange(aCurrent
, aNext
, rTestPosition
, fDistance
))
1653 // prepare next step
1659 // no edges, but points -> not closed. Check single point. Just
1660 // use isInEpsilonRange with twice the same point, it handles those well
1661 if(isInEpsilonRange(aCurrent
, aCurrent
, rTestPosition
, fDistance
))
1671 B2DPolygon
createPolygonFromRect( const B2DRectangle
& rRect
, double fRadius
)
1673 const double fZero(0.0);
1674 const double fOne(1.0);
1676 if(fTools::lessOrEqual(fRadius
, fZero
))
1678 // no radius, use rectangle
1679 return createPolygonFromRect( rRect
);
1681 else if(fTools::moreOrEqual(fRadius
, fOne
))
1683 // full radius, use ellipse
1684 const B2DPoint
aCenter(rRect
.getCenter());
1685 const double fRadiusX(rRect
.getWidth() / 2.0);
1686 const double fRadiusY(rRect
.getHeight() / 2.0);
1688 return createPolygonFromEllipse( aCenter
, fRadiusX
, fRadiusY
);
1692 // create rectangle with two radii between ]0.0 .. 1.0[
1693 return createPolygonFromRect( rRect
, fRadius
, fRadius
);
1697 B2DPolygon
createPolygonFromRect( const B2DRectangle
& rRect
, double fRadiusX
, double fRadiusY
)
1699 const double fZero(0.0);
1700 const double fOne(1.0);
1702 // crop to useful values
1703 if(fTools::less(fRadiusX
, fZero
))
1707 else if(fTools::more(fRadiusX
, fOne
))
1712 if(fTools::less(fRadiusY
, fZero
))
1716 else if(fTools::more(fRadiusY
, fOne
))
1721 if(fZero
== fRadiusX
|| fZero
== fRadiusY
)
1725 // at least in one direction no radius, use rectangle.
1726 // Do not use createPolygonFromRect() here since original
1727 // creator (historical reasons) still creates a start point at the
1728 // bottom center, so do the same here to get the same line patterns.
1729 // Due to this the order of points is different, too.
1730 const B2DPoint
aBottomCenter(rRect
.getCenter().getX(), rRect
.getMaxY());
1731 aRetval
.append(aBottomCenter
);
1733 aRetval
.append( B2DPoint( rRect
.getMinX(), rRect
.getMaxY() ) );
1734 aRetval
.append( B2DPoint( rRect
.getMinX(), rRect
.getMinY() ) );
1735 aRetval
.append( B2DPoint( rRect
.getMaxX(), rRect
.getMinY() ) );
1736 aRetval
.append( B2DPoint( rRect
.getMaxX(), rRect
.getMaxY() ) );
1739 aRetval
.setClosed( true );
1743 else if(fOne
== fRadiusX
&& fOne
== fRadiusY
)
1745 // in both directions full radius, use ellipse
1746 const B2DPoint
aCenter(rRect
.getCenter());
1747 const double fRectRadiusX(rRect
.getWidth() / 2.0);
1748 const double fRectRadiusY(rRect
.getHeight() / 2.0);
1750 return createPolygonFromEllipse( aCenter
, fRectRadiusX
, fRectRadiusY
);
1755 const double fBowX((rRect
.getWidth() / 2.0) * fRadiusX
);
1756 const double fBowY((rRect
.getHeight() / 2.0) * fRadiusY
);
1757 const double fKappa((M_SQRT2
- 1.0) * 4.0 / 3.0);
1759 // create start point at bottom center
1760 if(fOne
!= fRadiusX
)
1762 const B2DPoint
aBottomCenter(rRect
.getCenter().getX(), rRect
.getMaxY());
1763 aRetval
.append(aBottomCenter
);
1768 const B2DPoint
aBottomRight(rRect
.getMaxX(), rRect
.getMaxY());
1769 const B2DPoint
aStart(aBottomRight
+ B2DPoint(-fBowX
, 0.0));
1770 const B2DPoint
aStop(aBottomRight
+ B2DPoint(0.0, -fBowY
));
1771 aRetval
.append(aStart
);
1772 aRetval
.appendBezierSegment(interpolate(aStart
, aBottomRight
, fKappa
), interpolate(aStop
, aBottomRight
, fKappa
), aStop
);
1775 // create second bow
1777 const B2DPoint
aTopRight(rRect
.getMaxX(), rRect
.getMinY());
1778 const B2DPoint
aStart(aTopRight
+ B2DPoint(0.0, fBowY
));
1779 const B2DPoint
aStop(aTopRight
+ B2DPoint(-fBowX
, 0.0));
1780 aRetval
.append(aStart
);
1781 aRetval
.appendBezierSegment(interpolate(aStart
, aTopRight
, fKappa
), interpolate(aStop
, aTopRight
, fKappa
), aStop
);
1786 const B2DPoint
aTopLeft(rRect
.getMinX(), rRect
.getMinY());
1787 const B2DPoint
aStart(aTopLeft
+ B2DPoint(fBowX
, 0.0));
1788 const B2DPoint
aStop(aTopLeft
+ B2DPoint(0.0, fBowY
));
1789 aRetval
.append(aStart
);
1790 aRetval
.appendBezierSegment(interpolate(aStart
, aTopLeft
, fKappa
), interpolate(aStop
, aTopLeft
, fKappa
), aStop
);
1795 const B2DPoint
aBottomLeft(rRect
.getMinX(), rRect
.getMaxY());
1796 const B2DPoint
aStart(aBottomLeft
+ B2DPoint(0.0, -fBowY
));
1797 const B2DPoint
aStop(aBottomLeft
+ B2DPoint(fBowX
, 0.0));
1798 aRetval
.append(aStart
);
1799 aRetval
.appendBezierSegment(interpolate(aStart
, aBottomLeft
, fKappa
), interpolate(aStop
, aBottomLeft
, fKappa
), aStop
);
1803 aRetval
.setClosed( true );
1805 // remove double created points if there are extreme radii envolved
1806 if(fOne
== fRadiusX
|| fOne
== fRadiusY
)
1808 aRetval
.removeDoublePoints();
1815 B2DPolygon
createPolygonFromRect( const B2DRectangle
& rRect
)
1819 aRetval
.append( B2DPoint( rRect
.getMinX(), rRect
.getMinY() ) );
1820 aRetval
.append( B2DPoint( rRect
.getMaxX(), rRect
.getMinY() ) );
1821 aRetval
.append( B2DPoint( rRect
.getMaxX(), rRect
.getMaxY() ) );
1822 aRetval
.append( B2DPoint( rRect
.getMinX(), rRect
.getMaxY() ) );
1825 aRetval
.setClosed( true );
1830 B2DPolygon
createPolygonFromCircle( const B2DPoint
& rCenter
, double fRadius
)
1832 return createPolygonFromEllipse( rCenter
, fRadius
, fRadius
);
1835 void appendUnitCircleQuadrant(B2DPolygon
& rPolygon
, sal_uInt32 nQuadrant
)
1837 const double fZero(0.0);
1838 const double fOne(1.0);
1839 const double fKappa((M_SQRT2
- 1.0) * 4.0 / 3.0);
1841 // create closed unit-circle with 4 segments
1844 case 0 : // first quadrant
1846 rPolygon
.append(B2DPoint(fOne
, fZero
));
1847 rPolygon
.appendBezierSegment(B2DPoint(fOne
, fKappa
), B2DPoint(fKappa
, fOne
), B2DPoint(fZero
, fOne
));
1850 case 1 : // second quadrant
1852 rPolygon
.append(B2DPoint(fZero
, fOne
));
1853 rPolygon
.appendBezierSegment(B2DPoint(-fKappa
, fOne
), B2DPoint(-fOne
, fKappa
), B2DPoint(-fOne
, fZero
));
1856 case 2 : // third quadrant
1858 rPolygon
.append(B2DPoint(-fOne
, fZero
));
1859 rPolygon
.appendBezierSegment(B2DPoint(-fOne
, -fKappa
), B2DPoint(-fKappa
, -fOne
), B2DPoint(fZero
, -fOne
));
1862 default : // last quadrant
1864 rPolygon
.append(B2DPoint(fZero
, -fOne
));
1865 rPolygon
.appendBezierSegment(B2DPoint(fKappa
, -fOne
), B2DPoint(fOne
, -fKappa
), B2DPoint(fOne
, fZero
));
1871 B2DPolygon
createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant
)
1875 // create unit-circle with all 4 segments, close it
1876 appendUnitCircleQuadrant(aRetval
, nStartQuadrant
% 4); nStartQuadrant
++;
1877 appendUnitCircleQuadrant(aRetval
, nStartQuadrant
% 4); nStartQuadrant
++;
1878 appendUnitCircleQuadrant(aRetval
, nStartQuadrant
% 4); nStartQuadrant
++;
1879 appendUnitCircleQuadrant(aRetval
, nStartQuadrant
% 4); nStartQuadrant
++;
1880 aRetval
.setClosed(true);
1882 // remove double points between segments created by segmented creation
1883 aRetval
.removeDoublePoints();
1888 B2DPolygon
createPolygonFromEllipse( const B2DPoint
& rCenter
, double fRadiusX
, double fRadiusY
)
1890 const double fOne(1.0);
1891 B2DPolygon
aRetval(createPolygonFromUnitCircle());
1893 // transformation necessary?
1894 const sal_Bool
bScale(!fTools::equal(fRadiusX
, fOne
) || !fTools::equal(fRadiusY
, fOne
));
1895 const sal_Bool
bTranslate(!rCenter
.equalZero());
1897 if(bScale
|| bTranslate
)
1899 B2DHomMatrix aMatrix
;
1903 aMatrix
.scale(fRadiusX
, fRadiusY
);
1908 aMatrix
.translate(rCenter
.getX(), rCenter
.getY());
1911 aRetval
.transform(aMatrix
);
1917 void appendUnitCircleQuadrantSegment(B2DPolygon
& rPolygon
, sal_uInt32 nQuadrant
, double fStart
, double fEnd
)
1919 OSL_ENSURE(fStart
>= 0.0 && fStart
<= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)");
1920 OSL_ENSURE(fEnd
>= 0.0 && fEnd
<= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)");
1921 OSL_ENSURE(fEnd
>= fStart
, "appendUnitCircleQuadrantSegment: Access out of range (!)");
1922 const double fOne(1.0);
1923 const bool bStartIsZero(fTools::equalZero(fStart
));
1924 const bool bEndIsOne(fTools::equal(fEnd
, fOne
));
1926 if(bStartIsZero
&& bEndIsOne
)
1929 appendUnitCircleQuadrant(rPolygon
, nQuadrant
);
1934 B2DPolygon aQuadrant
;
1935 appendUnitCircleQuadrant(aQuadrant
, nQuadrant
);
1936 const bool bStartEndEqual(fTools::equal(fStart
, fEnd
));
1942 // both zero, add start point
1943 rPolygon
.append(aQuadrant
.getB2DPoint(0L));
1947 // both one, add end point
1948 rPolygon
.append(aQuadrant
.getB2DPoint(1L));
1952 // both equal but not zero, add split point
1953 B2DCubicBezier
aCubicBezier(
1954 aQuadrant
.getB2DPoint(0L), aQuadrant
.getNextControlPoint(0L),
1955 aQuadrant
.getPrevControlPoint(1L), aQuadrant
.getB2DPoint(1L));
1957 aCubicBezier
.split(fStart
, &aCubicBezier
, 0);
1958 rPolygon
.append(aCubicBezier
.getEndPoint());
1963 B2DCubicBezier
aCubicBezier(
1964 aQuadrant
.getB2DPoint(0L), aQuadrant
.getNextControlPoint(0L),
1965 aQuadrant
.getPrevControlPoint(1L), aQuadrant
.getB2DPoint(1L));
1967 aCubicBezier
= aCubicBezier
.snippet(fStart
, fEnd
);
1968 rPolygon
.append(aCubicBezier
.getStartPoint());
1969 rPolygon
.appendBezierSegment(aCubicBezier
.getControlPointA(), aCubicBezier
.getControlPointB(), aCubicBezier
.getEndPoint());
1974 B2DPolygon
createPolygonFromUnitEllipseSegment( double fStart
, double fEnd
)
1978 // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI
1979 // falls back to 0.0 to ensure a unique definition
1980 if(fTools::less(fStart
, 0.0))
1985 if(fTools::moreOrEqual(fStart
, F_2PI
))
1990 if(fTools::less(fEnd
, 0.0))
1995 if(fTools::moreOrEqual(fEnd
, F_2PI
))
2000 const sal_uInt32
nQuadrantStart(sal_uInt32(fStart
/ F_PI2
) % 4L);
2001 const sal_uInt32
nQuadrantEnd(sal_uInt32(fEnd
/ F_PI2
) % 4L);
2002 sal_uInt32
nCurrentQuadrant(nQuadrantStart
);
2003 bool bStartDone(false);
2004 bool bEndDone(false);
2008 if(!bStartDone
&& nQuadrantStart
== nCurrentQuadrant
)
2010 if(nQuadrantStart
== nQuadrantEnd
&& fTools::moreOrEqual(fEnd
, fStart
))
2012 // both in one quadrant and defining the complete segment, create start to end
2013 double fSplitOffsetStart((fStart
- (nCurrentQuadrant
* F_PI2
)) / F_PI2
);
2014 double fSplitOffsetEnd((fEnd
- (nCurrentQuadrant
* F_PI2
)) / F_PI2
);
2015 appendUnitCircleQuadrantSegment(aRetval
, nCurrentQuadrant
, fSplitOffsetStart
, fSplitOffsetEnd
);
2016 bStartDone
= bEndDone
= true;
2020 // create start to quadrant end
2021 const double fSplitOffsetStart((fStart
- (nCurrentQuadrant
* F_PI2
)) / F_PI2
);
2022 appendUnitCircleQuadrantSegment(aRetval
, nCurrentQuadrant
, fSplitOffsetStart
, 1.0);
2026 else if(!bEndDone
&& nQuadrantEnd
== nCurrentQuadrant
)
2028 // create quadrant start to end
2029 const double fSplitOffsetEnd((fEnd
- (nCurrentQuadrant
* F_PI2
)) / F_PI2
);
2030 appendUnitCircleQuadrantSegment(aRetval
, nCurrentQuadrant
, 0.0, fSplitOffsetEnd
);
2035 // add quadrant completely
2036 appendUnitCircleQuadrant(aRetval
, nCurrentQuadrant
);
2040 nCurrentQuadrant
= (nCurrentQuadrant
+ 1L) % 4L;
2042 while(!(bStartDone
&& bEndDone
));
2044 // remove double points between segments created by segmented creation
2045 aRetval
.removeDoublePoints();
2050 B2DPolygon
createPolygonFromEllipseSegment( const B2DPoint
& rCenter
, double fRadiusX
, double fRadiusY
, double fStart
, double fEnd
)
2052 B2DPolygon
aRetval(createPolygonFromUnitEllipseSegment(fStart
, fEnd
));
2054 // transformation necessary?
2055 const double fOne(1.0);
2056 const sal_Bool
bScale(!fTools::equal(fRadiusX
, fOne
) || !fTools::equal(fRadiusY
, fOne
));
2057 const sal_Bool
bTranslate(!rCenter
.equalZero());
2059 if(bScale
|| bTranslate
)
2061 B2DHomMatrix aMatrix
;
2065 aMatrix
.scale(fRadiusX
, fRadiusY
);
2070 aMatrix
.translate(rCenter
.getX(), rCenter
.getY());
2073 aRetval
.transform(aMatrix
);
2079 bool hasNeutralPoints(const B2DPolygon
& rCandidate
)
2081 OSL_ENSURE(!rCandidate
.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)");
2082 const sal_uInt32
nPointCount(rCandidate
.count());
2084 if(nPointCount
> 2L)
2086 B2DPoint
aPrevPoint(rCandidate
.getB2DPoint(nPointCount
- 1L));
2087 B2DPoint
aCurrPoint(rCandidate
.getB2DPoint(0L));
2089 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
2091 const B2DPoint
aNextPoint(rCandidate
.getB2DPoint((a
+ 1) % nPointCount
));
2092 const B2DVector
aPrevVec(aPrevPoint
- aCurrPoint
);
2093 const B2DVector
aNextVec(aNextPoint
- aCurrPoint
);
2094 const B2VectorOrientation
aOrientation(getOrientation(aNextVec
, aPrevVec
));
2096 if(ORIENTATION_NEUTRAL
== aOrientation
)
2098 // current has neutral orientation
2104 aPrevPoint
= aCurrPoint
;
2105 aCurrPoint
= aNextPoint
;
2113 B2DPolygon
removeNeutralPoints(const B2DPolygon
& rCandidate
)
2115 if(hasNeutralPoints(rCandidate
))
2117 const sal_uInt32
nPointCount(rCandidate
.count());
2119 B2DPoint
aPrevPoint(rCandidate
.getB2DPoint(nPointCount
- 1L));
2120 B2DPoint
aCurrPoint(rCandidate
.getB2DPoint(0L));
2122 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
2124 const B2DPoint
aNextPoint(rCandidate
.getB2DPoint((a
+ 1) % nPointCount
));
2125 const B2DVector
aPrevVec(aPrevPoint
- aCurrPoint
);
2126 const B2DVector
aNextVec(aNextPoint
- aCurrPoint
);
2127 const B2VectorOrientation
aOrientation(getOrientation(aNextVec
, aPrevVec
));
2129 if(ORIENTATION_NEUTRAL
== aOrientation
)
2131 // current has neutral orientation, leave it out and prepare next
2132 aCurrPoint
= aNextPoint
;
2136 // add current point
2137 aRetval
.append(aCurrPoint
);
2140 aPrevPoint
= aCurrPoint
;
2141 aCurrPoint
= aNextPoint
;
2145 while(aRetval
.count() && ORIENTATION_NEUTRAL
== getOrientationForIndex(aRetval
, 0L))
2150 // copy closed state
2151 aRetval
.setClosed(rCandidate
.isClosed());
2161 bool isConvex(const B2DPolygon
& rCandidate
)
2163 OSL_ENSURE(!rCandidate
.areControlPointsUsed(), "isConvex: ATM works not for curves (!)");
2164 const sal_uInt32
nPointCount(rCandidate
.count());
2166 if(nPointCount
> 2L)
2168 const B2DPoint
aPrevPoint(rCandidate
.getB2DPoint(nPointCount
- 1L));
2169 B2DPoint
aCurrPoint(rCandidate
.getB2DPoint(0L));
2170 B2DVector
aCurrVec(aPrevPoint
- aCurrPoint
);
2171 B2VectorOrientation
aOrientation(ORIENTATION_NEUTRAL
);
2173 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
2175 const B2DPoint
aNextPoint(rCandidate
.getB2DPoint((a
+ 1) % nPointCount
));
2176 const B2DVector
aNextVec(aNextPoint
- aCurrPoint
);
2177 const B2VectorOrientation
aCurrentOrientation(getOrientation(aNextVec
, aCurrVec
));
2179 if(ORIENTATION_NEUTRAL
== aOrientation
)
2181 // set start value, maybe neutral again
2182 aOrientation
= aCurrentOrientation
;
2186 if(ORIENTATION_NEUTRAL
!= aCurrentOrientation
&& aCurrentOrientation
!= aOrientation
)
2188 // different orientations found, that's it
2194 aCurrPoint
= aNextPoint
;
2195 aCurrVec
= -aNextVec
;
2202 B2VectorOrientation
getOrientationForIndex(const B2DPolygon
& rCandidate
, sal_uInt32 nIndex
)
2204 OSL_ENSURE(nIndex
< rCandidate
.count(), "getOrientationForIndex: index out of range (!)");
2205 const B2DPoint
aPrev(rCandidate
.getB2DPoint(getIndexOfPredecessor(nIndex
, rCandidate
)));
2206 const B2DPoint
aCurr(rCandidate
.getB2DPoint(nIndex
));
2207 const B2DPoint
aNext(rCandidate
.getB2DPoint(getIndexOfSuccessor(nIndex
, rCandidate
)));
2208 const B2DVector
aBack(aPrev
- aCurr
);
2209 const B2DVector
aForw(aNext
- aCurr
);
2211 return getOrientation(aForw
, aBack
);
2214 bool isPointOnLine(const B2DPoint
& rStart
, const B2DPoint
& rEnd
, const B2DPoint
& rCandidate
, bool bWithPoints
)
2216 if(rCandidate
.equal(rStart
) || rCandidate
.equal(rEnd
))
2218 // candidate is in epsilon around start or end -> inside
2221 else if(rStart
.equal(rEnd
))
2223 // start and end are equal, but candidate is outside their epsilon -> outside
2228 const B2DVector
aEdgeVector(rEnd
- rStart
);
2229 const B2DVector
aTestVector(rCandidate
- rStart
);
2231 if(areParallel(aEdgeVector
, aTestVector
))
2233 const double fZero(0.0);
2234 const double fOne(1.0);
2235 const double fParamTestOnCurr(fabs(aEdgeVector
.getX()) > fabs(aEdgeVector
.getY())
2236 ? aTestVector
.getX() / aEdgeVector
.getX()
2237 : aTestVector
.getY() / aEdgeVector
.getY());
2239 if(fTools::more(fParamTestOnCurr
, fZero
) && fTools::less(fParamTestOnCurr
, fOne
))
2249 bool isPointOnPolygon(const B2DPolygon
& rCandidate
, const B2DPoint
& rPoint
, bool bWithPoints
)
2251 const B2DPolygon
aCandidate(rCandidate
.areControlPointsUsed() ? rCandidate
.getDefaultAdaptiveSubdivision() : rCandidate
);
2252 const sal_uInt32
nPointCount(aCandidate
.count());
2254 if(nPointCount
> 1L)
2256 const sal_uInt32
nLoopCount(aCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
2257 B2DPoint
aCurrentPoint(aCandidate
.getB2DPoint(0L));
2259 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
2261 const B2DPoint
aNextPoint(aCandidate
.getB2DPoint((a
+ 1L) % nPointCount
));
2263 if(isPointOnLine(aCurrentPoint
, aNextPoint
, rPoint
, bWithPoints
))
2268 aCurrentPoint
= aNextPoint
;
2271 else if(nPointCount
&& bWithPoints
)
2273 return rPoint
.equal(aCandidate
.getB2DPoint(0L));
2279 bool isPointInTriangle(const B2DPoint
& rA
, const B2DPoint
& rB
, const B2DPoint
& rC
, const B2DPoint
& rCandidate
, bool bWithBorder
)
2281 if(arePointsOnSameSideOfLine(rA
, rB
, rC
, rCandidate
, bWithBorder
))
2283 if(arePointsOnSameSideOfLine(rB
, rC
, rA
, rCandidate
, bWithBorder
))
2285 if(arePointsOnSameSideOfLine(rC
, rA
, rB
, rCandidate
, bWithBorder
))
2295 bool arePointsOnSameSideOfLine(const B2DPoint
& rStart
, const B2DPoint
& rEnd
, const B2DPoint
& rCandidateA
, const B2DPoint
& rCandidateB
, bool bWithLine
)
2297 const B2DVector
aLineVector(rEnd
- rStart
);
2298 const B2DVector
aVectorToA(rEnd
- rCandidateA
);
2299 const double fCrossA(aLineVector
.cross(aVectorToA
));
2301 if(fTools::equalZero(fCrossA
))
2303 // one point on the line
2307 const B2DVector
aVectorToB(rEnd
- rCandidateB
);
2308 const double fCrossB(aLineVector
.cross(aVectorToB
));
2310 if(fTools::equalZero(fCrossB
))
2312 // one point on the line
2316 // return true if they both have the same sign
2317 return ((fCrossA
> 0.0) == (fCrossB
> 0.0));
2320 void addTriangleFan(const B2DPolygon
& rCandidate
, B2DPolygon
& rTarget
)
2322 const sal_uInt32
nCount(rCandidate
.count());
2326 const B2DPoint
aStart(rCandidate
.getB2DPoint(0L));
2327 B2DPoint
aLast(rCandidate
.getB2DPoint(1L));
2329 for(sal_uInt32
a(2L); a
< nCount
; a
++)
2331 const B2DPoint
aCurrent(rCandidate
.getB2DPoint(a
));
2332 rTarget
.append(aStart
);
2333 rTarget
.append(aLast
);
2334 rTarget
.append(aCurrent
);
2344 /// return 0 for input of 0, -1 for negative and 1 for positive input
2345 inline int lcl_sgn( const double n
)
2347 return n
== 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n
);
2351 bool isRectangle( const B2DPolygon
& rPoly
)
2353 // polygon must be closed to resemble a rect, and contain
2354 // at least four points.
2355 if( !rPoly
.isClosed() ||
2361 // number of 90 degree turns the polygon has taken
2364 int nVerticalEdgeType
=0;
2365 int nHorizontalEdgeType
=0;
2366 bool bNullVertex(true);
2367 bool bCWPolygon(false); // when true, polygon is CW
2368 // oriented, when false, CCW
2369 bool bOrientationSet(false); // when false, polygon
2370 // orientation has not yet
2373 // scan all _edges_ (which involves coming back to point 0
2374 // for the last edge - thus the modulo operation below)
2375 const sal_Int32
nCount( rPoly
.count() );
2376 for( sal_Int32 i
=0; i
<nCount
; ++i
)
2378 const B2DPoint
& rPoint0( rPoly
.getB2DPoint(i
% nCount
) );
2379 const B2DPoint
& rPoint1( rPoly
.getB2DPoint((i
+1) % nCount
) );
2381 // is 0 for zero direction vector, 1 for south edge and -1
2382 // for north edge (standard screen coordinate system)
2383 int nCurrVerticalEdgeType( lcl_sgn( rPoint1
.getY() - rPoint0
.getY() ) );
2385 // is 0 for zero direction vector, 1 for east edge and -1
2386 // for west edge (standard screen coordinate system)
2387 int nCurrHorizontalEdgeType( lcl_sgn(rPoint1
.getX() - rPoint0
.getX()) );
2389 if( nCurrVerticalEdgeType
&& nCurrHorizontalEdgeType
)
2390 return false; // oblique edge - for sure no rect
2392 const bool bCurrNullVertex( !nCurrVerticalEdgeType
&& !nCurrHorizontalEdgeType
);
2394 // current vertex is equal to previous - just skip,
2395 // until we have a real edge
2396 if( bCurrNullVertex
)
2399 // if previous edge has two identical points, because
2400 // no previous edge direction was available, simply
2401 // take this first non-null edge as the start
2402 // direction. That's what will happen here, if
2403 // bNullVertex is false
2406 // 2D cross product - is 1 for CW and -1 for CCW turns
2407 const int nCrossProduct( nHorizontalEdgeType
*nCurrVerticalEdgeType
-
2408 nVerticalEdgeType
*nCurrHorizontalEdgeType
);
2410 if( !nCrossProduct
)
2411 continue; // no change in orientation -
2412 // collinear edges - just go on
2414 // if polygon orientation is not set, we'll
2416 if( !bOrientationSet
)
2418 bCWPolygon
= nCrossProduct
== 1;
2419 bOrientationSet
= true;
2423 // if current turn orientation is not equal
2424 // initial orientation, this is not a
2425 // rectangle (as rectangles have consistent
2427 if( (nCrossProduct
== 1) != bCWPolygon
)
2433 // More than four 90 degree turns are an
2434 // indication that this must not be a rectangle.
2439 // store current state for the next turn
2440 nVerticalEdgeType
= nCurrVerticalEdgeType
;
2441 nHorizontalEdgeType
= nCurrHorizontalEdgeType
;
2442 bNullVertex
= false; // won't reach this line,
2443 // if bCurrNullVertex is
2450 B3DPolygon
createB3DPolygonFromB2DPolygon(const B2DPolygon
& rCandidate
, double fZCoordinate
)
2452 if(rCandidate
.areControlPointsUsed())
2454 // call myself recursively with subdivided input
2455 const B2DPolygon
aCandidate(adaptiveSubdivideByAngle(rCandidate
));
2456 return createB3DPolygonFromB2DPolygon(aCandidate
, fZCoordinate
);
2462 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
2464 B2DPoint
aPoint(rCandidate
.getB2DPoint(a
));
2465 aRetval
.append(B3DPoint(aPoint
.getX(), aPoint
.getY(), fZCoordinate
));
2468 // copy closed state
2469 aRetval
.setClosed(rCandidate
.isClosed());
2475 B2DPolygon
createB2DPolygonFromB3DPolygon(const B3DPolygon
& rCandidate
, const B3DHomMatrix
& rMat
)
2478 const sal_uInt32
nCount(rCandidate
.count());
2479 const bool bIsIdentity(rMat
.isIdentity());
2481 for(sal_uInt32
a(0L); a
< nCount
; a
++)
2483 B3DPoint
aCandidate(rCandidate
.getB3DPoint(a
));
2490 aRetval
.append(B2DPoint(aCandidate
.getX(), aCandidate
.getY()));
2493 // copy closed state
2494 aRetval
.setClosed(rCandidate
.isClosed());
2499 double getDistancePointToEndlessRay(const B2DPoint
& rPointA
, const B2DPoint
& rPointB
, const B2DPoint
& rTestPoint
, double& rCut
)
2501 if(rPointA
.equal(rPointB
))
2504 const B2DVector
aVector(rTestPoint
- rPointA
);
2505 return aVector
.getLength();
2509 // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
2510 const B2DVector
aVector1(rPointB
- rPointA
);
2511 const B2DVector
aVector2(rTestPoint
- rPointA
);
2512 const double fDividend((aVector2
.getX() * aVector1
.getX()) + (aVector2
.getY() * aVector1
.getY()));
2513 const double fDivisor((aVector1
.getX() * aVector1
.getX()) + (aVector1
.getY() * aVector1
.getY()));
2515 rCut
= fDividend
/ fDivisor
;
2517 const B2DPoint
aCutPoint(rPointA
+ rCut
* aVector1
);
2518 const B2DVector
aVector(rTestPoint
- aCutPoint
);
2519 return aVector
.getLength();
2523 double getSmallestDistancePointToEdge(const B2DPoint
& rPointA
, const B2DPoint
& rPointB
, const B2DPoint
& rTestPoint
, double& rCut
)
2525 if(rPointA
.equal(rPointB
))
2528 const B2DVector
aVector(rTestPoint
- rPointA
);
2529 return aVector
.getLength();
2533 // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
2534 const B2DVector
aVector1(rPointB
- rPointA
);
2535 const B2DVector
aVector2(rTestPoint
- rPointA
);
2536 const double fDividend((aVector2
.getX() * aVector1
.getX()) + (aVector2
.getY() * aVector1
.getY()));
2537 const double fDivisor((aVector1
.getX() * aVector1
.getX()) + (aVector1
.getY() * aVector1
.getY()));
2538 const double fCut(fDividend
/ fDivisor
);
2542 // not in line range, get distance to PointA
2544 return aVector2
.getLength();
2548 // not in line range, get distance to PointB
2550 const B2DVector
aVector(rTestPoint
- rPointB
);
2551 return aVector
.getLength();
2556 const B2DPoint
aCutPoint(rPointA
+ fCut
* aVector1
);
2557 const B2DVector
aVector(rTestPoint
- aCutPoint
);
2559 return aVector
.getLength();
2564 double getSmallestDistancePointToPolygon(const B2DPolygon
& rCandidate
, const B2DPoint
& rTestPoint
, sal_uInt32
& rEdgeIndex
, double& rCut
)
2566 double fRetval(DBL_MAX
);
2567 const sal_uInt32
nPointCount(rCandidate
.count());
2569 if(nPointCount
> 1L)
2571 const double fZero(0.0);
2572 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
2573 B2DCubicBezier aBezier
;
2574 aBezier
.setStartPoint(rCandidate
.getB2DPoint(0));
2576 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
2578 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
2579 aBezier
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
2582 bool bEdgeIsCurve(false);
2584 if(rCandidate
.areControlPointsUsed())
2586 aBezier
.setControlPointA(rCandidate
.getNextControlPoint(a
));
2587 aBezier
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
2588 aBezier
.testAndSolveTrivialBezier();
2589 bEdgeIsCurve
= aBezier
.isBezier();
2594 fEdgeDist
= aBezier
.getSmallestDistancePointToBezierSegment(rTestPoint
, fNewCut
);
2598 fEdgeDist
= getSmallestDistancePointToEdge(aBezier
.getStartPoint(), aBezier
.getEndPoint(), rTestPoint
, fNewCut
);
2601 if(DBL_MAX
== fRetval
|| fEdgeDist
< fRetval
)
2603 fRetval
= fEdgeDist
;
2607 if(fTools::equal(fRetval
, fZero
))
2609 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
2615 // prepare next step
2616 aBezier
.setStartPoint(aBezier
.getEndPoint());
2621 // correct rEdgeIndex when not last point
2622 if(rCandidate
.isClosed())
2624 rEdgeIndex
= getIndexOfSuccessor(rEdgeIndex
, rCandidate
);
2629 if(rEdgeIndex
!= nEdgeCount
- 1L)
2641 B2DPoint
distort(const B2DPoint
& rCandidate
, const B2DRange
& rOriginal
, const B2DPoint
& rTopLeft
, const B2DPoint
& rTopRight
, const B2DPoint
& rBottomLeft
, const B2DPoint
& rBottomRight
)
2643 if(fTools::equalZero(rOriginal
.getWidth()) || fTools::equalZero(rOriginal
.getHeight()))
2649 const double fRelativeX((rCandidate
.getX() - rOriginal
.getMinX()) / rOriginal
.getWidth());
2650 const double fRelativeY((rCandidate
.getY() - rOriginal
.getMinY()) / rOriginal
.getHeight());
2651 const double fOneMinusRelativeX(1.0 - fRelativeX
);
2652 const double fOneMinusRelativeY(1.0 - fRelativeY
);
2653 const double fNewX((fOneMinusRelativeY
) * ((fOneMinusRelativeX
) * rTopLeft
.getX() + fRelativeX
* rTopRight
.getX()) +
2654 fRelativeY
* ((fOneMinusRelativeX
) * rBottomLeft
.getX() + fRelativeX
* rBottomRight
.getX()));
2655 const double fNewY((fOneMinusRelativeX
) * ((fOneMinusRelativeY
) * rTopLeft
.getY() + fRelativeY
* rBottomLeft
.getY()) +
2656 fRelativeX
* ((fOneMinusRelativeY
) * rTopRight
.getY() + fRelativeY
* rBottomRight
.getY()));
2658 return B2DPoint(fNewX
, fNewY
);
2662 B2DPolygon
distort(const B2DPolygon
& rCandidate
, const B2DRange
& rOriginal
, const B2DPoint
& rTopLeft
, const B2DPoint
& rTopRight
, const B2DPoint
& rBottomLeft
, const B2DPoint
& rBottomRight
)
2664 const sal_uInt32
nPointCount(rCandidate
.count());
2666 if(nPointCount
&& 0.0 != rOriginal
.getWidth() && 0.0 != rOriginal
.getHeight())
2670 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
2672 aRetval
.append(distort(rCandidate
.getB2DPoint(a
), rOriginal
, rTopLeft
, rTopRight
, rBottomLeft
, rBottomRight
));
2674 if(rCandidate
.areControlPointsUsed())
2676 if(!rCandidate
.getPrevControlPoint(a
).equalZero())
2678 aRetval
.setPrevControlPoint(a
, distort(rCandidate
.getPrevControlPoint(a
), rOriginal
, rTopLeft
, rTopRight
, rBottomLeft
, rBottomRight
));
2681 if(!rCandidate
.getNextControlPoint(a
).equalZero())
2683 aRetval
.setNextControlPoint(a
, distort(rCandidate
.getNextControlPoint(a
), rOriginal
, rTopLeft
, rTopRight
, rBottomLeft
, rBottomRight
));
2688 aRetval
.setClosed(rCandidate
.isClosed());
2697 B2DPolygon
rotateAroundPoint(const B2DPolygon
& rCandidate
, const B2DPoint
& rCenter
, double fAngle
)
2699 const sal_uInt32
nPointCount(rCandidate
.count());
2700 B2DPolygon
aRetval(rCandidate
);
2704 B2DHomMatrix aMatrix
;
2706 aMatrix
.translate(-rCenter
.getX(), -rCenter
.getY());
2707 aMatrix
.rotate(fAngle
);
2708 aMatrix
.translate(rCenter
.getX(), rCenter
.getY());
2710 aRetval
.transform(aMatrix
);
2716 B2DPolygon
expandToCurve(const B2DPolygon
& rCandidate
)
2718 B2DPolygon
aRetval(rCandidate
);
2720 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
2722 expandToCurveInPoint(aRetval
, a
);
2728 bool expandToCurveInPoint(B2DPolygon
& rCandidate
, sal_uInt32 nIndex
)
2730 OSL_ENSURE(nIndex
< rCandidate
.count(), "expandToCurveInPoint: Access to polygon out of range (!)");
2731 bool bRetval(false);
2732 const sal_uInt32
nPointCount(rCandidate
.count());
2737 if(!rCandidate
.isPrevControlPointUsed(nIndex
))
2739 if(!rCandidate
.isClosed() && 0 == nIndex
)
2741 // do not create previous vector for start point of open polygon
2745 const sal_uInt32
nPrevIndex((nIndex
+ (nPointCount
- 1)) % nPointCount
);
2746 rCandidate
.setPrevControlPoint(nIndex
, interpolate(rCandidate
.getB2DPoint(nIndex
), rCandidate
.getB2DPoint(nPrevIndex
), 1.0 / 3.0));
2752 if(!rCandidate
.isNextControlPointUsed(nIndex
))
2754 if(!rCandidate
.isClosed() && nIndex
+ 1 == nPointCount
)
2756 // do not create next vector for end point of open polygon
2760 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
2761 rCandidate
.setNextControlPoint(nIndex
, interpolate(rCandidate
.getB2DPoint(nIndex
), rCandidate
.getB2DPoint(nNextIndex
), 1.0 / 3.0));
2770 B2DPolygon
setContinuity(const B2DPolygon
& rCandidate
, B2VectorContinuity eContinuity
)
2772 B2DPolygon
aRetval(rCandidate
);
2774 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
2776 setContinuityInPoint(aRetval
, a
, eContinuity
);
2782 bool setContinuityInPoint(B2DPolygon
& rCandidate
, sal_uInt32 nIndex
, B2VectorContinuity eContinuity
)
2784 OSL_ENSURE(nIndex
< rCandidate
.count(), "setContinuityInPoint: Access to polygon out of range (!)");
2785 bool bRetval(false);
2786 const sal_uInt32
nPointCount(rCandidate
.count());
2790 const B2DPoint
aCurrentPoint(rCandidate
.getB2DPoint(nIndex
));
2794 case CONTINUITY_NONE
:
2796 if(rCandidate
.isPrevControlPointUsed(nIndex
))
2798 if(!rCandidate
.isClosed() && 0 == nIndex
)
2800 // remove existing previous vector for start point of open polygon
2801 rCandidate
.resetPrevControlPoint(nIndex
);
2805 const sal_uInt32
nPrevIndex((nIndex
+ (nPointCount
- 1)) % nPointCount
);
2806 rCandidate
.setPrevControlPoint(nIndex
, interpolate(aCurrentPoint
, rCandidate
.getB2DPoint(nPrevIndex
), 1.0 / 3.0));
2812 if(rCandidate
.isNextControlPointUsed(nIndex
))
2814 if(!rCandidate
.isClosed() && nIndex
== nPointCount
+ 1)
2816 // remove next vector for end point of open polygon
2817 rCandidate
.resetNextControlPoint(nIndex
);
2821 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
2822 rCandidate
.setNextControlPoint(nIndex
, interpolate(aCurrentPoint
, rCandidate
.getB2DPoint(nNextIndex
), 1.0 / 3.0));
2830 case CONTINUITY_C1
:
2832 if(rCandidate
.isPrevControlPointUsed(nIndex
) && rCandidate
.isNextControlPointUsed(nIndex
))
2834 // lengths both exist since both are used
2835 B2DVector
aVectorPrev(rCandidate
.getPrevControlPoint(nIndex
) - aCurrentPoint
);
2836 B2DVector
aVectorNext(rCandidate
.getNextControlPoint(nIndex
) - aCurrentPoint
);
2837 const double fLenPrev(aVectorPrev
.getLength());
2838 const double fLenNext(aVectorNext
.getLength());
2839 aVectorPrev
.normalize();
2840 aVectorNext
.normalize();
2841 const B2VectorOrientation
aOrientation(getOrientation(aVectorPrev
, aVectorNext
));
2843 if(ORIENTATION_NEUTRAL
== aOrientation
&& aVectorPrev
.scalar(aVectorNext
) < 0.0)
2845 // parallel and opposite direction; check length
2846 if(fTools::equal(fLenPrev
, fLenNext
))
2848 // this would be even C2, but we want C1. Use the lengths of the corresponding edges.
2849 const sal_uInt32
nPrevIndex((nIndex
+ (nPointCount
- 1)) % nPointCount
);
2850 const sal_uInt32
nNextIndex((nIndex
+ 1) % nPointCount
);
2851 const double fLenPrevEdge(B2DVector(rCandidate
.getB2DPoint(nPrevIndex
) - aCurrentPoint
).getLength() * (1.0 / 3.0));
2852 const double fLenNextEdge(B2DVector(rCandidate
.getB2DPoint(nNextIndex
) - aCurrentPoint
).getLength() * (1.0 / 3.0));
2854 rCandidate
.setControlPoints(nIndex
,
2855 aCurrentPoint
+ (aVectorPrev
* fLenPrevEdge
),
2856 aCurrentPoint
+ (aVectorNext
* fLenNextEdge
));
2862 // not parallel or same direction, set vectors and length
2863 const B2DVector
aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev
+ aVectorNext
));
2865 if(ORIENTATION_POSITIVE
== aOrientation
)
2867 rCandidate
.setControlPoints(nIndex
,
2868 aCurrentPoint
- (aNormalizedPerpendicular
* fLenPrev
),
2869 aCurrentPoint
+ (aNormalizedPerpendicular
* fLenNext
));
2873 rCandidate
.setControlPoints(nIndex
,
2874 aCurrentPoint
+ (aNormalizedPerpendicular
* fLenPrev
),
2875 aCurrentPoint
- (aNormalizedPerpendicular
* fLenNext
));
2883 case CONTINUITY_C2
:
2885 if(rCandidate
.isPrevControlPointUsed(nIndex
) && rCandidate
.isNextControlPointUsed(nIndex
))
2887 // lengths both exist since both are used
2888 B2DVector
aVectorPrev(rCandidate
.getPrevControlPoint(nIndex
) - aCurrentPoint
);
2889 B2DVector
aVectorNext(rCandidate
.getNextControlPoint(nIndex
) - aCurrentPoint
);
2890 const double fCommonLength((aVectorPrev
.getLength() + aVectorNext
.getLength()) / 2.0);
2891 aVectorPrev
.normalize();
2892 aVectorNext
.normalize();
2893 const B2VectorOrientation
aOrientation(getOrientation(aVectorPrev
, aVectorNext
));
2895 if(ORIENTATION_NEUTRAL
== aOrientation
&& aVectorPrev
.scalar(aVectorNext
) < 0.0)
2897 // parallel and opposite direction; set length. Use one direction for better numerical correctness
2898 const B2DVector
aScaledDirection(aVectorPrev
* fCommonLength
);
2900 rCandidate
.setControlPoints(nIndex
,
2901 aCurrentPoint
+ aScaledDirection
,
2902 aCurrentPoint
- aScaledDirection
);
2906 // not parallel or same direction, set vectors and length
2907 const B2DVector
aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev
+ aVectorNext
));
2908 const B2DVector
aPerpendicular(aNormalizedPerpendicular
* fCommonLength
);
2910 if(ORIENTATION_POSITIVE
== aOrientation
)
2912 rCandidate
.setControlPoints(nIndex
,
2913 aCurrentPoint
- aPerpendicular
,
2914 aCurrentPoint
+ aPerpendicular
);
2918 rCandidate
.setControlPoints(nIndex
,
2919 aCurrentPoint
+ aPerpendicular
,
2920 aCurrentPoint
- aPerpendicular
);
2934 B2DPolygon
growInNormalDirection(const B2DPolygon
& rCandidate
, double fValue
)
2938 if(rCandidate
.areControlPointsUsed())
2940 // call myself recursively with subdivided input
2941 const B2DPolygon
aCandidate(adaptiveSubdivideByAngle(rCandidate
));
2942 return growInNormalDirection(aCandidate
, fValue
);
2947 const sal_uInt32
nPointCount(rCandidate
.count());
2951 B2DPoint
aPrev(rCandidate
.getB2DPoint(nPointCount
- 1L));
2952 B2DPoint
aCurrent(rCandidate
.getB2DPoint(0L));
2954 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
2956 const B2DPoint
aNext(rCandidate
.getB2DPoint(a
+ 1L == nPointCount
? 0L : a
+ 1L));
2957 const B2DVector
aBack(aPrev
- aCurrent
);
2958 const B2DVector
aForw(aNext
- aCurrent
);
2959 const B2DVector
aPerpBack(getNormalizedPerpendicular(aBack
));
2960 const B2DVector
aPerpForw(getNormalizedPerpendicular(aForw
));
2961 B2DVector
aDirection(aPerpBack
- aPerpForw
);
2962 aDirection
.normalize();
2963 aDirection
*= fValue
;
2964 aRetval
.append(aCurrent
+ aDirection
);
2966 // prepare next step
2972 // copy closed state
2973 aRetval
.setClosed(rCandidate
.isClosed());
2984 B2DPolygon
reSegmentPolygon(const B2DPolygon
& rCandidate
, sal_uInt32 nSegments
)
2987 const sal_uInt32
nPointCount(rCandidate
.count());
2989 if(nPointCount
&& nSegments
)
2991 // get current segment count
2992 const sal_uInt32
nSegmentCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
2994 if(nSegmentCount
== nSegments
)
2996 aRetval
= rCandidate
;
3000 const double fLength(getLength(rCandidate
));
3001 const sal_uInt32
nLoopCount(rCandidate
.isClosed() ? nSegments
: nSegments
+ 1L);
3003 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
3005 const double fRelativePos((double)a
/ (double)nSegments
); // 0.0 .. 1.0
3006 const B2DPoint
aNewPoint(getPositionRelative(rCandidate
, fRelativePos
, fLength
));
3007 aRetval
.append(aNewPoint
);
3011 aRetval
.setClosed(rCandidate
.isClosed());
3018 B2DPolygon
reSegmentPolygonEdges(const B2DPolygon
& rCandidate
, sal_uInt32 nSubEdges
, bool bHandleCurvedEdges
, bool bHandleStraightEdges
)
3020 const sal_uInt32
nPointCount(rCandidate
.count());
3022 if(nPointCount
< 2 || nSubEdges
< 2 || (!bHandleCurvedEdges
&& !bHandleStraightEdges
))
3025 // - less than two points -> no edge at all
3026 // - less than two nSubEdges -> no resegment necessary
3027 // - neither bHandleCurvedEdges nor bHandleStraightEdges -> nothing to do
3033 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
3034 B2DCubicBezier aCurrentEdge
;
3036 // prepare first edge and add start point to target
3037 aCurrentEdge
.setStartPoint(rCandidate
.getB2DPoint(0));
3038 aRetval
.append(aCurrentEdge
.getStartPoint());
3040 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
3043 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
3044 aCurrentEdge
.setControlPointA(rCandidate
.getNextControlPoint(a
));
3045 aCurrentEdge
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
3046 aCurrentEdge
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
3048 if(aCurrentEdge
.isBezier())
3050 if(bHandleCurvedEdges
)
3052 for(sal_uInt32
b(nSubEdges
); b
> 1; b
--)
3054 const double fSplitPoint(1.0 / b
);
3055 B2DCubicBezier aLeftPart
;
3057 aCurrentEdge
.split(fSplitPoint
, &aLeftPart
, &aCurrentEdge
);
3058 aRetval
.appendBezierSegment(aLeftPart
.getControlPointA(), aLeftPart
.getControlPointB(), aLeftPart
.getEndPoint());
3062 // copy remaining segment to target
3063 aRetval
.appendBezierSegment(aCurrentEdge
.getControlPointA(), aCurrentEdge
.getControlPointB(), aCurrentEdge
.getEndPoint());
3067 if(bHandleStraightEdges
)
3069 for(sal_uInt32
b(nSubEdges
); b
> 1; b
--)
3071 const double fSplitPoint(1.0 / b
);
3072 const B2DPoint
aSplitPoint(interpolate(aCurrentEdge
.getStartPoint(), aCurrentEdge
.getEndPoint(), fSplitPoint
));
3074 aRetval
.append(aSplitPoint
);
3075 aCurrentEdge
.setStartPoint(aSplitPoint
);
3079 // copy remaining segment to target
3080 aRetval
.append(aCurrentEdge
.getEndPoint());
3083 // prepare next step
3084 aCurrentEdge
.setStartPoint(aCurrentEdge
.getEndPoint());
3087 // copy closed flag and return
3088 aRetval
.setClosed(rCandidate
.isClosed());
3093 B2DPolygon
interpolate(const B2DPolygon
& rOld1
, const B2DPolygon
& rOld2
, double t
)
3095 OSL_ENSURE(rOld1
.count() == rOld2
.count(), "B2DPolygon interpolate: Different geometry (!)");
3097 if(fTools::lessOrEqual(t
, 0.0) || rOld1
== rOld2
)
3101 else if(fTools::moreOrEqual(t
, 1.0))
3108 const bool bInterpolateVectors(rOld1
.areControlPointsUsed() || rOld2
.areControlPointsUsed());
3109 aRetval
.setClosed(rOld1
.isClosed() && rOld2
.isClosed());
3111 for(sal_uInt32
a(0L); a
< rOld1
.count(); a
++)
3113 aRetval
.append(interpolate(rOld1
.getB2DPoint(a
), rOld2
.getB2DPoint(a
), t
));
3115 if(bInterpolateVectors
)
3117 aRetval
.setPrevControlPoint(a
, interpolate(rOld1
.getPrevControlPoint(a
), rOld2
.getPrevControlPoint(a
), t
));
3118 aRetval
.setNextControlPoint(a
, interpolate(rOld1
.getNextControlPoint(a
), rOld2
.getNextControlPoint(a
), t
));
3126 bool isPolyPolygonEqualRectangle( const B2DPolyPolygon
& rPolyPoly
,
3127 const B2DRange
& rRect
)
3129 // exclude some cheap cases first
3130 if( rPolyPoly
.count() != 1 )
3133 // fill array with rectangle vertices
3134 const B2DPoint aPoints
[] =
3136 B2DPoint(rRect
.getMinX(),rRect
.getMinY()),
3137 B2DPoint(rRect
.getMaxX(),rRect
.getMinY()),
3138 B2DPoint(rRect
.getMaxX(),rRect
.getMaxY()),
3139 B2DPoint(rRect
.getMinX(),rRect
.getMaxY())
3142 const B2DPolygon
& rPoly( rPolyPoly
.getB2DPolygon(0) );
3143 const sal_uInt32
nCount( rPoly
.count() );
3144 const double epsilon
= ::std::numeric_limits
<double>::epsilon();
3146 for(unsigned int j
=0; j
<4; ++j
)
3148 const B2DPoint
&p1
= aPoints
[j
];
3149 const B2DPoint
&p2
= aPoints
[(j
+1)%4];
3150 bool bPointOnBoundary
= false;
3151 for( sal_uInt32 i
=0; i
<nCount
; ++i
)
3153 const B2DPoint
p(rPoly
.getB2DPoint(i
));
3156 // A = - | x1 y1 1 |
3158 double fDoubleArea
= p2
.getX()*p
.getY() -
3159 p2
.getY()*p
.getX() -
3160 p1
.getX()*p
.getY() +
3161 p1
.getY()*p
.getX() +
3162 p1
.getX()*p2
.getY() -
3163 p1
.getY()*p2
.getX();
3165 if(fDoubleArea
< epsilon
)
3167 bPointOnBoundary
=true;
3171 if(!(bPointOnBoundary
))
3179 // create simplified version of the original polygon by
3180 // replacing segments with spikes/loops and self intersections
3181 // by several trivial sub-segments
3182 B2DPolygon
createSimplifiedPolygon( const B2DPolygon
& rCandidate
)
3184 const sal_uInt32
nCount(rCandidate
.count());
3186 if(nCount
&& rCandidate
.areControlPointsUsed())
3188 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nCount
: nCount
- 1);
3190 B2DCubicBezier aSegment
;
3192 aSegment
.setStartPoint(rCandidate
.getB2DPoint(0));
3193 aRetval
.append(aSegment
.getStartPoint());
3195 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
3198 const sal_uInt32
nNextIndex((a
+ 1) % nCount
);
3199 aSegment
.setControlPointA(rCandidate
.getNextControlPoint(a
));
3200 aSegment
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
3201 aSegment
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
3203 if(aSegment
.isBezier())
3205 double fExtremumPos(0.0);
3206 sal_uInt32
nExtremumCounter(4);
3208 while(nExtremumCounter
-- && aSegment
.isBezier() && aSegment
.getMinimumExtremumPosition(fExtremumPos
))
3210 // split off left, now extremum-free part and append
3211 B2DCubicBezier aLeft
;
3213 aSegment
.split(fExtremumPos
, &aLeft
, &aSegment
);
3214 aLeft
.testAndSolveTrivialBezier();
3215 aSegment
.testAndSolveTrivialBezier();
3217 if(aLeft
.isBezier())
3219 aRetval
.appendBezierSegment(aLeft
.getControlPointA(), aLeft
.getControlPointB(), aLeft
.getEndPoint());
3223 aRetval
.append(aLeft
.getEndPoint());
3227 // append (evtl. reduced) rest of Segment
3228 if(aSegment
.isBezier())
3230 aRetval
.appendBezierSegment(aSegment
.getControlPointA(), aSegment
.getControlPointB(), aSegment
.getEndPoint());
3234 aRetval
.append(aSegment
.getEndPoint());
3239 // simple edge, append end point
3240 aRetval
.append(aSegment
.getEndPoint());
3243 // prepare next edge
3244 aSegment
.setStartPoint(aSegment
.getEndPoint());
3247 // copy closed flag and check for double points
3248 aRetval
.setClosed(rCandidate
.isClosed());
3249 aRetval
.removeDoublePoints();
3260 B2DPolygon
simplifyCurveSegments(const B2DPolygon
& rCandidate
)
3262 const sal_uInt32
nPointCount(rCandidate
.count());
3264 if(nPointCount
&& rCandidate
.areControlPointsUsed())
3267 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
3269 B2DCubicBezier aBezier
;
3270 aBezier
.setStartPoint(rCandidate
.getB2DPoint(0));
3273 aRetval
.append(aBezier
.getStartPoint());
3275 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
3277 // get values for edge
3278 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
3279 aBezier
.setEndPoint(rCandidate
.getB2DPoint(nNextIndex
));
3280 aBezier
.setControlPointA(rCandidate
.getNextControlPoint(a
));
3281 aBezier
.setControlPointB(rCandidate
.getPrevControlPoint(nNextIndex
));
3282 aBezier
.testAndSolveTrivialBezier();
3285 if(aBezier
.isBezier())
3287 // add edge with control vectors
3288 aRetval
.appendBezierSegment(aBezier
.getControlPointA(), aBezier
.getControlPointB(), aBezier
.getEndPoint());
3293 aRetval
.append(aBezier
.getEndPoint());
3297 aBezier
.setStartPoint(aBezier
.getEndPoint());
3300 if(rCandidate
.isClosed())
3302 // set closed flag, rescue control point and correct last double point
3303 closeWithGeometryChange(aRetval
);
3314 // makes the given indexed point the new polygon start point. To do that, the points in the
3315 // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
3316 // an assertion will be triggered
3317 B2DPolygon
makeStartPoint(const B2DPolygon
& rCandidate
, sal_uInt32 nIndexOfNewStatPoint
)
3319 const sal_uInt32
nPointCount(rCandidate
.count());
3321 if(nPointCount
> 2 && nIndexOfNewStatPoint
!= 0 && nIndexOfNewStatPoint
< nPointCount
)
3323 OSL_ENSURE(rCandidate
.isClosed(), "makeStartPoint: only valid for closed polygons (!)");
3326 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
3328 const sal_uInt32
nSourceIndex((a
+ nIndexOfNewStatPoint
) % nPointCount
);
3329 aRetval
.append(rCandidate
.getB2DPoint(nSourceIndex
));
3331 if(rCandidate
.areControlPointsUsed())
3333 aRetval
.setPrevControlPoint(a
, rCandidate
.getPrevControlPoint(nSourceIndex
));
3334 aRetval
.setNextControlPoint(a
, rCandidate
.getNextControlPoint(nSourceIndex
));
3344 B2DPolygon
createEdgesOfGivenLength(const B2DPolygon
& rCandidate
, double fLength
, double fStart
, double fEnd
)
3353 if(!fTools::equalZero(fLength
))
3370 // iterate and consume pieces with fLength. First subdivide to reduce input to line segments
3371 const B2DPolygon
aCandidate(rCandidate
.areControlPointsUsed() ? rCandidate
.getDefaultAdaptiveSubdivision() : rCandidate
);
3372 const sal_uInt32
nPointCount(aCandidate
.count());
3376 const bool bEndActive(!fTools::equalZero(fEnd
));
3377 const sal_uInt32
nEdgeCount(aCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
3378 B2DPoint
aCurrent(aCandidate
.getB2DPoint(0));
3379 double fPositionInEdge(fStart
);
3380 double fAbsolutePosition(fStart
);
3382 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
3384 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
3385 const B2DPoint
aNext(aCandidate
.getB2DPoint(nNextIndex
));
3386 const B2DVector
aEdge(aNext
- aCurrent
);
3387 double fEdgeLength(aEdge
.getLength());
3389 if(!fTools::equalZero(fEdgeLength
))
3391 while(fTools::less(fPositionInEdge
, fEdgeLength
))
3393 // move position on edge forward as long as on edge
3394 const double fScalar(fPositionInEdge
/ fEdgeLength
);
3395 aRetval
.append(aCurrent
+ (aEdge
* fScalar
));
3396 fPositionInEdge
+= fLength
;
3400 fAbsolutePosition
+= fLength
;
3402 if(fTools::more(fAbsolutePosition
, fEnd
))
3409 // substract length of current edge
3410 fPositionInEdge
-= fEdgeLength
;
3413 if(bEndActive
&& fTools::more(fAbsolutePosition
, fEnd
))
3418 // prepare next step
3422 // keep closed state
3423 aRetval
.setClosed(aCandidate
.isClosed());
3427 // source polygon has only one point, return unchanged
3428 aRetval
= aCandidate
;
3435 B2DPolygon
createWaveline(const B2DPolygon
& rCandidate
, double fWaveWidth
, double fWaveHeight
)
3439 if(fWaveWidth
< 0.0)
3444 if(fWaveHeight
< 0.0)
3449 const bool bHasWidth(!fTools::equalZero(fWaveWidth
));
3450 const bool bHasHeight(!fTools::equalZero(fWaveHeight
));
3456 // width and height, create waveline. First subdivide to reduce input to line segments
3457 // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it
3458 // may be added here again using the original last point from rCandidate. It may
3459 // also be the case that rCandidate was closed. To simplify things it is handled here
3460 // as if it was opened.
3461 // Result from createEdgesOfGivenLength contains no curved segments, handle as straight
3463 const B2DPolygon
aEqualLenghEdges(createEdgesOfGivenLength(rCandidate
, fWaveWidth
));
3464 const sal_uInt32
nPointCount(aEqualLenghEdges
.count());
3468 // iterate over straight edges, add start point
3469 B2DPoint
aCurrent(aEqualLenghEdges
.getB2DPoint(0));
3470 aRetval
.append(aCurrent
);
3472 for(sal_uInt32
a(0); a
< nPointCount
- 1; a
++)
3474 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
3475 const B2DPoint
aNext(aEqualLenghEdges
.getB2DPoint(nNextIndex
));
3476 const B2DVector
aEdge(aNext
- aCurrent
);
3477 const B2DVector
aPerpendicular(getNormalizedPerpendicular(aEdge
));
3478 const B2DVector
aControlOffset((aEdge
* 0.467308) - (aPerpendicular
* fWaveHeight
));
3480 // add curve segment
3481 aRetval
.appendBezierSegment(
3482 aCurrent
+ aControlOffset
,
3483 aNext
- aControlOffset
,
3486 // prepare next step
3493 // width but no height -> return original polygon
3494 aRetval
= rCandidate
;
3499 // no width -> no waveline, stay empty and return
3505 //////////////////////////////////////////////////////////////////////
3506 // comparators with tolerance for 2D Polygons
3508 bool equal(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, const double& rfSmallValue
)
3510 const sal_uInt32
nPointCount(rCandidateA
.count());
3512 if(nPointCount
!= rCandidateB
.count())
3515 const bool bClosed(rCandidateA
.isClosed());
3517 if(bClosed
!= rCandidateB
.isClosed())
3520 const bool bAreControlPointsUsed(rCandidateA
.areControlPointsUsed());
3522 if(bAreControlPointsUsed
!= rCandidateB
.areControlPointsUsed())
3525 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
3527 const B2DPoint
aPoint(rCandidateA
.getB2DPoint(a
));
3529 if(!aPoint
.equal(rCandidateB
.getB2DPoint(a
), rfSmallValue
))
3532 if(bAreControlPointsUsed
)
3534 const basegfx::B2DPoint
aPrev(rCandidateA
.getPrevControlPoint(a
));
3536 if(!aPrev
.equal(rCandidateB
.getPrevControlPoint(a
), rfSmallValue
))
3539 const basegfx::B2DPoint
aNext(rCandidateA
.getNextControlPoint(a
));
3541 if(!aNext
.equal(rCandidateB
.getNextControlPoint(a
), rfSmallValue
))
3549 bool equal(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
)
3551 const double fSmallValue(fTools::getSmallValue());
3553 return equal(rCandidateA
, rCandidateB
, fSmallValue
);
3556 // snap points of horizontal or vertical edges to discrete values
3557 B2DPolygon
snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon
& rCandidate
)
3559 const sal_uInt32
nPointCount(rCandidate
.count());
3563 // Start by copying the source polygon to get a writeable copy. The closed state is
3564 // copied by aRetval's initialisation, too, so no need to copy it in this method
3565 B2DPolygon
aRetval(rCandidate
);
3567 // prepare geometry data. Get rounded from original
3568 B2ITuple
aPrevTuple(basegfx::fround(rCandidate
.getB2DPoint(nPointCount
- 1)));
3569 B2DPoint
aCurrPoint(rCandidate
.getB2DPoint(0));
3570 B2ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
3572 // loop over all points. This will also snap the implicit closing edge
3573 // even when not closed, but that's no problem here
3574 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
3576 // get next point. Get rounded from original
3577 const bool bLastRun(a
+ 1 == nPointCount
);
3578 const sal_uInt32
nNextIndex(bLastRun
? 0 : a
+ 1);
3579 const B2DPoint
aNextPoint(rCandidate
.getB2DPoint(nNextIndex
));
3580 const B2ITuple
aNextTuple(basegfx::fround(aNextPoint
));
3583 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
3584 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
3585 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
3586 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
3587 const bool bSnapX(bPrevVertical
|| bNextVertical
);
3588 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
3590 if(bSnapX
|| bSnapY
)
3592 const B2DPoint
aSnappedPoint(
3593 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
3594 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY());
3596 aRetval
.setB2DPoint(a
, aSnappedPoint
);
3599 // prepare next point
3602 aPrevTuple
= aCurrTuple
;
3603 aCurrPoint
= aNextPoint
;
3604 aCurrTuple
= aNextTuple
;
3616 } // end of namespace tools
3617 } // end of namespace basegfx
3619 //////////////////////////////////////////////////////////////////////////////