diff options
author | Armin Le Grand <Armin.Le.Grand@Sun.COM> | 2009-10-08 17:35:07 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@Sun.COM> | 2009-10-08 17:35:07 +0200 |
commit | f678fbae66d80e762faa29e7ab0fe3703f005275 (patch) | |
tree | 9bba6afe53f6ac8b0d6fcfaed8252fea9fa18c1d /basegfx | |
parent | a6d693d98547c8aa172b5044c36b71160f8ad8f7 (diff) |
#i105459# enhanced circles/ellipes for more mathematical correctness
Diffstat (limited to 'basegfx')
-rw-r--r-- | basegfx/inc/basegfx/polygon/b2dpolygontools.hxx | 61 | ||||
-rw-r--r-- | basegfx/source/polygon/b2dpolygontools.cxx | 265 |
2 files changed, 136 insertions, 190 deletions
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx index 5eff6b0b9cc1..47ff41b75e70 100644 --- a/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx +++ b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx @@ -288,14 +288,6 @@ namespace basegfx */ B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius ); - /** append a unit circle with one point and the control vectors to the given polygon - */ - void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, bool bEndPoint); - - /** append a segment of unit circle with one point and the control vectors to the given polygon - */ - void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd, bool bEndPoint); - /** create a polygon which describes the unit circle and close it @param nStartQuadrant @@ -325,59 +317,6 @@ namespace basegfx */ B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ); - /** append a unit circle with one point and the control vectors to the given polygon - */ - void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant); - - /** append a segment of unit circle with start point, the control vectors and end point to the given polygon - */ - void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd); - - /** Create an ellipse polygon with given radii. - - This method creates an ellipse approximation consisting of - four cubic bezier segments, which approximate the given - ellipse with an error of less than 0.5 percent. - - @param rCenter - Center point of the circle - - @param fRadiusX - Radius of the ellipse in X direction - - @param fRadiusY - Radius of the ellipse in Y direction - - @param fStart - Start angle where the ellipe segment starts in the range [0.0 .. 2PI[ - - @param fEnd - End angle where the ellipe segment ends in the range [0.0 .. 2PI[ - */ - B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ); - - /** Create an ellipse polygon with given radii and the given angles, from start to end - - This method creates an ellipse approximation consisting of - four cubic bezier segments, which approximate the given - ellipse with an error of less than 0.5 percent. - - @param rCenter - Center point of the circle - - @param fRadiusX - Radius of the ellipse in X direction - - @param fRadiusY - Radius of the ellipse in Y direction - - @param fStart - Start angle where the ellipe segment starts in the range [0.0 .. 2PI[ - - @param fEnd - End angle where the ellipe segment ends in the range [0.0 .. 2PI[ - */ - /** Create an unit ellipse polygon with the given angles, from start to end */ B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd ); diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index 6e288786df6d..da3fa202c2a4 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -44,6 +44,7 @@ #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/curve/b2dbeziertools.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <osl/mutex.hxx> #include <numeric> #include <limits> @@ -55,6 +56,7 @@ #ifdef DBG_UTIL static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE; #endif +#define STEPSPERQUARTER (3) ////////////////////////////////////////////////////////////////////////////// @@ -1832,126 +1834,106 @@ namespace basegfx return createPolygonFromEllipse( rCenter, fRadius, fRadius ); } - void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant) + B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant) { - const double fZero(0.0); - const double fOne(1.0); + B2DPolygon aUnitCircle; const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER)); + const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER)); + + B2DPoint aPoint(1.0, 0.0); + B2DPoint aForward(1.0, fScaledKappa); + B2DPoint aBackward(1.0, -fScaledKappa); - // create closed unit-circle with 4 segments - switch(nQuadrant) + if(0 != nStartQuadrant) { - case 0 : // first quadrant - { - rPolygon.append(B2DPoint(fOne, fZero)); - rPolygon.appendBezierSegment(B2DPoint(fOne, fKappa), B2DPoint(fKappa, fOne), B2DPoint(fZero, fOne)); - break; - } - case 1 : // second quadrant - { - rPolygon.append(B2DPoint(fZero, fOne)); - rPolygon.appendBezierSegment(B2DPoint(-fKappa, fOne), B2DPoint(-fOne, fKappa), B2DPoint(-fOne, fZero)); - break; - } - case 2 : // third quadrant - { - rPolygon.append(B2DPoint(-fOne, fZero)); - rPolygon.appendBezierSegment(B2DPoint(-fOne, -fKappa), B2DPoint(-fKappa, -fOne), B2DPoint(fZero, -fOne)); - break; - } - default : // last quadrant - { - rPolygon.append(B2DPoint(fZero, -fOne)); - rPolygon.appendBezierSegment(B2DPoint(fKappa, -fOne), B2DPoint(fOne, -fKappa), B2DPoint(fOne, fZero)); - break; - } + const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4))); + aPoint *= aQuadrantMatrix; + aBackward *= aQuadrantMatrix; + aForward *= aQuadrantMatrix; } - } - - B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant) - { - B2DPolygon aRetval; - - // create unit-circle with all 4 segments, close it - appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++; - appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++; - appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++; - appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++; - aRetval.setClosed(true); - // remove double points between segments created by segmented creation - aRetval.removeDoublePoints(); - - return aRetval; - } + aUnitCircle.append(aPoint); - B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ) - { - B2DPolygon aRetval(createPolygonFromUnitCircle()); - const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY())); + for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++) + { + aPoint *= aRotateMatrix; + aBackward *= aRotateMatrix; + aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint); + aForward *= aRotateMatrix; + } - aRetval.transform(aMatrix); + aUnitCircle.setClosed(true); + aUnitCircle.removeDoublePoints(); - return aRetval; + return aUnitCircle; } - void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd) + B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant) { - 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)); - - if(bStartIsZero && bEndIsOne) + switch(nStartQuadrant % 4) { - // add completely - appendUnitCircleQuadrant(rPolygon, nQuadrant); - } - else - { - // split and add - B2DPolygon aQuadrant; - appendUnitCircleQuadrant(aQuadrant, nQuadrant); - const bool bStartEndEqual(fTools::equal(fStart, fEnd)); - - if(bStartEndEqual) + case 1 : { - if(bStartIsZero) + static B2DPolygon aUnitCircleStartQuadrantOne; + + if(!aUnitCircleStartQuadrantOne.count()) { - // both zero, add start point - rPolygon.append(aQuadrant.getB2DPoint(0L)); + ::osl::Mutex m_mutex; + aUnitCircleStartQuadrantOne = impCreateUnitCircle(1); } - else if(bEndIsOne) + + return aUnitCircleStartQuadrantOne; + } + case 2 : + { + static B2DPolygon aUnitCircleStartQuadrantTwo; + + if(!aUnitCircleStartQuadrantTwo.count()) { - // both one, add end point - rPolygon.append(aQuadrant.getB2DPoint(1L)); + ::osl::Mutex m_mutex; + aUnitCircleStartQuadrantTwo = impCreateUnitCircle(2); } - else - { - // both equal but not zero, add split point - B2DCubicBezier aCubicBezier( - aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L), - aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L)); - aCubicBezier.split(fStart, &aCubicBezier, 0); - rPolygon.append(aCubicBezier.getEndPoint()); + return aUnitCircleStartQuadrantTwo; + } + case 3 : + { + static B2DPolygon aUnitCircleStartQuadrantThree; + + if(!aUnitCircleStartQuadrantThree.count()) + { + ::osl::Mutex m_mutex; + aUnitCircleStartQuadrantThree = impCreateUnitCircle(3); } + + return aUnitCircleStartQuadrantThree; } - else + default : // case 0 : { - B2DCubicBezier aCubicBezier( - aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L), - aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L)); + static B2DPolygon aUnitCircleStartQuadrantZero; - aCubicBezier = aCubicBezier.snippet(fStart, fEnd); - rPolygon.append(aCubicBezier.getStartPoint()); - rPolygon.appendBezierSegment(aCubicBezier.getControlPointA(), aCubicBezier.getControlPointB(), aCubicBezier.getEndPoint()); + if(!aUnitCircleStartQuadrantZero.count()) + { + ::osl::Mutex m_mutex; + aUnitCircleStartQuadrantZero = impCreateUnitCircle(0); + } + + return aUnitCircleStartQuadrantZero; } } } + B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY ) + { + B2DPolygon aRetval(createPolygonFromUnitCircle()); + const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY())); + + aRetval.transform(aMatrix); + + return aRetval; + } + B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd ) { B2DPolygon aRetval; @@ -1978,49 +1960,74 @@ namespace basegfx fEnd = 0.0; } - 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); - - do + if(fTools::equal(fStart, fEnd)) { - 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); - bStartDone = bEndDone = true; - } - else - { - // create start to quadrant end - const double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2); - appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, 1.0); - bStartDone = true; - } - } - else if(!bEndDone && nQuadrantEnd == nCurrentQuadrant) + // same start and end angle, add single point + aRetval.append(B2DPoint(cos(fStart), sin(fStart))); + } + else + { + const sal_uInt32 nSegments(STEPSPERQUARTER * 4); + const double fAnglePerSegment(F_PI2 / STEPSPERQUARTER); + const sal_uInt32 nStartSegment(sal_uInt32(fStart / fAnglePerSegment) % nSegments); + const sal_uInt32 nEndSegment(sal_uInt32(fEnd / fAnglePerSegment) % nSegments); + const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER)); + + B2DPoint aSegStart(cos(fStart), sin(fStart)); + aRetval.append(aSegStart); + + if(nStartSegment == nEndSegment && fTools::more(fEnd, fStart)) { - // create quadrant start to end - const double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2); - appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd); - bEndDone = true; + // start and end in one sector and in the right order, create in one segment + const B2DPoint aSegEnd(cos(fEnd), sin(fEnd)); + const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment)); + + aRetval.appendBezierSegment( + aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), + aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), + aSegEnd); } else { - // add quadrant completely - appendUnitCircleQuadrant(aRetval, nCurrentQuadrant); - } + double fSegEndRad((nStartSegment + 1) * fAnglePerSegment); + double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment)); + B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad)); + + aRetval.appendBezierSegment( + aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), + aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), + aSegEnd); + + sal_uInt32 nSegment((nStartSegment + 1) % nSegments); + aSegStart = aSegEnd; + + while(nSegment != nEndSegment) + { + // No end in this sector, add full sector. + fSegEndRad = (nSegment + 1) * fAnglePerSegment; + aSegEnd = B2DPoint(cos(fSegEndRad), sin(fSegEndRad)); - // next step - nCurrentQuadrant = (nCurrentQuadrant + 1L) % 4L; + aRetval.appendBezierSegment( + aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa), + aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa), + aSegEnd); + + nSegment = (nSegment + 1) % nSegments; + aSegStart = aSegEnd; + } + + // End in this sector + const double fSegStartRad(nSegment * fAnglePerSegment); + fFactor = fScaledKappa * ((fEnd - fSegStartRad) / fAnglePerSegment); + aSegEnd = B2DPoint(cos(fEnd), sin(fEnd)); + + aRetval.appendBezierSegment( + aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor), + aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor), + aSegEnd); + } } - while(!(bStartDone && bEndDone)); // remove double points between segments created by segmented creation aRetval.removeDoublePoints(); |