计算机图形学--画线(齐)

计算机图形学-DDA Bresenham算法 中点画线法

DDA

DDA是数字微分分析式(Digital Differential Analyzer)的缩写。设直线之起点为(x1,y1),终点为(x2,y2),则斜率m为:
m=(y_2-y_1)/(x_2-x_1 )=dy/dx
直线中的每一点坐标都可以由前一点坐标变化一个增量(Dx, Dy)而得到,即表示为递归式:
xi+1=xi+Dx
yi+1=yi+Dy
并有关系: Dy = m · Dx
递归式的初值为直线的起点(x1,y1),这样,就可以用加法来生成一条直线。具体方法是:
在这里插入图片描述
按照直线从(x1, y1)到(x2, y2)的方向不同,分为8个象限(图1)。对于方向在第1a象限内的直线而言,Dx = 1,Dy = m。对于方向在第1b象限内的直线而言,取值Dy = 1,Dx = 1/m。各象限中直线生成时Dx,Dy的取值列在表1之中。
在这里插入图片描述
当|dx|>|dy|时
|Dx|=1, |Dy|=m;
否则:
Dx=1/m, |Dy|=1;
Dx,Dy的符号与dx,dy的符号相同。

def DDALine(x0, y0, x1, y1):  # 数值微分法画直线
    x0 = round(x0)  # 对坐标值取整
    x1 = round(x1)
    y0 = round(y0)
    y1 = round(y1)
    dx = x1 - x0
    dy = y1 - y0
    if dx == 0:  # 斜率不存在
        for y in range(y0, y1 + 1):  # 由于range函数取值不包含y1,因此要加1
            DrawPixel(x0, y)
        return
    k = dy / dx
    if -1 <= k <= 1:  # -1<=k<=1
        y = y0
        for x in range(x0, x1 + 1):  # 对变量x取整
            DrawPixel(x, round(y))
            y = y + k
    else:
        x = x0
        if k > 1:  # k>1
            for y in range(y0, y1 + 1):  # 对变量y取整,xy调换顺序
                DrawPixel(int(x + 0.5), y)
                x = x + 1 / k
        else:
            y = y0  # k<-1
            while y >= y1:  # 让y递减,x从小到大的顺序画线
                DrawPixel(round(x), y)
                y = y - 1
                x = x - 1 / k

Bresenham算法

这个算法由Bresenham在1965年提出。设直线从起点(x1, y1)到终点(x2, y2)。直线可表示为方程y=mx+b。其中
b = y1 - m * x1
m=(y_2-y_1)/(x_2-x_1 )=dy/dx
我们的讨论先将直线方向限于1a象限(图2)。在这种情况下,当直线光栅化时,x每次都增加1个单元,即xi+1=xi+1;而y的相应增加应当小于1。为了光栅化,yi+1只可能选择如下两种位置之一(图2)。
在这里插入图片描述
图2 中,yi+1的位置选择yi+1=yi 或者 yi+1=yi+1。选择的原则是看精确值y与yi及yi+1的距离d1及d2的大小而定。计算式为:
y = m(xi+1)+b (1)
d1 = y-yi (2)
d2 = yi+1-y (3)
如果d1-d2>0,则yi+1=yi+1,否则yi+1=yi。因此算法的关键在于简便地求出d1-d2的符号。将式(1),(2),(3)代入d1-d2,得
d1-d2=2y-2yi-1=2m(xi+1)-2yi+2b-1
用dx乘等式两边,并以Pi=dx(d1-d2)代入上述等式,得
Pi=2xidy-2yidx+2dy+dx(2b-1) (4)
d1-d2是我们用以判断符号的误差。由于在1a象限,dx总大于0,所以Pi仍旧可以用作判断符号的误差。Pi+1为:
Pi+1=Pi+2dy-2dx(yi+1-yi) (5)
误差的初值P1,可将x1, y1,和b代入式(4)中的xi, yi而得到:
P1=2dy-dx (6)
Bresenham算法的优点是:
① 不用浮点数,只用整数;
② 只做整数加减法和乘2运算,而乘2运算可以用硬件移位实现。
③ Bresenham算法速度很快,并适于用硬件实现。

# def Bresenhamline(x0, y0, x1, y1):
#     dx = x1 - x0
#     dy = y1 - y0
#     x = round(x0)
#     y = round(y0)
#     if dx == 0:  # 当直线斜率不存在
#         for y in range(round(y0), round(y1)):
#             DrawPixel(x, y)
#         return
#     k = dy / dx
#     e = -0.5
#     if 0 <= dy <= dx:  # 0<=k<=1
#         for i in range(0, dx):
#             DrawPixel(x, y)
#             x = x + 1
#             e = e + k
#             if e >= 0:
#                 y = y + 1
#                 e = e - 1
#     elif 0 <= dx < dy:  # k>1
#         for i in range(0, dy):
#             DrawPixel(x, y)
#             y = y + 1
#             e = e + 1 / k
#             if e >= 0:
#                 x = x + 1
#                 e = e - 1
#     elif dy <= 0 and dx > 0 and abs(dy) <= dx:  # -1<=k<0
#         e = 0.5
#         for i in range(0, dx):
#             DrawPixel(x, y)
#             x = x + 1
#             e = e + k
#             if e <= 0:
#                 y = y - 1
#                 e = e + 1
#     else:  # k<-1
#         e = 0.5
#         for i in range(0, -dy):
#             DrawPixel(x, y)
#             y = y - 1
#             e = e + 1 / k
#             if e <= 0:
#                 x = x + 1
#                 e = e + 1


