中点椭圆算法:
(对于原点为(xc,yc)的椭圆,假定圆心在坐标原点(0,0)的像素位置,把计算出每个椭圆上像素点(x,y)加到屏幕位置上,即(xc+x,yc+y) )
椭圆与圆不同,不能八分只能四分。中点椭圆算法将分成两部分应用于第一象限。在斜率绝对值小于1的区域内在x方向取单位步长,在斜率绝对值大于1的区域内在y方向取单位步长。
椭圆方程为 :
(其中,a 为沿X轴方向的长半轴长度,b为沿Y 轴方向的短半轴长度,a,b 均为整数)
根据微积分知识,该椭圆上一点(x,y)处的法向量为:
其中,i 和 j 分别为沿 x 轴和 y 轴方向的单位向量。
由图可知,在上半部分椭圆弧中,法向量的 y 分量较大(即 ),而在下半部分椭圆弧中,法向量的 x 分量较大(即
)。
因此,若当前中点,法向量 的 y 分量比 x 分量大,即
而在下一个中点,不等号改变方向,则说明椭圆弧从上半部分转入下半部分。
(1). 椭圆上半部分:
假定选择了 处的像素 P ,现在必须在 P 的右邻像素 E 和右下方相邻像素 NE 中选取一个像素。
在中点算法中,只需计算 (M表示中点),并测试它的符号即可。
若 F(M)< 0 , 则 M 在椭圆内,椭圆弧距离点 E 更近 ,选择 E ; 若 F(M)> 0 , 则 M 在椭圆外,椭圆弧距离点 NE 更近 ,选择 NE .
如果选择的是 E ,应判断:
如果选择的是 NE ,应判断:
因为第一个像素是端点 , 可直接计算 J 的初始值
,以此决定是选 E 还是选 NE。
第一个中点在 处,有
(2). 椭圆下半部分:
假定选择了 处的像素 P ,现在必须在 P 的上邻像素 E 和左上方相邻像素 NE 中选取一个像素。
在中点算法中,只需计算 (M表示中点),并测试它的符号即可。
若 F(M)< 0 , 则 M 在椭圆内,椭圆弧距离点 E 更近 ,选择 E ; 若 F(M)> 0 , 则 M 在椭圆外,椭圆弧距离点 NE 更近 ,选择 NE .
如果选择的是 E ,应判断:
如果选择的是 NE ,应判断:
因为第一个像素是端点 , 可直接计算 J 的初始值
,以此决定是选 E 还是选 NE。
第一个中点在 处,有
核心算法:
(在实现中,为消除分数部分,将 F 乘以 4 重新定义 F,使每个常量和判断变量均乘以 4 ,但这并不影响判断变量 J 的符号,因此仍可作为中点检测标准。)
from PySide2.QtCore import *
class Ellipse:
def __init__(self, p, a, b):
self.p = p
self.a = a
self.b = b
def pointsEllipse(self):
points = []
if self.a == 0 or self.b == 0:
return points
x = 0
y = self.b
d = 4*self.b*self.b - 4*self.a*self.a*self.b + self.a*self.a
ps0 = []
ps1 = []
ps2 = []
ps3 = []
while self.a*self.a*(2*y - 1) >= 2*(self.b*self.b*(x + 1)):
ps0.append(QPoint(x, y))
ps1.append(QPoint(-x, y))
ps2.append(QPoint(x, -y))
ps3.append(QPoint(-x, -y))
if d < 0:
d = d + 4*self.b*self.b*(2*x + 3)
else:
d = d + 4*self.b*self.b*(2*x + 3) - 8*self.a*self.a*(y - 1)
y -= 1
x += 1
ps0.append(QPoint(x, y))
ps1.append(QPoint(-x, y))
ps2.append(QPoint(x, -y))
ps3.append(QPoint(-x, -y))
x = self.a
y = 0
d = 4*self.a*self.a - 4*self.a*self.b*self.b + self.b*self.b
while 2*(self.a*self.a*(y - 1)) < self.b*self.b*(2*x - 1):
ps0.append(QPoint(x, y))
ps1.append(QPoint(-x, y))
ps2.append(QPoint(x, -y))
ps3.append(QPoint(-x, -y))
if d < 0:
d += 4*self.a*self.a*(2*y + 3)
else:
d += 4*self.a*self.a*(2*y + 3) - 8*self.b*self.b*(x -1)
x -= 1
y += 1
for p1 in ps0:
p1.setX(p1.x()+self.p.x())
p1.setY(p1.y()+self.p.y())
points.append(p1)
for p1 in ps1:
p1.setX(p1.x() + self.p.x())
p1.setY(p1.y() + self.p.y())
points.append(p1)
for p1 in ps2:
p1.setX(p1.x() + self.p.x())
p1.setY(p1.y() + self.p.y())
points.append(p1)
for p1 in ps3:
p1.setX(p1.x() + self.p.x())
p1.setY(p1.y() + self.p.y())
points.append(p1)
return points
加上UI界面实现效果:
PS: 如需参考完整代码,请移步:https://download.csdn.net/download/qq_42185999/11834679 进行下载