【问题标题】:How do I check if a simplex contains the origin?如何检查单纯形是否包含原点?
【发布时间】:2014-03-16 03:53:01
【问题描述】:

我正在实现Gilbert-Johnson-Keerthi 算法,该算法计算两个对象是否相交(即碰撞)。

我的代码的入口点是hasCollided 函数,它接受两个点列表,如果它们相交则返回True。我相信我已经正确地实现了这篇论文 - 但是,我仍然需要实现 contains 函数。

contains 函数应确定单纯形是否包含原点。我不确定如何实现这一点。

如何有效地确定单纯形(点的集合)是否包含原点?


以下是我的实现:

type Simplex = Set (Vector Double)

hasCollided :: [Vector Double] -> [Vector Double] -> Bool
hasCollided points1 points2 = gjk points1 points2 simplex (scale (-1) direction) p
  where simplex   = insert p empty
        p         = support points1 points2 direction
        direction = fromList [1, 0, 0]

gjk :: [Vector Double] -> [Vector Double] -> Simplex -> Vector Double -> Vector Double -> Bool
gjk points1 points2 simplex direction lastAdded =
  if p <.> direction < 0 then False
  else
    if contains simplex' (fromList [0, 0, 0]) direction p then True
    else gjk points1 points2 simplex' direction' p
  where p          = support points1 points2 direction
        simplex'   = insert p simplex
        direction' = cross ab $ cross ao ab
        ab         = sub p lastAdded
        ao         = sub origin3D lastAdded

辅助函数是:

contains :: Simplex -> Vector Double -> Vector Double -> Vector Double -> Bool
contains simplex point direction lastAdded = undefined


support :: [Vector Double] -> [Vector Double] -> Vector Double -> Vector Double
support points1 points2 direction = sub p1 p2
  where p1 = getFarthestPoint points1 direction
        p2 = getFarthestPoint points2 direction

getFarthestPoint :: [Vector Double] -> Vector Double -> Vector Double
getFarthestPoint points direction = points !! index
  where index       = fromJust $ elemIndex (maximum dotproducts) dotproducts
        dotproducts = map (direction <.>) points

origin3D :: Vector Double
origin3D = fromList [0, 0, 0]

【问题讨论】:

  • 如果单纯形联合的凸包包含原点的单例集等于原始单纯形,那么原点必须在单纯形内。这是一种(昂贵的)方法。
  • @ThomasM.DuBuisson 我不太明白你所说的原始单纯形是什么意思?
  • 我的意思是:contains simp pnt = not $ simp == convexHull (union simp (singleton pnt))。我看到你的 contains 函数需要更多参数,所以也许我回答的问题与你的问题不同。
  • @ThomasM.DuBuisson 我认为是这样,但这可能是我的术语。通过包含,我的意思是原点(0, 0, 0) 位于四面体单纯形的体积内,如果这有意义吗?
  • 这真的不是关于编程的问题,是吗?更多a mathematics question。无论如何,这是检查单纯形是否包含任何给定点的方法:考虑通过用给定点替换其中一个顶点获得的单纯形。 Compute its volume 并记住结果的符号。对每个顶点执行此操作。如果所有符号都一致,则该点位于单纯形内。延伸阅读:barycentric coordinates

标签: math haskell physics linear-algebra collision


【解决方案1】:

我会采用不聪明的“让我们做一些线性代数来解决它”的方法。

单纯形中的每个点都是定义单纯形的点的convex combination。凸组合只是一个线性组合,其中系数都 >= 0 并且加起来为 1。

“单纯形是否包含原点”等同于询问是否存在等于零向量的单纯形点的凸组合。我们可以把它写成矩阵表达式吗?

假设我们正在处理由四个向量定义的单纯形,x1x4

我们将形成这些向量的任意线性组合,y = a1*x1 + a2*x2 + a3*x3 + a4*x4

我们希望找到a1a4,使得y 是零向量,a1 + a2 + a3 + a4 = 1

