【问题标题】:Fast Voxel Traversal 2D快速体素遍历 2D
【发布时间】:2017-05-08 12:21:30
【问题描述】:

我正在尝试遍历一条线所经过的所有单元格。我发现Fast Voxel Traversal Algorithm 似乎符合我的需求,但我目前发现它不准确。下面是一个带有红线和点作为算法给出的体素坐标的图。正如你所看到的,除了 (4, 7) 点之外它几乎是正确的,因为它应该是 (5,6)。我不确定我是否正确地实现了算法,所以我已经将它包含在 Python 中。所以我想我的问题是我的实现正确还是有更好的算法?

谢谢

def getVoxelTraversalPts(strPt, endPt, geom):
  Max_Delta = 1000000.0
  #origin
  x0 = geom[0]
  y0 = geom[3]

  (sX, sY) = (strPt[0], strPt[1])
  (eX, eY) = (endPt[0], endPt[1])
  dx = geom[1]
  dy = geom[5]

  sXIndex = ((sX - x0) / dx)
  sYIndex = ((sY - y0) / dy)
  eXIndex = ((eX - sXIndex) / dx) + sXIndex
  eYIndex = ((eY - sYIndex) / dy) + sYIndex

  deltaX = float(eXIndex - sXIndex)
  deltaXSign = 1 if deltaX > 0 else -1 if deltaX < 0 else 0
  stepX = deltaXSign

  tDeltaX = min((deltaXSign / deltaX), Max_Delta) if deltaXSign != 0 else Max_Delta
  maxX = tDeltaX * (1 - sXIndex + int(sXIndex)) if deltaXSign > 0 else tDeltaX * (sXIndex - int(sXIndex))

  deltaY = float(eYIndex - sYIndex)
  deltaYSign = 1 if deltaY > 0 else -1 if deltaY < 0 else 0
  stepY = deltaYSign

  tDeltaY = min(deltaYSign / deltaY, Max_Delta) if deltaYSign != 0 else Max_Delta
  maxY = tDeltaY * (1 - sYIndex + int(sYIndex)) if deltaYSign > 0 else tDeltaY * (sYIndex - int(sYIndex))

  x = sXIndex
  y = sYIndex

  ptsIndexes = []
  pt = [round(x), round(y)]
  ptsIndexes.append(pt)
  prevPt = pt
  while True:
    if maxX < maxY:
        maxX += tDeltaX
        x += deltaXSign
    else:
        maxY += tDeltaY
        y += deltaYSign

    pt = [round(x), round(y)]
    if pt != prevPt:
        #print pt
        ptsIndexes.append(pt)
        prevPt = pt

    if maxX > 1 and maxY > 1:
        break

  return (ptsIndexes)

【问题讨论】:

  • 你可以尝试在codereview.stackexchange.com上提问
  • 多年前我已经实现了这个算法并且效果很好。比较不容易比较,因为(似乎)您使用的是以整数为中心的体素。当我调试时,我记录了每个被触摸的单元格的输入参数和坐标。
  • @MBo:我在另一个问题上使用了你之前的回答来让我开始。感谢那。如果我正确地实现了它,并且看起来我做到了。算法会错过单元格。看起来它提供了很好的覆盖率
  • 能否在图片中标出进入单元格的位置并在日志中输出参数?给定案例的确切输入参数是什么?
  • 输入坐标的数据类型有哪些?您使用的是哪个 Python 版本?您可能会失去初始分区的精度...

标签: python algorithm math geometry raytracing


【解决方案1】:

您正在行走的体素从 0.0 开始,即第一个体素跨越的空间从 0.0 到 1.0,而不是您似乎假设的从 -0.5 到 0.5。换句话说,它们是用虚线标记的,而不是实线。

如果您希望体素成为您的方式,则必须修复初始 maxX 和 maxY 计算。

【讨论】:

  • @vinh 你好?有帮助吗?
【解决方案2】:

我想为了完整起见,我决定使用不同的算法。这里引用的dtb's answer on another question

这里是实现

def getIntersectPts(strPt, endPt, geom=[0,1,0,0,0,1]):
'''
    Find intersections pts for every half cell size
    ** cell size has only been tested with 1

    Returns cell coordinates that the line passes through
'''

x0 = geom[0]
y0 = geom[3]

(sX, sY) = (strPt[0], strPt[1])
(eX, eY) = (endPt[0], endPt[1])
xSpace = geom[1]
ySpace = geom[5]

