Answers:
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;
}
}
现在,对于其他象限,您只需更改++cx
or ++cy
和循环条件。如果将其用于冲突,则可能必须实现所有4个版本,否则可以通过适当地交换起点和终点来摆脱两个版本。
您的假设不一定是要找到像元,而是要找到它在此网格上交叉的线。
例如,以您的图像为例,我们可以不突出显示单元格,而是突出显示它穿过的网格线:
然后,这表明如果它越过网格线,则该线两侧的单元格即为已填充的单元格。
您可以使用相交算法,通过将点缩放为像素来确定您的浮点线是否与这些点相交。如果您的浮动坐标:像素比例为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; }
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演示:
今天,我遇到了同样的问题,在a鼠山上做成了一大堆意大利面,但最终得到了可行的结果:https : //github.com/SnpM/Pan-Line-Algorithm。
从自述文件:
此算法的核心概念与Bresenham的相似,它在一个轴上增加1个单位,在另一轴上测试增加的值。分数使增加难度变得相当大,但是,必须添加很多比萨饼。例如,以5的斜率从X = .21到X = 1.21进行增加会产生复杂的问题(这些讨厌的数字之间的坐标模式)很难预测),但以1的斜率从1递增到2会带来一个简单的问题。整数之间的坐标模式很容易解决(仅垂直于增量轴的一条线)。为了得到一个简单的问题,将增量偏移为一个整数,小数部分的所有计算都单独进行。因此,与其在.21上开始递增,
自述文件比代码更好地解释了该解决方案。我正计划对其进行修改,以减少引起头痛的麻烦。
我知道这个问题要迟到一年了,但我希望其他寻求解决此问题的人能够得到这个答案。