iMSTK
Interactive Medical Simulation Toolkit
imstkPbdContactConstraint.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkPbdContactConstraint.h"
8 #include "imstkCollisionUtils.h"
9 
10 namespace imstk
11 {
12 void
14  const double dt, const SolverType& solverType)
15 {
16  if (dt == 0.0)
17  {
18  return;
19  }
20 
21  // dcdx is normalized
22  double c = 0.0;
23  bool update = this->computeValueAndGradient(bodies, c,
24  m_dcdx);
25  if (!update)
26  {
27  return;
28  }
29 
30  // Compute generalized inverse mass sum
31  double w = 0.0;
32  for (size_t i = 0; i < m_particles.size(); i++)
33  {
34  w += computeGeneralizedInvMass(bodies, i, m_r[i]) * m_dcdx[i].squaredNorm();
35  }
36  if (w == 0.0)
37  {
38  return;
39  }
40 
41  double dlambda = 0.0;
42  double alpha = 0.0;
43  switch (solverType)
44  {
45  case (SolverType::PBD):
46  dlambda = -c * m_stiffness / w;
47  break;
48  case (SolverType::xPBD):
49  default:
50  alpha = m_compliance / (dt * dt);
51  dlambda = -(c + alpha * m_lambda) / (w + alpha);
52  m_lambda += dlambda;
53  break;
54  }
55 
56  for (size_t i = 0; i < m_particles.size(); i++)
57  {
58  const double invMass = bodies.getInvMass(m_particles[i]);
59  if (invMass > 0.0)
60  {
61  // Positional impulse
62  const Vec3d p = dlambda * m_dcdx[i];
63  const Vec3d dx = p * invMass;
64  bodies.getPosition(m_particles[i]) += dx;
65 
66  if (bodies.getBodyType(m_particles[i]) == PbdBody::Type::RIGID)
67  {
68  Quatd& q = bodies.getOrientation(m_particles[i]);
69  const Mat3d& invInteria = bodies.getInvInertia(m_particles[i]);
70 
71  Vec3d rot = m_r[i].cross(p);
72  // Might be able to avoid a division in the inverse under assuming its unit
73  rot = q.inverse()._transformVector(rot);
74  rot = invInteria * rot;
75  rot = q._transformVector(rot);
76 
77  // Apply a max rotation limit, quaternions can only represent
78  // rotations up to 180deg
79  double scale = 1.0;
80  const double phi = rot.norm();
81  if (phi > 0.5) // Max rot
82  {
83  scale = 0.5 / phi;
84  }
85 
86  const Quatd dq = Quatd(0.0,
87  rot[0] * scale,
88  rot[1] * scale,
89  rot[2] * scale) * q;
90  q.coeffs() += dq.coeffs() * 0.5;
91  q.normalize();
92  }
93  }
94  }
95 }
96 
97 void
99 {
100  if (!m_correctVelocity)
101  {
102  return;
103  }
104 
105  // Assumed equal and opposite normals/constraint gradients
106  const Vec3d contactNormal = m_dcdx[0].normalized();
107 
108  // We can also assume equal and opposite normals/constraint gradients
109  const Vec3d relativeVelocity = computeRelativeVelocity(bodies);
110  const double vNMag = contactNormal.dot(relativeVelocity);
111  const Vec3d vN = vNMag * contactNormal;
112  const Vec3d vT = relativeVelocity - vN;
113  const double vTMag = vT.norm();
114 
115  if (vTMag == 0.0)
116  {
117  return;
118  }
119  // Avoid jitter with threshold here
120  //const double restitution = (std::abs(vNMag) < 1.0e-10) ? 0.0 : m_restitution;
121 
122  // Velocity correction for friction & restitution
123  const Vec3d dV = (vT / vTMag) * std::min(m_friction * getForce(dt) * dt, vTMag) +
124  contactNormal * (-vNMag + std::min(-m_restitution * vNMag, 0.0));
125 
126  // Compute generalized inverse mass sum
127  double w = 0.0;
128  for (size_t i = 0; i < m_particles.size(); i++)
129  {
130  w += computeGeneralizedInvMass(bodies, i, m_r[i]) ;
131  }
132  if (w == 0.0)
133  {
134  return;
135  }
136 
137  const Vec3d p = dV / w;
138  for (size_t i = 0; i < m_particles.size(); i++)
139  {
140  const double invMass = bodies.getInvMass(m_particles[i]);
141  if (invMass > 0.0)
142  {
143  bodies.getVelocity(m_particles[i]) += p * invMass * m_weights[i];
144 
145  if (bodies.getBodyType(m_particles[i]) == PbdBody::Type::RIGID)
146  {
147  const Mat3d& invInteria = bodies.getInvInertia(m_particles[i]);
148  const Quatd& orientation = bodies.getOrientation(m_particles[i]);
149 
150  // Apply inertia from rest pose
151  Vec3d rot = m_r[i].cross(p);
152  rot = orientation.inverse()._transformVector(rot);
153  rot = invInteria * rot;
154  rot = orientation._transformVector(rot);
155  bodies.getAngularVelocity(m_particles[i]) += rot;
156  }
157  }
158  }
159 }
160 
161 void
163  const PbdState& state,
164  const PbdParticleId& bodyId,
165  const Vec3d contactPtOnBody,
166  const PbdParticleId& x0, const PbdParticleId& x1, const PbdParticleId& x2,
167  const double compliance)
168 {
169  m_particles[0] = bodyId;
170  // Compute local position on body
171  m_r[0] = contactPtOnBody - state.getPosition(bodyId);
172  m_particles[1] = x0;
173  m_particles[2] = x1;
174  m_particles[3] = x2;
175 
176  setCompliance(compliance);
177 }
178 
179 bool
180 PbdTriangleToBodyConstraint::computeInterpolantsAndContact(const PbdState& bodies,
181  std::vector<double>& weights, Vec3d& contactNormal, double& depth) const
182 {
183  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
184  const Vec3d& x1 = bodies.getPosition(m_particles[1]);
185  const Vec3d& x2 = bodies.getPosition(m_particles[2]);
186  const Vec3d& x3 = bodies.getPosition(m_particles[3]);
187 
188  // Global position
189  const Vec3d p = bodyPos + m_r[0];
190 
191  // Compute barycentric coordinates u,v,w
192  const Vec3d bary = baryCentric(p, x1, x2, x3);
193  if (bary.hasNaN())
194  {
195  return false;
196  }
197  // Point
198  weights[0] = 1.0;
199  // Triangle off by 1 for point weight storage at [0]
200  weights[1] = bary[0];
201  weights[2] = bary[1];
202  weights[3] = bary[2];
203 
204  // This constraint becomes invalid if moved out of the triangle
205  if (weights[1] < 0.0 || weights[2] < 0.0 || weights[3] < 0.0)
206  {
207  return false;
208  }
209 
210  // Triangle normal (pointing up on standard counter clockwise triangle)
211  contactNormal = (x2 - x1).cross(x3 - x1).normalized();
212  // Point could be on either side of triangle, we want to resolve to the triangles plane
213  depth = (p - x1).dot(contactNormal);
214 
215  return true;
216 }
217 
218 bool
220  double& c,
221  std::vector<Vec3d>& n)
222 {
223  Vec3d normal = Vec3d::Zero();
224  double depth = 0.0;
225  if (!computeInterpolantsAndContact(bodies, m_weights, normal, depth))
226  {
227  c = 0.0;
228  return false;
229  }
230 
231  if (depth >= 0.0)
232  {
233  return false;
234  }
235 
236  // A
237  n[0] = normal;
238 
239  // B
240  n[1] = -m_weights[1] * normal;
241  n[2] = -m_weights[2] * normal;
242  n[3] = -m_weights[3] * normal;
243 
244  c = depth;
245 
246  return true;
247 }
248 
249 Vec3d
250 PbdTriangleToBodyConstraint::computeRelativeVelocity(PbdState& bodies)
251 {
252  Vec3d normal = Vec3d::Zero();
253  double depth = 0.0;
254  if (!computeInterpolantsAndContact(bodies, m_weights, normal, depth))
255  {
256  return Vec3d::Zero();
257  }
258  m_weights[0] = -m_weights[0];
259 
260  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
261  // Global position
262  const Vec3d contactPt = bodyPos + m_r[0];
263  const Vec3d v0 = getVelocityOnRigidBody(bodies, m_particles[0].first, contactPt);
264 
265  //const Vec3d& x1 = bodies.getPosition(m_particles[1]);
266  const Vec3d& v1 = bodies.getPosition(m_particles[1]);
267  //const Vec3d& x2 = bodies.getPosition(m_particles[2]);
268  const Vec3d& v2 = bodies.getPosition(m_particles[2]);
269  //const Vec3d& x3 = bodies.getPosition(m_particles[3]);
270  const Vec3d& v3 = bodies.getPosition(m_particles[3]);
271  const Vec3d v123 = v1 * m_weights[1] + v2 * m_weights[2] + v3 * m_weights[3];
272 
273  // We can also assume equal and opposite normals/constraint gradients
274  return v0 - v123;
275 }
276 
277 void
279  const PbdState& state,
280  const PbdParticleId& bodyId,
281  const Vec3d contactPtOnBody,
282  const PbdParticleId& x0,
283  const double compliance)
284 {
285  m_particles[0] = bodyId;
286  // Compute local position on body
287  m_r[0] = contactPtOnBody - state.getPosition(bodyId);
288  m_particles[1] = x0;
289 
290  // Infinite stiffness/completely rigid
291  setCompliance(compliance);
292 }
293 
294 bool
296  double& c,
297  std::vector<Vec3d>& n)
298 {
299  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
300 
301  // Global position
302  const Vec3d p = bodyPos + m_r[0];
303 
304  // Current position during solve
305  const Vec3d& x1 = bodies.getPosition(m_particles[1]);
306 
307  const Vec3d diff = x1 - p;
308  c = diff.norm();
309 
310  if (c == 0.0)
311  {
312  return false;
313  }
314 
315  const Vec3d normal = diff / c;
316 
317  // A (direction to move body)
318  n[0] = -normal;
319  // B (direction to move vertex)
320  n[1] = normal;
321 
322  return true;
323 }
324 
325 Vec3d
326 PbdVertexToBodyConstraint::computeRelativeVelocity(PbdState& bodies)
327 {
328  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
329  // Global position
330  const Vec3d contactPt = bodyPos + m_r[0];
331  const Vec3d v0 = getVelocityOnRigidBody(bodies, m_particles[0].first, contactPt);
332 
333  m_weights[0] = 1.0;
334  m_weights[1] = -1.0;
335 
336  //const Vec3d& x1 = bodies.getPosition(m_particles[1]);
337  const Vec3d& v1 = bodies.getVelocity(m_particles[1]);
338  return v0 - v1;
339 }
340 
341 void
343  const PbdState& state,
344  const PbdParticleId& bodyId,
345  const Vec3d contactPtOnBody,
346  const PbdParticleId& x0, const PbdParticleId& x1,
347  const double compliance)
348 {
349  m_particles[0] = bodyId;
350  // Compute local position on body
351  m_r[0] = contactPtOnBody - state.getPosition(bodyId);
352  m_particles[1] = x0;
353  m_particles[2] = x1;
354 
355  setCompliance(compliance);
356 }
357 
358 bool
359 PbdEdgeToBodyConstraint::computeInterpolantsAndContact(const PbdState& bodies,
360  std::vector<double>& weights, Vec3d& normal, double& depth) const
361 {
362  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
363 
364  // Global position
365  const Vec3d p = bodyPos + m_r[0];
366 
367  // Just project p onto x3-x2. Get the normal component for distance to line
368  const Vec3d& x1 = bodies.getPosition(m_particles[1]);
369  const Vec3d& x2 = bodies.getPosition(m_particles[2]);
370 
371  const Vec3d ab = x2 - x1;
372  const double length = ab.norm();
373  if (length == 0.0)
374  {
375  return false;
376  }
377  const Vec3d dir1 = ab / length;
378 
379  // Project onto the line
380  const Vec3d diff = p - x1;
381  const double p1 = dir1.dot(diff);
382  if (p1 < 0.0 || p1 > length)
383  {
384  return false;
385  }
386  // Remove tangent component to get normal
387  const Vec3d diff1 = diff - p1 * dir1;
388  const double l = diff1.norm();
389  if (l == 0.0)
390  {
391  return false;
392  }
393  normal = diff1 / l;
394  const double u = p1 / length;
395 
396  // Point
397  weights[0] = 1.0;
398  // Edge
399  weights[1] = (1.0 - u);
400  weights[2] = u;
401 
402  depth = l;
403  return true;
404 }
405 
406 bool
408  double& c,
409  std::vector<Vec3d>& n)
410 {
411  Vec3d normal = Vec3d::Zero();
412  double depth = 0.0;
413  if (!computeInterpolantsAndContact(bodies, m_weights, normal, depth))
414  {
415  c = 0.0;
416  return false;
417  }
418 
419  // A
420  n[0] = normal;
421  // B
422  n[1] = -m_weights[1] * normal;
423  n[2] = -m_weights[2] * normal;
424 
425  c = depth;
426 
427  return true;
428 }
429 
430 Vec3d
431 PbdEdgeToBodyConstraint::computeRelativeVelocity(PbdState& bodies)
432 {
433  Vec3d normal = Vec3d::Zero();
434  double depth = 0.0;
435  if (!computeInterpolantsAndContact(bodies, m_weights, normal, depth))
436  {
437  return Vec3d::Zero();
438  }
439  m_weights[0] = -m_weights[0];
440 
441  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
442  // Global position
443  const Vec3d contactPt = bodyPos + m_r[0];
444  const Vec3d v0 = getVelocityOnRigidBody(bodies, m_particles[0].first, contactPt);
445 
446  //const Vec3d& x1 = bodies.getPosition(m_particles[1]);
447  const Vec3d& v1 = bodies.getPosition(m_particles[1]);
448  //const Vec3d& x2 = bodies.getPosition(m_particles[2]);
449  const Vec3d& v2 = bodies.getPosition(m_particles[2]);
450  const Vec3d v12 = v1 * m_weights[1] + v2 * m_weights[2];
451  return v0 - v12;
452 }
453 
454 bool
456  double& c,
457  std::vector<Vec3d>& n)
458 {
459  // Transform local position to acquire transformed global (for constraint reprojection)
460  const Vec3d& bodyPos0 = bodies.getPosition(m_particles[0]);
461  Vec3d p0 = bodyPos0;
462  if (bodies.getBodyType(m_particles[0]) != PbdBody::Type::DEFORMABLE)
463  {
464  m_r[0] = bodies.getOrientation(m_particles[0])._transformVector(m_rest_r[0]);
465  p0 += m_r[0];
466  }
467 
468  const Vec3d& bodyPos1 = bodies.getPosition(m_particles[1]);
469  Vec3d p1 = bodyPos1;
470  if (bodies.getBodyType(m_particles[1]) != PbdBody::Type::DEFORMABLE)
471  {
472  m_r[1] = bodies.getOrientation(m_particles[1])._transformVector(m_rest_r[1]);
473  p1 += m_r[1];
474  }
475 
476  // Move according to the difference
477  Vec3d diff = p1 - p0;
478  const double length = diff.norm();
479  if (length == 0.0)
480  {
481  return false;
482  }
483  diff /= length;
484 
485  // A
486  n[0] = diff.normalized();
487  // B
488  n[1] = -n[0];
489 
490  c = -length;
491 
492  return true;
493 }
494 
495 bool
497  double& c,
498  std::vector<Vec3d>& n)
499 {
500  const Vec3d& bodyPos0 = bodies.getPosition(m_particles[0]);
501  m_r[0] = bodies.getOrientation(m_particles[0])._transformVector(m_rest_r[0]);
502  const Vec3d p0 = bodyPos0 + m_r[0];
503 
504  const Vec3d& bodyPos1 = bodies.getPosition(m_particles[1]);
505  m_r[1] = bodies.getOrientation(m_particles[1])._transformVector(m_rest_r[1]);
506  const Vec3d p1 = bodyPos1 + m_r[1];
507 
508  const Vec3d diff = p1 - p0;
509 
510  c = diff.dot(m_contactNormal);
511 
512  // A
513  n[0] = -m_contactNormal;
514  // B
515  n[1] = m_contactNormal;
516 
517  return true;
518 }
519 
520 bool
522  double& c, std::vector<Vec3d>& n)
523 {
524  const Vec3d& bodyPos = bodies.getPosition(m_particles[0]);
525  const Quatd& bodyOrientation = bodies.getOrientation(m_particles[0]);
526  const Vec3d p = bodyPos + bodyOrientation._transformVector(m_p_rest);
527  const Vec3d q = bodyPos + bodyOrientation._transformVector(m_q_rest);
528 
529  // Compute distance to pq
530  const Vec3d dir = (q - p).normalized();
531 
532  const Vec3d& pt = bodies.getPosition(m_particles[1]);
533 
534  const Vec3d diff = pt - q;
535  const double l = diff.dot(dir);
536  const Vec3d dist = diff - l * dir;
537 
538  n[1] = dist.normalized();
539  n[0] = -n[1];
540  // Pt on line - line body center
541  m_r[0] = (pt - dist) - bodyPos;
542 
543  c = dist.norm();
544 
545  return true;
546 }
547 } // namespace imstk
std::vector< Vec3d > m_dcdx
Normalized constraint gradients (per particle)
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const PbdParticleId &x1, const double compliance=0.0)
Initialize the constraint.
std::pair< int, int > PbdParticleId
Index pair that refers to a particle in a PbdState. Index 0 is the body id, Index 1 is the particle i...
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.
double m_lambda
Lagrange multiplier.
std::vector< PbdParticleId > m_particles
body, particle index
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.
void projectConstraint(PbdState &bodies, const double dt, const SolverType &type) override
Update positions by projecting constraints.
double computeGeneralizedInvMass(const PbdState &bodies, const size_t particleIndex) const
Compute generalized inverse mass of the particle. Note perf sensitive function. It has been intention...
Compound Geometry.
void correctVelocity(PbdState &bodies, const double dt) override
Solve the velocities given to the constraint.
double m_compliance
used in xPBD, inverse of Stiffness
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const PbdParticleId &x1, const PbdParticleId &x2, const double compliance=0.0)
Initialize the constraint.
virtual bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &dcdx)=0
Compute value and gradient of the constraint.
double getForce(const double dt) const
Get the force magnitude, valid after solving lambda Only valid with xpbd.
double m_stiffness
used in PBD, [0, 1]
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const double compliance=0.0)
Initialize the constraint.
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.
SolverType
Type of solvers.
Vec3d getVelocityOnRigidBody(PbdState &bodies, const int bodyId, const Vec3d &pt)
Returns the velocity at the given point on body Either body in collision could be rigid body...
Provides interface for accessing particles from a 2d array of PbdBody,Particles.
Definition: imstkPbdBody.h:229
bool computeValueAndGradient(PbdState &bodies, double &c, std::vector< Vec3d > &n) override
Compute value and gradient of the constraint.