sXIndex = ((sX - x0) / xSpace)
sYIndex = ((sY - y0) / ySpace)
eXIndex = ((eX - sXIndex) / xSpace) + sXIndex
eYIndex = ((eY - sYIndex) / ySpace) + sYIndex


dx = (eXIndex - sXIndex)
dy = (eYIndex - sYIndex)
xHeading = 1.0 if dx > 0 else -1.0 if dx < 0 else 0.0
yHeading = 1.0 if dy > 0 else -1.0 if dy < 0 else 0.0

xOffset = (1 - (math.modf(sXIndex)[0]))
yOffset = (1 - (math.modf(sYIndex)[0]))

ptsIndexes = []
x = sXIndex
y = sYIndex
pt = (x, y) #1st pt

if dx != 0:
    m = (float(dy) / float(dx))
    b = float(sY - sX * m )

dx = abs(int(dx))
dy = abs(int(dy))

if dx == 0:
    for h in range(0, dy + 1):
        pt = (x, y + (yHeading *h))
        ptsIndexes.append(pt)

    return ptsIndexes

#print("m {}, dx {}, dy {}, b {}, xdir {}, ydir {}".format(m, dx, dy, b, xHeading, yHeading))
#print("x {}, y {}, {} {}".format(sXIndex, sYIndex, eXIndex, eYIndex))

#snap to half a cell size so we can find intersections on cell boundaries
sXIdxSp = round(2.0 * sXIndex) / 2.0
sYIdxSp = round(2.0 * sYIndex) / 2.0
eXIdxSp = round(2.0 * eXIndex) / 2.0
eYIdxSp = round(2.0 * eYIndex) / 2.0
# ptsIndexes.append(pt)
prevPt = False
#advance half grid size
for w in range(0, dx * 4):
    x = xHeading * (w / 2.0) + sXIdxSp
    y = (x * m + b)
    if xHeading < 0:
        if x < eXIdxSp:
            break
    else:
        if x > eXIdxSp:
            break

    pt = (round(x), round(y)) #snapToGrid
    # print(w, x, y)

    if prevPt != pt:
        ptsIndexes.append(pt)
        prevPt = pt
#advance half grid size
for h in range(0, dy * 4):
    y = yHeading * (h / 2.0) + sYIdxSp
    x = ((y - b) / m)
    if yHeading < 0:
        if y < eYIdxSp:
            break
    else:
        if y > eYIdxSp:
            break
    pt = (round(x), round(y)) # snapToGrid
    # print(h, x, y)

    if prevPt != pt:
        ptsIndexes.append(pt)
        prevPt = pt

return set(ptsIndexes) #elminate duplicates

【讨论】:

  • 这并不能真正回答您的问题。您可能应该编辑您的原始问题以添加此信息,并使用适当的上下文。
【解决方案3】:

没有人有时间阅读您发布的论文并确定您是否正确实施了它。

不过,这里有个问题。您使用的算法 (a) 实际上是为了确定一条线通过的所有单元格,还是 (b) 在两点之间形成一条直线的体素近似?

我更熟悉执行 (b) 的 Bresenham's line algorithm。这是它的图片:

请注意,单元格的选择是“美观的”,但会省略线路通过的某些单元格。包括这些会使这条线“更丑”。

我怀疑你的体素线算法也发生了类似的事情。但是,查看您的数据和 Bresenham 图像提出了一个简单的解决方案。沿着发现的单元格走,但是,每当你必须做一个对角线步骤时,请考虑两个中间单元格。然后,您可以使用直线-矩形相交算法(请参阅here)来确定哪些候选单元格应该包含但未包含在内。

【讨论】:

  • 我更希望有人已经实施了算法并且可能“正确”地做到了,这样他们就可以帮助我修复地雷或至少确认我的结果。但是是的,我对布雷森纳姆的看法太简单了。我想我得再好好看看它了。
  • @Vinh:那个人偶然发现这个问题的几率似乎很低。也许您可以写信给论文的作者,询问他们是否有可以发送给您的实现?我并不是说 Bresenham 是一个更好的选择,只是说它可能相似并且存在相同的问题。
  • 鉴于本文的重点是描述加速光线追踪的算法(而不是画线),您可以确定它会沿路径访问 所有 体素.
猜你喜欢
  • 2015-02-09
  • 2014-09-16
  • 1970-01-01
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 1970-01-01
相关资源
最近更新 更多