Bump version to 5.0-43
[LibreOffice.git] / basegfx / source / polygon / b3dpolygontools.cxx
blob10e35813fffacdcd5c15ea78817f69eec25fbcd5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <basegfx/polygon/b3dpolygontools.hxx>
22 #include <basegfx/polygon/b3dpolygon.hxx>
23 #include <basegfx/numeric/ftools.hxx>
24 #include <basegfx/range/b3drange.hxx>
25 #include <basegfx/point/b2dpoint.hxx>
26 #include <basegfx/matrix/b3dhommatrix.hxx>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/tuple/b3ituple.hxx>
30 #include <numeric>
32 namespace basegfx
34 namespace tools
36 // B3DPolygon tools
37 void checkClosed(B3DPolygon& rCandidate)
39 while(rCandidate.count() > 1L
40 && rCandidate.getB3DPoint(0L).equal(rCandidate.getB3DPoint(rCandidate.count() - 1L)))
42 rCandidate.setClosed(true);
43 rCandidate.remove(rCandidate.count() - 1L);
47 sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
49 OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
51 if(nIndex + 1L < rCandidate.count())
53 return nIndex + 1L;
55 else
57 return 0L;
61 B3DRange getRange(const B3DPolygon& rCandidate)
63 B3DRange aRetval;
64 const sal_uInt32 nPointCount(rCandidate.count());
66 for(sal_uInt32 a(0L); a < nPointCount; a++)
68 const B3DPoint aTestPoint(rCandidate.getB3DPoint(a));
69 aRetval.expand(aTestPoint);
72 return aRetval;
75 B3DVector getNormal(const B3DPolygon& rCandidate)
77 return rCandidate.getNormal();
80 double getLength(const B3DPolygon& rCandidate)
82 double fRetval(0.0);
83 const sal_uInt32 nPointCount(rCandidate.count());
85 if(nPointCount > 1L)
87 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
89 for(sal_uInt32 a(0L); a < nLoopCount; a++)
91 const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate));
92 const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
93 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
94 const B3DVector aVector(aNextPoint - aCurrentPoint);
95 fRetval += aVector.getLength();
99 return fRetval;
102 void applyLineDashing(const B3DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fDotDashLength)
104 const sal_uInt32 nPointCount(rCandidate.count());
105 const sal_uInt32 nDotDashCount(rDotDashArray.size());
107 if(fTools::lessOrEqual(fDotDashLength, 0.0))
109 fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
112 if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
114 // clear targets
115 if(pLineTarget)
117 pLineTarget->clear();
120 if(pGapTarget)
122 pGapTarget->clear();
125 // prepare current edge's start
126 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
127 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
129 // prepare DotDashArray iteration and the line/gap switching bool
130 sal_uInt32 nDotDashIndex(0);
131 bool bIsLine(true);
132 double fDotDashMovingLength(rDotDashArray[0]);
133 B3DPolygon aSnippet;
135 // iterate over all edges
136 for(sal_uInt32 a(0); a < nEdgeCount; a++)
138 // update current edge
139 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
140 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
141 const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
143 if(!fTools::equalZero(fEdgeLength))
145 double fLastDotDashMovingLength(0.0);
146 while(fTools::less(fDotDashMovingLength, fEdgeLength))
148 // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
149 const bool bHandleLine(bIsLine && pLineTarget);
150 const bool bHandleGap(!bIsLine && pGapTarget);
152 if(bHandleLine || bHandleGap)
154 if(!aSnippet.count())
156 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
159 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
161 if(bHandleLine)
163 pLineTarget->append(aSnippet);
165 else
167 pGapTarget->append(aSnippet);
170 aSnippet.clear();
173 // prepare next DotDashArray step and flip line/gap flag
174 fLastDotDashMovingLength = fDotDashMovingLength;
175 fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
176 bIsLine = !bIsLine;
179 // append snippet [fLastDotDashMovingLength, fEdgeLength]
180 const bool bHandleLine(bIsLine && pLineTarget);
181 const bool bHandleGap(!bIsLine && pGapTarget);
183 if(bHandleLine || bHandleGap)
185 if(!aSnippet.count())
187 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
190 aSnippet.append(aNextPoint);
193 // prepare move to next edge
194 fDotDashMovingLength -= fEdgeLength;
197 // prepare next edge step (end point gets new start point)
198 aCurrentPoint = aNextPoint;
201 // append last intermediate results (if exists)
202 if(aSnippet.count())
204 if(bIsLine && pLineTarget)
206 pLineTarget->append(aSnippet);
208 else if(!bIsLine && pGapTarget)
210 pGapTarget->append(aSnippet);
214 // check if start and end polygon may be merged
215 if(pLineTarget)
217 const sal_uInt32 nCount(pLineTarget->count());
219 if(nCount > 1)
221 // these polygons were created above, there exists none with less than two points,
222 // thus dircet point access below is allowed
223 const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
224 B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
226 if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
228 // start of first and end of last are the same -> merge them
229 aLast.append(aFirst);
230 aLast.removeDoublePoints();
231 pLineTarget->setB3DPolygon(0, aLast);
232 pLineTarget->remove(nCount - 1);
237 if(pGapTarget)
239 const sal_uInt32 nCount(pGapTarget->count());
241 if(nCount > 1)
243 // these polygons were created above, there exists none with less than two points,
244 // thus dircet point access below is allowed
245 const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0));
246 B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1));
248 if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
250 // start of first and end of last are the same -> merge them
251 aLast.append(aFirst);
252 aLast.removeDoublePoints();
253 pGapTarget->setB3DPolygon(0, aLast);
254 pGapTarget->remove(nCount - 1);
259 else
261 // parameters make no sense, just add source to targets
262 if(pLineTarget)
264 pLineTarget->append(rCandidate);
267 if(pGapTarget)
269 pGapTarget->append(rCandidate);
274 B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
276 B3DPolygon aRetval(rCandidate);
278 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
280 B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
281 aVector.normalize();
282 aRetval.setNormal(a, aVector);
285 return aRetval;
288 B3DPolygon invertNormals( const B3DPolygon& rCandidate)
290 B3DPolygon aRetval(rCandidate);
292 if(aRetval.areNormalsUsed())
294 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
296 aRetval.setNormal(a, -aRetval.getNormal(a));
300 return aRetval;
303 B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
305 B3DPolygon aRetval(rCandidate);
307 if(bChangeX || bChangeY)
309 // create projection of standard texture coordinates in (X, Y) onto
310 // the 3d coordinates straight
311 const double fWidth(rRange.getWidth());
312 const double fHeight(rRange.getHeight());
313 const bool bWidthSet(!fTools::equalZero(fWidth));
314 const bool bHeightSet(!fTools::equalZero(fHeight));
315 const double fOne(1.0);
317 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
319 const B3DPoint aPoint(aRetval.getB3DPoint(a));
320 B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a));
322 if(bChangeX)
324 if(bWidthSet)
326 aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
328 else
330 aTextureCoordinate.setX(0.0);
334 if(bChangeY)
336 if(bHeightSet)
338 aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
340 else
342 aTextureCoordinate.setY(fOne);
346 aRetval.setTextureCoordinate(a, aTextureCoordinate);
350 return aRetval;
353 B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
355 B3DPolygon aRetval(rCandidate);
357 if(bChangeX || bChangeY)
359 // create texture coordinates using sphere projection to cartesian coordinates,
360 // use object's center as base
361 const double fOne(1.0);
362 const sal_uInt32 nPointCount(aRetval.count());
363 bool bPolarPoints(false);
364 sal_uInt32 a;
366 // create center cartesian coordinates to have a possibility to decide if on boundary
367 // transitions which value to choose
368 const B3DRange aPlaneRange(getRange(rCandidate));
369 const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter);
370 const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI));
372 for(a = 0L; a < nPointCount; a++)
374 const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
375 const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI));
376 B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
378 if(fTools::equalZero(fY))
380 // point is a north polar point, no useful X-coordinate can be created.
381 if(bChangeY)
383 aTexCoor.setY(0.0);
385 if(bChangeX)
387 bPolarPoints = true;
391 else if(fTools::equal(fY, fOne))
393 // point is a south polar point, no useful X-coordinate can be created. Set
394 // Y-coordinte, though
395 if(bChangeY)
397 aTexCoor.setY(fOne);
399 if(bChangeX)
401 bPolarPoints = true;
405 else
407 double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI));
409 // correct cartesinan point coordiante dependent from center value
410 if(fX > fXCenter + 0.5)
412 fX -= fOne;
414 else if(fX < fXCenter - 0.5)
416 fX += fOne;
419 if(bChangeX)
421 aTexCoor.setX(fX);
424 if(bChangeY)
426 aTexCoor.setY(fY);
430 aRetval.setTextureCoordinate(a, aTexCoor);
433 if(bPolarPoints)
435 // correct X-texture coordinates if polar points are contained. Those
436 // coordinates cannot be correct, so use prev or next X-coordinate
437 for(a = 0L; a < nPointCount; a++)
439 B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
441 if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne))
443 // get prev, next TexCoor and test for pole
444 const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L));
445 const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount));
446 const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne));
447 const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne));
449 if(!bPrevPole && !bNextPole)
451 // both no poles, mix them
452 aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0);
454 else if(!bNextPole)
456 // copy next
457 aTexCoor.setX(aNextTexCoor.getX());
459 else
461 // copy prev, even if it's a pole, hopefully it is already corrected
462 aTexCoor.setX(aPrevTexCoor.getX());
465 aRetval.setTextureCoordinate(a, aTexCoor);
471 return aRetval;
474 bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
476 if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
478 return true;
480 else
482 bool bRetval(false);
483 const B3DVector aPlaneNormal(rCandidate.getNormal());
485 if(!aPlaneNormal.equalZero())
487 const sal_uInt32 nPointCount(rCandidate.count());
489 if(nPointCount)
491 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1));
492 const double fAbsX(fabs(aPlaneNormal.getX()));
493 const double fAbsY(fabs(aPlaneNormal.getY()));
494 const double fAbsZ(fabs(aPlaneNormal.getZ()));
496 if(fAbsX > fAbsY && fAbsX > fAbsZ)
498 // normal points mostly in X-Direction, use YZ-Polygon projection for check
499 // x -> y, y -> z
500 for(sal_uInt32 a(0); a < nPointCount; a++)
502 const B3DPoint aPreviousPoint(aCurrentPoint);
503 aCurrentPoint = rCandidate.getB3DPoint(a);
505 // cross-over in Z?
506 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
507 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
509 if(bCompZA != bCompZB)
511 // cross-over in Y?
512 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
513 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
515 if(bCompYA == bCompYB)
517 if(bCompYA)
519 bRetval = !bRetval;
522 else
524 const double fCompare(
525 aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) *
526 (aPreviousPoint.getY() - aCurrentPoint.getY()) /
527 (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
529 if(fTools::more(fCompare, rPoint.getY()))
531 bRetval = !bRetval;
537 else if(fAbsY > fAbsX && fAbsY > fAbsZ)
539 // normal points mostly in Y-Direction, use XZ-Polygon projection for check
540 // x -> x, y -> z
541 for(sal_uInt32 a(0); a < nPointCount; a++)
543 const B3DPoint aPreviousPoint(aCurrentPoint);
544 aCurrentPoint = rCandidate.getB3DPoint(a);
546 // cross-over in Z?
547 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
548 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
550 if(bCompZA != bCompZB)
552 // cross-over in X?
553 const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
554 const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
556 if(bCompXA == bCompXB)
558 if(bCompXA)
560 bRetval = !bRetval;
563 else
565 const double fCompare(
566 aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) *
567 (aPreviousPoint.getX() - aCurrentPoint.getX()) /
568 (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
570 if(fTools::more(fCompare, rPoint.getX()))
572 bRetval = !bRetval;
578 else
580 // normal points mostly in Z-Direction, use XY-Polygon projection for check
581 // x -> x, y -> y
582 for(sal_uInt32 a(0); a < nPointCount; a++)
584 const B3DPoint aPreviousPoint(aCurrentPoint);
585 aCurrentPoint = rCandidate.getB3DPoint(a);
587 // cross-over in Y?
588 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
589 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
591 if(bCompYA != bCompYB)
593 // cross-over in X?
594 const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
595 const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
597 if(bCompXA == bCompXB)
599 if(bCompXA)
601 bRetval = !bRetval;
604 else
606 const double fCompare(
607 aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
608 (aPreviousPoint.getX() - aCurrentPoint.getX()) /
609 (aPreviousPoint.getY() - aCurrentPoint.getY()));
611 if(fTools::more(fCompare, rPoint.getX()))
613 bRetval = !bRetval;
622 return bRetval;
626 bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints)
628 if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
630 // candidate is in epsilon around start or end -> inside
631 return bWithPoints;
633 else if(rStart.equal(rEnd))
635 // start and end are equal, but candidate is outside their epsilon -> outside
636 return false;
638 else
640 const B3DVector aEdgeVector(rEnd - rStart);
641 const B3DVector aTestVector(rCandidate - rStart);
643 if(areParallel(aEdgeVector, aTestVector))
645 double fParamTestOnCurr(0.0);
647 if(aEdgeVector.getX() > aEdgeVector.getY())
649 if(aEdgeVector.getX() > aEdgeVector.getZ())
651 // X is biggest
652 fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
654 else
656 // Z is biggest
657 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
660 else
662 if(aEdgeVector.getY() > aEdgeVector.getZ())
664 // Y is biggest
665 fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
667 else
669 // Z is biggest
670 fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
674 if(fTools::more(fParamTestOnCurr, 0.0) && fTools::less(fParamTestOnCurr, 1.0))
676 return true;
680 return false;
684 bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints)
686 const sal_uInt32 nPointCount(rCandidate.count());
688 if(nPointCount > 1L)
690 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
691 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
693 for(sal_uInt32 a(0); a < nLoopCount; a++)
695 const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount));
697 if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
699 return true;
702 aCurrentPoint = aNextPoint;
705 else if(nPointCount && bWithPoints)
707 return rPoint.equal(rCandidate.getB3DPoint(0));
710 return false;
713 bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
715 if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd))
717 const B3DVector aTestEdge(rEdgeEnd - rEdgeStart);
718 const double fScalarEdge(rPlaneNormal.scalar(aTestEdge));
720 if(!fTools::equalZero(fScalarEdge))
722 const B3DVector aCompareEdge(rPlanePoint - rEdgeStart);
723 const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge));
725 fCut = fScalarCompare / fScalarEdge;
726 return true;
730 return false;
733 // snap points of horizontal or vertical edges to discrete values
734 B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate)
736 const sal_uInt32 nPointCount(rCandidate.count());
738 if(nPointCount > 1)
740 // Start by copying the source polygon to get a writeable copy. The closed state is
741 // copied by aRetval's initialisation, too, so no need to copy it in this method
742 B3DPolygon aRetval(rCandidate);
744 // prepare geometry data. Get rounded from original
745 B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1)));
746 B3DPoint aCurrPoint(rCandidate.getB3DPoint(0));
747 B3ITuple aCurrTuple(basegfx::fround(aCurrPoint));
749 // loop over all points. This will also snap the implicit closing edge
750 // even when not closed, but that's no problem here
751 for(sal_uInt32 a(0); a < nPointCount; a++)
753 // get next point. Get rounded from original
754 const bool bLastRun(a + 1 == nPointCount);
755 const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
756 const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
757 const B3ITuple aNextTuple(basegfx::fround(aNextPoint));
759 // get the states
760 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
761 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
762 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
763 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
764 const bool bSnapX(bPrevVertical || bNextVertical);
765 const bool bSnapY(bPrevHorizontal || bNextHorizontal);
767 if(bSnapX || bSnapY)
769 const B3DPoint aSnappedPoint(
770 bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
771 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(),
772 aCurrPoint.getZ());
774 aRetval.setB3DPoint(a, aSnappedPoint);
777 // prepare next point
778 if(!bLastRun)
780 aPrevTuple = aCurrTuple;
781 aCurrPoint = aNextPoint;
782 aCurrTuple = aNextTuple;
786 return aRetval;
788 else
790 return rCandidate;
794 } // end of namespace tools
795 } // end of namespace basegfx
797 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */