1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <osl/diagnose.h>
31 #include <basegfx/polygon/b3dpolygontools.hxx>
32 #include <basegfx/polygon/b3dpolygon.hxx>
33 #include <basegfx/numeric/ftools.hxx>
34 #include <basegfx/range/b3drange.hxx>
35 #include <basegfx/point/b2dpoint.hxx>
36 #include <basegfx/matrix/b3dhommatrix.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/tuple/b3ituple.hxx>
42 //////////////////////////////////////////////////////////////////////////////
49 void checkClosed(B3DPolygon
& rCandidate
)
51 while(rCandidate
.count() > 1L
52 && rCandidate
.getB3DPoint(0L).equal(rCandidate
.getB3DPoint(rCandidate
.count() - 1L)))
54 rCandidate
.setClosed(true);
55 rCandidate
.remove(rCandidate
.count() - 1L);
59 // Get successor and predecessor indices. Returning the same index means there
60 // is none. Same for successor.
61 sal_uInt32
getIndexOfPredecessor(sal_uInt32 nIndex
, const B3DPolygon
& rCandidate
)
63 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
69 else if(rCandidate
.count())
71 return rCandidate
.count() - 1L;
79 sal_uInt32
getIndexOfSuccessor(sal_uInt32 nIndex
, const B3DPolygon
& rCandidate
)
81 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
83 if(nIndex
+ 1L < rCandidate
.count())
93 B3DRange
getRange(const B3DPolygon
& rCandidate
)
96 const sal_uInt32
nPointCount(rCandidate
.count());
98 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
100 const B3DPoint
aTestPoint(rCandidate
.getB3DPoint(a
));
101 aRetval
.expand(aTestPoint
);
107 B3DVector
getNormal(const B3DPolygon
& rCandidate
)
109 return rCandidate
.getNormal();
112 B3DVector
getPositiveOrientedNormal(const B3DPolygon
& rCandidate
)
114 B3DVector
aRetval(rCandidate
.getNormal());
116 if(ORIENTATION_NEGATIVE
== getOrientation(rCandidate
))
124 B2VectorOrientation
getOrientation(const B3DPolygon
& rCandidate
)
126 B2VectorOrientation
eRetval(ORIENTATION_NEUTRAL
);
128 if(rCandidate
.count() > 2L)
130 const double fSignedArea(getSignedArea(rCandidate
));
132 if(fSignedArea
> 0.0)
134 eRetval
= ORIENTATION_POSITIVE
;
136 else if(fSignedArea
< 0.0)
138 eRetval
= ORIENTATION_NEGATIVE
;
145 double getSignedArea(const B3DPolygon
& rCandidate
)
148 const sal_uInt32
nPointCount(rCandidate
.count());
152 const B3DVector
aAbsNormal(absolute(getNormal(rCandidate
)));
153 sal_uInt16
nCase(3); // default: ignore z
155 if(aAbsNormal
.getX() > aAbsNormal
.getY())
157 if(aAbsNormal
.getX() > aAbsNormal
.getZ())
159 nCase
= 1; // ignore x
162 else if(aAbsNormal
.getY() > aAbsNormal
.getZ())
164 nCase
= 2; // ignore y
167 B3DPoint
aPreviousPoint(rCandidate
.getB3DPoint(nPointCount
- 1L));
169 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
171 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(a
));
176 fRetval
+= aPreviousPoint
.getZ() * aCurrentPoint
.getY();
177 fRetval
-= aPreviousPoint
.getY() * aCurrentPoint
.getZ();
180 fRetval
+= aPreviousPoint
.getX() * aCurrentPoint
.getZ();
181 fRetval
-= aPreviousPoint
.getZ() * aCurrentPoint
.getX();
184 fRetval
+= aPreviousPoint
.getX() * aCurrentPoint
.getY();
185 fRetval
-= aPreviousPoint
.getY() * aCurrentPoint
.getX();
190 aPreviousPoint
= aCurrentPoint
;
196 fRetval
/= 2.0 * aAbsNormal
.getX();
199 fRetval
/= 2.0 * aAbsNormal
.getY();
202 fRetval
/= 2.0 * aAbsNormal
.getZ();
210 double getArea(const B3DPolygon
& rCandidate
)
214 if(rCandidate
.count() > 2)
216 fRetval
= getSignedArea(rCandidate
);
217 const double fZero(0.0);
219 if(fTools::less(fRetval
, fZero
))
228 double getEdgeLength(const B3DPolygon
& rCandidate
, sal_uInt32 nIndex
)
230 OSL_ENSURE(nIndex
< rCandidate
.count(), "getEdgeLength: Access to polygon out of range (!)");
232 const sal_uInt32
nPointCount(rCandidate
.count());
234 if(nIndex
< nPointCount
)
236 if(rCandidate
.isClosed() || ((nIndex
+ 1L) != nPointCount
))
238 const sal_uInt32
nNextIndex(getIndexOfSuccessor(nIndex
, rCandidate
));
239 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(nIndex
));
240 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
241 const B3DVector
aVector(aNextPoint
- aCurrentPoint
);
242 fRetval
= aVector
.getLength();
249 double getLength(const B3DPolygon
& rCandidate
)
252 const sal_uInt32
nPointCount(rCandidate
.count());
256 const sal_uInt32
nLoopCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
258 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
260 const sal_uInt32
nNextIndex(getIndexOfSuccessor(a
, rCandidate
));
261 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(a
));
262 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
263 const B3DVector
aVector(aNextPoint
- aCurrentPoint
);
264 fRetval
+= aVector
.getLength();
271 B3DPoint
getPositionAbsolute(const B3DPolygon
& rCandidate
, double fDistance
, double fLength
)
274 const sal_uInt32
nPointCount(rCandidate
.count());
278 sal_uInt32
nIndex(0L);
279 bool bIndexDone(false);
280 const double fZero(0.0);
281 double fEdgeLength(fZero
);
283 // get length if not given
284 if(fTools::equalZero(fLength
))
286 fLength
= getLength(rCandidate
);
289 // handle fDistance < 0.0
290 if(fTools::less(fDistance
, fZero
))
292 if(rCandidate
.isClosed())
294 // if fDistance < 0.0 increment with multiple of fLength
295 sal_uInt32
nCount(sal_uInt32(-fDistance
/ fLength
));
296 fDistance
+= double(nCount
+ 1L) * fLength
;
300 // crop to polygon start
306 // handle fDistance >= fLength
307 if(fTools::moreOrEqual(fDistance
, fLength
))
309 if(rCandidate
.isClosed())
311 // if fDistance >= fLength decrement with multiple of fLength
312 sal_uInt32
nCount(sal_uInt32(fDistance
/ fLength
));
313 fDistance
-= (double)(nCount
) * fLength
;
317 // crop to polygon end
319 nIndex
= nPointCount
- 1L;
324 // look for correct index. fDistance is now [0.0 .. fLength[
329 // get length of next edge
330 fEdgeLength
= getEdgeLength(rCandidate
, nIndex
);
332 if(fTools::moreOrEqual(fDistance
, fEdgeLength
))
335 fDistance
-= fEdgeLength
;
340 // it's on this edge, stop
343 } while (!bIndexDone
);
346 // get the point using nIndex
347 aRetval
= rCandidate
.getB3DPoint(nIndex
);
349 // if fDistance != 0.0, move that length on the edge. The edge
350 // length is in fEdgeLength.
351 if(!fTools::equalZero(fDistance
))
353 sal_uInt32
nNextIndex(getIndexOfSuccessor(nIndex
, rCandidate
));
354 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
355 double fRelative(fZero
);
357 if(!fTools::equalZero(fEdgeLength
))
359 fRelative
= fDistance
/ fEdgeLength
;
362 // add calculated average value to the return value
363 aRetval
+= interpolate(aRetval
, aNextPoint
, fRelative
);
370 B3DPoint
getPositionRelative(const B3DPolygon
& rCandidate
, double fDistance
, double fLength
)
372 // get length if not given
373 if(fTools::equalZero(fLength
))
375 fLength
= getLength(rCandidate
);
378 // multiply fDistance with real length to get absolute position and
379 // use getPositionAbsolute
380 return getPositionAbsolute(rCandidate
, fDistance
* fLength
, fLength
);
383 void applyLineDashing(const B3DPolygon
& rCandidate
, const ::std::vector
<double>& rDotDashArray
, B3DPolyPolygon
* pLineTarget
, B3DPolyPolygon
* pGapTarget
, double fDotDashLength
)
385 const sal_uInt32
nPointCount(rCandidate
.count());
386 const sal_uInt32
nDotDashCount(rDotDashArray
.size());
388 if(fTools::lessOrEqual(fDotDashLength
, 0.0))
390 fDotDashLength
= ::std::accumulate(rDotDashArray
.begin(), rDotDashArray
.end(), 0.0);
393 if(fTools::more(fDotDashLength
, 0.0) && (pLineTarget
|| pGapTarget
) && nPointCount
)
398 pLineTarget
->clear();
406 // prepare current edge's start
407 B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(0));
408 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
410 // prepare DotDashArray iteration and the line/gap switching bool
411 sal_uInt32
nDotDashIndex(0);
413 double fDotDashMovingLength(rDotDashArray
[0]);
416 // iterate over all edges
417 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
419 // update current edge
420 double fLastDotDashMovingLength(0.0);
421 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
422 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
423 const double fEdgeLength(B3DVector(aNextPoint
- aCurrentPoint
).getLength());
425 while(fTools::less(fDotDashMovingLength
, fEdgeLength
))
427 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
428 const bool bHandleLine(bIsLine
&& pLineTarget
);
429 const bool bHandleGap(!bIsLine
&& pGapTarget
);
431 if(bHandleLine
|| bHandleGap
)
433 if(!aSnippet
.count())
435 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fLastDotDashMovingLength
/ fEdgeLength
));
438 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fDotDashMovingLength
/ fEdgeLength
));
442 pLineTarget
->append(aSnippet
);
446 pGapTarget
->append(aSnippet
);
452 // prepare next DotDashArray step and flip line/gap flag
453 fLastDotDashMovingLength
= fDotDashMovingLength
;
454 fDotDashMovingLength
+= rDotDashArray
[(++nDotDashIndex
) % nDotDashCount
];
458 // append snippet [fLastDotDashMovingLength, fEdgeLength]
459 const bool bHandleLine(bIsLine
&& pLineTarget
);
460 const bool bHandleGap(!bIsLine
&& pGapTarget
);
462 if(bHandleLine
|| bHandleGap
)
464 if(!aSnippet
.count())
466 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fLastDotDashMovingLength
/ fEdgeLength
));
469 aSnippet
.append(aNextPoint
);
472 // prepare move to next edge
473 fDotDashMovingLength
-= fEdgeLength
;
475 // prepare next edge step (end point gets new start point)
476 aCurrentPoint
= aNextPoint
;
479 // append last intermediate results (if exists)
482 if(bIsLine
&& pLineTarget
)
484 pLineTarget
->append(aSnippet
);
486 else if(!bIsLine
&& pGapTarget
)
488 pGapTarget
->append(aSnippet
);
492 // check if start and end polygon may be merged
495 const sal_uInt32
nCount(pLineTarget
->count());
499 // these polygons were created above, there exists none with less than two points,
500 // thus dircet point access below is allowed
501 const B3DPolygon
aFirst(pLineTarget
->getB3DPolygon(0));
502 B3DPolygon
aLast(pLineTarget
->getB3DPolygon(nCount
- 1));
504 if(aFirst
.getB3DPoint(0).equal(aLast
.getB3DPoint(aLast
.count() - 1)))
506 // start of first and end of last are the same -> merge them
507 aLast
.append(aFirst
);
508 aLast
.removeDoublePoints();
509 pLineTarget
->setB3DPolygon(0, aLast
);
510 pLineTarget
->remove(nCount
- 1);
517 const sal_uInt32
nCount(pGapTarget
->count());
521 // these polygons were created above, there exists none with less than two points,
522 // thus dircet point access below is allowed
523 const B3DPolygon
aFirst(pGapTarget
->getB3DPolygon(0));
524 B3DPolygon
aLast(pGapTarget
->getB3DPolygon(nCount
- 1));
526 if(aFirst
.getB3DPoint(0).equal(aLast
.getB3DPoint(aLast
.count() - 1)))
528 // start of first and end of last are the same -> merge them
529 aLast
.append(aFirst
);
530 aLast
.removeDoublePoints();
531 pGapTarget
->setB3DPolygon(0, aLast
);
532 pGapTarget
->remove(nCount
- 1);
539 // parameters make no sense, just add source to targets
542 pLineTarget
->append(rCandidate
);
547 pGapTarget
->append(rCandidate
);
552 B3DPolygon
applyDefaultNormalsSphere( const B3DPolygon
& rCandidate
, const B3DPoint
& rCenter
)
554 B3DPolygon
aRetval(rCandidate
);
556 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
558 B3DVector
aVector(aRetval
.getB3DPoint(a
) - rCenter
);
560 aRetval
.setNormal(a
, aVector
);
566 B3DPolygon
invertNormals( const B3DPolygon
& rCandidate
)
568 B3DPolygon
aRetval(rCandidate
);
570 if(aRetval
.areNormalsUsed())
572 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
574 aRetval
.setNormal(a
, -aRetval
.getNormal(a
));
581 B3DPolygon
applyDefaultTextureCoordinatesParallel( const B3DPolygon
& rCandidate
, const B3DRange
& rRange
, bool bChangeX
, bool bChangeY
)
583 B3DPolygon
aRetval(rCandidate
);
585 if(bChangeX
|| bChangeY
)
587 // create projection of standard texture coordinates in (X, Y) onto
588 // the 3d coordinates straight
589 const double fWidth(rRange
.getWidth());
590 const double fHeight(rRange
.getHeight());
591 const bool bWidthSet(!fTools::equalZero(fWidth
));
592 const bool bHeightSet(!fTools::equalZero(fHeight
));
593 const double fOne(1.0);
595 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
597 const B3DPoint
aPoint(aRetval
.getB3DPoint(a
));
598 B2DPoint
aTextureCoordinate(aRetval
.getTextureCoordinate(a
));
604 aTextureCoordinate
.setX((aPoint
.getX() - rRange
.getMinX()) / fWidth
);
608 aTextureCoordinate
.setX(0.0);
616 aTextureCoordinate
.setY(fOne
- ((aPoint
.getY() - rRange
.getMinY()) / fHeight
));
620 aTextureCoordinate
.setY(fOne
);
624 aRetval
.setTextureCoordinate(a
, aTextureCoordinate
);
631 B3DPolygon
applyDefaultTextureCoordinatesSphere( const B3DPolygon
& rCandidate
, const B3DPoint
& rCenter
, bool bChangeX
, bool bChangeY
)
633 B3DPolygon
aRetval(rCandidate
);
635 if(bChangeX
|| bChangeY
)
637 // create texture coordinates using sphere projection to cartesian coordinates,
638 // use object's center as base
639 const double fOne(1.0);
640 const sal_uInt32
nPointCount(aRetval
.count());
641 bool bPolarPoints(false);
644 // create center cartesian coordinates to have a possibility to decide if on boundary
645 // transitions which value to choose
646 const B3DRange
aPlaneRange(getRange(rCandidate
));
647 const B3DPoint
aPlaneCenter(aPlaneRange
.getCenter() - rCenter
);
648 const double fXCenter(fOne
- ((atan2(aPlaneCenter
.getZ(), aPlaneCenter
.getX()) + F_PI
) / F_2PI
));
650 for(a
= 0L; a
< nPointCount
; a
++)
652 const B3DVector
aVector(aRetval
.getB3DPoint(a
) - rCenter
);
653 const double fY(fOne
- ((atan2(aVector
.getY(), aVector
.getXZLength()) + F_PI2
) / F_PI
));
654 B2DPoint
aTexCoor(aRetval
.getTextureCoordinate(a
));
656 if(fTools::equalZero(fY
))
658 // point is a north polar point, no useful X-coordinate can be created.
669 else if(fTools::equal(fY
, fOne
))
671 // point is a south polar point, no useful X-coordinate can be created. Set
672 // Y-coordinte, though
685 double fX(fOne
- ((atan2(aVector
.getZ(), aVector
.getX()) + F_PI
) / F_2PI
));
687 // correct cartesinan point coordiante dependent from center value
688 if(fX
> fXCenter
+ 0.5)
692 else if(fX
< fXCenter
- 0.5)
708 aRetval
.setTextureCoordinate(a
, aTexCoor
);
713 // correct X-texture coordinates if polar points are contained. Those
714 // coordinates cannot be correct, so use prev or next X-coordinate
715 for(a
= 0L; a
< nPointCount
; a
++)
717 B2DPoint
aTexCoor(aRetval
.getTextureCoordinate(a
));
719 if(fTools::equalZero(aTexCoor
.getY()) || fTools::equal(aTexCoor
.getY(), fOne
))
721 // get prev, next TexCoor and test for pole
722 const B2DPoint
aPrevTexCoor(aRetval
.getTextureCoordinate(a
? a
- 1L : nPointCount
- 1L));
723 const B2DPoint
aNextTexCoor(aRetval
.getTextureCoordinate((a
+ 1L) % nPointCount
));
724 const bool bPrevPole(fTools::equalZero(aPrevTexCoor
.getY()) || fTools::equal(aPrevTexCoor
.getY(), fOne
));
725 const bool bNextPole(fTools::equalZero(aNextTexCoor
.getY()) || fTools::equal(aNextTexCoor
.getY(), fOne
));
727 if(!bPrevPole
&& !bNextPole
)
729 // both no poles, mix them
730 aTexCoor
.setX((aPrevTexCoor
.getX() + aNextTexCoor
.getX()) / 2.0);
735 aTexCoor
.setX(aNextTexCoor
.getX());
739 // copy prev, even if it's a pole, hopefully it is already corrected
740 aTexCoor
.setX(aPrevTexCoor
.getX());
743 aRetval
.setTextureCoordinate(a
, aTexCoor
);
752 bool isInEpsilonRange(const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, const B3DPoint
& rTestPosition
, double fDistance
)
755 const B3DVector
aEdge(rEdgeEnd
- rEdgeStart
);
756 bool bDoDistanceTestStart(false);
757 bool bDoDistanceTestEnd(false);
759 if(aEdge
.equalZero())
761 // no edge, just a point. Do one of the distance tests.
762 bDoDistanceTestStart
= true;
766 // calculate fCut in aEdge
767 const B3DVector
aTestEdge(rTestPosition
- rEdgeStart
);
768 const double fScalarTestEdge(aEdge
.scalar(aTestEdge
));
769 const double fScalarStartEdge(aEdge
.scalar(rEdgeStart
));
770 const double fScalarEdge(aEdge
.scalar(aEdge
));
771 const double fCut((fScalarTestEdge
- fScalarStartEdge
) / fScalarEdge
);
772 const double fZero(0.0);
773 const double fOne(1.0);
775 if(fTools::less(fCut
, fZero
))
777 // left of rEdgeStart
778 bDoDistanceTestStart
= true;
780 else if(fTools::more(fCut
, fOne
))
783 bDoDistanceTestEnd
= true;
787 // inside line [0.0 .. 1.0]
788 const B3DPoint
aCutPoint(interpolate(rEdgeStart
, rEdgeEnd
, fCut
));
789 const B3DVector
aDelta(rTestPosition
- aCutPoint
);
790 const double fDistanceSquare(aDelta
.scalar(aDelta
));
792 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
803 if(bDoDistanceTestStart
)
805 const B3DVector
aDelta(rTestPosition
- rEdgeStart
);
806 const double fDistanceSquare(aDelta
.scalar(aDelta
));
808 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
813 else if(bDoDistanceTestEnd
)
815 const B3DVector
aDelta(rTestPosition
- rEdgeEnd
);
816 const double fDistanceSquare(aDelta
.scalar(aDelta
));
818 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
827 bool isInEpsilonRange(const B3DPolygon
& rCandidate
, const B3DPoint
& rTestPosition
, double fDistance
)
829 const sal_uInt32
nPointCount(rCandidate
.count());
833 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
834 B3DPoint
aCurrent(rCandidate
.getB3DPoint(0));
839 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
841 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
842 const B3DPoint
aNext(rCandidate
.getB3DPoint(nNextIndex
));
844 if(isInEpsilonRange(aCurrent
, aNext
, rTestPosition
, fDistance
))
855 // no edges, but points -> not closed. Check single point. Just
856 // use isInEpsilonRange with twice the same point, it handles those well
857 if(isInEpsilonRange(aCurrent
, aCurrent
, rTestPosition
, fDistance
))
867 bool isInside(const B3DPolygon
& rCandidate
, const B3DPoint
& rPoint
, bool bWithBorder
)
869 if(bWithBorder
&& isPointOnPolygon(rCandidate
, rPoint
, true))
876 const B3DVector
aPlaneNormal(rCandidate
.getNormal());
878 if(!aPlaneNormal
.equalZero())
880 const sal_uInt32
nPointCount(rCandidate
.count());
884 B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(nPointCount
- 1));
885 const double fAbsX(fabs(aPlaneNormal
.getX()));
886 const double fAbsY(fabs(aPlaneNormal
.getY()));
887 const double fAbsZ(fabs(aPlaneNormal
.getZ()));
889 if(fAbsX
> fAbsY
&& fAbsX
> fAbsZ
)
891 // normal points mostly in X-Direction, use YZ-Polygon projection for check
893 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
895 const B3DPoint
aPreviousPoint(aCurrentPoint
);
896 aCurrentPoint
= rCandidate
.getB3DPoint(a
);
899 const bool bCompZA(fTools::more(aPreviousPoint
.getZ(), rPoint
.getZ()));
900 const bool bCompZB(fTools::more(aCurrentPoint
.getZ(), rPoint
.getZ()));
902 if(bCompZA
!= bCompZB
)
905 const bool bCompYA(fTools::more(aPreviousPoint
.getY(), rPoint
.getY()));
906 const bool bCompYB(fTools::more(aCurrentPoint
.getY(), rPoint
.getY()));
908 if(bCompYA
== bCompYB
)
917 const double fCompare(
918 aCurrentPoint
.getY() - (aCurrentPoint
.getZ() - rPoint
.getZ()) *
919 (aPreviousPoint
.getY() - aCurrentPoint
.getY()) /
920 (aPreviousPoint
.getZ() - aCurrentPoint
.getZ()));
922 if(fTools::more(fCompare
, rPoint
.getY()))
930 else if(fAbsY
> fAbsX
&& fAbsY
> fAbsZ
)
932 // normal points mostly in Y-Direction, use XZ-Polygon projection for check
934 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
936 const B3DPoint
aPreviousPoint(aCurrentPoint
);
937 aCurrentPoint
= rCandidate
.getB3DPoint(a
);
940 const bool bCompZA(fTools::more(aPreviousPoint
.getZ(), rPoint
.getZ()));
941 const bool bCompZB(fTools::more(aCurrentPoint
.getZ(), rPoint
.getZ()));
943 if(bCompZA
!= bCompZB
)
946 const bool bCompXA(fTools::more(aPreviousPoint
.getX(), rPoint
.getX()));
947 const bool bCompXB(fTools::more(aCurrentPoint
.getX(), rPoint
.getX()));
949 if(bCompXA
== bCompXB
)
958 const double fCompare(
959 aCurrentPoint
.getX() - (aCurrentPoint
.getZ() - rPoint
.getZ()) *
960 (aPreviousPoint
.getX() - aCurrentPoint
.getX()) /
961 (aPreviousPoint
.getZ() - aCurrentPoint
.getZ()));
963 if(fTools::more(fCompare
, rPoint
.getX()))
973 // normal points mostly in Z-Direction, use XY-Polygon projection for check
975 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
977 const B3DPoint
aPreviousPoint(aCurrentPoint
);
978 aCurrentPoint
= rCandidate
.getB3DPoint(a
);
981 const bool bCompYA(fTools::more(aPreviousPoint
.getY(), rPoint
.getY()));
982 const bool bCompYB(fTools::more(aCurrentPoint
.getY(), rPoint
.getY()));
984 if(bCompYA
!= bCompYB
)
987 const bool bCompXA(fTools::more(aPreviousPoint
.getX(), rPoint
.getX()));
988 const bool bCompXB(fTools::more(aCurrentPoint
.getX(), rPoint
.getX()));
990 if(bCompXA
== bCompXB
)
999 const double fCompare(
1000 aCurrentPoint
.getX() - (aCurrentPoint
.getY() - rPoint
.getY()) *
1001 (aPreviousPoint
.getX() - aCurrentPoint
.getX()) /
1002 (aPreviousPoint
.getY() - aCurrentPoint
.getY()));
1004 if(fTools::more(fCompare
, rPoint
.getX()))
1019 bool isInside(const B3DPolygon
& rCandidate
, const B3DPolygon
& rPolygon
, bool bWithBorder
)
1021 const sal_uInt32
nPointCount(rPolygon
.count());
1023 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
1025 const B3DPoint
aTestPoint(rPolygon
.getB3DPoint(a
));
1027 if(!isInside(rCandidate
, aTestPoint
, bWithBorder
))
1036 bool isPointOnLine(const B3DPoint
& rStart
, const B3DPoint
& rEnd
, const B3DPoint
& rCandidate
, bool bWithPoints
)
1038 if(rCandidate
.equal(rStart
) || rCandidate
.equal(rEnd
))
1040 // candidate is in epsilon around start or end -> inside
1043 else if(rStart
.equal(rEnd
))
1045 // start and end are equal, but candidate is outside their epsilon -> outside
1050 const B3DVector
aEdgeVector(rEnd
- rStart
);
1051 const B3DVector
aTestVector(rCandidate
- rStart
);
1053 if(areParallel(aEdgeVector
, aTestVector
))
1055 const double fZero(0.0);
1056 const double fOne(1.0);
1057 double fParamTestOnCurr(0.0);
1059 if(aEdgeVector
.getX() > aEdgeVector
.getY())
1061 if(aEdgeVector
.getX() > aEdgeVector
.getZ())
1064 fParamTestOnCurr
= aTestVector
.getX() / aEdgeVector
.getX();
1069 fParamTestOnCurr
= aTestVector
.getZ() / aEdgeVector
.getZ();
1074 if(aEdgeVector
.getY() > aEdgeVector
.getZ())
1077 fParamTestOnCurr
= aTestVector
.getY() / aEdgeVector
.getY();
1082 fParamTestOnCurr
= aTestVector
.getZ() / aEdgeVector
.getZ();
1086 if(fTools::more(fParamTestOnCurr
, fZero
) && fTools::less(fParamTestOnCurr
, fOne
))
1096 bool isPointOnPolygon(const B3DPolygon
& rCandidate
, const B3DPoint
& rPoint
, bool bWithPoints
)
1098 const sal_uInt32
nPointCount(rCandidate
.count());
1100 if(nPointCount
> 1L)
1102 const sal_uInt32
nLoopCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
1103 B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(0));
1105 for(sal_uInt32
a(0); a
< nLoopCount
; a
++)
1107 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint((a
+ 1) % nPointCount
));
1109 if(isPointOnLine(aCurrentPoint
, aNextPoint
, rPoint
, bWithPoints
))
1114 aCurrentPoint
= aNextPoint
;
1117 else if(nPointCount
&& bWithPoints
)
1119 return rPoint
.equal(rCandidate
.getB3DPoint(0));
1125 bool getCutBetweenLineAndPlane(const B3DVector
& rPlaneNormal
, const B3DPoint
& rPlanePoint
, const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, double& fCut
)
1127 if(!rPlaneNormal
.equalZero() && !rEdgeStart
.equal(rEdgeEnd
))
1129 const B3DVector
aTestEdge(rEdgeEnd
- rEdgeStart
);
1130 const double fScalarEdge(rPlaneNormal
.scalar(aTestEdge
));
1132 if(!fTools::equalZero(fScalarEdge
))
1134 const B3DVector
aCompareEdge(rPlanePoint
- rEdgeStart
);
1135 const double fScalarCompare(rPlaneNormal
.scalar(aCompareEdge
));
1137 fCut
= fScalarCompare
/ fScalarEdge
;
1145 bool getCutBetweenLineAndPolygon(const B3DPolygon
& rCandidate
, const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, double& fCut
)
1147 const sal_uInt32
nPointCount(rCandidate
.count());
1149 if(nPointCount
> 2 && !rEdgeStart
.equal(rEdgeEnd
))
1151 const B3DVector
aPlaneNormal(rCandidate
.getNormal());
1153 if(!aPlaneNormal
.equalZero())
1155 const B3DPoint
aPointOnPlane(rCandidate
.getB3DPoint(0));
1157 return getCutBetweenLineAndPlane(aPlaneNormal
, aPointOnPlane
, rEdgeStart
, rEdgeEnd
, fCut
);
1164 //////////////////////////////////////////////////////////////////////
1165 // comparators with tolerance for 3D Polygons
1167 bool equal(const B3DPolygon
& rCandidateA
, const B3DPolygon
& rCandidateB
, const double& rfSmallValue
)
1169 const sal_uInt32
nPointCount(rCandidateA
.count());
1171 if(nPointCount
!= rCandidateB
.count())
1174 const bool bClosed(rCandidateA
.isClosed());
1176 if(bClosed
!= rCandidateB
.isClosed())
1179 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
1181 const B3DPoint
aPoint(rCandidateA
.getB3DPoint(a
));
1183 if(!aPoint
.equal(rCandidateB
.getB3DPoint(a
), rfSmallValue
))
1190 bool equal(const B3DPolygon
& rCandidateA
, const B3DPolygon
& rCandidateB
)
1192 const double fSmallValue(fTools::getSmallValue());
1194 return equal(rCandidateA
, rCandidateB
, fSmallValue
);
1197 // snap points of horizontal or vertical edges to discrete values
1198 B3DPolygon
snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon
& rCandidate
)
1200 const sal_uInt32
nPointCount(rCandidate
.count());
1204 // Start by copying the source polygon to get a writeable copy. The closed state is
1205 // copied by aRetval's initialisation, too, so no need to copy it in this method
1206 B3DPolygon
aRetval(rCandidate
);
1208 // prepare geometry data. Get rounded from original
1209 B3ITuple
aPrevTuple(basegfx::fround(rCandidate
.getB3DPoint(nPointCount
- 1)));
1210 B3DPoint
aCurrPoint(rCandidate
.getB3DPoint(0));
1211 B3ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1213 // loop over all points. This will also snap the implicit closing edge
1214 // even when not closed, but that's no problem here
1215 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
1217 // get next point. Get rounded from original
1218 const bool bLastRun(a
+ 1 == nPointCount
);
1219 const sal_uInt32
nNextIndex(bLastRun
? 0 : a
+ 1);
1220 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
1221 const B3ITuple
aNextTuple(basegfx::fround(aNextPoint
));
1224 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1225 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1226 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1227 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1228 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1229 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1231 if(bSnapX
|| bSnapY
)
1233 const B3DPoint
aSnappedPoint(
1234 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1235 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY(),
1238 aRetval
.setB3DPoint(a
, aSnappedPoint
);
1241 // prepare next point
1244 aPrevTuple
= aCurrTuple
;
1245 aCurrPoint
= aNextPoint
;
1246 aCurrTuple
= aNextTuple
;
1258 } // end of namespace tools
1259 } // end of namespace basegfx
1261 //////////////////////////////////////////////////////////////////////////////