update dev300-m58
[ooovba.git] / basegfx / source / polygon / b3dpolygontools.cxx
blob9164793f7bb10fa8d9d0764274e469ac0bd2dc64
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
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>
43 #include <numeric>
45 //////////////////////////////////////////////////////////////////////////////
47 namespace basegfx
49 namespace tools
51 // B3DPolygon tools
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 (!)");
68 if(nIndex)
70 return nIndex - 1L;
72 else if(rCandidate.count())
74 return rCandidate.count() - 1L;
76 else
78 return nIndex;
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())
88 return nIndex + 1L;
90 else
92 return 0L;
96 B3DRange getRange(const B3DPolygon& rCandidate)
98 B3DRange aRetval;
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);
107 return aRetval;
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))
121 aRetval = -aRetval;
124 return aRetval;
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;
145 return eRetval;
148 double getSignedArea(const B3DPolygon& rCandidate)
150 double fRetval(0.0);
151 const sal_uInt32 nPointCount(rCandidate.count());
153 if(nPointCount > 2)
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));
176 switch(nCase)
178 case 1: // ignore x
179 fRetval += aPreviousPoint.getZ() * aCurrentPoint.getY();
180 fRetval -= aPreviousPoint.getY() * aCurrentPoint.getZ();
181 break;
182 case 2: // ignore y
183 fRetval += aPreviousPoint.getX() * aCurrentPoint.getZ();
184 fRetval -= aPreviousPoint.getZ() * aCurrentPoint.getX();
185 break;
186 case 3: // ignore z
187 fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
188 fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
189 break;
192 // prepare next step
193 aPreviousPoint = aCurrentPoint;
196 switch(nCase)
198 case 1: // ignore x
199 fRetval /= 2.0 * aAbsNormal.getX();
200 break;
201 case 2: // ignore y
202 fRetval /= 2.0 * aAbsNormal.getY();
203 break;
204 case 3: // ignore z
205 fRetval /= 2.0 * aAbsNormal.getZ();
206 break;
210 return fRetval;
213 double getArea(const B3DPolygon& rCandidate)
215 double fRetval(0.0);
217 if(rCandidate.count() > 2)
219 fRetval = getSignedArea(rCandidate);
220 const double fZero(0.0);
222 if(fTools::less(fRetval, fZero))
224 fRetval = -fRetval;
228 return fRetval;
231 double getEdgeLength(const B3DPolygon& rCandidate, sal_uInt32 nIndex)
233 OSL_ENSURE(nIndex < rCandidate.count(), "getEdgeLength: Access to polygon out of range (!)");
234 double fRetval(0.0);
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();
249 return fRetval;
252 double getLength(const B3DPolygon& rCandidate)
254 double fRetval(0.0);
255 const sal_uInt32 nPointCount(rCandidate.count());
257 if(nPointCount > 1L)
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();
271 return fRetval;
274 B3DPoint getPositionAbsolute(const B3DPolygon& rCandidate, double fDistance, double fLength)
276 B3DPoint aRetval;
277 const sal_uInt32 nPointCount(rCandidate.count());
279 if(nPointCount > 1L)
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;
301 else
303 // crop to polygon start
304 fDistance = fZero;
305 bIndexDone = true;
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;
318 else
320 // crop to polygon end
321 fDistance = fZero;
322 nIndex = nPointCount - 1L;
323 bIndexDone = true;
327 // look for correct index. fDistance is now [0.0 .. fLength[
328 if(!bIndexDone)
332 // get length of next edge
333 fEdgeLength = getEdgeLength(rCandidate, nIndex);
335 if(fTools::moreOrEqual(fDistance, fEdgeLength))
337 // go to next edge
338 fDistance -= fEdgeLength;
339 nIndex++;
341 else
343 // it's on this edge, stop
344 bIndexDone = true;
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);
370 return aRetval;
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)
398 // clear targets
399 if(pLineTarget)
401 pLineTarget->clear();
404 if(pGapTarget)
406 pGapTarget->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);
415 bool bIsLine(true);
416 double fDotDashMovingLength(rDotDashArray[0]);
417 B3DPolygon aSnippet;
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));
443 if(bHandleLine)
445 pLineTarget->append(aSnippet);
447 else
449 pGapTarget->append(aSnippet);
452 aSnippet.clear();
455 // prepare next DotDashArray step and flip line/gap flag
456 fLastDotDashMovingLength = fDotDashMovingLength;
457 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
458 bIsLine = !bIsLine;
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)
483 if(aSnippet.count())
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
496 if(pLineTarget)
498 const sal_uInt32 nCount(pLineTarget->count());
500 if(nCount > 1)
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);
518 if(pGapTarget)
520 const sal_uInt32 nCount(pGapTarget->count());
522 if(nCount > 1)
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);
540 else
542 // parameters make no sense, just add source to targets
543 if(pLineTarget)
545 pLineTarget->append(rCandidate);
548 if(pGapTarget)
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);
562 aVector.normalize();
563 aRetval.setNormal(a, aVector);
566 return aRetval;
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));
581 return aRetval;
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));
603 if(bChangeX)
605 if(bWidthSet)
607 aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
609 else
611 aTextureCoordinate.setX(0.0);
615 if(bChangeY)
617 if(bHeightSet)
619 aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
621 else
623 aTextureCoordinate.setY(fOne);
627 aRetval.setTextureCoordinate(a, aTextureCoordinate);
631 return aRetval;
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);
645 sal_uInt32 a;
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.
662 if(bChangeY)
664 aTexCoor.setY(0.0);
666 if(bChangeX)
668 bPolarPoints = true;
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
676 if(bChangeY)
678 aTexCoor.setY(fOne);
680 if(bChangeX)
682 bPolarPoints = true;
686 else
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)
693 fX -= fOne;
695 else if(fX < fXCenter - 0.5)
697 fX += fOne;
700 if(bChangeX)
702 aTexCoor.setX(fX);
705 if(bChangeY)
707 aTexCoor.setY(fY);
711 aRetval.setTextureCoordinate(a, aTexCoor);
714 if(bPolarPoints)
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);
735 else if(!bNextPole)
737 // copy next
738 aTexCoor.setX(aNextTexCoor.getX());
740 else
742 // copy prev, even if it's a pole, hopefully it is already corrected
743 aTexCoor.setX(aPrevTexCoor.getX());
746 aRetval.setTextureCoordinate(a, aTexCoor);
752 return aRetval;
755 bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance)
757 // build edge vector
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;
767 else
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))
785 // right of rEdgeEnd
786 bDoDistanceTestEnd = true;
788 else
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)
797 return true;
799 else
801 return false;
806 if(bDoDistanceTestStart)
808 const B3DVector aDelta(rTestPosition - rEdgeStart);
809 const double fDistanceSquare(aDelta.scalar(aDelta));
811 if(fDistanceSquare <= fDistance * fDistance * fDistance)
813 return true;
816 else if(bDoDistanceTestEnd)
818 const B3DVector aDelta(rTestPosition - rEdgeEnd);
819 const double fDistanceSquare(aDelta.scalar(aDelta));
821 if(fDistanceSquare <= fDistance * fDistance * fDistance)
823 return true;
827 return false;
830 bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance)
832 const sal_uInt32 nPointCount(rCandidate.count());
834 if(nPointCount)
836 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
837 B3DPoint aCurrent(rCandidate.getB3DPoint(0));
839 if(nEdgeCount)
841 // edges
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))
849 return true;
852 // prepare next step
853 aCurrent = aNext;
856 else
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))
862 return true;
867 return false;
870 bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
872 if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
874 return true;
876 else
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
889 B3DHomMatrix aTrans;
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
903 B3DHomMatrix aTrans;
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);
912 else
914 // normal points mostly in Z-Direction, use XY-Polygon projection for check
915 B3DHomMatrix aTrans;
917 const B2DPolygon aXY(createB2DPolygonFromB3DPolygon(rCandidate, aTrans));
919 return isInside(aXY, B2DPoint(rPoint.getX(), rPoint.getY()), bWithBorder);
923 return false;
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))
937 return false;
941 return true;
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
949 return bWithPoints;
951 else if(rStart.equal(rEnd))
953 // start and end are equal, but candidate is outside their epsilon -> outside
954 return false;
956 else
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())
971 // X is biggest
972 fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
974 else
976 // Z is biggest
977 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
980 else
982 if(aEdgeVector.getY() > aEdgeVector.getZ())
984 // Y is biggest
985 fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
987 else
989 // Z is biggest
990 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
994 if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
996 return true;
1000 return false;
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))
1019 return true;
1022 aCurrentPoint = aNextPoint;
1025 else if(nPointCount && bWithPoints)
1027 return rPoint.equal(rCandidate.getB3DPoint(0));
1030 return false;
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;
1046 return true;
1050 return false;
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);
1069 return false;
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())
1080 return false;
1082 const bool bClosed(rCandidateA.isClosed());
1084 if(bClosed != rCandidateB.isClosed())
1085 return false;
1087 for(sal_uInt32 a(0); a < nPointCount; a++)
1089 const B3DPoint aPoint(rCandidateA.getB3DPoint(a));
1091 if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue))
1092 return false;
1095 return true;
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());
1110 if(nPointCount > 1)
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));
1131 // get the states
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(),
1144 aCurrPoint.getZ());
1146 aRetval.setB3DPoint(a, aSnappedPoint);
1149 // prepare next point
1150 if(!bLastRun)
1152 aPrevTuple = aCurrTuple;
1153 aCurrPoint = aNextPoint;
1154 aCurrTuple = aNextTuple;
1158 return aRetval;
1160 else
1162 return rCandidate;
1166 } // end of namespace tools
1167 } // end of namespace basegfx
1169 //////////////////////////////////////////////////////////////////////////////
1171 // eof