移动物理对象以使其穿透的最小尺寸 Vec3 = 0

minimum-size Vec3 that move Physics object to make penetration = 0

提问人:cppBeginner 提问时间:5/21/2019 最后编辑:cppBeginner 更新时间:5/29/2019 访问量:351

问:

这是一个例子(见图):-

  • 2 个红色矩形是静态对象(即它不能移动)。
  • 蓝色球是动态物体。

到目前为止,我设法获得了所有具有穿透力的信息。让我们将其视为我们的输入:-

  • 为了解决和球之间的穿透问题,我可以通过 OR 移动球。AVec3(1,0,0)Vec3(0,2,0)
  • 为了解决和球之间的穿透问题,我可以通过 移动球。BVec3(0,1,0)

^ 我将其存储为 2D Vec3 数组问题 = {{Vec3{1,0,0},Vec3{0,2,0}},{Vec3{0,1,0}}}

如何找到物理对象(例如示例中的球)的最佳运动(最小尺寸)以尽可能减少穿透力?

此示例中的最佳解决方案是“移动球:size=1.414”。Vec3(1,1,0)

enter image description here

在实际情况下,情况可能更加复杂和丑陋,
例如,A&B(和其他物理对象)都可能试图将球推向几乎相反的方向。(下图)

enter image description here

^ 在某些场景中,2D 阵列问题可能缺少一些细节,但为了简单起见,让我们假设它准确地解释了所有信息。

C++ 代码

这是我的MCVE(coliru):-

#include<iostream>
#include <utility>
#include <vector>
#include <array>
#include <math.h>

using Vec3=std::array<float, 3>;
float dotProduct(Vec3 vec1,Vec3 vec2){
    return vec1[0]*vec2[0]+vec1[1]*vec2[1]+vec1[2]*vec2[2];
}
float size2(Vec3 vec1){
    return vec1[0]*vec1[0]+vec1[1]*vec1[1]+vec1[2]*vec1[2];
}
Vec3 mulFloat(Vec3 vec1,float m){
    return Vec3{vec1[0]*m,vec1[1]*m,vec1[2]*m};
}
Vec3 normalize(Vec3 vec1){
    return mulFloat(vec1,1/sqrt(size2(vec1)));
}

这是:-main()

int main() {
    std::vector<std::vector<Vec3>> problem;
    std::vector<Vec3> location1;
    location1.push_back(Vec3{0,2,0});
    location1.push_back(Vec3{1,0,0});
    problem.push_back(location1);
    std::vector<Vec3> location2;
    location2.push_back(Vec3{0,1,0});
    problem.push_back(location2);
    //^ INPUT
    //----- insert YOUR ALGORITHM here  ------
    Vec3 solution=Vec3{0,2,0};

    float totalResidual=0;
    for(auto& location : problem){
        float residualRemainMin=1000000;//numerical limit
        for(auto& orgResidual : location){
            Vec3 orgResidualNormalize=normalize(orgResidual);
            float orgResidualSize=sqrt(size2(orgResidual));
            float residualModifyBy=-dotProduct(orgResidualNormalize,solution);//#1
            //"residualModifyBy" is usually negative
            float remainResidual=std::max(0.0f,orgResidualSize+residualModifyBy);
            //^ "max" because it has no advantage to reduce residual to < 0
            residualRemainMin=std::min(residualRemainMin,remainResidual);
            //^ "min" because the "OR" word
        }
        totalResidual+=residualRemainMin;
    }
    std::cout<<"totalResidual="<<totalResidual;
    return 0;
}

代码中注意:残差减少 。
我对这个公式的推导来自这张图片:-
(#1)dotProduct(solution,normalize(orgResidual) )
enter image description here

旁注

我觉得对每个组合进行置换太慢了(我不确定如何进行)。
我还认为,一般的迭代方法太慢了,无法收敛。

我只是想要一些指导方针;我不介意只有描述而没有代码的答案。

编辑 (2019年6月29日)

更多测试用例:-

{{(0,3,0)},{(2,2,0)}}            ===> (1,3,0)       
{{(0,1,1),(10,0,0)},{(0,2,3)}}   ===> (0,2,3)  
{{(99,1,0),(99,-1,0),(100,0,0)}} ===> (99,1,0) OR (99,-1,0)    (equally good)
C++ 算法 -不可 知数学-优化 物理-引擎

评论

0赞 Timo 5/21/2019
澄清一下:我们在 2D 空间中,对吧?
0赞 cppBeginner 5/21/2019
@Timo 不幸的是,我们是3D的。
0赞 cppBeginner 5/21/2019
@Timo 如果你对2D有一些想法,我相信也很有可能将其应用于3D。XD的

答:

-1赞 MrSmith42 5/21/2019 #1

我认为在步骤中为您提供正确/最佳答案的实现将非常复杂。

我的方法是:

  1. 如果与任何物体都没有交集,则沿物体方向移动球(而不是向已经接触的物体方向移动) 转到 0;
  2. 检测与对象的所有非法交叉点
  3. 对于每个交叉点,计算您需要球移动的方向以消除交叉点(最简单的情况是垂直于物体表面的移动量乘以交叉点深度的大小)
  4. 沿 2 中计算的方向移动球。
  5. 转到 0

如果球的位置不再改变(或在有限次数的迭代后),则终止循环

在第一个示例中,这将在一次迭代中找到最佳解决方案。
在第二个示例中,球在每次迭代中都会缓慢向上移动,直到不再存在交点)

评论

0赞 cppBeginner 5/21/2019
您的解决方案可能会返回 Vec3(0,2,0)。?
0赞 MrSmith42 5/21/2019
在某些情况下,可以,但不是在问题中提到的起始位置(示例 1)。在第一次迭代中,我的方法是将球向右移动 1 米,向顶部移动 1 米。在下一个循环中,它将检测到不可能进行任何改进。
0赞 cppBeginner 5/21/2019
你在哪里提到过?在“3.”?顺便说一句,我很伤心,不能保证最好的结果。
0赞 MrSmith42 5/21/2019
@cppBeginner:键是针对每个十字路口的。因此,球将向右和向顶移动。
2赞 cppBeginner 5/21/2019
最佳结果是(第一优先级)最小保持穿透力,(第二优先级)最小距离移动。我同意“该算法将始终将球连接到至少 2 个表面”。