让你的点是A
和B
,具有各自的坐标(xA,yA)
和(xB,yB)
。
两个点之间的线段的参数方程由下式给出:
A + t * (B-A) = (xA + t * (xB - xA), yA + t * (yB - yA))
其中t
取所有值在 0 和 1 之间。
您需要考虑沿线段的任一坐标的所有积分值。这将为您提供线和单元格一侧的交点,因此您可以将与此侧相邻的两个单元格标记为“已遍历”。
这是执行此操作的算法的概要,沿线对交叉点进行排序:
- 从单元格 A 开始
- 当你不在单元格 B 时:
- 找到您的线段与 x 轴的下一个交点
- 找到你的线段与y轴的下一个交点
- 取最近的一个,标记相邻的单元格,然后移动到它
有一些特殊情况,例如仅在一个角落接触的单元格。为了在前面的算法中特别对待那些,你可以识别出两个潜在的未来交叉点是相同的。
这是一个快速的 python 演示,我在其中缩放(乘以)t
参数方程的所有值,dx * dy
因此您不必除以dx
or dy
,除非您想要精确的交点坐标。
from math import floor
def sign(n):
return (n > 0) - (n < 0)
def raytrace(A, B):
""" Return all cells of the unit grid crossed by the line segment between
A and B.
"""
(xA, yA) = A
(xB, yB) = B
(dx, dy) = (xB - xA, yB - yA)
(sx, sy) = (sign(dx), sign(dy))
grid_A = (floor(A[0]), floor(A[1]))
grid_B = (floor(B[0]), floor(B[1]))
(x, y) = grid_A
traversed=[grid_A]
tIx = dy * (x + sx - xA) if dx != 0 else float("+inf")
tIy = dx * (y + sy - yA) if dy != 0 else float("+inf")
while (x,y) != grid_B:
# NB if tIx == tIy we increment both x and y
(movx, movy) = (tIx <= tIy, tIy <= tIx)
if movx:
# intersection is at (x + sx, yA + tIx / dx^2)
x += sx
tIx = dy * (x + sx - xA)
if movy:
# intersection is at (xA + tIy / dy^2, y + sy)
y += sy
tIy = dx * (y + sy - yA)
traversed.append( (x,y) )
return traversed
如果您的单元格宽度是w
并且具有坐标的单元格0, 0
从(x0, y0)
(即[x0 , x0 + w] * [y0, y0 + w]
)开始,则在调用函数时对其进行规范化,即代替
raytrace( (1,1.5) , (5,2.5) )
利用
raytrace( ((1 - x0) / w, (1.5 - y0) / w) , ((4 - x0) / w, (1.5 - y0) / w) )