有谁知道具有以下描述的多边界框包含检测算法(或实现参考):
- 让我们收集 Axis Aligned Bounding Boxes,其中一些可能相交
- 和一个简单的 3D 形状,例如一个球体(或者它可以是另一个 AABB)。
- 我需要能够检测形状是否完全包含在 AABB-s 中的算法。换句话说,球体的任何部分都不在 AABB-s 之外。
问题是:很容易测试单个 AABB 中的包容性,但是存在形状可能在多个 AABB-s 之间分割的情况,甚至可以与多个 AABB-s 相交但球体的某些部分的情况在外面。
有谁知道具有以下描述的多边界框包含检测算法(或实现参考):
问题是:很容易测试单个 AABB 中的包容性,但是存在形状可能在多个 AABB-s 之间分割的情况,甚至可以与多个 AABB-s 相交但球体的某些部分的情况在外面。
IMO,您可以通过扫描平面方法来做到这一点。
按顶部和底部“应用程序”(z 坐标)对所有 AABB 进行排序。现在考虑一个水平面,它从一个面到另一个面向下移动,每次更新一个活动列表(即那些与平面相交的框)。一个框在其顶面进入列表并在其底面离开它。
平面的场景部分将由一组矩形和可能的圆形组成。在每一步,圆都需要完全包含在矩形的联合中。
请注意,您还需要在赤道附近的平面上停下来(这不会修改活动列表),因为球体是那里的“最大”。
这样做,您将最初的问题归结为一组包含矩形和圆形的二维包含子问题。
遵循相同的原则,您可以通过扫描线技术解决后者,按矩形顶部/底部的纵坐标对矩形进行排序并移动活动列表。在每一步中,iso-y 线的部分定义了一组线段,每个矩形一个,可能一个圆形,并且通过对 x 上的边界进行排序很容易证明包含。
换句话说,通过 3D 扫描过程,您可以分解棱柱板中的空间并构建与球体的交叉点。通过 2D 扫描过程,您可以将平面分解为矩形板并构建与截面盘的交叉点。
在代数上,这个问题可以表示为对实数的约束满足问题。点(x,y,z)
在具有中心坐标(cx,cy,cz)
和半径的圆内的条件r
是:
C := (x-cx)^2 + (y-cy)^2 + (z-cz)^2 - r^2 <= 0
点在 AABB 内的条件是:
B := x0 <= x /\ x <= x1 /\ y0 <= x /\ y <= y1 /\ z0 <= z /\ z <= z1
其中/\
表示“和”并且x0, x1, ..., z1
是实数。
现在给定一个圆圈和几个边界框,问题是约束列表是否
C /\ !(B1 \/ ... \/ Bn)
可以满足。如果是,则在球体内有一个点,但在任何 AABB 内都没有。由于只有三个变量x,y,z
和最多 2 个度数的多项式,现有算法/库可以有效地解决这个问题。(例如Z3,请参阅此介绍)。
受 Yves 递归扫描平面算法思想的启发,这里有一个更精细的版本,用于尝试在球体内找到一个未被任何给定框覆盖的点。
首先,我们必须找到 z 坐标的所有值,当沿 z 轴移动时,相应平面中的完全覆盖范围会发生变化。这可能发生在
一旦这些 z 值被收集、排序并限制在球体的 z 范围内,我们就会将球体的 z 范围划分为多个区间。我们在每个 z 区间(例如中心)内选择一个值来检查相应平面中的覆盖范围。每个 2D 切口都可以类似于 3D 问题来解决 - 从而将覆盖检查减少到一组 1D 问题。在一维情况下,我们有一个区间而不是球体或圆,我们也有区间而不是盒子或矩形。因此,球体对盒子的覆盖问题被简化为一个区间对一组区间的一组平凡的覆盖问题。
main 函数的实现可能如下所示:
# if the n-dimensional sphere is not fully covered by the boxes
# find a point inside the sphere but outside the boxes
# by a recursive sweep-plane algorithm.
# center: n-dimensional point
# radius: real value
# boxes: list of n-dimensional boxes (each box is a list of n intervals)
def getUncoveredSphereWitness(center, radius, boxes):
sphereLimitsN = [center[-1]-radius, center[-1]+radius]
if len(center) == 1:
# 1D case
witness = getUncoveredIntervalWitness(sphereLimitsN, [box[0] for box in boxes])
return [witness] if witness is not None else None
boxLimitsN = sum([b[-1] for b in boxes], [])
cutLimitsN = getCutLimitsN_boxes(center, radius, boxes)
limitsN = list(set(sphereLimitsN + boxLimitsN + cutLimitsN))
limitsN.sort()
# get centers of relevant intervals
coordNValsToCheck = []
for b in limitsN:
if b > sphereLimitsN[1]: break
if b > sphereLimitsN[0]:
coordNValsToCheck.append((bPrev+b)/2.)
bPrev = b
for z in coordNValsToCheck:
# reduce to a problem of with 1 dimension less
centerN1, radiusN1 = cutSphereN(center, radius, z)
boxesN1 = cutBoxesN(boxes, z)
witness = getUncoveredSphereWitness(centerN1, radiusN1, boxesN1)
if witness is not None:
return witness+[z] # lift witness to full dimension by appending coordinate
return None