理解Bresenham算法

325 阅读1分钟

Bresenham算法

  • 算法用途:高效的进行直线或圆的栅格化

问题建模

  1. 给定起始点(x0,y0)(x_0,y_0)和终点(x1,y1)(x_1,y_1),且满足x1>x0x_1 > x_0,斜率为k=ΔyΔxk=\frac{\Delta{y}}{\Delta{x}}

  2. 使选择的像素,满足到直线y=mx+by=mx+b的垂直误差最小。

  3. 设当前的像素点为(xk,yk)(x_k,y_k),下一个候选点为:

    • 右邻居H(xk+1,yk)H(x_k+1, y_k)
    • 右上邻居V(xk+1,yk+1)V(x_k+1, y_k+1)
  4. 定义垂直距离偏差:

    • dlower=yrealyk=m(xk+1)+bykd_{lower}=y_{real} - y_k = m(x_k + 1) + b - y_k
    • dupper=(yk+1)yreal=yk+1m(xk+1)bd_{upper}=(y_k + 1)-y_{real} = y_k + 1 - m(x_k + 1) - b
  5. 选择依据:比较dlowerd_{lower}与d_{upper},选择较小一方为对应的点。

  6. 设定用于决策的变量:

    pk=Δx(dlowerdupper)=2yreal2yk1=2Δy(xk+1)2Δxb2ΔxykΔx=2Δyxk2Δxyk+(2Δy2ΔxbΔx)p_k=\Delta{x}(d_{lower}-d_{upper}) \\ = 2y_{real} - 2y_k - 1 \\ = 2\Delta{y}(x_k+1) - 2\Delta{x}b - 2\Delta{x}y_k - \Delta{x} \\ = 2\Delta{y}x_k - 2\Delta{x}y_k + (2\Delta{y}-2\Delta{x}b-\Delta{x})

    后面括号括起来的部分为常量。

  7. 为了消除浮点预算,构建递推关系,构建过程如下:

    pk+1pk=2Δyxk+12Δxyk+12Δyxk+2Δxyk=2Δy(xk+1)2Δyxk+2Δxyk2Δxyk+1=2Δy2Δx(yk+1yk)p_{k+1}-p_{k} = 2\Delta{y}x_{k+1}-2\Delta{x}y_{k+1}-2\Delta{y}x_k+2\Delta{x}y_k \\ = 2\Delta{y}(x_k + 1)- 2\Delta{y}x_k + 2\Delta{x}y_k-2\Delta{x}y_{k+1} \\ = 2\Delta{y} - 2\Delta{x}(y_{k+1}-y_{k})

    递推的关系长这样:pk+1=pk+2Δy2Δx(yk+1yk)p_{k+1} = p_{k} + 2\Delta{y} - 2\Delta{x}(y_{k+1}-y_{k})

    • pk>0p_{k} > 0时,yk+1=yk+1y_{k+1} = y_{k} + 1,则pk+1=pk+2Δy2Δxp_{k+1}=p_{k}+2\Delta{y}-2\Delta{x}
    • pk<0p_{k} < 0时,yk+1=yky_{k+1} = y_{k},则pk+1=pk+2Δyp_{k+1}=p_{k}+2\Delta{y}
  8. 递推的初始值: p0=2ΔyΔxp_0 = 2\Delta{y}-\Delta{x}

算法流程

根据上述建模过程,有如下流程:

  1. 计算Δx,Δy\Delta{x},\Delta{y}
  2. 比较Δx,Δy|\Delta{x}|,|\Delta{y}|,取大的那个为增长方向。以xx为增长方向为例。
  3. p0p_0出发,初始误差设为e=Δxe=-\Delta{x}
    每当x=x+1x = x+1, e=e+2Δye=e+2\Delta{y}。若e>0e>0,则y=y+1y=y+1e=e2Δxe=e-2\Delta{x}
  4. 重复1,2,3,直到到达p1p_1

代码实现

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

struct Point
{
    int x;
    int y;
};

vector<Point> BresenhamFilling(const Point& p1, const Point&p2)
{
    vector<Point> points;
    int dx = p2.x - p1.x;
    int dy = p2.y - p1.y;
    int ux = dx > 0 ? 1 : -1;
    int uy = dy > 0 ? 1 : -1;
    dx = abs(dx);
    dy = abs(dy);
    int x = p1.x;
    int y = p1.y;
    if (dx > dy) // 以x为增量
    {
        int e = -dx; 
        for (int i = 0; i < dx; ++i)
        {
            x += ux;
            e += 2 * dy;
            if (e >= 0)
            {
                y += uy;
                e -= 2 * dx;
            }
            if (x != p2.x && y != p2.y)
            {
                points.push_back({x, y});
            }
        }
    }
    else // 以y为增量
    {
        int e = -dy;
        for (int i = 0; i < dy; i++)
        {
            y += uy;
            e += 2 * dx;
            if (e >= 0)
            {
                x += ux;
                e -= 2 * dy;
            }
            if (x != p2.x || y != p2.y)
            {
                points.push_back({x, y});
            }
        }
    }
    return points;
}