#i105459# enhanced circles/ellipes for more mathematical correctness
This commit is contained in:
@@ -288,14 +288,6 @@ namespace basegfx
|
|||||||
*/
|
*/
|
||||||
B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius );
|
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
|
/** create a polygon which describes the unit circle and close it
|
||||||
|
|
||||||
@param nStartQuadrant
|
@param nStartQuadrant
|
||||||
@@ -325,59 +317,6 @@ namespace basegfx
|
|||||||
*/
|
*/
|
||||||
B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY );
|
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
|
/** 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 );
|
B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd );
|
||||||
|
@@ -44,6 +44,7 @@
|
|||||||
#include <basegfx/matrix/b2dhommatrix.hxx>
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
||||||
#include <basegfx/curve/b2dbeziertools.hxx>
|
#include <basegfx/curve/b2dbeziertools.hxx>
|
||||||
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
||||||
|
#include <osl/mutex.hxx>
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -55,6 +56,7 @@
|
|||||||
#ifdef DBG_UTIL
|
#ifdef DBG_UTIL
|
||||||
static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE;
|
static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE;
|
||||||
#endif
|
#endif
|
||||||
|
#define STEPSPERQUARTER (3)
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -1832,57 +1834,94 @@ namespace basegfx
|
|||||||
return createPolygonFromEllipse( rCenter, fRadius, fRadius );
|
return createPolygonFromEllipse( rCenter, fRadius, fRadius );
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant)
|
B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant)
|
||||||
{
|
{
|
||||||
const double fZero(0.0);
|
B2DPolygon aUnitCircle;
|
||||||
const double fOne(1.0);
|
|
||||||
const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
|
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));
|
||||||
|
|
||||||
// create closed unit-circle with 4 segments
|
B2DPoint aPoint(1.0, 0.0);
|
||||||
switch(nQuadrant)
|
B2DPoint aForward(1.0, fScaledKappa);
|
||||||
|
B2DPoint aBackward(1.0, -fScaledKappa);
|
||||||
|
|
||||||
|
if(0 != nStartQuadrant)
|
||||||
{
|
{
|
||||||
case 0 : // first quadrant
|
const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4)));
|
||||||
{
|
aPoint *= aQuadrantMatrix;
|
||||||
rPolygon.append(B2DPoint(fOne, fZero));
|
aBackward *= aQuadrantMatrix;
|
||||||
rPolygon.appendBezierSegment(B2DPoint(fOne, fKappa), B2DPoint(fKappa, fOne), B2DPoint(fZero, fOne));
|
aForward *= aQuadrantMatrix;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aUnitCircle.append(aPoint);
|
||||||
|
|
||||||
|
for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++)
|
||||||
|
{
|
||||||
|
aPoint *= aRotateMatrix;
|
||||||
|
aBackward *= aRotateMatrix;
|
||||||
|
aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint);
|
||||||
|
aForward *= aRotateMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
aUnitCircle.setClosed(true);
|
||||||
|
aUnitCircle.removeDoublePoints();
|
||||||
|
|
||||||
|
return aUnitCircle;
|
||||||
}
|
}
|
||||||
|
|
||||||
B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
|
B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
|
||||||
{
|
{
|
||||||
B2DPolygon aRetval;
|
switch(nStartQuadrant % 4)
|
||||||
|
{
|
||||||
|
case 1 :
|
||||||
|
{
|
||||||
|
static B2DPolygon aUnitCircleStartQuadrantOne;
|
||||||
|
|
||||||
// create unit-circle with all 4 segments, close it
|
if(!aUnitCircleStartQuadrantOne.count())
|
||||||
appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
|
{
|
||||||
appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
|
::osl::Mutex m_mutex;
|
||||||
appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
|
aUnitCircleStartQuadrantOne = impCreateUnitCircle(1);
|
||||||
appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
|
}
|
||||||
aRetval.setClosed(true);
|
|
||||||
|
|
||||||
// remove double points between segments created by segmented creation
|
return aUnitCircleStartQuadrantOne;
|
||||||
aRetval.removeDoublePoints();
|
}
|
||||||
|
case 2 :
|
||||||
|
{
|
||||||
|
static B2DPolygon aUnitCircleStartQuadrantTwo;
|
||||||
|
|
||||||
return aRetval;
|
if(!aUnitCircleStartQuadrantTwo.count())
|
||||||
|
{
|
||||||
|
::osl::Mutex m_mutex;
|
||||||
|
aUnitCircleStartQuadrantTwo = impCreateUnitCircle(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aUnitCircleStartQuadrantTwo;
|
||||||
|
}
|
||||||
|
case 3 :
|
||||||
|
{
|
||||||
|
static B2DPolygon aUnitCircleStartQuadrantThree;
|
||||||
|
|
||||||
|
if(!aUnitCircleStartQuadrantThree.count())
|
||||||
|
{
|
||||||
|
::osl::Mutex m_mutex;
|
||||||
|
aUnitCircleStartQuadrantThree = impCreateUnitCircle(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aUnitCircleStartQuadrantThree;
|
||||||
|
}
|
||||||
|
default : // case 0 :
|
||||||
|
{
|
||||||
|
static B2DPolygon aUnitCircleStartQuadrantZero;
|
||||||
|
|
||||||
|
if(!aUnitCircleStartQuadrantZero.count())
|
||||||
|
{
|
||||||
|
::osl::Mutex m_mutex;
|
||||||
|
aUnitCircleStartQuadrantZero = impCreateUnitCircle(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aUnitCircleStartQuadrantZero;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
|
B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
|
||||||
@@ -1895,63 +1934,6 @@ namespace basegfx
|
|||||||
return aRetval;
|
return aRetval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
// add completely
|
|
||||||
appendUnitCircleQuadrant(rPolygon, nQuadrant);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// split and add
|
|
||||||
B2DPolygon aQuadrant;
|
|
||||||
appendUnitCircleQuadrant(aQuadrant, nQuadrant);
|
|
||||||
const bool bStartEndEqual(fTools::equal(fStart, fEnd));
|
|
||||||
|
|
||||||
if(bStartEndEqual)
|
|
||||||
{
|
|
||||||
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.getNextControlPoint(0L),
|
|
||||||
aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L));
|
|
||||||
|
|
||||||
aCubicBezier.split(fStart, &aCubicBezier, 0);
|
|
||||||
rPolygon.append(aCubicBezier.getEndPoint());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
B2DCubicBezier aCubicBezier(
|
|
||||||
aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L),
|
|
||||||
aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L));
|
|
||||||
|
|
||||||
aCubicBezier = aCubicBezier.snippet(fStart, fEnd);
|
|
||||||
rPolygon.append(aCubicBezier.getStartPoint());
|
|
||||||
rPolygon.appendBezierSegment(aCubicBezier.getControlPointA(), aCubicBezier.getControlPointB(), aCubicBezier.getEndPoint());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
|
B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
|
||||||
{
|
{
|
||||||
B2DPolygon aRetval;
|
B2DPolygon aRetval;
|
||||||
@@ -1978,49 +1960,74 @@ namespace basegfx
|
|||||||
fEnd = 0.0;
|
fEnd = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sal_uInt32 nQuadrantStart(sal_uInt32(fStart / F_PI2) % 4L);
|
if(fTools::equal(fStart, fEnd))
|
||||||
const sal_uInt32 nQuadrantEnd(sal_uInt32(fEnd / F_PI2) % 4L);
|
|
||||||
sal_uInt32 nCurrentQuadrant(nQuadrantStart);
|
|
||||||
bool bStartDone(false);
|
|
||||||
bool bEndDone(false);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
if(!bStartDone && nQuadrantStart == 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))
|
||||||
{
|
{
|
||||||
if(nQuadrantStart == nQuadrantEnd && fTools::moreOrEqual(fEnd, fStart))
|
// start and end in one sector and in the right order, create in one segment
|
||||||
{
|
const B2DPoint aSegEnd(cos(fEnd), sin(fEnd));
|
||||||
// both in one quadrant and defining the complete segment, create start to end
|
const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment));
|
||||||
double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2);
|
|
||||||
double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2);
|
aRetval.appendBezierSegment(
|
||||||
appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, fSplitOffsetEnd);
|
aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
|
||||||
bStartDone = bEndDone = true;
|
aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
|
||||||
}
|
aSegEnd);
|
||||||
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)
|
|
||||||
{
|
|
||||||
// create quadrant start to end
|
|
||||||
const double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2);
|
|
||||||
appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd);
|
|
||||||
bEndDone = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// add quadrant completely
|
double fSegEndRad((nStartSegment + 1) * fAnglePerSegment);
|
||||||
appendUnitCircleQuadrant(aRetval, nCurrentQuadrant);
|
double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment));
|
||||||
}
|
B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad));
|
||||||
|
|
||||||
// next step
|
aRetval.appendBezierSegment(
|
||||||
nCurrentQuadrant = (nCurrentQuadrant + 1L) % 4L;
|
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));
|
||||||
|
|
||||||
|
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
|
// remove double points between segments created by segmented creation
|
||||||
aRetval.removeDoublePoints();
|
aRetval.removeDoublePoints();
|
||||||
|
Reference in New Issue
Block a user