如何在两点之间施加约束

时间:2019-04-16 18:18:06

标签: c++ simulation game-physics glm-math

我想在施加重力的同时在两点之间施加约束。我绘制的下图演示了点2的开始和结束位置,其中不包括中间时间步位置,并假设点1具有固定位置:

Two points separated by a fixed constraint

我有一个定义如下的点类:

class Point{
  glm::vec3 position;
  glm::vec3 op; // original position
  glm::vec3 velocity;
  float mass;
};

我可以使用以下命令定义两个点并找到两个点之间的原始长度:

Point p1;
p1.position = glm::vec3(0, 10, 0);
p1.op = p1.position;
p1.velocity = glm::vec3(0, 0, 0);
p1.mass = 1.0f;

Point p2;
p2.position = glm::vec3(10, 10, 0);
p2.op = p2.position;
p2.velocity = glm::vec3(0, 0, 0);
p2.mass = 1.0f;

float original_length_p1_p2 = glm::length(p2.op- p1.op);

我在点类内部有一个更新功能,该功能在特定的时间步内运行,应通过施加重力来更新点位置:

glm::vec3 gravity(0,-9.8,0);
...
void update(float dt){
  velocity += gravity * dt;
  position += velocity * dt;
}

这些点存储在向量内部,更新函数的调用如下:

std::vector<Point> myPoints;
...
for(int n = 0; n < myPoints.size(); n++){
  myPoints[n].update(dt);
}

现在,我希望能够在这两个点之间施加类似于弹簧的约束,该约束将像简单的类似于弹簧的摆一样摆动。我尝试将以下内容添加到上述for循环中:

void applyConstraint(Point &p1, Point &p2, float dt){
    float change = (glm::length(p1.position-p2.position) - glm::length(p1.op-p2.op)) / glm::length(p1.position-p2.position);
    p1.position -= 0.5 * (p1.position-p2.position) * change * dt;
    p2.position += 0.5 * (p1.position-p2.position) * change * dt;
}

但是尝试此操作时,p2不受限制地下降。我怎样才能确保p2与图像相似?

更新 applyConstraint:

void Scene::applyConstraint(Point &p1, Point &p2, float dt) {
    float change = (glm::length(p1.position - p2.position) - glm::length(p1.op - p2.op)) / glm::length(p1.position - p2.position);
    glm::vec3 force = 0.5f * (p1.position - p2.position) * change * dt;
    glm::vec3 accel1 = (-force / p1.mass) * dt;
    glm::vec3 accel2 = (force / p2.mass) * dt;
    p1.velocity += accel1 * dt;
    p2.velocity += accel2 * dt;
    p1.position += p1.velocity * dt;
    p2.position += p2.velocity * dt;
}

2 个答案:

答案 0 :(得分:1)

您的代码中存在三个问题。首先,对每个约束应用Euler积分,但是在每次迭代结束时应只应用一次。其次,点p1应该是固定的。第三,您没有在力计算中考虑质量。

要解决此问题,请在force结构中添加一个Point向量,并使用以下代码:

// Reset forces
p1.force = glm::vec3(0, 0, 0);
p2.force = glm::vec3(0, 0, 0);

// Add gravity
p1.force += gravity / p1.mass ;
p2.force += gravity / p2.mass ;

// Add spring forces
// To be put in applyConstraint, without dependency on dt
float k = 1 ;
glm::vec3 difference = p1.position - p2.position;
float current_length = glm::length(difference);
float original_length = glm::length(p2.op- p1.op);
float displacement = (current_length - original_length) / current_length;
p1.force -= k * displacement * difference ;
p2.force += k * displacement * difference ;

// Euler integration
p1.velocity += p1.force / p1.mass * dt ;
p2.velocity += p2.force / p2.mass * dt ;
//p1.position += p1.velocity * dt ; // This point is an anchor
p2.position += p2.velocity * dt ;

更改k以调整弹簧的弹性。如果您知道想要的行为,请使用this website上给出的公式对其进行计算。

您还可以使用p2.force -= c * p2.velocity向系统中添加阻尼,其中cdamping ratio

答案 1 :(得分:0)

您没有正确计算加速度。力= m * a。乘以dt可让您通过Euler积分获得速度。更好的集成方法将有助于提高准确性。我想你只是想要一个春天。摆通常意味着您想要一个固定的距离约束,但您认为您的意思只是重复的振荡位置。

警告:我没有将其通过编译器进行处理,因此我的加速可能会向后。

我也考虑使用双重缓冲位置。您不想像这样在循环中计算所有位置,否则您可以根据不同时间步长中的位置来计算力。

import pandas as pd
df = pd.read_excel('Medicare.xlsx', skiprows=5)
df.head()
hospitals = pd.unique(df[['Provider Name']].values)
hospAR = pd.unique(df[['Provider Name', 'Provider State'='AR']])
hospAK = pd.unique(df[['Provider Name', 'Provider State'='AK']])