#![warn(missing_docs)]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeDocTests;
pub mod assemble;
pub mod cache;
pub mod cone;
pub mod convex;
pub mod gradient;
pub mod material;
pub mod solver;
pub use assemble::{ContactAssembly, assemble, contact_wrenches, generalized_impulse};
pub use cache::{ContactCache, ContactKey};
pub use cone::{contact_frame, in_cone, in_cone_interior, project_cone};
pub use convex::{
ContactCoupling, ContactProblem, ContactRow, ContactSolution, ContactSolverConfig, GPU_SWEEPS,
regularization_depth_derivative, regularization_diag, solve_contacts, solve_contacts_warm,
};
pub use material::{ContactMaterial, SolImp, SolRef};
pub use solver::{
CYL_AXIS_EPS, GroundSupport, cylinder_rim_basis, cylinder_rim_dir, find_contacts,
find_ground_contacts, find_ground_contacts_model, find_ground_contacts_model_with_drop,
find_ground_contacts_model_with_offset, find_ground_contacts_model_with_support,
find_heightfield_contacts_model, find_heightfield_contacts_model_with_drop,
find_heightfield_contacts_model_with_support,
};
#[allow(deprecated)]
pub use solver::{contact_forces, contact_forces_implicit};
use phyz_collision::Collision;
use phyz_math::{SpatialVec, Vec3};
#[deprecated(
note = "penalty contact is superseded by the convex solve (`assemble` + \
`solve_contacts`); its friction law makes friction vanish at low \
sliding speed regardless of normal load, so nothing ever sticks"
)]
pub fn compute_contact_force(
collision: &Collision,
material: &ContactMaterial,
velocity_i: &Vec3,
velocity_j: &Vec3,
) -> SpatialVec {
let depth = collision.penetration_depth;
if depth <= 0.0 {
return SpatialVec::zero();
}
let normal = collision.contact_normal;
let rel_vel = velocity_j - velocity_i;
let normal_vel = rel_vel.dot(normal);
let k = material.stiffness;
let c = material.damping;
let p = 1.0;
let force_magnitude = k * depth.powf(p) - c * normal_vel;
let force_magnitude = force_magnitude.max(0.0);
let force = normal * force_magnitude;
let tangent_vel = rel_vel - normal * normal_vel;
let tangent_speed = tangent_vel.norm();
let friction_force = if tangent_speed > 1e-10 {
let tangent_dir = tangent_vel / tangent_speed;
let friction_magnitude = (material.friction * force_magnitude).min(c * tangent_speed);
-tangent_dir * friction_magnitude
} else {
Vec3::zeros()
};
let total_force = force + friction_force;
SpatialVec::new(Vec3::zeros(), total_force)
}
#[deprecated(
note = "penalty contact is superseded by the convex solve (`assemble` + \
`solve_contacts`); its friction law makes friction vanish at low \
sliding speed regardless of normal load, so nothing ever sticks"
)]
pub fn compute_contact_force_implicit(
collision: &Collision,
material: &ContactMaterial,
velocity_i: &Vec3,
velocity_j: &Vec3,
mass_i: f64,
mass_j: f64,
dt: f64,
) -> SpatialVec {
let depth = collision.penetration_depth;
if depth <= 0.0 {
return SpatialVec::zero();
}
let normal = collision.contact_normal;
let rel_vel = velocity_j - velocity_i;
let normal_vel = rel_vel.dot(normal);
let k = material.stiffness;
let c = material.damping;
let m_eff = if !mass_i.is_finite() && !mass_j.is_finite() {
return SpatialVec::zero();
} else if !mass_i.is_finite() {
mass_j
} else if !mass_j.is_finite() {
mass_i
} else if mass_i + mass_j > 0.0 {
(mass_i * mass_j) / (mass_i + mass_j)
} else {
return SpatialVec::zero();
};
if m_eff <= 0.0 || !m_eff.is_finite() {
return SpatialVec::zero();
}
let denom = m_eff + dt * c + dt * dt * k;
let force_magnitude = m_eff * (k * depth - (c + dt * k) * normal_vel) / denom;
let force_magnitude = force_magnitude.max(0.0);
let force = normal * force_magnitude;
let tangent_vel = rel_vel - normal * normal_vel;
let tangent_speed = tangent_vel.norm();
let friction_force = if tangent_speed > 1e-10 {
let tangent_dir = tangent_vel / tangent_speed;
let friction_magnitude = (material.friction * force_magnitude).min(c * tangent_speed);
-tangent_dir * friction_magnitude
} else {
Vec3::zeros()
};
SpatialVec::new(Vec3::zeros(), force + friction_force)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(deprecated)]
fn test_contact_force_zero_depth() {
let collision = Collision {
body_i: 0,
body_j: 1,
contact_point: Vec3::zeros(),
contact_normal: Vec3::z(),
penetration_depth: 0.0,
};
let material = ContactMaterial::default();
let force = compute_contact_force(&collision, &material, &Vec3::zeros(), &Vec3::zeros());
assert!(force.linear.norm() < 1e-10);
}
#[test]
#[allow(deprecated)]
fn test_contact_force_penetration() {
let collision = Collision {
body_i: 0,
body_j: 1,
contact_point: Vec3::zeros(),
contact_normal: Vec3::z(),
penetration_depth: 0.1,
};
let material = ContactMaterial {
stiffness: 1000.0,
..Default::default()
};
let force = compute_contact_force(&collision, &material, &Vec3::zeros(), &Vec3::zeros());
assert!(force.linear.norm() > 0.0);
assert!(force.linear.dot(Vec3::z()) > 0.0);
}
}