Algorithm 如何使用Bresenham创建任意厚度的线?

Algorithm 如何使用Bresenham创建任意厚度的线?,algorithm,graphics,Algorithm,Graphics,我目前正在使用Bresenham的算法来绘制线条,但它们(当然)的厚度只有一个像素。我的问题是,画任意厚度的线最有效的方法是什么 我使用的语言是C。我认为最好的方法是画一个矩形,而不是一条线,因为一条有宽度的线是一个二维对象。尝试绘制一组平行线以避免过度绘制(减少写入带宽)和欠绘制(缺少像素)将非常复杂。从起点、终点和宽度计算矩形的角点并不难 因此,在下面的评论之后,这样做的过程是:- 创建一个矩形,长度与所需线条相同,宽度等于所需宽度,因此(0,0)到(宽度,长度) 使用二维变换将矩形角坐标旋

我目前正在使用Bresenham的算法来绘制线条,但它们(当然)的厚度只有一个像素。我的问题是,画任意厚度的线最有效的方法是什么


我使用的语言是C。

我认为最好的方法是画一个矩形,而不是一条线,因为一条有宽度的线是一个二维对象。尝试绘制一组平行线以避免过度绘制(减少写入带宽)和欠绘制(缺少像素)将非常复杂。从起点、终点和宽度计算矩形的角点并不难

