#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 );
|
||||
|
||||
/** 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 );
|
||||
|
@@ -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,57 +1834,94 @@ 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));
|
||||
|
||||
// create closed unit-circle with 4 segments
|
||||
switch(nQuadrant)
|
||||
B2DPoint aPoint(1.0, 0.0);
|
||||
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;
|
||||
aBackward *= aQuadrantMatrix;
|
||||
aForward *= aQuadrantMatrix;
|
||||
}
|
||||
|
||||
aUnitCircle.append(aPoint);
|
||||
|
||||
for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
aPoint *= aRotateMatrix;
|
||||
aBackward *= aRotateMatrix;
|
||||
aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint);
|
||||
aForward *= aRotateMatrix;
|
||||
}
|
||||
|
||||
aUnitCircle.setClosed(true);
|
||||
aUnitCircle.removeDoublePoints();
|
||||
|
||||
return aUnitCircle;
|
||||
}
|
||||
|
||||
B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
|
||||
{
|
||||
B2DPolygon aRetval;
|
||||
switch(nStartQuadrant % 4)
|
||||
{
|
||||
case 1 :
|
||||
{
|
||||
static B2DPolygon aUnitCircleStartQuadrantOne;
|
||||
|
||||
// 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);
|
||||
if(!aUnitCircleStartQuadrantOne.count())
|
||||
{
|
||||
::osl::Mutex m_mutex;
|
||||
aUnitCircleStartQuadrantOne = impCreateUnitCircle(1);
|
||||
}
|
||||
|
||||
// remove double points between segments created by segmented creation
|
||||
aRetval.removeDoublePoints();
|
||||
return aUnitCircleStartQuadrantOne;
|
||||
}
|
||||
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 )
|
||||
@@ -1895,63 +1934,6 @@ namespace basegfx
|
||||
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 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;
|
||||
// same start and end angle, add single point
|
||||
aRetval.append(B2DPoint(cos(fStart), sin(fStart)));
|
||||
}
|
||||
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)
|
||||
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));
|
||||
|
||||
aRetval.appendBezierSegment(
|
||||
aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa),
|
||||
aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa),
|
||||
aSegEnd);
|
||||
|
||||
nSegment = (nSegment + 1) % nSegments;
|
||||
aSegStart = aSegEnd;
|
||||
}
|
||||
|
||||
// next step
|
||||
nCurrentQuadrant = (nCurrentQuadrant + 1L) % 4L;
|
||||
// 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();
|
||||
|
Reference in New Issue
Block a user