diff options
author | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:58:09 +0000 |
---|---|---|
committer | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:58:09 +0000 |
commit | 44392fb2000289f66968fcc901b2860a1368e695 (patch) | |
tree | 1ee4c2b22f390780bcbe203c8d3b6ec4245898b7 /basegfx/source/polygon | |
parent | b62bca7a2c2e30fefcc0e8ddce3f5b440ab13080 (diff) |
INTEGRATION: CWS canvas02 (1.19.8); FILE MERGED
2005/10/08 13:19:22 thb 1.19.8.6: RESYNC: (1.19-1.20); FILE MERGED
2005/09/05 10:08:53 mbu 1.19.8.5: isPolyPolygonEqualRectangle()
2005/08/30 22:58:23 thb 1.19.8.4: #i52876# Corrected tools::isRectangle(); added isRectangle() also for poly-polygon; removed isPolyPolygonEqualRectangle() (because of the redundancy)
2005/08/30 15:14:46 mbu 1.19.8.3: isPolyPolygonEqualRectangle()
2005/07/28 15:01:25 thb 1.19.8.2: Join from cws_src680_aw024: #i48939# and new rendering subsystem need AW's clipper changes
2005/07/28 10:10:20 thb 1.19.8.1: Join from cws_src680_aw024: #i48939# and new rendering subsystem need AW's clipper changes
Diffstat (limited to 'basegfx/source/polygon')
-rw-r--r-- | basegfx/source/polygon/b2dpolygontools.cxx | 1177 |
1 files changed, 1048 insertions, 129 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index 57a2f919e0e5..5c82af69c828 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -4,9 +4,9 @@ * * $RCSfile: b2dpolygontools.cxx,v $ * - * $Revision: 1.20 $ + * $Revision: 1.21 $ * - * last change: $Author: rt $ $Date: 2005-09-07 20:46:23 $ + * last change: $Author: kz $ $Date: 2005-11-02 13:58:09 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -45,6 +45,10 @@ #include <osl/diagnose.h> #endif +#ifndef INCLUDED_RTL_MATH_HXX +#include <rtl/math.hxx> +#endif + #ifndef _BGFX_POLYGON_B2DPOLYGON_HXX #include <basegfx/polygon/b2dpolygon.hxx> #endif @@ -69,7 +73,20 @@ #include <basegfx/polygon/b2dpolypolygoncutter.hxx> #endif +#ifndef _BGFX_POINT_B3DPOINT_HXX +#include <basegfx/point/b3dpoint.hxx> +#endif + +#ifndef _BGFX_MATRIX_B3DHOMMATRIX_HXX +#include <basegfx/matrix/b3dhommatrix.hxx> +#endif + +#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX +#include <basegfx/matrix/b2dhommatrix.hxx> +#endif + #include <numeric> +#include <limits> // #i37443# #define ANGLE_BOUND_START_VALUE (2.25) @@ -124,17 +141,21 @@ namespace basegfx { return nIndex + 1L; } - else + else if(nIndex + 1L == rCandidate.count()) { return 0L; } + else + { + return nIndex; + } } B2VectorOrientation getOrientation(const B2DPolygon& rCandidate) { B2VectorOrientation eRetval(ORIENTATION_NEUTRAL); - if(rCandidate.count() > 2L) + if(rCandidate.count() > 2L || rCandidate.areControlVectorsUsed()) { const double fSignedArea(getSignedArea(rCandidate)); @@ -170,19 +191,11 @@ namespace basegfx B2DPolyPolygon removeIntersections(const B2DPolygon& rCandidate, bool bKeepOrientations) { - OSL_ENSURE(!rCandidate.areControlPointsUsed(), "removeIntersections: ATM works not for curves (!)"); B2DPolyPolygon aRetval; if(rCandidate.count() > 2L) { - B2DPolyPolygonCutter aCutter; - - B2DPolygon aPreparedCandidate(rCandidate); - aPreparedCandidate.removeDoublePoints(); - aCutter.addPolygon(aPreparedCandidate); - aCutter.removeSelfIntersections(); - - aRetval = aCutter.getPolyPolygon(); + aRetval = SolveCrossovers(rCandidate); if(bKeepOrientations && aRetval.count() > 1L) { @@ -419,56 +432,64 @@ namespace basegfx bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder) { - OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isInside: ATM works not for curves (!)"); + const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? adaptiveSubdivideByCount(rCandidate, 6L) : rCandidate); bool bRetval(false); - const sal_uInt32 nPointCount(rCandidate.count()); + const sal_uInt32 nPointCount(aCandidate.count()); - if(nPointCount > 2L) + for(sal_uInt32 a(0L); a < nPointCount; a++) { - B2DPoint aPreviousPoint(rCandidate.getB2DPoint(nPointCount - 1L)); + const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a)); - for(sal_uInt32 a(0L); a < nPointCount; a++) + if(bWithBorder && aCurrentPoint.equal(rPoint)) { - const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(a)); + return true; + } - // test epsilon around points and line - if(isPointOnLine(aPreviousPoint, aCurrentPoint, rPoint, true)) - { - return bWithBorder; - } + // cross-over in Y? + const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L)); + const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); + const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); - // cross-over in Y? - const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); - const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); + if(bCompYA != bCompYB) + { + const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); + const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); - if(bCompYA != bCompYB) + if(bCompXA == bCompXB) { - const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); - const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); - - if(bCompXA == bCompXB) + if(bCompXA) { - if(bCompXA) - { - bRetval = !bRetval; - } + bRetval = !bRetval; } else { - const double fCompare = - aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) * - (aPreviousPoint.getX() - aCurrentPoint.getX()) / - (aPreviousPoint.getY() - aCurrentPoint.getY()); - - if(fTools::moreOrEqual(fCompare, rPoint.getX())) + // it may still be a touch with a vertical line when bWithBorder==true, + // check for it. If it is, return true + if(bWithBorder + && fTools::equal(aPreviousPoint.getX(), rPoint.getX()) + && fTools::equal(aCurrentPoint.getX(), rPoint.getX())) { - bRetval = !bRetval; + return true; } } } + else + { + const double fCompare = + aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) * + (aPreviousPoint.getX() - aCurrentPoint.getX()) / + (aPreviousPoint.getY() - aCurrentPoint.getY()); - // prepare next step - aPreviousPoint = aCurrentPoint; + if(bWithBorder && fTools::equal(fCompare, rPoint.getX())) + { + // point on line, when bWithBorder=true, all is done + return true; + } + else if(fTools::more(fCompare, rPoint.getX())) + { + bRetval = !bRetval; + } + } } } @@ -477,13 +498,15 @@ namespace basegfx bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder) { - const sal_uInt32 nPointCount(rPolygon.count()); + const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? adaptiveSubdivideByCount(rCandidate, 6L) : rCandidate); + const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? adaptiveSubdivideByCount(rPolygon, 6L) : rPolygon); + const sal_uInt32 nPointCount(aPolygon.count()); for(sal_uInt32 a(0L); a < nPointCount; a++) { - const B2DPoint aTestPoint(rPolygon.getB2DPoint(a)); + const B2DPoint aTestPoint(aPolygon.getB2DPoint(a)); - if(!isInside(rCandidate, aTestPoint, bWithBorder)) + if(!isInside(aCandidate, aTestPoint, bWithBorder)) { return false; } @@ -531,16 +554,16 @@ namespace basegfx double getSignedArea(const B2DPolygon& rCandidate) { - OSL_ENSURE(!rCandidate.areControlPointsUsed(), "getSignedArea: ATM works not for curves (!)"); + const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? adaptiveSubdivideByCount(rCandidate, 6L) : rCandidate); double fRetval(0.0); - const sal_uInt32 nPointCount(rCandidate.count()); + const sal_uInt32 nPointCount(aCandidate.count()); if(nPointCount > 2) { for(sal_uInt32 a(0L); a < nPointCount; a++) { - const B2DPoint aPreviousPoint(rCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L)); - const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(a)); + const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L)); + const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a)); fRetval += aPreviousPoint.getX() * aCurrentPoint.getY(); fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX(); @@ -556,7 +579,7 @@ namespace basegfx { double fRetval(0.0); - if(rCandidate.count() > 2) + if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed()) { fRetval = getSignedArea(rCandidate); const double fZero(0.0); @@ -573,7 +596,7 @@ namespace basegfx double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex) { OSL_ENSURE(!rCandidate.areControlPointsUsed(), "getEdgeLength: ATM works not for curves (!)"); - OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); + OSL_ENSURE(nIndex < rCandidate.count(), "getEdgeLength: Access to polygon out of range (!)"); double fRetval(0.0); const sal_uInt32 nPointCount(rCandidate.count()); @@ -1368,82 +1391,451 @@ namespace basegfx // with distance fDistance and rounded edges (start and end point). bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance) { - OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isInEpsilonRange: ATM works not for curves (!)"); + if(rCandidate.areControlPointsUsed()) + { + // call myself recursively with subdivided input + const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate)); + return isInEpsilonRange(aCandidate, rTestPosition, fDistance); + } + else + { + if(rCandidate.count()) + { + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); - if(rCandidate.count()) + for(sal_uInt32 a(0L); a < nEdgeCount; a++) + { + B2DPoint aStart(rCandidate.getB2DPoint(a)); + const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate)); + B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex)); + + if(isInEpsilonRange(aStart, aEnd, rTestPosition, fDistance)) + { + return true; + } + } + } + + return false; + } + } + + B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadius ) + { + const double fZero(0.0); + const double fOne(1.0); + + if(fTools::lessOrEqual(fRadius, fZero)) { - const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); + // no radius, use rectangle + return createPolygonFromRect( rRect ); + } + else if(fTools::moreOrEqual(fRadius, fOne)) + { + // full radius, use ellipse + const B2DPoint aCenter(rRect.getCenter()); + const double fRadiusX(rRect.getWidth() / 2.0); + const double fRadiusY(rRect.getHeight() / 2.0); - for(sal_uInt32 a(0L); a < nEdgeCount; a++) + return createPolygonFromEllipse( aCenter, fRadiusX, fRadiusY ); + } + else + { + // create rectangle with two radii between ]0.0 .. 1.0[ + return createPolygonFromRect( rRect, fRadius, fRadius ); + } + } + + B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY ) + { + const double fZero(0.0); + const double fOne(1.0); + + // crop to useful values + if(fTools::less(fRadiusX, fZero)) + { + fRadiusX = fZero; + } + else if(fTools::more(fRadiusX, fOne)) + { + fRadiusX = fOne; + } + + if(fTools::less(fRadiusY, fZero)) + { + fRadiusY = fZero; + } + else if(fTools::more(fRadiusY, fOne)) + { + fRadiusY = fOne; + } + + if(fZero == fRadiusX || fZero == fRadiusY) + { + // at least in one direction no radius, use rectangle + return createPolygonFromRect( rRect ); + } + else if(fOne == fRadiusX && fOne == fRadiusY) + { + // in both directions full radius, use ellipse + const B2DPoint aCenter(rRect.getCenter()); + const double fRadiusX(rRect.getWidth() / 2.0); + const double fRadiusY(rRect.getHeight() / 2.0); + + return createPolygonFromEllipse( aCenter, fRadiusX, fRadiusY ); + } + else + { + B2DPolygon aRetval; + const double fBowX((rRect.getWidth() / 2.0) * fRadiusX); + const double fBowY((rRect.getHeight() / 2.0) * fRadiusY); + const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + + // create first bow + const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY()); + aRetval.append(aBottomRight + B2DPoint(0.0, -fBowY)); + aRetval.append(aBottomRight + B2DPoint(-fBowX, 0.0)); + aRetval.setControlPointA(0L, interpolate(aRetval.getB2DPoint(0L), aBottomRight, fKappa)); + aRetval.setControlPointB(0L, interpolate(aRetval.getB2DPoint(1L), aBottomRight, fKappa)); + + // create second bow + const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY()); + aRetval.append(aBottomLeft + B2DPoint(fBowX, 0.0)); + aRetval.append(aBottomLeft + B2DPoint(0.0, -fBowY)); + aRetval.setControlPointA(2L, interpolate(aRetval.getB2DPoint(2L), aBottomLeft, fKappa)); + aRetval.setControlPointB(2L, interpolate(aRetval.getB2DPoint(3L), aBottomLeft, fKappa)); + + // create third bow + const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY()); + aRetval.append(aTopLeft + B2DPoint(0.0, fBowY)); + aRetval.append(aTopLeft + B2DPoint(fBowX, 0.0)); + aRetval.setControlPointA(4L, interpolate(aRetval.getB2DPoint(4L), aTopLeft, fKappa)); + aRetval.setControlPointB(4L, interpolate(aRetval.getB2DPoint(5L), aTopLeft, fKappa)); + + // create forth bow + const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY()); + aRetval.append(aTopRight + B2DPoint(-fBowX, 0.0)); + aRetval.append(aTopRight + B2DPoint(0.0, fBowY)); + aRetval.setControlPointA(6L, interpolate(aRetval.getB2DPoint(6L), aTopRight, fKappa)); + aRetval.setControlPointB(6L, interpolate(aRetval.getB2DPoint(7L), aTopRight, fKappa)); + + // close + aRetval.setClosed( true ); + + // remove double created points if there is extreme radius + if(fOne == fRadiusX || fOne == fRadiusY) { - B2DPoint aStart(rCandidate.getB2DPoint(a)); - const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate)); - B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex)); + aRetval.removeDoublePoints(); + } + + return aRetval; + } + } + + B2DPolygon createPolygonFromRect( const B2DRectangle& rRect ) + { + B2DPolygon aRetval; + + aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) ); + aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) ); + aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) ); + aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) ); + + // close + aRetval.setClosed( true ); + + return aRetval; + } + + B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius ) + { + return createPolygonFromEllipse( rCenter, fRadius, fRadius ); + } + + void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, bool bEndPoint) + { + const double fZero(0.0); + const double fOne(1.0); + const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + const sal_uInt32 nIndex(rPolygon.count()); + + // create closed unit-circle with 4 segments + switch(nQuadrant) + { + case 0 : // first quadrant + { + rPolygon.append(B2DPoint(fOne, fZero)); + rPolygon.setControlPointA(nIndex, B2DPoint(fOne, fKappa)); + rPolygon.setControlPointB(nIndex, B2DPoint(fKappa, fOne)); - if(isInEpsilonRange(aStart, aEnd, rTestPosition, fDistance)) + if(bEndPoint) { - return true; + rPolygon.append(B2DPoint(fZero, fOne)); + } + + break; + } + case 1 : // second quadrant + { + rPolygon.append(B2DPoint(fZero, fOne)); + rPolygon.setControlPointA(nIndex, B2DPoint(-fKappa, fOne)); + rPolygon.setControlPointB(nIndex, B2DPoint(-fOne, fKappa)); + + if(bEndPoint) + { + rPolygon.append(B2DPoint(-fOne, fZero)); + } + + break; + } + case 2 : // third quadrant + { + rPolygon.append(B2DPoint(-fOne, fZero)); + rPolygon.setControlPointA(nIndex, B2DPoint(-fOne, -fKappa)); + rPolygon.setControlPointB(nIndex, B2DPoint(-fKappa, -fOne)); + + if(bEndPoint) + { + rPolygon.append(B2DPoint(fZero, -fOne)); + } + + break; + } + default : // last quadrant + { + rPolygon.append(B2DPoint(fZero, -fOne)); + rPolygon.setControlPointA(nIndex, B2DPoint(fKappa, -fOne)); + rPolygon.setControlPointB(nIndex, B2DPoint(fOne, -fKappa)); + + if(bEndPoint) + { + rPolygon.append(B2DPoint(fOne, fZero)); } + + break; } } + } - return false; + B2DPolygon createPolygonFromUnitCircle() + { + B2DPolygon aRetval; + + // create unit-circle with all 4 segments, close it + appendUnitCircleQuadrant(aRetval, 0L, false); + appendUnitCircleQuadrant(aRetval, 1L, false); + appendUnitCircleQuadrant(aRetval, 2L, false); + appendUnitCircleQuadrant(aRetval, 3L, false); + aRetval.setClosed(true); + + return aRetval; } - B2DPolygon createPolygonFromRect( const B2DRectangle& rRect ) + B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ) { - B2DPolygon aRet; + const double fOne(1.0); + B2DPolygon aRetval(createPolygonFromUnitCircle()); - const double aX1( rRect.getMinX() ); - const double aX2( rRect.getMaxX() ); - const double aY1( rRect.getMinY() ); - const double aY2( rRect.getMaxY() ); + // transformation necessary? + const sal_Bool bScale(!fTools::equal(fRadiusX, fOne) || !fTools::equal(fRadiusY, fOne)); + const sal_Bool bTranslate(!rCenter.equalZero()); - aRet.append( B2DPoint( aX1, aY1 ) ); - aRet.append( B2DPoint( aX2, aY1 ) ); - aRet.append( B2DPoint( aX2, aY2 ) ); - aRet.append( B2DPoint( aX1, aY2 ) ); - aRet.setClosed( true ); + if(bScale || bTranslate) + { + B2DHomMatrix aMatrix; - return aRet; + if(bScale) + { + aMatrix.scale(fRadiusX, fRadiusY); + } + + if(bTranslate) + { + aMatrix.translate(rCenter.getX(), rCenter.getY()); + } + + aRetval.transform(aMatrix); + } + + return aRetval; } - B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double nRadius ) + void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd, bool bEndPoint) { - return createPolygonFromEllipse( rCenter, nRadius, nRadius ); + OSL_ENSURE(fStart >= 0.0 && fStart <= 1.0, "appendUnitCircleQuadrant: Access out of range (!)"); + OSL_ENSURE(fEnd >= 0.0 && fEnd <= 1.0, "appendUnitCircleQuadrant: Access out of range (!)"); + OSL_ENSURE(fEnd >= fStart, "appendUnitCircleQuadrant: Access out of range (!)"); + const double fZero(0.0); + const double fOne(1.0); + const bool bStartIsZero(fTools::equalZero(fStart)); + const bool bEndIsOne(fTools::equal(fEnd, fOne)); + + if(bStartIsZero && bEndIsOne) + { + // add completely + appendUnitCircleQuadrant(rPolygon, nQuadrant, bEndPoint); + } + else + { + // split and add + B2DPolygon aQuadrant; + appendUnitCircleQuadrant(aQuadrant, nQuadrant, true); + const bool bStartEndEqual(fTools::equal(fStart, fEnd)); + + if(bStartEndEqual && bEndPoint) + { + if(bStartIsZero) + { + // both zero, add start point + rPolygon.append(aQuadrant.getB2DPoint(0L)); + } + else if(bEndIsOne) + { + // both one, add end point + rPolygon.append(aQuadrant.getB2DPoint(1L)); + } + else + { + // both equal but not zero, add split point + B2DCubicBezier aCubicBezier(aQuadrant.getB2DPoint(0L), aQuadrant.getControlPointA(0L), aQuadrant.getControlPointB(0L), aQuadrant.getB2DPoint(1L)); + B2DCubicBezier aCubicBezierWaste; + + aCubicBezier.split(fStart, aCubicBezier, aCubicBezierWaste); + + rPolygon.append(aCubicBezier.getEndPoint()); + } + } + else + { + B2DCubicBezier aCubicBezier(aQuadrant.getB2DPoint(0L), aQuadrant.getControlPointA(0L), aQuadrant.getControlPointB(0L), aQuadrant.getB2DPoint(1L)); + B2DCubicBezier aCubicBezierWaste; + + if(!bEndIsOne) + { + aCubicBezier.split(fEnd, aCubicBezier, aCubicBezierWaste); + + if(!bStartIsZero) + { + fStart /= fEnd; + } + } + + if(!bStartIsZero) + { + aCubicBezier.split(fStart, aCubicBezierWaste, aCubicBezier); + } + + const sal_uInt32 nIndex(rPolygon.count()); + rPolygon.append(aCubicBezier.getStartPoint()); + rPolygon.setControlPointA(nIndex, aCubicBezier.getControlPointA()); + rPolygon.setControlPointB(nIndex, aCubicBezier.getControlPointB()); + + if(bEndPoint) + { + rPolygon.append(aCubicBezier.getEndPoint()); + } + } + } } - B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double nRadiusX, double nRadiusY ) + B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd ) { - B2DPolygon aRet; + B2DPolygon aRetval; + + if(fTools::less(fStart, 0.0)) + { + fStart = 0.0; + } + + if(fTools::more(fStart, F_2PI)) + { + fStart = F_2PI; + } + + if(fTools::less(fEnd, 0.0)) + { + fEnd = 0.0; + } + + if(fTools::more(fEnd, F_2PI)) + { + fEnd = F_2PI; + } - const double aX( rCenter.getX() ); - const double aY( rCenter.getY() ); + const sal_uInt32 nQuadrantStart(sal_uInt32(fStart / F_PI2) % 4L); + const sal_uInt32 nQuadrantEnd(sal_uInt32(fEnd / F_PI2) % 4L); + sal_uInt32 nCurrentQuadrant(nQuadrantStart); + bool bStartDone(false); + bool bEndDone(false); - const double nKappa( (M_SQRT2-1.0)*4.0/3.0 ); - const double nlX( nRadiusX * nKappa ); - const double nlY( nRadiusY * nKappa ); + do + { + if(!bStartDone && nQuadrantStart == nCurrentQuadrant) + { + if(nQuadrantStart == nQuadrantEnd && fTools::moreOrEqual(fEnd, fStart)) + { + // both in one quadrant and defining the complete segment, create start to end + double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2); + double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, fSplitOffsetEnd, true); + bStartDone = bEndDone = true; + } + else + { + // create start to quadrant end + const double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, 1.0, false); + bStartDone = true; + } + } + else if(!bEndDone && nQuadrantEnd == nCurrentQuadrant) + { + // create quadrant start to end + const double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd, true); + bEndDone = true; + } + else + { + // add quadrant completely + appendUnitCircleQuadrant(aRetval, nCurrentQuadrant, false); + } - aRet.append( B2DPoint( aX, aY-nRadiusY ) ); - aRet.append( B2DPoint( aX+nRadiusX, aY ) ); - aRet.append( B2DPoint( aX, aY+nRadiusY ) ); - aRet.append( B2DPoint( aX-nRadiusX, aY ) ); + // next step + nCurrentQuadrant = (nCurrentQuadrant + 1L) % 4L; + } + while(!(bStartDone && bEndDone)); - aRet.setControlPointA( 0, B2DPoint( aX+nlX, aY-nRadiusY ) ); - aRet.setControlPointB( 0, B2DPoint( aX+nRadiusX, aY-nlY ) ); + return aRetval; + } + + B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd ) + { + B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd)); - aRet.setControlPointA( 1, B2DPoint( aX+nRadiusX, aY+nlY ) ); - aRet.setControlPointB( 1, B2DPoint( aX+nlX, aY+nRadiusY ) ); + // transformation necessary? + const double fOne(1.0); + const sal_Bool bScale(!fTools::equal(fRadiusX, fOne) || !fTools::equal(fRadiusY, fOne)); + const sal_Bool bTranslate(!rCenter.equalZero()); - aRet.setControlPointA( 2, B2DPoint( aX-nlX, aY+nRadiusY ) ); - aRet.setControlPointB( 2, B2DPoint( aX-nRadiusX, aY+nlY ) ); + if(bScale || bTranslate) + { + B2DHomMatrix aMatrix; + + if(bScale) + { + aMatrix.scale(fRadiusX, fRadiusY); + } - aRet.setControlPointA( 3, B2DPoint( aX-nRadiusX, aY-nlY ) ); - aRet.setControlPointB( 3, B2DPoint( aX-nlX, aY-nRadiusY ) ); + if(bTranslate) + { + aMatrix.translate(rCenter.getX(), rCenter.getY()); + } - aRet.setClosed( true ); + aRetval.transform(aMatrix); + } - return aRet; + return aRetval; } bool hasNeutralPoints(const B2DPolygon& rCandidate) @@ -1691,54 +2083,581 @@ namespace basegfx } } + namespace + { + /// return 0 for input of 0, -1 for negative and 1 for positive input + inline int lcl_sgn( const double n ) + { + return n == 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n); + } + } + bool isRectangle( const B2DPolygon& rPoly ) { - if( rPoly.count() != 4 || - !rPoly.isClosed() ) + // polygon must be closed to resemble a rect, and contain + // at least four points. + if( !rPoly.isClosed() || + rPoly.count() < 4 ) { return false; } - const ::basegfx::B2DPoint& rPoint0( rPoly.getB2DPoint(0) ); - const ::basegfx::B2DPoint& rPoint1( rPoly.getB2DPoint(1) ); + // number of 90 degree turns the polygon has taken + int nNumTurns(0); + + int nVerticalEdgeType=0; + int nHorizontalEdgeType=0; + bool bNullVertex(true); + bool bCWPolygon(false); // when true, polygon is CW + // oriented, when false, CCW + bool bOrientationSet(false); // when false, polygon + // orientation has not yet + // been determined. + + // scan all _edges_ (which involves coming back to point 0 + // for the last edge - thus the modulo operation below) + const sal_Int32 nCount( rPoly.count() ); + for( sal_Int32 i=0; i<nCount; ++i ) + { + const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) ); + const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) ); + + // is 0 for zero direction vector, 1 for south edge and -1 + // for north edge (standard screen coordinate system) + int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) ); + + // is 0 for zero direction vector, 1 for east edge and -1 + // for west edge (standard screen coordinate system) + int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) ); + + if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType ) + return false; // oblique edge - for sure no rect + + const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType ); + + // current vertex is equal to previous - just skip, + // until we have a real edge + if( bCurrNullVertex ) + continue; + + // if previous edge has two identical points, because + // no previous edge direction was available, simply + // take this first non-null edge as the start + // direction. That's what will happen here, if + // bNullVertex is false + if( !bNullVertex ) + { + // 2D cross product - is 1 for CW and -1 for CCW turns + const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType - + nVerticalEdgeType*nCurrHorizontalEdgeType ); + + if( !nCrossProduct ) + continue; // no change in orientation - + // collinear edges - just go on + + // if polygon orientation is not set, we'll + // determine it now + if( !bOrientationSet ) + { + bCWPolygon = nCrossProduct == 1; + bOrientationSet = true; + } + else + { + // if current turn orientation is not equal + // initial orientation, this is not a + // rectangle (as rectangles have consistent + // orientation). + if( (nCrossProduct == 1) != bCWPolygon ) + return false; + } + + ++nNumTurns; - bool bEdgeVertical( rPoint0.getX() == rPoint1.getX() ); - bool bEdgeHorizontal( rPoint0.getY() == rPoint1.getY() ); + // More than four 90 degree turns are an + // indication that this must not be a rectangle. + if( nNumTurns > 4 ) + return false; + } - if( !bEdgeVertical && !bEdgeHorizontal ) - return false; // oblique vertex - for sure no rect + // store current state for the next turn + nVerticalEdgeType = nCurrVerticalEdgeType; + nHorizontalEdgeType = nCurrHorizontalEdgeType; + bNullVertex = false; // won't reach this line, + // if bCurrNullVertex is + // true - see above + } - bool bNullVertex( bEdgeVertical && bEdgeHorizontal ); + return true; + } - for( sal_Int32 i=1; i<5; ++i ) + B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate) + { + if(rCandidate.areControlPointsUsed()) { - const ::basegfx::B2DPoint& rPoint0( rPoly.getB2DPoint( i%4) ); - const ::basegfx::B2DPoint& rPoint1( rPoly.getB2DPoint( (i+1)%4 ) ); + // call myself recursively with subdivided input + const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate)); + return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate); + } + else + { + B3DPolygon aRetval; - const bool bCurrEdgeVertical( rPoint0.getX() == rPoint1.getX() ); - const bool bCurrEdgeHorizontal( rPoint0.getY() == rPoint1.getY() ); + for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + { + B2DPoint aPoint(rCandidate.getB2DPoint(a)); + aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate)); + } - if( !bCurrEdgeVertical && !bCurrEdgeHorizontal ) - return false; // oblique vertex - for sure no rect + // copy closed state + aRetval.setClosed(rCandidate.isClosed()); - const bool bCurrNullVertex( bCurrEdgeVertical && bCurrEdgeHorizontal ); + return aRetval; + } + } - // direction change from last vertex? - if( !bNullVertex && !bCurrNullVertex && - (bEdgeVertical==bCurrEdgeVertical || - bEdgeHorizontal==bCurrEdgeHorizontal) ) + B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat) + { + B2DPolygon aRetval; + const sal_uInt32 nCount(rCandidate.count()); + const bool bIsIdentity(rMat.isIdentity()); + + for(sal_uInt32 a(0L); a < nCount; a++) + { + B3DPoint aCandidate(rCandidate.getB3DPoint(a)); + + if(!bIsIdentity) { - // nope, for sure no rect - return false; + aCandidate *= rMat; + } + + aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY())); + } + + // copy closed state + aRetval.setClosed(rCandidate.isClosed()); + + return aRetval; + } + + double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut) + { + if(rPointA.equal(rPointB)) + { + const B2DVector aVector(rTestPoint - rPointA); + return aVector.getLength(); + } + else + { + // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint + const B2DVector aVector1(rPointB - rPointA); + const B2DVector aVector2(rTestPoint - rPointA); + const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY())); + const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY())); + const double fCut(fDividend / fDivisor); + + if(fCut < 0.0) + { + // not in line range, get distance to PointA + rCut = 0.0; + return aVector2.getLength(); + } + else if(fCut > 1.0) + { + // not in line range, get distance to PointB + rCut = 1.0; + const B2DVector aVector(rTestPoint - rPointB); + return aVector.getLength(); + } + else + { + // in line range + const B2DPoint aCutPoint(rPointA + fCut * aVector1); + const B2DVector aVector(rTestPoint - aCutPoint); + rCut = fCut; + return aVector.getLength(); } + } + } + + double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut) + { + double fRetval(DBL_MAX); + + if(rCandidate.count() > 1L) + { + const double fZero(0.0); + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); + + for(sal_uInt32 a(0L); a < nEdgeCount; a++) + { + const B2DPoint aPointA(rCandidate.getB2DPoint(a)); + const B2DPoint aPointB(rCandidate.getB2DPoint(getIndexOfSuccessor(a, rCandidate))); + double fEdgeDist; + double fNewCut; - // might still be a rect - note that this code will - // accept all configurations of collinear points as - // rectangles, because they are representable as an - // axis-aligned rect. - bEdgeVertical = bCurrEdgeVertical; - bEdgeHorizontal = bCurrEdgeHorizontal; - bNullVertex = bCurrNullVertex; + if(rCandidate.areControlVectorsUsed()) + { + const B2DVector aVectorA(rCandidate.getControlVectorA(a)); + const B2DVector aVectorB(rCandidate.getControlVectorB(a)); + + if(aVectorA.equalZero() && aVectorB.equalZero()) + { + fEdgeDist = getSmallestDistancePointToEdge(aPointA, aPointB, rTestPoint, fNewCut); + } + else + { + B2DCubicBezier aBezier(aPointA, B2DPoint(aPointA + aVectorA), B2DPoint(aPointA + aVectorB), aPointB); + fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut); + } + } + else + { + fEdgeDist = getSmallestDistancePointToEdge(aPointA, aPointB, rTestPoint, fNewCut); + } + + if(DBL_MAX == fRetval || fEdgeDist < fRetval) + { + fRetval = fEdgeDist; + rEdgeIndex = a; + rCut = fNewCut; + + if(fTools::equal(fRetval, fZero)) + { + // already found zero distance, cannot get better. Ensure numerical zero value and end loop. + fRetval = 0.0; + break; + } + } + } + + if(1.0 == rCut) + { + // correct rEdgeIndex when not last point + if(rCandidate.isClosed()) + { + rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate); + rCut = 0.0; + } + else + { + if(rEdgeIndex != nEdgeCount - 1L) + { + rEdgeIndex++; + rCut = 0.0; + } + } + } + } + + return fRetval; + } + + B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight) + { + if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight())) + { + return rCandidate; + } + else + { + const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth()); + const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight()); + const double fOneMinusRelativeX(1.0 - fRelativeX); + const double fOneMinusRelativeY(1.0 - fRelativeY); + const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) + + fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX())); + const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) + + fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY())); + + return B2DPoint(fNewX, fNewY); + } + } + + B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight) + { + const sal_uInt32 nPointCount(rCandidate.count()); + + if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight()) + { + B2DPolygon aRetval; + + for(sal_uInt32 a(0L); a < nPointCount; a++) + { + aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); + + if(rCandidate.areControlVectorsUsed()) + { + if(!rCandidate.getControlVectorA(a).equalZero()) + { + aRetval.setControlPointA(a, distort(rCandidate.getControlPointA(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); + } + + if(!rCandidate.getControlVectorB(a).equalZero()) + { + aRetval.setControlPointB(a, distort(rCandidate.getControlPointB(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); + } + } + } + + aRetval.setClosed(rCandidate.isClosed()); + return aRetval; + } + else + { + return rCandidate; + } + } + + B2DPolygon rotateAroundPoint(const B2DPolygon& rCandidate, const B2DPoint& rCenter, double fAngle) + { + const sal_uInt32 nPointCount(rCandidate.count()); + B2DPolygon aRetval(rCandidate); + + if(nPointCount) + { + B2DHomMatrix aMatrix; + + aMatrix.translate(-rCenter.getX(), -rCenter.getY()); + aMatrix.rotate(fAngle); + aMatrix.translate(rCenter.getX(), rCenter.getY()); + + aRetval.transform(aMatrix); + } + + return aRetval; + } + + B2DPolygon expandToCurve(const B2DPolygon& rCandidate) + { + B2DPolygon aRetval(rCandidate); + + for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + { + expandToCurveInPoint(aRetval, a); + } + + return aRetval; + } + + bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex) + { + OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)"); + bool bRetval(false); + + if(rCandidate.count()) + { + // look for predecessor + const sal_uInt32 nPrev(getIndexOfPredecessor(nIndex, rCandidate)); + + if(nPrev != nIndex && rCandidate.getControlVectorB(nPrev).equalZero()) + { + rCandidate.setControlPointB(nPrev, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrev), 1.0 / 3.0)); + bRetval = true; + } + + // look for successor + const sal_uInt32 nNext(getIndexOfSuccessor(nIndex, rCandidate)); + + if(nNext != nIndex && rCandidate.getControlVectorA(nIndex).equalZero()) + { + rCandidate.setControlPointA(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNext), 1.0 / 3.0)); + bRetval = true; + } + } + + return bRetval; + } + + B2DPolygon setContinuity(const B2DPolygon& rCandidate, B2VectorContinuity eContinuity) + { + B2DPolygon aRetval(rCandidate); + + for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + { + setContinuityInPoint(aRetval, a, eContinuity); + } + + return aRetval; + } + + bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity) + { + OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)"); + bool bRetval(false); + + if(rCandidate.count()) + { + const sal_uInt32 nPrev(getIndexOfPredecessor(nIndex, rCandidate)); + const sal_uInt32 nNext(getIndexOfSuccessor(nIndex, rCandidate)); + + if(nPrev != nIndex && nNext != nIndex) + { + const B2DVector aControlVectorB(rCandidate.getControlVectorB(nPrev)); + const B2DVector aControlVectorA(rCandidate.getControlVectorA(nIndex)); + const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex)); + + switch(eContinuity) + { + case CONTINUITY_NONE : + { + if(!aControlVectorB.equalZero()) + { + rCandidate.setControlPointB(nPrev, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrev), 1.0 / 3.0)); + bRetval = true; + } + + if(!aControlVectorA.equalZero()) + { + rCandidate.setControlPointA(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNext), 1.0 / 3.0)); + bRetval = true; + } + + break; + } + case CONTINUITY_C1 : + { + if(!aControlVectorB.equalZero() && !aControlVectorA.equalZero()) + { + B2DVector aVectorPrev(rCandidate.getControlPointB(nPrev) - aCurrentPoint); + B2DVector aVectorNext(aControlVectorA); + const double fLenPrev(aVectorPrev.getLength()); + const double fLenNext(aVectorNext.getLength()); + aVectorPrev.normalize(); + aVectorNext.normalize(); + const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); + + if(ORIENTATION_NEUTRAL == aOrientation) + { + // already parallel, check length + if(fTools::equal(fLenPrev, fLenNext)) + { + // this would be even C2, but we want C1. Use the lengths of the corresponding edges. + const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrev) - aCurrentPoint).getLength() * (1.0 / 3.0)); + const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNext) - aCurrentPoint).getLength() * (1.0 / 3.0)); + + rCandidate.setControlPointB(nPrev, aCurrentPoint + (aVectorPrev * fLenPrevEdge)); + rCandidate.setControlPointA(nIndex, aCurrentPoint + (aVectorNext * fLenNextEdge)); + + bRetval = true; + } + } + else + { + // not parallel, set vectors and length + const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); + + if(ORIENTATION_POSITIVE == aOrientation) + { + rCandidate.setControlPointB(nPrev, aCurrentPoint - (aNormalizedPerpendicular * fLenPrev)); + rCandidate.setControlPointA(nIndex, aCurrentPoint + (aNormalizedPerpendicular * fLenNext)); + } + else + { + rCandidate.setControlPointB(nPrev, aCurrentPoint + (aNormalizedPerpendicular * fLenPrev)); + rCandidate.setControlPointA(nIndex, aCurrentPoint - (aNormalizedPerpendicular * fLenNext)); + } + + bRetval = true; + } + } + break; + } + case CONTINUITY_C2 : + { + if(!aControlVectorB.equalZero() && !aControlVectorA.equalZero()) + { + B2DVector aVectorPrev(rCandidate.getControlPointB(nPrev) - aCurrentPoint); + B2DVector aVectorNext(aControlVectorA); + const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0); + aVectorPrev.normalize(); + aVectorNext.normalize(); + const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); + + if(ORIENTATION_NEUTRAL == aOrientation) + { + // already parallel, set length. Use one direction for better numerical correctness + const B2DVector aScaledDirection(aVectorPrev * fCommonLength); + + rCandidate.setControlPointB(nPrev, aCurrentPoint + aScaledDirection); + rCandidate.setControlPointA(nIndex, aCurrentPoint - aScaledDirection); + } + else + { + // not parallel, set vectors and length + const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); + const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength); + + if(ORIENTATION_POSITIVE == aOrientation) + { + rCandidate.setControlPointB(nPrev, aCurrentPoint - aPerpendicular); + rCandidate.setControlPointA(nIndex, aCurrentPoint + aPerpendicular); + } + else + { + rCandidate.setControlPointB(nPrev, aCurrentPoint + aPerpendicular); + rCandidate.setControlPointA(nIndex, aCurrentPoint - aPerpendicular); + } + } + + bRetval = true; + } + break; + } + } + } + } + + return bRetval; + } + + bool isPolyPolygonEqualRectangle( const ::basegfx::B2DPolyPolygon& rPolyPoly, + const ::basegfx::B2DRange& rRect ) + { + // exclude some cheap cases first + if( rPolyPoly.count() != 1 ) + return false; + + // fill array with rectangle vertices + const ::basegfx::B2DPoint aPoints[] = + { + ::basegfx::B2DPoint(rRect.getMinX(),rRect.getMinY()), + ::basegfx::B2DPoint(rRect.getMaxX(),rRect.getMinY()), + ::basegfx::B2DPoint(rRect.getMaxX(),rRect.getMaxY()), + ::basegfx::B2DPoint(rRect.getMinX(),rRect.getMaxY()) + }; + + const ::basegfx::B2DPolygon& rPoly( rPolyPoly.getB2DPolygon(0) ); + const sal_uInt32 nCount( rPoly.count() ); + const double epsilon = ::std::numeric_limits<double>::epsilon(); + + for(unsigned int i=0; i<4; ++i) + { + const ::basegfx::B2DPoint &p1 = aPoints[i]; + const ::basegfx::B2DPoint &p2 = aPoints[(i+1)%4]; + bool bPointOnBoundary = false; + for( sal_uInt32 i=0; i<nCount; ++i ) + { + const ::basegfx::B2DPoint p(rPoly.getB2DPoint(i)); + + // 1 | x0 y0 1 | + // A = - | x1 y1 1 | + // 2 | x2 y2 1 | + double fDoubleArea = p2.getX()*p.getY() - + p2.getY()*p.getX() - + p1.getX()*p.getY() + + p1.getY()*p.getX() + + p1.getX()*p2.getY() - + p1.getY()*p2.getX(); + + if(fDoubleArea < epsilon) + { + bPointOnBoundary=true; + break; + } + } + if(!(bPointOnBoundary)) + return false; } return true; |