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: b3dpolygontools.cxx,v $
10 * $Revision: 1.11.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 <osl/diagnose.h>
34 #include <basegfx/polygon/b3dpolygontools.hxx>
35 #include <basegfx/polygon/b3dpolygon.hxx>
36 #include <basegfx/numeric/ftools.hxx>
37 #include <basegfx/range/b3drange.hxx>
38 #include <basegfx/point/b2dpoint.hxx>
39 #include <basegfx/matrix/b3dhommatrix.hxx>
40 #include <basegfx/polygon/b2dpolygon.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <basegfx/tuple/b3ituple.hxx>
45 //////////////////////////////////////////////////////////////////////////////
52 void checkClosed(B3DPolygon
& rCandidate
)
54 while(rCandidate
.count() > 1L
55 && rCandidate
.getB3DPoint(0L).equal(rCandidate
.getB3DPoint(rCandidate
.count() - 1L)))
57 rCandidate
.setClosed(true);
58 rCandidate
.remove(rCandidate
.count() - 1L);
62 // Get successor and predecessor indices. Returning the same index means there
63 // is none. Same for successor.
64 sal_uInt32
getIndexOfPredecessor(sal_uInt32 nIndex
, const B3DPolygon
& rCandidate
)
66 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
72 else if(rCandidate
.count())
74 return rCandidate
.count() - 1L;
82 sal_uInt32
getIndexOfSuccessor(sal_uInt32 nIndex
, const B3DPolygon
& rCandidate
)
84 OSL_ENSURE(nIndex
< rCandidate
.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
86 if(nIndex
+ 1L < rCandidate
.count())
96 B3DRange
getRange(const B3DPolygon
& rCandidate
)
99 const sal_uInt32
nPointCount(rCandidate
.count());
101 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
103 const B3DPoint
aTestPoint(rCandidate
.getB3DPoint(a
));
104 aRetval
.expand(aTestPoint
);
110 B3DVector
getNormal(const B3DPolygon
& rCandidate
)
112 return rCandidate
.getNormal();
115 B3DVector
getPositiveOrientedNormal(const B3DPolygon
& rCandidate
)
117 B3DVector
aRetval(rCandidate
.getNormal());
119 if(ORIENTATION_NEGATIVE
== getOrientation(rCandidate
))
127 B2VectorOrientation
getOrientation(const B3DPolygon
& rCandidate
)
129 B2VectorOrientation
eRetval(ORIENTATION_NEUTRAL
);
131 if(rCandidate
.count() > 2L)
133 const double fSignedArea(getSignedArea(rCandidate
));
135 if(fSignedArea
> 0.0)
137 eRetval
= ORIENTATION_POSITIVE
;
139 else if(fSignedArea
< 0.0)
141 eRetval
= ORIENTATION_NEGATIVE
;
148 double getSignedArea(const B3DPolygon
& rCandidate
)
151 const sal_uInt32
nPointCount(rCandidate
.count());
155 const B3DVector
aAbsNormal(absolute(getNormal(rCandidate
)));
156 sal_uInt16
nCase(3); // default: ignore z
158 if(aAbsNormal
.getX() > aAbsNormal
.getY())
160 if(aAbsNormal
.getX() > aAbsNormal
.getZ())
162 nCase
= 1; // ignore x
165 else if(aAbsNormal
.getY() > aAbsNormal
.getZ())
167 nCase
= 2; // ignore y
170 B3DPoint
aPreviousPoint(rCandidate
.getB3DPoint(nPointCount
- 1L));
172 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
174 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(a
));
179 fRetval
+= aPreviousPoint
.getZ() * aCurrentPoint
.getY();
180 fRetval
-= aPreviousPoint
.getY() * aCurrentPoint
.getZ();
183 fRetval
+= aPreviousPoint
.getX() * aCurrentPoint
.getZ();
184 fRetval
-= aPreviousPoint
.getZ() * aCurrentPoint
.getX();
187 fRetval
+= aPreviousPoint
.getX() * aCurrentPoint
.getY();
188 fRetval
-= aPreviousPoint
.getY() * aCurrentPoint
.getX();
193 aPreviousPoint
= aCurrentPoint
;
199 fRetval
/= 2.0 * aAbsNormal
.getX();
202 fRetval
/= 2.0 * aAbsNormal
.getY();
205 fRetval
/= 2.0 * aAbsNormal
.getZ();
213 double getArea(const B3DPolygon
& rCandidate
)
217 if(rCandidate
.count() > 2)
219 fRetval
= getSignedArea(rCandidate
);
220 const double fZero(0.0);
222 if(fTools::less(fRetval
, fZero
))
231 double getEdgeLength(const B3DPolygon
& rCandidate
, sal_uInt32 nIndex
)
233 OSL_ENSURE(nIndex
< rCandidate
.count(), "getEdgeLength: Access to polygon out of range (!)");
235 const sal_uInt32
nPointCount(rCandidate
.count());
237 if(nIndex
< nPointCount
)
239 if(rCandidate
.isClosed() || ((nIndex
+ 1L) != nPointCount
))
241 const sal_uInt32
nNextIndex(getIndexOfSuccessor(nIndex
, rCandidate
));
242 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(nIndex
));
243 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
244 const B3DVector
aVector(aNextPoint
- aCurrentPoint
);
245 fRetval
= aVector
.getLength();
252 double getLength(const B3DPolygon
& rCandidate
)
255 const sal_uInt32
nPointCount(rCandidate
.count());
259 const sal_uInt32
nLoopCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
261 for(sal_uInt32
a(0L); a
< nLoopCount
; a
++)
263 const sal_uInt32
nNextIndex(getIndexOfSuccessor(a
, rCandidate
));
264 const B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(a
));
265 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
266 const B3DVector
aVector(aNextPoint
- aCurrentPoint
);
267 fRetval
+= aVector
.getLength();
274 B3DPoint
getPositionAbsolute(const B3DPolygon
& rCandidate
, double fDistance
, double fLength
)
277 const sal_uInt32
nPointCount(rCandidate
.count());
281 sal_uInt32
nIndex(0L);
282 bool bIndexDone(false);
283 const double fZero(0.0);
284 double fEdgeLength(fZero
);
286 // get length if not given
287 if(fTools::equalZero(fLength
))
289 fLength
= getLength(rCandidate
);
292 // handle fDistance < 0.0
293 if(fTools::less(fDistance
, fZero
))
295 if(rCandidate
.isClosed())
297 // if fDistance < 0.0 increment with multiple of fLength
298 sal_uInt32
nCount(sal_uInt32(-fDistance
/ fLength
));
299 fDistance
+= double(nCount
+ 1L) * fLength
;
303 // crop to polygon start
309 // handle fDistance >= fLength
310 if(fTools::moreOrEqual(fDistance
, fLength
))
312 if(rCandidate
.isClosed())
314 // if fDistance >= fLength decrement with multiple of fLength
315 sal_uInt32
nCount(sal_uInt32(fDistance
/ fLength
));
316 fDistance
-= (double)(nCount
) * fLength
;
320 // crop to polygon end
322 nIndex
= nPointCount
- 1L;
327 // look for correct index. fDistance is now [0.0 .. fLength[
332 // get length of next edge
333 fEdgeLength
= getEdgeLength(rCandidate
, nIndex
);
335 if(fTools::moreOrEqual(fDistance
, fEdgeLength
))
338 fDistance
-= fEdgeLength
;
343 // it's on this edge, stop
346 } while (!bIndexDone
);
349 // get the point using nIndex
350 aRetval
= rCandidate
.getB3DPoint(nIndex
);
352 // if fDistance != 0.0, move that length on the edge. The edge
353 // length is in fEdgeLength.
354 if(!fTools::equalZero(fDistance
))
356 sal_uInt32
nNextIndex(getIndexOfSuccessor(nIndex
, rCandidate
));
357 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
358 double fRelative(fZero
);
360 if(!fTools::equalZero(fEdgeLength
))
362 fRelative
= fDistance
/ fEdgeLength
;
365 // add calculated average value to the return value
366 aRetval
+= interpolate(aRetval
, aNextPoint
, fRelative
);
373 B3DPoint
getPositionRelative(const B3DPolygon
& rCandidate
, double fDistance
, double fLength
)
375 // get length if not given
376 if(fTools::equalZero(fLength
))
378 fLength
= getLength(rCandidate
);
381 // multiply fDistance with real length to get absolute position and
382 // use getPositionAbsolute
383 return getPositionAbsolute(rCandidate
, fDistance
* fLength
, fLength
);
386 void applyLineDashing(const B3DPolygon
& rCandidate
, const ::std::vector
<double>& rDotDashArray
, B3DPolyPolygon
* pLineTarget
, B3DPolyPolygon
* pGapTarget
, double fDotDashLength
)
388 const sal_uInt32
nPointCount(rCandidate
.count());
389 const sal_uInt32
nDotDashCount(rDotDashArray
.size());
391 if(fTools::lessOrEqual(fDotDashLength
, 0.0))
393 fDotDashLength
= ::std::accumulate(rDotDashArray
.begin(), rDotDashArray
.end(), 0.0);
396 if(fTools::more(fDotDashLength
, 0.0) && (pLineTarget
|| pGapTarget
) && nPointCount
)
401 pLineTarget
->clear();
409 // prepare current edge's start
410 B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(0));
411 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
413 // prepare DotDashArray iteration and the line/gap switching bool
414 sal_uInt32
nDotDashIndex(0);
416 double fDotDashMovingLength(rDotDashArray
[0]);
419 // iterate over all edges
420 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
422 // update current edge
423 double fLastDotDashMovingLength(0.0);
424 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
425 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
426 const double fEdgeLength(B3DVector(aNextPoint
- aCurrentPoint
).getLength());
428 while(fTools::less(fDotDashMovingLength
, fEdgeLength
))
430 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
431 const bool bHandleLine(bIsLine
&& pLineTarget
);
432 const bool bHandleGap(!bIsLine
&& pGapTarget
);
434 if(bHandleLine
|| bHandleGap
)
436 if(!aSnippet
.count())
438 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fLastDotDashMovingLength
/ fEdgeLength
));
441 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fDotDashMovingLength
/ fEdgeLength
));
445 pLineTarget
->append(aSnippet
);
449 pGapTarget
->append(aSnippet
);
455 // prepare next DotDashArray step and flip line/gap flag
456 fLastDotDashMovingLength
= fDotDashMovingLength
;
457 fDotDashMovingLength
+= rDotDashArray
[(++nDotDashIndex
) % nDotDashCount
];
461 // append snippet [fLastDotDashMovingLength, fEdgeLength]
462 const bool bHandleLine(bIsLine
&& pLineTarget
);
463 const bool bHandleGap(!bIsLine
&& pGapTarget
);
465 if(bHandleLine
|| bHandleGap
)
467 if(!aSnippet
.count())
469 aSnippet
.append(interpolate(aCurrentPoint
, aNextPoint
, fLastDotDashMovingLength
/ fEdgeLength
));
472 aSnippet
.append(aNextPoint
);
475 // prepare move to next edge
476 fDotDashMovingLength
-= fEdgeLength
;
478 // prepare next edge step (end point gets new start point)
479 aCurrentPoint
= aNextPoint
;
482 // append last intermediate results (if exists)
485 if(bIsLine
&& pLineTarget
)
487 pLineTarget
->append(aSnippet
);
489 else if(!bIsLine
&& pGapTarget
)
491 pGapTarget
->append(aSnippet
);
495 // check if start and end polygon may be merged
498 const sal_uInt32
nCount(pLineTarget
->count());
502 // these polygons were created above, there exists none with less than two points,
503 // thus dircet point access below is allowed
504 const B3DPolygon
aFirst(pLineTarget
->getB3DPolygon(0));
505 B3DPolygon
aLast(pLineTarget
->getB3DPolygon(nCount
- 1));
507 if(aFirst
.getB3DPoint(0).equal(aLast
.getB3DPoint(aLast
.count() - 1)))
509 // start of first and end of last are the same -> merge them
510 aLast
.append(aFirst
);
511 aLast
.removeDoublePoints();
512 pLineTarget
->setB3DPolygon(0, aLast
);
513 pLineTarget
->remove(nCount
- 1);
520 const sal_uInt32
nCount(pGapTarget
->count());
524 // these polygons were created above, there exists none with less than two points,
525 // thus dircet point access below is allowed
526 const B3DPolygon
aFirst(pGapTarget
->getB3DPolygon(0));
527 B3DPolygon
aLast(pGapTarget
->getB3DPolygon(nCount
- 1));
529 if(aFirst
.getB3DPoint(0).equal(aLast
.getB3DPoint(aLast
.count() - 1)))
531 // start of first and end of last are the same -> merge them
532 aLast
.append(aFirst
);
533 aLast
.removeDoublePoints();
534 pGapTarget
->setB3DPolygon(0, aLast
);
535 pGapTarget
->remove(nCount
- 1);
542 // parameters make no sense, just add source to targets
545 pLineTarget
->append(rCandidate
);
550 pGapTarget
->append(rCandidate
);
555 B3DPolygon
applyDefaultNormalsSphere( const B3DPolygon
& rCandidate
, const B3DPoint
& rCenter
)
557 B3DPolygon
aRetval(rCandidate
);
559 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
561 B3DVector
aVector(aRetval
.getB3DPoint(a
) - rCenter
);
563 aRetval
.setNormal(a
, aVector
);
569 B3DPolygon
invertNormals( const B3DPolygon
& rCandidate
)
571 B3DPolygon
aRetval(rCandidate
);
573 if(aRetval
.areNormalsUsed())
575 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
577 aRetval
.setNormal(a
, -aRetval
.getNormal(a
));
584 B3DPolygon
applyDefaultTextureCoordinatesParallel( const B3DPolygon
& rCandidate
, const B3DRange
& rRange
, bool bChangeX
, bool bChangeY
)
586 B3DPolygon
aRetval(rCandidate
);
588 if(bChangeX
|| bChangeY
)
590 // create projection of standard texture coordinates in (X, Y) onto
591 // the 3d coordinates straight
592 const double fWidth(rRange
.getWidth());
593 const double fHeight(rRange
.getHeight());
594 const bool bWidthSet(!fTools::equalZero(fWidth
));
595 const bool bHeightSet(!fTools::equalZero(fHeight
));
596 const double fOne(1.0);
598 for(sal_uInt32
a(0L); a
< aRetval
.count(); a
++)
600 const B3DPoint
aPoint(aRetval
.getB3DPoint(a
));
601 B2DPoint
aTextureCoordinate(aRetval
.getTextureCoordinate(a
));
607 aTextureCoordinate
.setX((aPoint
.getX() - rRange
.getMinX()) / fWidth
);
611 aTextureCoordinate
.setX(0.0);
619 aTextureCoordinate
.setY(fOne
- ((aPoint
.getY() - rRange
.getMinY()) / fHeight
));
623 aTextureCoordinate
.setY(fOne
);
627 aRetval
.setTextureCoordinate(a
, aTextureCoordinate
);
634 B3DPolygon
applyDefaultTextureCoordinatesSphere( const B3DPolygon
& rCandidate
, const B3DPoint
& rCenter
, bool bChangeX
, bool bChangeY
)
636 B3DPolygon
aRetval(rCandidate
);
638 if(bChangeX
|| bChangeY
)
640 // create texture coordinates using sphere projection to cartesian coordinates,
641 // use object's center as base
642 const double fOne(1.0);
643 const sal_uInt32
nPointCount(aRetval
.count());
644 bool bPolarPoints(false);
647 // create center cartesian coordinates to have a possibility to decide if on boundary
648 // transitions which value to choose
649 const B3DRange
aPlaneRange(getRange(rCandidate
));
650 const B3DPoint
aPlaneCenter(aPlaneRange
.getCenter() - rCenter
);
651 const double fXCenter(fOne
- ((atan2(aPlaneCenter
.getZ(), aPlaneCenter
.getX()) + F_PI
) / F_2PI
));
653 for(a
= 0L; a
< nPointCount
; a
++)
655 const B3DVector
aVector(aRetval
.getB3DPoint(a
) - rCenter
);
656 const double fY(fOne
- ((atan2(aVector
.getY(), aVector
.getXZLength()) + F_PI2
) / F_PI
));
657 B2DPoint
aTexCoor(aRetval
.getTextureCoordinate(a
));
659 if(fTools::equalZero(fY
))
661 // point is a north polar point, no useful X-coordinate can be created.
672 else if(fTools::equal(fY
, fOne
))
674 // point is a south polar point, no useful X-coordinate can be created. Set
675 // Y-coordinte, though
688 double fX(fOne
- ((atan2(aVector
.getZ(), aVector
.getX()) + F_PI
) / F_2PI
));
690 // correct cartesinan point coordiante dependent from center value
691 if(fX
> fXCenter
+ 0.5)
695 else if(fX
< fXCenter
- 0.5)
711 aRetval
.setTextureCoordinate(a
, aTexCoor
);
716 // correct X-texture coordinates if polar points are contained. Those
717 // coordinates cannot be correct, so use prev or next X-coordinate
718 for(a
= 0L; a
< nPointCount
; a
++)
720 B2DPoint
aTexCoor(aRetval
.getTextureCoordinate(a
));
722 if(fTools::equalZero(aTexCoor
.getY()) || fTools::equal(aTexCoor
.getY(), fOne
))
724 // get prev, next TexCoor and test for pole
725 const B2DPoint
aPrevTexCoor(aRetval
.getTextureCoordinate(a
? a
- 1L : nPointCount
- 1L));
726 const B2DPoint
aNextTexCoor(aRetval
.getTextureCoordinate((a
+ 1L) % nPointCount
));
727 const bool bPrevPole(fTools::equalZero(aPrevTexCoor
.getY()) || fTools::equal(aPrevTexCoor
.getY(), fOne
));
728 const bool bNextPole(fTools::equalZero(aNextTexCoor
.getY()) || fTools::equal(aNextTexCoor
.getY(), fOne
));
730 if(!bPrevPole
&& !bNextPole
)
732 // both no poles, mix them
733 aTexCoor
.setX((aPrevTexCoor
.getX() + aNextTexCoor
.getX()) / 2.0);
738 aTexCoor
.setX(aNextTexCoor
.getX());
742 // copy prev, even if it's a pole, hopefully it is already corrected
743 aTexCoor
.setX(aPrevTexCoor
.getX());
746 aRetval
.setTextureCoordinate(a
, aTexCoor
);
755 bool isInEpsilonRange(const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, const B3DPoint
& rTestPosition
, double fDistance
)
758 const B3DVector
aEdge(rEdgeEnd
- rEdgeStart
);
759 bool bDoDistanceTestStart(false);
760 bool bDoDistanceTestEnd(false);
762 if(aEdge
.equalZero())
764 // no edge, just a point. Do one of the distance tests.
765 bDoDistanceTestStart
= true;
769 // calculate fCut in aEdge
770 const B3DVector
aTestEdge(rTestPosition
- rEdgeStart
);
771 const double fScalarTestEdge(aEdge
.scalar(aTestEdge
));
772 const double fScalarStartEdge(aEdge
.scalar(rEdgeStart
));
773 const double fScalarEdge(aEdge
.scalar(aEdge
));
774 const double fCut((fScalarTestEdge
- fScalarStartEdge
) / fScalarEdge
);
775 const double fZero(0.0);
776 const double fOne(1.0);
778 if(fTools::less(fCut
, fZero
))
780 // left of rEdgeStart
781 bDoDistanceTestStart
= true;
783 else if(fTools::more(fCut
, fOne
))
786 bDoDistanceTestEnd
= true;
790 // inside line [0.0 .. 1.0]
791 const B3DPoint
aCutPoint(interpolate(rEdgeStart
, rEdgeEnd
, fCut
));
792 const B3DVector
aDelta(rTestPosition
- aCutPoint
);
793 const double fDistanceSquare(aDelta
.scalar(aDelta
));
795 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
806 if(bDoDistanceTestStart
)
808 const B3DVector
aDelta(rTestPosition
- rEdgeStart
);
809 const double fDistanceSquare(aDelta
.scalar(aDelta
));
811 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
816 else if(bDoDistanceTestEnd
)
818 const B3DVector
aDelta(rTestPosition
- rEdgeEnd
);
819 const double fDistanceSquare(aDelta
.scalar(aDelta
));
821 if(fDistanceSquare
<= fDistance
* fDistance
* fDistance
)
830 bool isInEpsilonRange(const B3DPolygon
& rCandidate
, const B3DPoint
& rTestPosition
, double fDistance
)
832 const sal_uInt32
nPointCount(rCandidate
.count());
836 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
837 B3DPoint
aCurrent(rCandidate
.getB3DPoint(0));
842 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
844 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
845 const B3DPoint
aNext(rCandidate
.getB3DPoint(nNextIndex
));
847 if(isInEpsilonRange(aCurrent
, aNext
, rTestPosition
, fDistance
))
858 // no edges, but points -> not closed. Check single point. Just
859 // use isInEpsilonRange with twice the same point, it handles those well
860 if(isInEpsilonRange(aCurrent
, aCurrent
, rTestPosition
, fDistance
))
870 bool isInside(const B3DPolygon
& rCandidate
, const B3DPoint
& rPoint
, bool bWithBorder
)
872 if(bWithBorder
&& isPointOnPolygon(rCandidate
, rPoint
, true))
878 const B3DVector
aPlaneNormal(rCandidate
.getNormal());
880 if(!aPlaneNormal
.equalZero())
882 const double fAbsX(fabs(aPlaneNormal
.getX()));
883 const double fAbsY(fabs(aPlaneNormal
.getY()));
884 const double fAbsZ(fabs(aPlaneNormal
.getZ()));
886 if(fAbsX
> fAbsY
&& fAbsX
> fAbsZ
)
888 // normal points mostly in X-Direction, use YZ-Polygon projection for check
891 aTrans
.set(0, 0, 0.0);
892 aTrans
.set(0, 1, 1.0);
893 aTrans
.set(1, 1, 0.0);
894 aTrans
.set(1, 2, 1.0);
896 const B2DPolygon
aYZ(createB2DPolygonFromB3DPolygon(rCandidate
, aTrans
));
898 return isInside(aYZ
, B2DPoint(rPoint
.getY(), rPoint
.getZ()), bWithBorder
);
900 else if(fAbsY
> fAbsX
&& fAbsY
> fAbsZ
)
902 // normal points mostly in Y-Direction, use XZ-Polygon projection for check
905 aTrans
.set(1, 1, 0.0);
906 aTrans
.set(1, 2, 1.0);
908 const B2DPolygon
aXZ(createB2DPolygonFromB3DPolygon(rCandidate
, aTrans
));
910 return isInside(aXZ
, B2DPoint(rPoint
.getX(), rPoint
.getZ()), bWithBorder
);
914 // normal points mostly in Z-Direction, use XY-Polygon projection for check
917 const B2DPolygon
aXY(createB2DPolygonFromB3DPolygon(rCandidate
, aTrans
));
919 return isInside(aXY
, B2DPoint(rPoint
.getX(), rPoint
.getY()), bWithBorder
);
927 bool isInside(const B3DPolygon
& rCandidate
, const B3DPolygon
& rPolygon
, bool bWithBorder
)
929 const sal_uInt32
nPointCount(rPolygon
.count());
931 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
933 const B3DPoint
aTestPoint(rPolygon
.getB3DPoint(a
));
935 if(!isInside(rCandidate
, aTestPoint
, bWithBorder
))
944 bool isPointOnLine(const B3DPoint
& rStart
, const B3DPoint
& rEnd
, const B3DPoint
& rCandidate
, bool bWithPoints
)
946 if(rCandidate
.equal(rStart
) || rCandidate
.equal(rEnd
))
948 // candidate is in epsilon around start or end -> inside
951 else if(rStart
.equal(rEnd
))
953 // start and end are equal, but candidate is outside their epsilon -> outside
958 const B3DVector
aEdgeVector(rEnd
- rStart
);
959 const B3DVector
aTestVector(rCandidate
- rStart
);
961 if(areParallel(aEdgeVector
, aTestVector
))
963 const double fZero(0.0);
964 const double fOne(1.0);
965 double fParamTestOnCurr(0.0);
967 if(aEdgeVector
.getX() > aEdgeVector
.getY())
969 if(aEdgeVector
.getX() > aEdgeVector
.getZ())
972 fParamTestOnCurr
= aTestVector
.getX() / aEdgeVector
.getX();
977 fParamTestOnCurr
= aTestVector
.getZ() / aEdgeVector
.getZ();
982 if(aEdgeVector
.getY() > aEdgeVector
.getZ())
985 fParamTestOnCurr
= aTestVector
.getY() / aEdgeVector
.getY();
990 fParamTestOnCurr
= aTestVector
.getZ() / aEdgeVector
.getZ();
994 if(fTools::more(fParamTestOnCurr
, fZero
) && fTools::less(fParamTestOnCurr
, fOne
))
1004 bool isPointOnPolygon(const B3DPolygon
& rCandidate
, const B3DPoint
& rPoint
, bool bWithPoints
)
1006 const sal_uInt32
nPointCount(rCandidate
.count());
1008 if(nPointCount
> 1L)
1010 const sal_uInt32
nLoopCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
1011 B3DPoint
aCurrentPoint(rCandidate
.getB3DPoint(0));
1013 for(sal_uInt32
a(0); a
< nLoopCount
; a
++)
1015 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint((a
+ 1) % nPointCount
));
1017 if(isPointOnLine(aCurrentPoint
, aNextPoint
, rPoint
, bWithPoints
))
1022 aCurrentPoint
= aNextPoint
;
1025 else if(nPointCount
&& bWithPoints
)
1027 return rPoint
.equal(rCandidate
.getB3DPoint(0));
1033 bool getCutBetweenLineAndPlane(const B3DVector
& rPlaneNormal
, const B3DPoint
& rPlanePoint
, const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, double& fCut
)
1035 if(!rPlaneNormal
.equalZero() && !rEdgeStart
.equal(rEdgeEnd
))
1037 const B3DVector
aTestEdge(rEdgeEnd
- rEdgeStart
);
1038 const double fScalarEdge(rPlaneNormal
.scalar(aTestEdge
));
1040 if(!fTools::equalZero(fScalarEdge
))
1042 const B3DVector
aCompareEdge(rPlanePoint
- rEdgeStart
);
1043 const double fScalarCompare(rPlaneNormal
.scalar(aCompareEdge
));
1045 fCut
= fScalarCompare
/ fScalarEdge
;
1053 bool getCutBetweenLineAndPolygon(const B3DPolygon
& rCandidate
, const B3DPoint
& rEdgeStart
, const B3DPoint
& rEdgeEnd
, double& fCut
)
1055 const sal_uInt32
nPointCount(rCandidate
.count());
1057 if(nPointCount
> 2 && !rEdgeStart
.equal(rEdgeEnd
))
1059 const B3DVector
aPlaneNormal(rCandidate
.getNormal());
1061 if(!aPlaneNormal
.equalZero())
1063 const B3DPoint
aPointOnPlane(rCandidate
.getB3DPoint(0));
1065 return getCutBetweenLineAndPlane(aPlaneNormal
, aPointOnPlane
, rEdgeStart
, rEdgeEnd
, fCut
);
1072 //////////////////////////////////////////////////////////////////////
1073 // comparators with tolerance for 3D Polygons
1075 bool equal(const B3DPolygon
& rCandidateA
, const B3DPolygon
& rCandidateB
, const double& rfSmallValue
)
1077 const sal_uInt32
nPointCount(rCandidateA
.count());
1079 if(nPointCount
!= rCandidateB
.count())
1082 const bool bClosed(rCandidateA
.isClosed());
1084 if(bClosed
!= rCandidateB
.isClosed())
1087 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
1089 const B3DPoint
aPoint(rCandidateA
.getB3DPoint(a
));
1091 if(!aPoint
.equal(rCandidateB
.getB3DPoint(a
), rfSmallValue
))
1098 bool equal(const B3DPolygon
& rCandidateA
, const B3DPolygon
& rCandidateB
)
1100 const double fSmallValue(fTools::getSmallValue());
1102 return equal(rCandidateA
, rCandidateB
, fSmallValue
);
1105 // snap points of horizontal or vertical edges to discrete values
1106 B3DPolygon
snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon
& rCandidate
)
1108 const sal_uInt32
nPointCount(rCandidate
.count());
1112 // Start by copying the source polygon to get a writeable copy. The closed state is
1113 // copied by aRetval's initialisation, too, so no need to copy it in this method
1114 B3DPolygon
aRetval(rCandidate
);
1116 // prepare geometry data. Get rounded from original
1117 B3ITuple
aPrevTuple(basegfx::fround(rCandidate
.getB3DPoint(nPointCount
- 1)));
1118 B3DPoint
aCurrPoint(rCandidate
.getB3DPoint(0));
1119 B3ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1121 // loop over all points. This will also snap the implicit closing edge
1122 // even when not closed, but that's no problem here
1123 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
1125 // get next point. Get rounded from original
1126 const bool bLastRun(a
+ 1 == nPointCount
);
1127 const sal_uInt32
nNextIndex(bLastRun
? 0 : a
+ 1);
1128 const B3DPoint
aNextPoint(rCandidate
.getB3DPoint(nNextIndex
));
1129 const B3ITuple
aNextTuple(basegfx::fround(aNextPoint
));
1132 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1133 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1134 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1135 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1136 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1137 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1139 if(bSnapX
|| bSnapY
)
1141 const B3DPoint
aSnappedPoint(
1142 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1143 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY(),
1146 aRetval
.setB3DPoint(a
, aSnappedPoint
);
1149 // prepare next point
1152 aPrevTuple
= aCurrTuple
;
1153 aCurrPoint
= aNextPoint
;
1154 aCurrTuple
= aNextTuple
;
1166 } // end of namespace tools
1167 } // end of namespace basegfx
1169 //////////////////////////////////////////////////////////////////////////////