#include "stdafx.h"
#include "Arc2BezierAPI.h"
#include <math.h>
const float PI = 3.1415926f;
//static POINTF s_ptfCtrl[4];
////////////////////////////////////////////////////////////////////////// begin
// 用三次Bezier逼近圆弧时的分类
// 1,假设是完整圆,则先分成3份
// 2,否则,2.1 若大于240度,则划分为3份
// 2.2 否则,若大于120度,则划分为2份
// 2.3 否则,直接逼近
////////////////////////////////////////////////////////////////////////// end
// 用三次Bezier逼近圆弧:圆弧要等分成多少段
int Arc2Bezier_SplitNum(IN const POINTF ptfCenter, // 圆心
IN const float fRadius, // 半径
IN const BOOL bCircle, // 是否整圆
IN const float fAngleStartIN, // 起始角度
IN const float fAngleEndIN, // 终止角度 默认终止角度大于起始角度,且都在0-2*PI之间
IN const float fThreshold, // 误差阈值
IN OUT int & nArcs // 圆弧等分段数
)
{
//nArcs = 1;
float fStartAngle = fAngleStartIN;
float fEndAngle = fAngleEndIN;
// 完整圆
if (bCircle)
{
nArcs = 3;
fStartAngle = 0.f;
fEndAngle = PI * 0.6666667f;
}
else
{
if (fAngleEndIN - fAngleStartIN > PI * 4 / 3) // 大于240度
{
nArcs = 3;
fEndAngle = fStartAngle + (fEndAngle - fStartAngle) /3;
}
else if (fAngleEndIN - fAngleStartIN > PI * 2 / 3) // 大于120度
{
nArcs = 2;
fEndAngle = fStartAngle + (fEndAngle - fStartAngle) /2;
}
else
{
nArcs = 1;
}
}
// 对片段进行逼近
DividedArc2Bezier(ptfCenter, fRadius, fStartAngle, fEndAngle, fThreshold, nArcs);
return nArcs;
} // END OF Arc2Bezier_SplitNum
// 用三次Bezier逼近圆弧: 得到控制顶点数组
void Arc2Bezier_GetCtrlPts(IN const POINTF ptfCenter, // 圆心
IN const float fRadius, // 半径
IN const float fAngleStart, // 起始角度
IN const float fAngleEnd, // 终止角度 默认终止角度大于起始角度,且都在0-2*PI之间
IN const int nArcs, // 圆弧等分段数
OUT POINTF* ptfCtrlPts // 逼近结果 控制顶点数组 数组大小:nArcs * 3 + 1
)
{
int i = 0;
int j = 0;
int k = 0;
POINTF ptfCtrl[4];
float fAngleGap = (fAngleEnd-fAngleStart)/nArcs;
float fStart = fAngleStart, fEnd = fAngleStart + fAngleGap;
Get4CtrlPts(ptfCenter, fRadius, fStart, fEnd, ptfCtrl);
for (i=0; i<4; i++)
{
ptfCtrlPts[k++] = ptfCtrl[i];
} // end of for
// 可以在此处全部添加
for (i=1; i<nArcs; i++)
{
fStart = fEnd;
fEnd += fAngleGap;
Get4CtrlPts(ptfCenter, fRadius, fStart, fEnd, ptfCtrl);
for (j=1; j<4; j++)
{
ptfCtrlPts[k++] = ptfCtrl[j];
} // end of inner-for
} // end of outer-for
} // END OF Arc2Bezier_GetCtrlPts
// 用三次Bezier逼近圆弧片段
void DividedArc2Bezier(IN const POINTF ptfCenter, // 圆心
IN const float fRadius, // 半径
IN const float fAngleStart, // 起始角度
IN OUT float fAngleEnd, // 终止角度 默认终止角度大于起始角度,且都在0-2*PI之间
IN const float fThreshold, // 误差阈值
//IN OUT POINTF * ptfCtrlPts, // 单段逼近结果 4个控制顶点
//OUT CPtrArray * pArrBezier, // 逼近结果 控制顶点数组
IN OUT int & nArcs // 圆等分段数
)
{
// 算法步骤:
//1, 三分圆,或四分圆
//2, 求逼近的Bezier曲线
// 求最佳逼近Bezier曲线,或者用固定的方式求出Bezier曲线
//3, 求最大逼近误差
//4, 判断最大逼近误差是否满足误差要求
// 分段进行逼近
//2, 求逼近的Bezier曲线
// 求最佳逼近Bezier曲线,或者用固定的方式求出Bezier曲线
// 暂时先用中点定中间两个控制顶点.
POINTF ptfCtrl[4];
// 在这里,可以选择各种实现方式
Get4CtrlPts(ptfCenter, fRadius, fAngleStart, fAngleEnd, ptfCtrl);
//3, 求最大逼近误差
float fErr = GetMaxDis(ptfCenter, fRadius, ptfCtrl );
//4, 判断最大逼近误差是否满足误差要求
if (fErr <= fThreshold)
{
return;
}
else
{
if (fErr > 2.f* fThreshold)
{
nArcs *= 2;
fAngleEnd = fAngleStart + (fAngleEnd - fAngleStart) /2;
}
else
{
fAngleEnd = fAngleStart + nArcs*(fAngleEnd - fAngleStart) /(nArcs+1);
nArcs++;
}
DividedArc2Bezier(ptfCenter, fRadius, fAngleStart, fAngleEnd, fThreshold, nArcs);
}
} // END OF DividedArc2Bezier
// 得到逼近一段圆弧的Bezier曲线
void Get4CtrlPts(
IN const POINTF ptfCenter, // 圆心
IN const float fRadius, // 半径
IN const float fAngleStart, // 起始角度
IN const float fAngleEnd, // 终止角度
OUT POINTF * ptfCtrl // 输出: 四个控制顶点
)
{
//////////////////////////////////////////////////////////////////////////
// 圆心O:ptfCenter
// 弧端点: A
// B
// A, B处切线的交点:Q
// OQ方向:A+B-2*O再归一化
// OQ长度:|OQ| = fRadius / cos a
// 其中a = PI / nArcs = (fAngleEnd - fAngleStart)/2
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// 1,求A,B
// 2,求Q
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
float a = (fAngleEnd - fAngleStart)*0.5f; //PI / nArcs;
// 1,求A,B
POINTF A, B, Q;
A.x = ptfCenter.x + fRadius * cos(fAngleStart);
A.y = ptfCenter.y - fRadius * sin(fAngleStart);
B.x = ptfCenter.x + fRadius * cos(fAngleEnd);
B.y = ptfCenter.y - fRadius * sin(fAngleEnd);
// 2,求Q
// OQ 长度
float fDisOQ = fRadius / cos(a);
// OQ 方向
POINTF fV;
fV.x = A.x + B.x - ptfCenter.x - ptfCenter.x;
fV.y = A.y + B.y - ptfCenter.y - ptfCenter.y;
//// 注意到A点的坐标值,故可简化为
//fV.x = fRadius + B.x - ptfCenter.x;
//fV.y = B.y - ptfCenter.y;
float fDisTemp = sqrtf( fV.x*fV.x + fV.y*fV.y );
Q.x = ptfCenter.x + fDisOQ * fV.x / fDisTemp;
Q.y = ptfCenter.y + fDisOQ * fV.y / fDisTemp;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// 求控制顶点
// 说明:
// ptfCtrl[0] 就是A
// ptfCtrl[1] 就是(A+Q)/2
// ptfCtrl[2] 就是(B+Q)/2
// ptfCtrl[3] 就是B
//////////////////////////////////////////////////////////////////////////
ptfCtrl[0] = A;
ptfCtrl[1].x = (A.x+Q.x)*0.5f;
ptfCtrl[1].y = (A.y+Q.y)*0.5f;
ptfCtrl[2].x = (B.x+Q.x)*0.5f;
ptfCtrl[2].y = (B.y+Q.y)*0.5f;
ptfCtrl[3] = B;
} // END OF Get4CtrlPts
// 计算最大误差
float GetMaxDis(IN POINTF ptfCenter, // 圆心
IN float fRadius, //半径
IN POINTF * ptfCtrl // Bezier控制顶点
)
{
float fMaxDis = 0.0f;
// 求Bezier中点到圆心的距离 与圆半径之差
POINTF ptfMid;
ptfMid.x = (ptfCtrl[0].x + 3*ptfCtrl[1].x + 3*ptfCtrl[2].x + ptfCtrl[3].x ) * 0.125f;
ptfMid.y = (ptfCtrl[0].y + 3*ptfCtrl[1].y + 3*ptfCtrl[2].y + ptfCtrl[3].y ) * 0.125f;
fMaxDis = abs( sqrtf( (ptfMid.x - ptfCenter.x) * (ptfMid.x - ptfCenter.x) + (ptfMid.y - ptfCenter.y)*(ptfMid.y - ptfCenter.y) )
- fRadius);
return fMaxDis;
} // END OF GetMaxDis
// 释放Bezier数组
void ReleaseBezierArr()
{
} // END OF ReleaseBezierArr
- 1
- 2
- 3
前往页