def IntegerBresenhamline(x0, y0, x1, y1):
    x0 = round(x0)  # 对坐标值取整
    x1 = round(x1)
    y0 = round(y0)
    y1 = round(y1)
    if x1 < x0:
        tx = x1
        x1 = x0
        x0 = tx
        ty = y1
        y1 = y0
        y0 = ty
    dx = x1 - x0
    dy = y1 - y0
    x = x0
    y = y0
    if dx == 0:  # k不存在
        for y in range(y0, y1 + 1):
            DrawPixel(x0, y)
    elif 0 <= dy <= dx:  # 0<=k<=1
        e = -dx
        for i in range(0, dx + 1):
            DrawPixel(x, y)
            x = x + 1
            e = e + 2 * dy
            if e >= 0:
                y = y + 1
                e = e - 2 * dx
    elif 0 < dx < dy:  # k>1
        e = -dy
        for i in range(0, dy + 1):
            DrawPixel(x, y)
            y = y + 1
            e = e + 2 * dx
            if e >= 0:
                x = x + 1
                e = e - 2 * dy
    elif dy < 0 and dx > 0 and abs(dy) <= dx:  # -1<=-k<0
        e = dx
        for i in range(0, dx + 1):
            DrawPixel(x, y)
            x = x + 1
            e = e + 2 * dy
            if e <= 0:
                y = y - 1
                e = e + 2 * dx
    else:  # k<-1
        e = -dy
        for i in range(0, -dy):
            DrawPixel(x, y)
            y = y - 1
            e = e - 2 * dx
            if e <= 0:
                x = x + 1
                e = e - 2 * dy
中点画线法

假定直线斜率k在0~1之间,当前象素点为(xp,yp),则下一个象素点有两种可选择点(xp+1,yp)或P2(xp+1,yp+1)。若P1与P2的中点(xp+1,yp+0.5)称为M,Q为理想直线与x=xp+1垂线的交点。当M在Q的下方时,则取P2应为下一个象素点;当M在Q的上方时,则取P1为下一个象素点。这就是中点画线法的基本原理。
图3中点画线法每步迭代涉及的象素和中点示意图
在这里插入图片描述

def MidpointLine(x0, y0, x1, y1):  # 中点画线法
    x0 = round(x0)  # 对坐标值取整
    x1 = round(x1)
    y0 = round(y0)
    y1 = round(y1)
    if x1 < x0:  # 使画线的顺序是x从小到大的顺序
        tx = x1
        x1 = x0
        x0 = tx
        ty = y1
        y1 = y0
        y0 = ty
    a = y0 - y1
    b = x1 - x0
    d1 = 2 * a + b
    d2 = a - 2 * b
    x = x0
    y = y0
    DrawPixel(x, y)  # 画出初始点
    if a * b <= 0:  # k>0或k不存在
        if a + b >= 0:  # 0<=k<=1
            while x <= x1:
                if d1 < 0:  # 取中点上方的点
                    y = y + 1
                    d1 += 2*(a + b)
                else:  # 取中点下方
                    d1 += 2*a
                x = x + 1
                DrawPixel(x, y)
        else:  # k>1或k不存在
            while y <= y1:
                if d1 < 0:
                    d1 = d1 + 2*b
                else:
                    d1 = d1 + 2*(a + b)
                    x = x + 1
                y = y + 1
                DrawPixel(x, y)
    else:  # k<0
        if a <= b:  # -1<=k<0
            while x <= x1:
                if d2 >= 0:
                    y = y - 1
                    d2 += 2*(a - b)
                else:
                    d2 += 2*a
                x = x + 1
                DrawPixel(x, y)
        else:
            while y >= y1:  # k<-1
                if d2 > 0:
                    d2 = d2 - 2*b
                else:
                    d2 = d2 + 2*(a - b)
                    x = x + 1
                y = y - 1
                DrawPixel(x, y)
附加:需要的其他部分
import math
import turtle

turtle.hideturtle()  # 掩藏箭头
turtle.delay(0)


def DrawPixel(x, y, self=None):  # 画点
    turtle.up()
    turtle.goto(x, y)
    turtle.down()
    turtle.dot(3)


def DrawArrowhead():  # 画箭头
    turtle.pensize(1)
    turtle.color('black', 'black')
    turtle.begin_fill()
    turtle.left(120)
    turtle.forward(5)
    turtle.right(150)
    turtle.forward(10)
    turtle.right(120)
    turtle.forward(10)
    turtle.right(150)
    turtle.forward(5)
    turtle.end_fill()


def DrawCoordinates():  # 画坐标系
    turtle.up()
    turtle.goto(-200, 0)
    turtle.down()
    turtle.forward(400)
    DrawArrowhead()
    turtle.up()
    turtle.goto(0, -200)
    turtle.down()
    turtle.left(30)
    turtle.forward(400)
    DrawArrowhead()

感谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值