在 C 中实现椭圆方程的正确方法是什么?
What's the correct way to implement the ellipse equation in C?
我正在尝试在由 MCU 驱动的绘图仪上渲染椭圆,因此可使用的内存不足,并且首选整数算法。
我有这个等式
并且我尝试通过以下方式在 C 中实现它:
y = sqrt( (b*b) * ( 1 - ( (x*x) / (a*a) )));
其中 y
、b
、x
和 a
是整数值,但结果是错误的。
Q1 这是椭圆方程的正确实现吗?
Q2 还有其他方法吗?
执行正确。我将 y、b、x 和 a 设置为双精度值,这修复了错误的输出。
画椭圆(用线勾勒)最好是参数方程
这里轴对齐的椭圆:
x=x0+a*cos(t);
y=y0+b*sin(t);
其中:
(x0,y0)
是椭圆中心
a,b
是半轴
t
是 angular 参数 t=<0,2*M_PI>
- 因此形成一个循环,其中
t
以足够小的步长完成整个循环
- 在 t
的每个步骤中计算 (x,y)
每一步 - interpolate/render 行(从最后一点到新点)
因为您的 x,y,x0,y0,a,b
是整数,请将它们转换为 float/double 或为 cos[],sin[]
创建整数 table 例如:
int tcos[360],tsin[360];
其中 tcos[i]=float(1000.0*cos(float(i)*M_PI/180.0));
现在只使用整数,例如:
for (i=0;i<360;i++)
{
x=x0+(a*tcos(i))/1000;
y=y0+(b*tsin(i))/1000;
//...
}
如果您需要像素完美渲染或渲染填充椭圆
那么你需要使用不同的方法(与你的等式相同)
- 以像素步长循环一个轴
- 计算另一个轴坐标
- 渲染两者 pixels/or 填充行
例如轴对齐 (0,0)
中心椭圆:
for (x=-a;x<=a;x++)
{
y = sqrt( (b*b) - ( (x*x*b*b) / (a*a) )));
// render pixels: (x,+y) and (x,-y) or join them by line
}
如果你需要整数 sqrt
然后实现一个(而不是使用 math.h
)例如:
int bits(DWORD p)
{
DWORD m=0x80000000; int b=32;
for (;m;m>>=1,b--)
if (p>=m) break;
return b;
}
DWORD sqrt(const DWORD &x)
{
DWORD m,a;
m=(bits(x)>>1); // bits(x) just return position of MSB nonzero bit can use m=16; instead
if (m) m=1<<m; else m=1;
for (a=0;m;m>>=1) { a|=m; if (a*a>x) a^=m; }
return a;
}
其中 DWORD
是无符号 32 位 int 数据类型。
填充不需要sqrt
您可以改为遍历该区域并确定像素是否在内部:
for (y=-b;y<=b;y++)
for (x=-a;x<=a;x++)
if ( (y*y) <= ( (b*b) - ( (x*x*b*b) / (a*a) ) ) )
// render pixel: (x,y)
我正在尝试在由 MCU 驱动的绘图仪上渲染椭圆,因此可使用的内存不足,并且首选整数算法。
我有这个等式
并且我尝试通过以下方式在 C 中实现它:
y = sqrt( (b*b) * ( 1 - ( (x*x) / (a*a) )));
其中 y
、b
、x
和 a
是整数值,但结果是错误的。
Q1 这是椭圆方程的正确实现吗?
Q2 还有其他方法吗?
执行正确。我将 y、b、x 和 a 设置为双精度值,这修复了错误的输出。
画椭圆(用线勾勒)最好是参数方程
这里轴对齐的椭圆:
x=x0+a*cos(t); y=y0+b*sin(t);
其中:
(x0,y0)
是椭圆中心a,b
是半轴t
是 angular 参数t=<0,2*M_PI>
- 因此形成一个循环,其中
t
以足够小的步长完成整个循环 - 在 t 的每个步骤中计算
- interpolate/render 行(从最后一点到新点)
(x,y)
每一步- 因此形成一个循环,其中
因为您的
x,y,x0,y0,a,b
是整数,请将它们转换为 float/double 或为cos[],sin[]
创建整数 table 例如:int tcos[360],tsin[360];
其中
tcos[i]=float(1000.0*cos(float(i)*M_PI/180.0));
现在只使用整数,例如:for (i=0;i<360;i++) { x=x0+(a*tcos(i))/1000; y=y0+(b*tsin(i))/1000; //... }
如果您需要像素完美渲染或渲染填充椭圆
那么你需要使用不同的方法(与你的等式相同)
- 以像素步长循环一个轴
- 计算另一个轴坐标
- 渲染两者 pixels/or 填充行
例如轴对齐
(0,0)
中心椭圆:for (x=-a;x<=a;x++) { y = sqrt( (b*b) - ( (x*x*b*b) / (a*a) ))); // render pixels: (x,+y) and (x,-y) or join them by line }
如果你需要整数
sqrt
然后实现一个(而不是使用math.h
)例如:int bits(DWORD p) { DWORD m=0x80000000; int b=32; for (;m;m>>=1,b--) if (p>=m) break; return b; } DWORD sqrt(const DWORD &x) { DWORD m,a; m=(bits(x)>>1); // bits(x) just return position of MSB nonzero bit can use m=16; instead if (m) m=1<<m; else m=1; for (a=0;m;m>>=1) { a|=m; if (a*a>x) a^=m; } return a; }
其中
DWORD
是无符号 32 位 int 数据类型。填充不需要sqrt
您可以改为遍历该区域并确定像素是否在内部:
for (y=-b;y<=b;y++) for (x=-a;x<=a;x++) if ( (y*y) <= ( (b*b) - ( (x*x*b*b) / (a*a) ) ) ) // render pixel: (x,y)