提问人:cppBeginner 提问时间:5/21/2019 最后编辑:cppBeginner 更新时间:5/29/2019 访问量:351
移动物理对象以使其穿透的最小尺寸 Vec3 = 0
minimum-size Vec3 that move Physics object to make penetration = 0
问:
这是一个例子(见图):-
- 2 个红色矩形是静态对象(即它不能移动)。
- 蓝色球是动态物体。
到目前为止,我设法获得了所有具有穿透力的信息。让我们将其视为我们的输入:-
- 为了解决和球之间的穿透问题,我可以通过 OR 移动球。
A
Vec3(1,0,0)
Vec3(0,2,0)
- 为了解决和球之间的穿透问题,我可以通过 移动球。
B
Vec3(0,1,0)
^ 我将其存储为 2D Vec3 数组问题 = {{Vec3{1,0,0},Vec3{0,2,0}},{Vec3{0,1,0}}}
。
如何找到物理对象(例如示例中的球)的最佳运动(最小尺寸)以尽可能减少穿透力?
此示例中的最佳解决方案是“移动球:size=1.414”。Vec3(1,1,0)
在实际情况下,情况可能更加复杂和丑陋,
例如,A&B(和其他物理对象)都可能试图将球推向几乎相反的方向。(下图)
^ 在某些场景中,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) )
旁注
我觉得对每个组合进行置换太慢了(我不确定如何进行)。
我还认为,一般的迭代方法太慢了,无法收敛。
我只是想要一些指导方针;我不介意只有描述而没有代码的答案。
编辑 (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)
答:
-1赞
MrSmith42
5/21/2019
#1
我认为在步骤中为您提供正确/最佳答案的实现将非常复杂。
我的方法是:
- 如果与任何物体都没有交集,则沿物体方向移动球(而不是向已经接触的物体方向移动) 转到 0;
- 检测与对象的所有非法交叉点
- 对于每个交叉点,计算您需要球移动的方向以消除交叉点(最简单的情况是垂直于物体表面的移动量乘以交叉点深度的大小)
- 沿 2 中计算的方向移动球。
- 转到 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 个表面”。
评论