中点椭圆算法----计算机图形学

中点椭圆算法:

(对于原点为(xc,yc)的椭圆,假定圆心在坐标原点(0,0)的像素位置,把计算出每个椭圆上像素点(x,y)加到屏幕位置上,即(xc+x,yc+y) )

 

椭圆与圆不同,不能八分只能四分。中点椭圆算法将分成两部分应用于第一象限。在斜率绝对值小于1的区域内在x方向取单位步长,在斜率绝对值大于1的区域内在y方向取单位步长。

    

椭圆方程为 :F(x,y)=b^{2}*x^{2}+a^{2}*y^{2}-a^{2}*b^{2}

(其中,a 为沿X轴方向的长半轴长度,b为沿Y 轴方向的短半轴长度,a,b 均为整数)

 

根据微积分知识,该椭圆上一点(x,y)处的法向量为:

                                                                       N(x,y)=\frac{\partial F}{\partial x}*i+\frac{\partial F}{\partial y}*j

                                                                                     =2*b^{2}*x*i + 2*a^{2}*y*j

其中,i 和 j 分别为沿  x 轴和 y 轴方向的单位向量。

由图可知,在上半部分椭圆弧中,法向量的 y 分量较大(即 2*b^{2}*x<2*a^{2}*y),而在下半部分椭圆弧中,法向量的 x 分量较大(即 2*b^{2}*x<2*a^{2}*y)。

 

因此,若当前中点,法向量 (2*b^{2}*(x_{p}+1) , 2*a^{2}*(y_{p}-1/2)) 的 y 分量比 x 分量大,即

                                                                                                           b^{2}*(x_{p}+1) < a^{2}*(y_{p}-1/2)

而在下一个中点,不等号改变方向,则说明椭圆弧从上半部分转入下半部分。

 

 

(1).   椭圆上半部分:

 

假定选择了 (x_{p},y_{p})  处的像素 P ,现在必须在 P 的右邻像素 E 和右下方相邻像素 NE 中选取一个像素。

在中点算法中,只需计算  F(M)=F(x_{p}+1,y_{p}-1/2)  (M表示中点),并测试它的符号即可。

若 F(M)< 0 , 则 M 在椭圆内,椭圆弧距离点 E 更近 ,选择 E ; 若 F(M)> 0 , 则 M 在椭圆外,椭圆弧距离点 NE 更近 ,选择 NE .

J_{i}=F(M) = F(x_{p}+1,y_{p}-1/2) =b^{2}*(x_{p}+1)^{2}+a^{2}*(y_{p}-1/2)^{2}-a^{2}*b^{2}

 

如果选择的是 E ,应判断:

J_{i+1}=F(Me) = F(x_{p}+2,y_{p}-1/2) =b^{2}*(x_{p}+2)^{2}+a^{2}*(y_{p}-1/2)^{2}-a^{2}*b^{2}

                                                               = F(x_{p}+1,y_{p}-1/2)+b^{2}(2*x_{p}+3)

                                                               = J_{i}+b^{2}(2*x_{p}+3)

如果选择的是 NE ,应判断:

J_{i+1}=F(Mne) = F(x_{p}+2,y_{p}-1/2) =b^{2}*(x_{p}+2)^{2}+a^{2}*(y_{p}-3/2)^{2}-a^{2}*b^{2}

                                                                 = F(x_{p}+1,y_{p}-1/2)+b^{2}(2*x_{p}+3)+a^{2}(-2*y_{p}+2)

                                                                 = J_{i}+b^{2}(2*x_{p}+3)+a^{2}(-2*y_{p}+2)

 

因为第一个像素是端点 (0,b) , 可直接计算 J 的初始值 J_{start} ,以此决定是选 E 还是选 NE。

第一个中点在 (0+1,b-1/2) 处,有

J_{start} =F(1,b-1/2)= b^{2}+a^{2}*(b-1/2)^{2}-a^{2}*b^{2}

                                                 = b^{2}+a^{2}*(-b+1/4)

                                          

 

 

(2).    椭圆下半部分:

 

 

假定选择了 (x_{p},y_{p})  处的像素 P ,现在必须在 P 的上邻像素 E 和左上方相邻像素 NE 中选取一个像素。

在中点算法中,只需计算  F(M)=F(x_{p}-1/2,y_{p}+1)  (M表示中点),并测试它的符号即可。

若 F(M)< 0 , 则 M 在椭圆内,椭圆弧距离点 E 更近 ,选择 E ; 若 F(M)> 0 , 则 M 在椭圆外,椭圆弧距离点 NE 更近 ,选择 NE .

J_{i}=F(M) = F(x_{p}-1/2,y_{p}+1) =b^{2}*(x_{p}-1/2)^{2}+a^{2}*(y_{p}+1)^{2}-a^{2}*b^{2}

 

如果选择的是 E ,应判断:

J_{i+1}=F(Me) = F(x_{p}-1/2,y_{p}+2) =b^{2}*(x_{p}-1/2)^{2}+a^{2}*(y_{p}+2)^{2}-a^{2}*b^{2}

                                                               = F(x_{p}-1/2,y_{p}+1)+a^{2}*(2*y_{p}+3)

                                                               = J_{i}+a^{2}*(2*y_{p}+3)

如果选择的是 NE ,应判断:

J_{i+1}=F(Mne) = F(x_{p}-3/2,y_{p}+2) = b^{2}*(x_{p}-3/2)^{2}+a^{2}*(y_{p}+2)^{2}-a^{2}*b^{2}

                                                                 = F(x_{p}-1/2,y_{p}+1)+a^{2}*(2*y_{p}+3)+b^{2}*(-2*x_{p}+2)

                                                                 = J_{i}+a^{2}*(2*y_{p}+3)+b^{2}*(-2*x_{p}+2)

 

    

因为第一个像素是端点 (a,0) , 可直接计算 J 的初始值 J_{start} ,以此决定是选 E 还是选 NE。

第一个中点在 (a-1/2,0+1) 处,有

J_{start} =F(a-1/2,1)= b^{2}*(a-1/2)^{2}+a^{2}-a^{2}*b^{2}

                                                 = a^{2}+b^{2}*(-a+1/4)

 

核心算法:

(在实现中,为消除分数部分,将 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   进行下载

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值