diff options
author | Oliver Bolte <obo@openoffice.org> | 2007-07-18 10:06:27 +0000 |
---|---|---|
committer | Oliver Bolte <obo@openoffice.org> | 2007-07-18 10:06:27 +0000 |
commit | 2c3f7d6f61ba38d3ebc71cf6d8795d136a134361 (patch) | |
tree | 33f331054ea4b94fdb21b786a6f04d8f15a23476 /basegfx/source/polygon/b2dpolygontools.cxx | |
parent | cbf1f8734dde81174e0119d8f42cf69e5eb308c5 (diff) |
INTEGRATION: CWS aw051 (1.24.24); FILE MERGED
2007/06/12 16:31:10 aw 1.24.24.8: #i77162# Continued adaptions to bezier handling
2007/06/11 14:41:47 aw 1.24.24.7: #i77162# 2nd adaptions to new bezier handling
2007/06/08 14:37:43 aw 1.24.24.6: #i77162# adaptions to changed B2DPolygon bezier handling
2007/06/07 11:20:44 aw 1.24.24.5: #i77162# added getContinuityInPoint to B2DPolygon
2007/06/07 09:32:50 aw 1.24.24.4: #i77162# changes to B2DPolygon bezier handling
2007/06/06 15:49:41 aw 1.24.24.3: #i77162# B2DPolygin control point interface changes
2007/05/10 13:21:19 aw 1.24.24.2: #i76891#
2007/05/10 09:48:51 aw 1.24.24.1: #i76891#
Diffstat (limited to 'basegfx/source/polygon/b2dpolygontools.cxx')
-rw-r--r-- | basegfx/source/polygon/b2dpolygontools.cxx | 920 |
1 files changed, 508 insertions, 412 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index 0dfcf9f49431..375711aad79a 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -4,9 +4,9 @@ * * $RCSfile: b2dpolygontools.cxx,v $ * - * $Revision: 1.24 $ + * $Revision: 1.25 $ * - * last change: $Author: ihi $ $Date: 2006-11-14 14:08:08 $ + * last change: $Author: obo $ $Date: 2007-07-18 11:06:27 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -68,10 +68,6 @@ #include <basegfx/curve/b2dcubicbezier.hxx> #endif -#ifndef _BGFX_CURVE_B2DBEZIERTOOLS_HXX -#include <basegfx/curve/b2dbeziertools.hxx> -#endif - #ifndef _BGFX_POLYGON_B2DPOLYPOLYGONCUTTER_HXX #include <basegfx/polygon/b2dpolypolygoncutter.hxx> #endif @@ -105,14 +101,50 @@ namespace basegfx { namespace tools { - // B2DPolygon tools - void checkClosed(B2DPolygon& rCandidate) + void openWithGeometryChange(B2DPolygon& rCandidate) + { + if(rCandidate.isClosed()) + { + if(rCandidate.count()) + { + rCandidate.append(rCandidate.getB2DPoint(0)); + + if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0)) + { + rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0)); + rCandidate.resetPrevControlPoint(0); + } + } + + rCandidate.setClosed(false); + } + } + + void closeWithGeometryChange(B2DPolygon& rCandidate) { - while(rCandidate.count() > 1L - && rCandidate.getB2DPoint(0L).equal(rCandidate.getB2DPoint(rCandidate.count() - 1L))) + if(!rCandidate.isClosed()) { + while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1)) + { + if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1)) + { + rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1)); + } + + rCandidate.remove(rCandidate.count() - 1); + } + rCandidate.setClosed(true); - rCandidate.remove(rCandidate.count() - 1L); + } + } + + void checkClosed(B2DPolygon& rCandidate) + { + OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)"); + + if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1)) + { + closeWithGeometryChange(rCandidate); } } @@ -158,7 +190,7 @@ namespace basegfx { B2VectorOrientation eRetval(ORIENTATION_NEUTRAL); - if(rCandidate.count() > 2L || rCandidate.areControlVectorsUsed()) + if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed()) { const double fSignedArea(getSignedArea(rCandidate)); @@ -177,19 +209,7 @@ namespace basegfx B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex) { - OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)"); - B2VectorContinuity eRetval(CONTINUITY_NONE); - - if(rCandidate.count() > 1L && rCandidate.areControlPointsUsed()) - { - const sal_uInt32 nPrevInd(getIndexOfPredecessor(nIndex, rCandidate)); - const B2DVector aForwardVector(rCandidate.getControlVectorA(nIndex)); - const B2DVector aBackVector(rCandidate.getControlPointB(nPrevInd) - rCandidate.getB2DPoint(nIndex)); - - eRetval = getContinuity(aBackVector, aForwardVector); - } - - return eRetval; + return rCandidate.getContinuityInPoint(nIndex); } B2DPolyPolygon removeIntersections(const B2DPolygon& rCandidate, bool bKeepOrientations) @@ -233,204 +253,214 @@ namespace basegfx B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound) { - B2DPolygon aRetval(rCandidate); - - if(aRetval.areControlPointsUsed()) + if(rCandidate.areControlPointsUsed()) { - const sal_uInt32 nPointCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); - aRetval.clear(); + const sal_uInt32 nPointCount(rCandidate.count()); + B2DPolygon aRetval; - for(sal_uInt32 a(0L); a < nPointCount; a++) + if(nPointCount) { - const B2DVector aVectorA(rCandidate.getControlVectorA(a)); - const B2DVector aVectorB(rCandidate.getControlVectorB(a)); + // prepare edge-oriented loop + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); + B2DCubicBezier aBezier; + aBezier.setStartPoint(rCandidate.getB2DPoint(0)); + + // add start point (always) + aRetval.append(aBezier.getStartPoint()); - if(!aVectorA.equalZero() || !aVectorB.equalZero()) + for(sal_uInt32 a(0L); a < nEdgeCount; a++) { - // vectors are used, get points - const sal_uInt32 nNext(getIndexOfSuccessor(a, rCandidate)); - B2DPoint aPointA(rCandidate.getB2DPoint(a)); - B2DPoint aPointB(rCandidate.getB2DPoint(nNext)); + // get next and control points + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); + aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aBezier.testAndSolveTrivialBezier(); - // build CubicBezier segment - B2DCubicBezier aBezier( - aPointA, B2DPoint(aPointA + aVectorA), B2DPoint(aPointA + aVectorB), aPointB); + if(aBezier.isBezier()) + { + // add curved edge and generate DistanceBound + double fBound(0.0); - // generate DistanceBound - double fBound; + if(0.0 == fDistanceBound) + { + // If not set, use B2DCubicBezier functionality to guess a rough value + const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0); - if(0.0 == fDistanceBound) - { - // If not set, use B2DCubicBezier functionality to guess a rough - // value - const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0); + // take 1/100th of the rough curve length + fBound = fRoughLength * 0.01; + } + else + { + // use given bound value + fBound = fDistanceBound; + } - // take 1/100th of the rouch curve length - fBound = fRoughLength * 0.01; + // make sure bound value is not too small. The base units are 1/100th mm, thus + // just make sure it's not smaller then 1/100th of that + if(fBound < 0.01) + { + fBound = 0.01; + } + + // call adaptive subdivide which adds edges to aRetval accordingly + aBezier.adaptiveSubdivideByDistance(aRetval, fBound); } else { - // use given bound value - fBound = fDistanceBound; - } - - // make sure bound value is not too small. The base units are 1/100th mm, thus - // just make sure it's not smaller then 1/100th of that - if(fBound < 0.01) - { - fBound = 0.01; + // add non-curved edge + aRetval.append(aBezier.getEndPoint()); } - // call adaptive subdivide, do not add last point - adaptiveSubdivideByDistance(aRetval, aBezier, fBound, false); + // prepare next step + aBezier.setStartPoint(aBezier.getEndPoint()); } - else + + if(rCandidate.isClosed()) { - // no vectors used, add point - aRetval.append(rCandidate.getB2DPoint(a)); + // set closed flag and correct last point (which is added double now). + closeWithGeometryChange(aRetval); } } - // add last point if not closed. If the last point had vectors, - // they are lost since they make no sense. - if(!rCandidate.isClosed()) - { - aRetval.append(rCandidate.getB2DPoint(rCandidate.count() - 1)); - } - - // check closed flag, aRetval was cleared and thus it may be invalid. - if(aRetval.isClosed() != rCandidate.isClosed()) - { - aRetval.setClosed(rCandidate.isClosed()); - } + return aRetval; + } + else + { + return rCandidate; } - - return aRetval; } B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound) { - B2DPolygon aRetval(rCandidate); - - if(aRetval.areControlPointsUsed()) + if(rCandidate.areControlPointsUsed()) { - const sal_uInt32 nPointCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); - aRetval.clear(); + const sal_uInt32 nPointCount(rCandidate.count()); + B2DPolygon aRetval; - // #i37443# prepare convenient AngleBound if none was given - if(0.0 == fAngleBound) + if(nPointCount) { + // prepare edge-oriented loop + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); + B2DCubicBezier aBezier; + aBezier.setStartPoint(rCandidate.getB2DPoint(0)); + + // add start point (always) + aRetval.append(aBezier.getStartPoint()); + + // #i37443# prepare convenient AngleBound if none was given + if(0.0 == fAngleBound) + { #ifdef DBG_UTIL - fAngleBound = fAngleBoundStartValue; + fAngleBound = fAngleBoundStartValue; #else - fAngleBound = ANGLE_BOUND_START_VALUE; + fAngleBound = ANGLE_BOUND_START_VALUE; #endif - } - else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE)) - { - fAngleBound = 0.1; - } - - for(sal_uInt32 a(0L); a < nPointCount; a++) - { - const B2DVector aVectorA(rCandidate.getControlVectorA(a)); - const B2DVector aVectorB(rCandidate.getControlVectorB(a)); + } + else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE)) + { + fAngleBound = 0.1; + } - if(!aVectorA.equalZero() || !aVectorB.equalZero()) + for(sal_uInt32 a(0L); a < nEdgeCount; a++) { - // vectors are used, get points - const sal_uInt32 nNext(getIndexOfSuccessor(a, rCandidate)); - B2DPoint aPointA(rCandidate.getB2DPoint(a)); - B2DPoint aPointB(rCandidate.getB2DPoint(nNext)); + // get next and control points + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); + aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aBezier.testAndSolveTrivialBezier(); - // build CubicBezier segment - B2DCubicBezier aBezier( - aPointA, B2DPoint(aPointA + aVectorA), B2DPoint(aPointA + aVectorB), aPointB); + if(aBezier.isBezier()) + { + // call adaptive subdivide + aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true); + } + else + { + // add non-curved edge + aRetval.append(aBezier.getEndPoint()); + } - // call adaptive subdivide, do not add last point - // #i37443# allow unsharpen the criteria - aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, false, true); + // prepare next step + aBezier.setStartPoint(aBezier.getEndPoint()); } - else + + if(rCandidate.isClosed()) { - // no vectors used, add point - aRetval.append(rCandidate.getB2DPoint(a)); + // set closed flag and correct last point (which is added double now). + closeWithGeometryChange(aRetval); } } - // add last point if not closed. If the last point had vectors, - // they are lost since they make no sense. - if(!rCandidate.isClosed()) - { - aRetval.append(rCandidate.getB2DPoint(rCandidate.count() - 1)); - } - - // check closed flag, aRetval was cleared and thus it may be invalid. - if(aRetval.isClosed() != rCandidate.isClosed()) - { - aRetval.setClosed(rCandidate.isClosed()); - } + return aRetval; + } + else + { + return rCandidate; } - - return aRetval; } B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount) { - B2DPolygon aRetval(rCandidate); - - if(aRetval.areControlPointsUsed()) + if(rCandidate.areControlPointsUsed()) { - const sal_uInt32 nPointCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); - aRetval.clear(); + const sal_uInt32 nPointCount(rCandidate.count()); + B2DPolygon aRetval; - // #i37443# prepare convenient count if none was given - if(0L == nCount) + if(nPointCount) { - nCount = COUNT_SUBDIVIDE_DEFAULT; - } + // prepare edge-oriented loop + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); + B2DCubicBezier aBezier; + aBezier.setStartPoint(rCandidate.getB2DPoint(0)); - for(sal_uInt32 a(0L); a < nPointCount; a++) - { - const B2DVector aVectorA(rCandidate.getControlVectorA(a)); - const B2DVector aVectorB(rCandidate.getControlVectorB(a)); + // add start point (always) + aRetval.append(aBezier.getStartPoint()); - if(!aVectorA.equalZero() || !aVectorB.equalZero()) + // #i37443# prepare convenient count if none was given + if(0L == nCount) { - // vectors are used, get points - const sal_uInt32 nNext(getIndexOfSuccessor(a, rCandidate)); - B2DPoint aPointA(rCandidate.getB2DPoint(a)); - B2DPoint aPointB(rCandidate.getB2DPoint(nNext)); + nCount = COUNT_SUBDIVIDE_DEFAULT; + } - // build CubicBezier segment - B2DCubicBezier aBezier( - aPointA, B2DPoint(aPointA + aVectorA), B2DPoint(aPointA + aVectorB), aPointB); + for(sal_uInt32 a(0L); a < nEdgeCount; a++) + { + // get next and control points + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); + aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aBezier.testAndSolveTrivialBezier(); + + if(aBezier.isBezier()) + { + // call adaptive subdivide + aBezier.adaptiveSubdivideByCount(aRetval, nCount); + } + else + { + // add non-curved edge + aRetval.append(aBezier.getEndPoint()); + } - // call adaptive subdivide, do not add last point - aBezier.adaptiveSubdivideByCount(aRetval, nCount, false); + // prepare next step + aBezier.setStartPoint(aBezier.getEndPoint()); } - else + + if(rCandidate.isClosed()) { - // no vectors used, add point - aRetval.append(rCandidate.getB2DPoint(a)); + // set closed flag and correct last point (which is added double now). + closeWithGeometryChange(aRetval); } } - // add last point if not closed. If the last point had vectors, - // they are lost since they make no sense. - if(!rCandidate.isClosed()) - { - aRetval.append(rCandidate.getB2DPoint(rCandidate.count() - 1)); - } - - // check closed flag, aRetval was cleared and thus it may be invalid. - if(aRetval.isClosed() != rCandidate.isClosed()) - { - aRetval.setClosed(rCandidate.isClosed()); - } + return aRetval; + } + else + { + return rCandidate; } - - return aRetval; } bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder) @@ -513,26 +543,33 @@ namespace basegfx B2DRange getRange(const B2DPolygon& rCandidate) { - B2DRange aRetval; const sal_uInt32 nPointCount(rCandidate.count()); + B2DRange aRetval; if(rCandidate.areControlPointsUsed()) { - for(sal_uInt32 a(0L); a < nPointCount; a++) + if(nPointCount) { - const B2DPoint aTestPoint(rCandidate.getB2DPoint(a)); - const B2DVector aVectorA(rCandidate.getControlVectorA(a)); - const B2DVector aVectorB(rCandidate.getControlVectorB(a)); - aRetval.expand(aTestPoint); + B2DPoint aCurrent(rCandidate.getB2DPoint(0)); - if(!aVectorA.equalZero()) + for(sal_uInt32 a(0L); a < nPointCount; a++) { - aRetval.expand(aTestPoint + aVectorA); - } + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex)); - if(!aVectorB.equalZero()) - { - aRetval.expand(aTestPoint + aVectorB); + aRetval.expand(aCurrent); + + const B2DPoint aControlPointNext(rCandidate.getNextControlPoint(a)); + const B2DPoint aControlPointPrev(rCandidate.getPrevControlPoint(nNextIndex)); + + if(!aControlPointNext.equal(aCurrent) || !aControlPointPrev.equal(aNext)) + { + aRetval.expand(aControlPointNext); + aRetval.expand(aControlPointPrev); + } + + // prepare next step + aCurrent = aNext; } } } @@ -1214,7 +1251,7 @@ namespace basegfx // prepare candidate, evtl. remove curves B2DPolygon aCandidate(rCandidate); - if(aCandidate.areControlVectorsUsed()) + if(aCandidate.areControlPointsUsed()) { aCandidate = adaptiveSubdivideByAngle(aCandidate); } @@ -1489,31 +1526,39 @@ namespace basegfx // 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)); + { + const B2DPoint aStart(aBottomRight + B2DPoint(0.0, -fBowY)); + const B2DPoint aStop(aBottomRight + B2DPoint(-fBowX, 0.0)); + aRetval.append(aStart); + aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop); + } // 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)); + { + const B2DPoint aStart(aBottomLeft + B2DPoint(fBowX, 0.0)); + const B2DPoint aStop(aBottomLeft + B2DPoint(0.0, -fBowY)); + aRetval.append(aStart); + aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop); + } // 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)); + { + const B2DPoint aStart(aTopLeft + B2DPoint(0.0, fBowY)); + const B2DPoint aStop(aTopLeft + B2DPoint(fBowX, 0.0)); + aRetval.append(aStart); + aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop); + } // 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)); + { + const B2DPoint aStart(aTopRight + B2DPoint(-fBowX, 0.0)); + const B2DPoint aStop(aTopRight + B2DPoint(0.0, fBowY)); + aRetval.append(aStart); + aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop); + } // close aRetval.setClosed( true ); @@ -1548,12 +1593,11 @@ namespace basegfx return createPolygonFromEllipse( rCenter, fRadius, fRadius ); } - void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, bool bEndPoint) + void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant) { 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) @@ -1561,53 +1605,25 @@ namespace basegfx case 0 : // first 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)); - } - + rPolygon.appendBezierSegment(B2DPoint(fOne, fKappa), B2DPoint(fKappa, fOne), 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)); - } - + rPolygon.appendBezierSegment(B2DPoint(-fKappa, fOne), B2DPoint(-fOne, fKappa), 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)); - } - + rPolygon.appendBezierSegment(B2DPoint(-fOne, -fKappa), B2DPoint(-fKappa, -fOne), 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)); - } - + rPolygon.appendBezierSegment(B2DPoint(fKappa, -fOne), B2DPoint(fOne, -fKappa), B2DPoint(fOne, fZero)); break; } } @@ -1618,12 +1634,15 @@ namespace basegfx 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); + appendUnitCircleQuadrant(aRetval, 0); + appendUnitCircleQuadrant(aRetval, 1); + appendUnitCircleQuadrant(aRetval, 2); + appendUnitCircleQuadrant(aRetval, 3); aRetval.setClosed(true); + // remove double points between segments created by segmented creation + aRetval.removeDoublePoints(); + return aRetval; } @@ -1656,11 +1675,11 @@ namespace basegfx return aRetval; } - void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd, bool bEndPoint) + void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd) { - 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 (!)"); + OSL_ENSURE(fStart >= 0.0 && fStart <= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)"); + OSL_ENSURE(fEnd >= 0.0 && fEnd <= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)"); + OSL_ENSURE(fEnd >= fStart, "appendUnitCircleQuadrantSegment: Access out of range (!)"); const double fOne(1.0); const bool bStartIsZero(fTools::equalZero(fStart)); const bool bEndIsOne(fTools::equal(fEnd, fOne)); @@ -1668,16 +1687,16 @@ namespace basegfx if(bStartIsZero && bEndIsOne) { // add completely - appendUnitCircleQuadrant(rPolygon, nQuadrant, bEndPoint); + appendUnitCircleQuadrant(rPolygon, nQuadrant); } else { // split and add B2DPolygon aQuadrant; - appendUnitCircleQuadrant(aQuadrant, nQuadrant, true); + appendUnitCircleQuadrant(aQuadrant, nQuadrant); const bool bStartEndEqual(fTools::equal(fStart, fEnd)); - if(bStartEndEqual && bEndPoint) + if(bStartEndEqual) { if(bStartIsZero) { @@ -1692,17 +1711,20 @@ namespace basegfx else { // both equal but not zero, add split point - B2DCubicBezier aCubicBezier(aQuadrant.getB2DPoint(0L), aQuadrant.getControlPointA(0L), aQuadrant.getControlPointB(0L), aQuadrant.getB2DPoint(1L)); + B2DCubicBezier aCubicBezier( + aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L), + aQuadrant.getPrevControlPoint(1L), 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 aCubicBezier( + aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L), + aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L)); B2DCubicBezier aCubicBezierWaste; if(!bEndIsOne) @@ -1720,15 +1742,8 @@ namespace basegfx 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()); - } + rPolygon.appendBezierSegment(aCubicBezier.getControlPointA(), aCubicBezier.getControlPointB(), aCubicBezier.getEndPoint()); } } } @@ -1772,14 +1787,14 @@ namespace basegfx // 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); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, fSplitOffsetEnd); 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); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, 1.0); bStartDone = true; } } @@ -1787,13 +1802,13 @@ namespace basegfx { // create quadrant start to end const double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2); - appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd, true); + appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd); bEndDone = true; } else { // add quadrant completely - appendUnitCircleQuadrant(aRetval, nCurrentQuadrant, false); + appendUnitCircleQuadrant(aRetval, nCurrentQuadrant); } // next step @@ -1801,6 +1816,9 @@ namespace basegfx } while(!(bStartDone && bEndDone)); + // remove double points between segments created by segmented creation + aRetval.removeDoublePoints(); + return aRetval; } @@ -1836,16 +1854,16 @@ namespace basegfx bool hasNeutralPoints(const B2DPolygon& rCandidate) { OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)"); + const sal_uInt32 nPointCount(rCandidate.count()); - if(rCandidate.count() > 2L) + if(nPointCount > 2L) { - B2DPoint aPrevPoint(rCandidate.getB2DPoint(rCandidate.count() - 1L)); + B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); - for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + for(sal_uInt32 a(0L); a < nPointCount; a++) { - const bool bLast(a + 1L == rCandidate.count()); - const B2DPoint aNextPoint(rCandidate.getB2DPoint(bLast ? 0L : a + 1L)); + const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); const B2DVector aPrevVec(aPrevPoint - aCurrPoint); const B2DVector aNextVec(aNextPoint - aCurrPoint); const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec)); @@ -1858,11 +1876,8 @@ namespace basegfx else { // prepare next - if(!bLast) - { - aPrevPoint = aCurrPoint; - aCurrPoint = aNextPoint; - } + aPrevPoint = aCurrPoint; + aCurrPoint = aNextPoint; } } } @@ -1874,14 +1889,14 @@ namespace basegfx { if(hasNeutralPoints(rCandidate)) { + const sal_uInt32 nPointCount(rCandidate.count()); B2DPolygon aRetval; - B2DPoint aPrevPoint(rCandidate.getB2DPoint(rCandidate.count() - 1L)); + B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); - for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + for(sal_uInt32 a(0L); a < nPointCount; a++) { - const bool bLast(a + 1L == rCandidate.count()); - const B2DPoint aNextPoint(rCandidate.getB2DPoint(bLast ? 0L : a + 1L)); + const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); const B2DVector aPrevVec(aPrevPoint - aCurrPoint); const B2DVector aNextVec(aNextPoint - aCurrPoint); const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec)); @@ -1889,10 +1904,7 @@ namespace basegfx if(ORIENTATION_NEUTRAL == aOrientation) { // current has neutral orientation, leave it out and prepare next - if(!bLast) - { - aCurrPoint = aNextPoint; - } + aCurrPoint = aNextPoint; } else { @@ -1900,11 +1912,8 @@ namespace basegfx aRetval.append(aCurrPoint); // prepare next - if(!bLast) - { - aPrevPoint = aCurrPoint; - aCurrPoint = aNextPoint; - } + aPrevPoint = aCurrPoint; + aCurrPoint = aNextPoint; } } @@ -1927,18 +1936,18 @@ namespace basegfx bool isConvex(const B2DPolygon& rCandidate) { OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)"); + const sal_uInt32 nPointCount(rCandidate.count()); - if(rCandidate.count() > 2L) + if(nPointCount > 2L) { - const B2DPoint aPrevPoint(rCandidate.getB2DPoint(rCandidate.count() - 1L)); + const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L)); B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L)); B2DVector aCurrVec(aPrevPoint - aCurrPoint); B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL); - for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + for(sal_uInt32 a(0L); a < nPointCount; a++) { - const bool bLast(a + 1L == rCandidate.count()); - const B2DPoint aNextPoint(rCandidate.getB2DPoint(bLast ? 0L : a + 1L)); + const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount)); const B2DVector aNextVec(aNextPoint - aCurrPoint); const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec)); @@ -1957,11 +1966,8 @@ namespace basegfx } // prepare next - if(!bLast) - { - aCurrPoint = aNextPoint; - aCurrVec = -aNextVec; - } + aCurrPoint = aNextPoint; + aCurrVec = -aNextVec; } } @@ -2308,37 +2314,38 @@ namespace basegfx double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut) { double fRetval(DBL_MAX); + const sal_uInt32 nPointCount(rCandidate.count()); - if(rCandidate.count() > 1L) + if(nPointCount > 1L) { const double fZero(0.0); - const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? rCandidate.count() : rCandidate.count() - 1L); + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); + B2DCubicBezier aBezier; + aBezier.setStartPoint(rCandidate.getB2DPoint(0)); for(sal_uInt32 a(0L); a < nEdgeCount; a++) { - const B2DPoint aPointA(rCandidate.getB2DPoint(a)); - const B2DPoint aPointB(rCandidate.getB2DPoint(getIndexOfSuccessor(a, rCandidate))); + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); double fEdgeDist; double fNewCut; + bool bEdgeIsCurve(false); - if(rCandidate.areControlVectorsUsed()) + if(rCandidate.areControlPointsUsed()) { - const B2DVector aVectorA(rCandidate.getControlVectorA(a)); - const B2DVector aVectorB(rCandidate.getControlVectorB(a)); + aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); + aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aBezier.testAndSolveTrivialBezier(); + bEdgeIsCurve = aBezier.isBezier(); + } - 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); - } + if(bEdgeIsCurve) + { + fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut); } else { - fEdgeDist = getSmallestDistancePointToEdge(aPointA, aPointB, rTestPoint, fNewCut); + fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut); } if(DBL_MAX == fRetval || fEdgeDist < fRetval) @@ -2354,6 +2361,9 @@ namespace basegfx break; } } + + // prepare next step + aBezier.setStartPoint(aBezier.getEndPoint()); } if(1.0 == rCut) @@ -2411,16 +2421,16 @@ namespace basegfx { aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); - if(rCandidate.areControlVectorsUsed()) + if(rCandidate.areControlPointsUsed()) { - if(!rCandidate.getControlVectorA(a).equalZero()) + if(!rCandidate.getPrevControlPoint(a).equalZero()) { - aRetval.setControlPointA(a, distort(rCandidate.getControlPointA(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); + aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); } - if(!rCandidate.getControlVectorB(a).equalZero()) + if(!rCandidate.getNextControlPoint(a).equalZero()) { - aRetval.setControlPointB(a, distort(rCandidate.getControlPointB(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); + aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); } } } @@ -2469,24 +2479,23 @@ namespace basegfx { OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)"); bool bRetval(false); + const sal_uInt32 nPointCount(rCandidate.count()); - if(rCandidate.count()) + if(nPointCount) { - // look for predecessor - const sal_uInt32 nPrev(getIndexOfPredecessor(nIndex, rCandidate)); - - if(nPrev != nIndex && rCandidate.getControlVectorB(nPrev).equalZero()) + // predecessor + if(!rCandidate.isPrevControlPointUsed(nIndex)) { - rCandidate.setControlPointB(nPrev, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrev), 1.0 / 3.0)); + const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); + rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0)); bRetval = true; } - // look for successor - const sal_uInt32 nNext(getIndexOfSuccessor(nIndex, rCandidate)); - - if(nNext != nIndex && rCandidate.getControlVectorA(nIndex).equalZero()) + // successor + if(!rCandidate.isNextControlPointUsed(nIndex)) { - rCandidate.setControlPointA(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNext), 1.0 / 3.0)); + const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); + rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0)); bRetval = true; } } @@ -2510,125 +2519,127 @@ namespace basegfx { OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)"); bool bRetval(false); + const sal_uInt32 nPointCount(rCandidate.count()); - if(rCandidate.count()) + if(nPointCount) { - const sal_uInt32 nPrev(getIndexOfPredecessor(nIndex, rCandidate)); - const sal_uInt32 nNext(getIndexOfSuccessor(nIndex, rCandidate)); + const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex)); - if(nPrev != nIndex && nNext != nIndex) + switch(eContinuity) { - const B2DVector aControlVectorB(rCandidate.getControlVectorB(nPrev)); - const B2DVector aControlVectorA(rCandidate.getControlVectorA(nIndex)); - const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex)); + case CONTINUITY_NONE : + { + if(rCandidate.isPrevControlPointUsed(nIndex)) + { + const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); + rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0)); + bRetval = true; + } - switch(eContinuity) + if(rCandidate.isNextControlPointUsed(nIndex)) + { + const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); + rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0)); + bRetval = true; + } + + break; + } + case CONTINUITY_C1 : { - case CONTINUITY_NONE : + if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex)) { - if(!aControlVectorB.equalZero()) + B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint); + B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint); + const double fLenPrev(aVectorPrev.getLength()); + const double fLenNext(aVectorNext.getLength()); + aVectorPrev.normalize(); + aVectorNext.normalize(); + const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); + + if(ORIENTATION_NEUTRAL == aOrientation) { - rCandidate.setControlPointB(nPrev, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrev), 1.0 / 3.0)); - bRetval = true; + // 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 sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount); + const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); + const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0)); + const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0)); + + rCandidate.setControlPoints(nIndex, + aCurrentPoint + (aVectorPrev * fLenPrevEdge), + aCurrentPoint + (aVectorNext * fLenNextEdge)); + bRetval = true; + } } - - if(!aControlVectorA.equalZero()) + else { - rCandidate.setControlPointA(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNext), 1.0 / 3.0)); - bRetval = true; - } + // not parallel, set vectors and length + const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); - 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) + if(ORIENTATION_POSITIVE == 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; - } + rCandidate.setControlPoints(nIndex, + aCurrentPoint - (aNormalizedPerpendicular * fLenPrev), + aCurrentPoint + (aNormalizedPerpendicular * fLenNext)); } 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; + rCandidate.setControlPoints(nIndex, + aCurrentPoint + (aNormalizedPerpendicular * fLenPrev), + aCurrentPoint - (aNormalizedPerpendicular * fLenNext)); } + + bRetval = true; } - break; } - case CONTINUITY_C2 : + break; + } + case CONTINUITY_C2 : + { + if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex)) { - if(!aControlVectorB.equalZero() && !aControlVectorA.equalZero()) + B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint); + B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint); + const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0); + aVectorPrev.normalize(); + aVectorNext.normalize(); + const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext)); + + if(ORIENTATION_NEUTRAL == aOrientation) { - 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); + // already parallel, set length. Use one direction for better numerical correctness + const B2DVector aScaledDirection(aVectorPrev * fCommonLength); + + rCandidate.setControlPoints(nIndex, + aCurrentPoint + aScaledDirection, + aCurrentPoint - aScaledDirection); + } + else + { + // not parallel, set vectors and length + const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext)); + const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength); - rCandidate.setControlPointB(nPrev, aCurrentPoint + aScaledDirection); - rCandidate.setControlPointA(nIndex, aCurrentPoint - aScaledDirection); + if(ORIENTATION_POSITIVE == aOrientation) + { + rCandidate.setControlPoints(nIndex, + aCurrentPoint - aPerpendicular, + aCurrentPoint + aPerpendicular); } 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); - } + rCandidate.setControlPoints(nIndex, + aCurrentPoint + aPerpendicular, + aCurrentPoint - aPerpendicular); } - - bRetval = true; } - break; + + bRetval = true; } + break; } } } @@ -2688,6 +2699,91 @@ namespace basegfx return true; } + // #i76891# + B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate) + { + const sal_uInt32 nPointCount(rCandidate.count()); + + if(nPointCount && rCandidate.areControlPointsUsed()) + { + // prepare loop + const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); + B2DPolygon aRetval; + B2DCubicBezier aBezier; + aBezier.setStartPoint(rCandidate.getB2DPoint(0)); + + // add start point + aRetval.append(aBezier.getStartPoint()); + + for(sal_uInt32 a(0L); a < nEdgeCount; a++) + { + // get values for edge + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rCandidate.getNextControlPoint(a)); + aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex)); + aBezier.testAndSolveTrivialBezier(); + + // still bezier? + if(aBezier.isBezier()) + { + // add edge with control vectors + aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint()); + } + else + { + // add edge + aRetval.append(aBezier.getEndPoint()); + } + + // next point + aBezier.setStartPoint(aBezier.getEndPoint()); + } + + if(rCandidate.isClosed()) + { + // set closed flag, rescue control point and correct last double point + closeWithGeometryChange(aRetval); + } + + return aRetval; + } + else + { + return rCandidate; + } + } + + // makes the given indexed point the new polygon start point. To do that, the points in the + // polygon will be rotated. This is only valid for closed polygons, for non-closed ones + // an assertion will be triggered + B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint) + { + const sal_uInt32 nPointCount(rCandidate.count()); + + if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount) + { + OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)"); + B2DPolygon aRetval; + + for(sal_uInt32 a(0); a < nPointCount; a++) + { + const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount); + aRetval.append(rCandidate.getB2DPoint(nSourceIndex)); + + if(rCandidate.areControlPointsUsed()) + { + aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex)); + aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex)); + } + } + + return aRetval; + } + + return rCandidate; + } + } // end of namespace tools } // end of namespace basegfx |