如何将Bresenham的线算法推广到浮点端点?


12

我正在尝试结合两件事。我正在写一个游戏,我需要确定与浮点端点在一条线上的网格正方形。

线槽网格

此外,我需要它包括它碰到的所有网格正方形(即不仅是布雷森纳姆的线条,而且是蓝色的线条):

布雷森纳姆vs全场

有人可以向我提供有关该操作方法的任何见解吗?显而易见的解决方案是使用朴素的行算法,但是有没有更优化的东西(更快)?


万一链接脱机,只需用谷歌搜索“更快的射线追踪体素遍历算法”
Gustavo Maciel 2014年

Answers:


9

您正在寻找网格遍历算法。本文给出了很好的实现;

这是在纸上找到的2D基本实现:

loop {
    if(tMaxX < tMaxY) {
        tMaxX= tMaxX + tDeltaX;
        X= X + stepX;
    } else {
        tMaxY= tMaxY + tDeltaY;
        Y= Y + stepY;
    }
    NextVoxel(X,Y);
}

纸上还有一个3D射线投射版本。

万一链接腐烂了,您会发现许多反射镜,其名称为:一种用于射线追踪的更快的体素遍历算法


好吧,尴尬。我想,我将答案转给您,并投票给ltjax。因为我根据您到该论文的链接解决了问题。
SmartK8

5

Blue的想法很好,但是实现起来有点笨拙。实际上,您无需sqrt即可轻松实现。现在,让我们假设您排除退化的情况(BeginX==EndX || BeginY==EndY),而只关注第一象限中的线方向,因此BeginX < EndX && BeginY < EndY。您还必须至少为另一个象限实现一个版本,但这与第一个象限的版本非常相似-您仅检查其他边缘。用C'ish伪代码:

int cx = floor(BeginX); // Begin/current cell coords
int cy = floor(BeginY);
int ex = floor(EndX); // End cell coords
int ey = floor(EndY);

// Delta or direction
double dx = EndX-BeginX;
double dy = EndY-BeginY;

while (cx < ex && cy < ey)
{
  // find intersection "time" in x dir
  float t0 = (ceil(BeginX)-BeginX)/dx;
  float t1 = (ceil(BeginY)-BeginY)/dy;

  visit_cell(cx, cy);

  if (t0 < t1) // cross x boundary first=?
  {
    ++cx;
    BeginX += t0*dx;
    BeginY += t0*dy;
  }
  else
  {
    ++cy;
    BeginX += t1*dx;
    BeginY += t1*dy;
  }
}

现在,对于其他象限,您只需更改++cxor ++cy和循环条件。如果将其用于冲突,则可能必须实现所有4个版本,否则可以通过适当地交换起点和终点来摆脱两个版本。


Gustavo Maciel提供的算法效率更高。它仅确定第一个Ts,然后将1垂直或水平加1,然后将Ts移位一个像元大小。但由于他没有将其转换为答案,因此我将其作为最近的答案。
SmartK8

3

您的假设不一定是要找到像元,而是要找到它在此网格上交叉的线。

例如,以您的图像为例,我们可以不突出显示单元格,而是突出显示它穿过的网格线:

红线

然后,这表明如果它越过网格线,则该线两侧的单元格即为已填充的单元格。

您可以使用相交算法,通过将点缩放为像素来确定您的浮点线是否与这些点相交。如果您的浮动坐标:像素比例为1.0:1,那么您将被排序,并且可以直接对其进行翻译。使用线段相交算法,您可以检查左下线(1,7)(2,7)是否与线(1.3,6.2)(6.51,2.9)相交。http://alienryderflex.com/intersect/

需要从c到C#的一些转换,但是您可以从该论文中了解。如果链接断开,我将在下面放置代码。

//  public domain function by Darel Rex Finley, 2006

//  Determines the intersection point of the line defined by points A and B with the
//  line defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line is undefined.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if the lines are parallel.
  if (Cy==Dy) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

如果只需要找出线段的时间(和位置)相交,则可以如下修改函数:

//  public domain function by Darel Rex Finley, 2006  

//  Determines the intersection point of the line segment defined by points A and B
//  with the line segment defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineSegmentIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line segment is zero-length.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  Fail if the segments share an end-point.
  if (Ax==Cx && Ay==Cy || Bx==Cx && By==Cy
  ||  Ax==Dx && Ay==Dy || Bx==Dx && By==Dy) {
    return NO; }

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if segment C-D doesn't cross line A-B.
  if (Cy<0. && Dy<0. || Cy>=0. && Dy>=0.) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  Fail if segment C-D crosses line A-B outside of segment A-B.
  if (ABpos<0. || ABpos>distAB) return NO;

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

嗨,网格遍历恰好是为了优化整个网格上数千条线的交点。这不能通过数千条直线相交来解决。我在游戏中有一张地图,上面有玩家无法跨越的地线。可能有成千上万种。我需要确定要为其计算昂贵路口的路口。为了确定这些,我只想计算出玩家运动线(或来自光源的光)中的那些线的交点。在您的情况下,我需要确定每个回合与〜256x256x2线段的交点。根本不会优化。
SmartK8

但是仍然感谢您的回答。从技术上讲,它可以正常工作。但是对我来说不可行。
SmartK8

3
float difX = end.x - start.x;
float difY = end.y - start.y;
float dist = abs(difX) + abs(difY);

float dx = difX / dist;
float dy = difY / dist;

for (int i = 0, int x, int y; i <= ceil(dist); i++) {
    x = floor(start.x + dx * i);
    y = floor(start.y + dy * i);
    draw(x,y);
}
return true;

JS演示:

伊姆古尔


1
由于浮点数值错误,这对我来说是失败的(循环将对下一个整数中的最小分数进行额外的迭代,这将使行结束点超出“结束”位置)。最简单的解决方法是首先将dist作为ceil计算出来,因此dx,dy除以循环的整数迭代次数(这意味着您可以在for循环中丢失ceil(dist))。
PeteB

0

今天,我遇到了同样的问题,在a鼠山上做成了一大堆意大利面,但最终得到了可行的结果:https : //github.com/SnpM/Pan-Line-Algorithm

从自述文件:

此算法的核心概念与Bresenham的相似,它在一个轴上增加1个单位,在另一轴上测试增加的值。分数使增加难度变得相当大,但是,必须添加很多比萨饼。例如,以5的斜率从X = .21到X = 1.21进行增加会产生复杂的问题(这些讨厌的数字之间的坐标模式)很难预测),但以1的斜率从1递增到2会带来一个简单的问题。整数之间的坐标模式很容易解决(仅垂直于增量轴的一条线)。为了得到一个简单的问题,将增量偏移为一个整数,小数部分的所有计算都单独进行。因此,与其在.21上开始递增,

自述文件比代码更好地解释了该解决方案。我正计划对其进行修改,以减少引起头痛的麻烦。

我知道这个问题要迟到一年了,但我希望其他寻求解决此问题的人能够得到这个答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.