如果单纯形是三维欧几里得空间中的点,我将展示线性系统的样子;让向量xi 有分量xi1xi2xi3

[ x11  x21  x31  x41 ] [ a1 ]   [ 0 ]
[ x12  x22  x32  x42 ] [ a2 ] = [ 0 ]
[ x13  x23  x33  x43 ] [ a3 ]   [ 0 ]
[  1    1    1    1  ] [ a4 ]   [ 1 ]

这个线性系统的前三行对应y必须为零的约束,即我们可以通过x1x4的某种线性组合到达原点。最后一行对应于系数加起来为 1 的约束,这对于线性组合成为凸组合是必要的但不是充分的。矩阵方程没有表达的约束是ai &gt;= 0

选择您最喜欢的求解线性系统的方法并应用它。如果构成单纯形的向量是线性独立的,那么您将找不到任何解。如果线性系统有一个或多个解并且至少一个解具有所有ai &gt;= 0,则单纯形包含原点。

对于最后一步的算法,我没有任何现成的描述,确定解决方案集是否包含所有系数均为正的任何点。我建议在纸上写几个例子——我希望你能找到一个。

编辑:确定解集是否包括所有系数为正的点实际上与确定解空间与ai &gt;= 0 的交点定义的单纯形是否包括原点以外的任何点相同。

这意味着这种解决方法减少了问题

输入单形 是否包含原点?”

另一个单纯形(从输入单纯形派生)是否包含除原点以外的任何点?”

我认为这是一个将问题简化为另一个(希望更容易)问题的可爱示例。

【讨论】:

    【解决方案2】:

    (如果无法识别公式,请将主题更改为浅色)

    要严格确定该点是否属于单纯形,您只需要知道最大 d + 2 行列式 d * d 大小的符号。

    让:

    然后我们可以构造一个矩阵(j,k 索引意味着:排除j 行并从d 剩余行的每一行中减去从原点到点k 的向量;行中的所有左侧定义一个方面靠在j 顶点):

    上述矩阵的行列式是d! 乘以d-维的单纯形超体积,由公式中涉及的点构成(严格来说是平行面体的有向超体积,其边由矩阵行给出)。

    如果点在单纯形内,则以下所有等式都为真(匹配j0 点对相对于面的方向(定向超体积的符号)):

    但我们可以注意到,

    所以我们只能从比较左侧计算一个行列式 (?):

    并假设,该标志翻转为下一个js。

    因此,我们应该至少计算d*d 矩阵的2 行列式和最大d + 2(A1,1 和 Aj,0 sup> 对于 {1, 2, ..., d + 1} 中的所有 j)。如果符号在某个步骤上不匹配,则该点位于单纯形的当前面之外,因此根本不在单纯形之外。

    附加:

    如果某些右侧行列式为零,则该点与相应面的平面共面。

    【讨论】:

    • 你知道这个算法的由来吗?
    • @ASimmons 这是我自己的算法。如果考虑行列式的几何意义(即平行排列的定向超体积),它被认为是微不足道的。它与格拉姆矩阵的几何解释有关。可能出现的唯一困难是确定应该省略矩阵中的哪条线(偶数或奇数)以获得行列式的正确符号。
    • @ASimmons 我的错误:不是 Jacobian,而是 Gramian。
    • 感谢您的及时回复!
    【解决方案3】:

    您可以使用重心坐标! 检查this问题的答案以查看详细信息。

    判断一个点 $p = (p_1, \dots, p_n)$ 是否属于单纯形的想法是将其写在重心坐标 $p_\lambda = (\lambda_1, \dots, \lambda_n)$ 中尊重您要检查的单纯形。

    如果$p$的重心坐标满足

    $$ \lambda_i \ge 0,\ \sum \lambda_i \le 1, \i = 1, \dots, n+1 $$

    那么 $p$ 属于单纯形。否则不会。

    这减少了寻找点的重心坐标的问题。

    【讨论】:

      猜你喜欢
      • 2012-03-24
      • 1970-01-01
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多