因此,在下面的评论之后,这样做的过程是:-

  • 创建一个矩形,长度与所需线条相同,宽度等于所需宽度,因此(0,0)到(宽度,长度)
  • 使用二维变换将矩形角坐标旋转并平移到所需位置
  • 使用硬件加速渲染器(例如OpenGL四边形渲染器*)或软件光栅化器对旋转的矩形进行光栅化。它可以使用四栅格或一对三角形(例如左上角和右下角)进行渲染
  • 注*:如果您使用的是OpenGL,也可以同时执行步骤2。当然,使用OpenGL确实意味着理解OpenGL(大而复杂),而这个应用程序可能会使它在开发的后期阶段很难实现。

    以下是Bresenham绘制加厚线算法的一个修改版本

    您可能还想查看一个用于高质量和高性能2D图形软件渲染的库。请看一看,了解它的功能


    那篇关于墨菲修改后的布列森厄姆线条图的论文看起来很有用,但只有链接的答案在这里的价值有限,所以这里有一点小结


    有厚度的线是矩形。该算法使用外部Bresenham循环沿矩形的一条边单步走,而不实际绘制它。Bresenham环在外环的每一步绘制一条垂直线。通过将(x,y)坐标和误差项从外环传递到内环,它确保所有垂直线“同相”,确保矩形填充时没有间隙。每个像素集只设置一次。

    一些简单的使用方法:

  • 对于任意宽度n,其中n为奇数。对于绘制的任何点p,也绘制其上/下n/2的点(如果直线>45度角,则改为从一侧到另一侧绘制)。
    • 这不是一条正确的线条,更像是一支斜体笔,但速度非常快
  • 对于起点p(x,y),拾取点t0和b,使其以p为中心,但间隔n个像素。对于端点,执行相同的操作,得到t1 b1。从t0->t1,t1->b1,b1->t0,b0->t1画线。填充生成的矩形。
    • 这里的技巧是拾取点,使它们看起来与路径方向正交
  • 对于线上的每个点p,不要绘制点,而是绘制一个圆。
    • 这样做的好处是,无论方向如何,端点都“干净”
    • 除了第一个圆外,不需要渲染任何实心圆
    • 有点慢

  • 我假设您将绘制从一条边界线到另一条边界线的水平跨距,并使用Bresenham的方法(在单个循环中)计算每条边界线的x值

    我没试过


    端点可能需要注意,以免它们看起来奇怪地被切断。

    对于我的嵌入式热敏打印机应用程序,使用Bresenham的算法,线条太细了。我没有GL或任何花哨的东西。最后,我简单地减小了Y值,并在第一个值下画了更多的线。每增加一行厚度。从单色位图打印到热敏位图,实现效果非常快。

    使用另一个Bresenham循环,在矩形方向上修改原始行的起始和结束位置。 问题是要有效地找到正确的起点,在画下一行时不要画任何像素两次(或跳过一个像素)

    Github提供了可运行和测试的C代码

    这里是一个测试页面,其中包含一些由该代码创建的示例行。 黑色像素是算法的起点


    此链接底部的示例是javascript,但应该很容易适应C。这是一种相当简单的抗锯齿算法,用于绘制可变厚度的线。

    为了获得最佳精度,并且对于较厚的线也具有良好的性能,尤其是,您可以将线绘制为多边形。一些伪代码:

    draw_line(x1,y1,x2,y2,thickness)
      Point p[4];
      angle = atan2(y2-y1,x2-x1);
      p[0].x = x1 + thickness*cos(angle+PI/2);
      p[0].y = y1 + thickness*sin(angle+PI/2);
      p[1].x = x1 + thickness*cos(angle-PI/2);
      p[1].y = y1 + thickness*sin(angle-PI/2);
      p[2].x = x2 + thickness*cos(angle-PI/2);
      p[2].y = y2 + thickness*sin(angle-PI/2);
      p[3].x = x2 + thickness*cos(angle+PI/2);
      p[3].y = y2 + thickness*sin(angle+PI/2);
      draw_polygon(p,4)
    

    也可以选择在每个端点画一个圆。

    我不久前也遇到过同样的问题。
    基于此,我创建了一个Matlab参考实现,我想与大家分享一下

    我经常这样做是为了生成用于多孔介质模拟的纤维和球体的图像。我有一个非常简单的方法,使用一种非常标准的图像分析技术,称为“距离变换”。这需要访问一些图像分析软件包。我将Python与Scipy结合使用,因此这没有问题。以下是将随机分布的点转换为球体的演示:

    import scipy as sp
    import scipy.ndimage as spim
    
    im1 = sp.rand(100, 100) < 0.995  # Create random points in space
    dt = spim.distance_transform_edt(im1)
    im2 = dt < 5  # To create sphere with a radius of 5
    
    将scipy作为sp导入
    将scipy.ndimage作为spim导入
    im1=sp.rand(100100)<0.995#在空间中创建随机点
    dt=spim.距离\u变换\u edt(im1)
    im2=dt<5#创建半径为5的球体
    

    就这样!对于非常大的图像,距离变换可能会很慢,但是有一种有效的版本。例如,ImageJ有一个并行的。显然,要创建厚纤维,只需创建薄纤维的图像,然后应用上面的步骤2和步骤3。

    最简单的创建方法
    0 1 0
    1 1 1
    0 1 0
    
    def drawline(x1,y1,x2,y2,**kwargs):  
        if kwargs.get('thickness')==None:
            thickness=1
        else:
            thickness=kwargs['thickness']
        if kwargs.get('roundcap')==None:
            roundcap=False
        else:
            roundcap=True
        angle = np.arctan2(y2-y1,x2-x1)
        xx = np.zeros(4)
        yy = np.zeros(4)
        xx[0] = np.round(x1 + thickness*np.cos(angle+np.pi/2))
        yy[0] = np.round(y1 + thickness*np.sin(angle+np.pi/2))
        xx[1] = np.round(x1 + thickness*np.cos(angle-np.pi/2))
        yy[1] = np.round(y1 + thickness*np.sin(angle-np.pi/2))
        xx[2] = np.round(x2 + thickness*np.cos(angle-np.pi/2))
        yy[2] = np.round(y2 + thickness*np.sin(angle-np.pi/2))
        xx[3] = np.round(x2 + thickness*np.cos(angle+np.pi/2))
        yy[3] = np.round(y2 + thickness*np.sin(angle+np.pi/2))
        u,v=polygon(xx,yy)    
        if roundcap:
            temp1x, temp1y = circle(x1,y1,thickness)
            temp2x, temp2y = circle(x1,y1,thickness)
            u = np.append(u,temp1x,temp2x)
            v = np.append(v,temp1y,temp2y)
        return u,v
    
    drawline(10,10,50,50,thickness=3,roundcap=False)