use std::collections::HashMap;
use crate::field::{NodeId, Vec2};
pub const CONTACT_SLOP: f32 = 0.5;
pub const CONTACT_SKIN: f32 = 1.5;
pub const MAX_PHYSICS_SPEED: f32 = 1600.0;
pub const CONTACT_RESTITUTION: f32 = 0.02;
pub const CONTACT_FRICTION: f32 = 0.22;
pub const MAX_CONTACT_IMPULSE: f32 = 380.0;
pub const MAX_POSITION_CORRECTION: f32 = 48.0;
pub const POSITION_SOLVER_ITERS: usize = 6;
pub const PHYSICS_REST_EPSILON: f32 = 4.0;
#[allow(clippy::too_many_arguments)]
pub fn resolve_contact_pair(
positions: &mut HashMap<NodeId, Vec2>,
velocities: &mut HashMap<NodeId, Vec2>,
a: NodeId,
b: NodeId,
dx: f32,
dy: f32,
gap_x: f32,
gap_y: f32,
inv_mass_a: f32,
inv_mass_b: f32,
) {
let solve_x = gap_x >= gap_y;
let normal = if solve_x {
Vec2 {
x: if dx.abs() > f32::EPSILON {
dx.signum()
} else if a.as_u64() < b.as_u64() {
-1.0
} else {
1.0
},
y: 0.0,
}
} else {
Vec2 {
x: 0.0,
y: if dy.abs() > f32::EPSILON {
dy.signum()
} else {
1.0
},
}
};
let penetration = if solve_x {
(-gap_x).max(0.0)
} else {
(-gap_y).max(0.0)
};
if penetration > 0.0 {
let correction = (penetration + CONTACT_SLOP).min(MAX_POSITION_CORRECTION);
let total_inv = inv_mass_a + inv_mass_b;
if total_inv > 0.0 {
let move_a = correction * (inv_mass_a / total_inv);
let move_b = correction * (inv_mass_b / total_inv);
if let Some(pos) = positions.get_mut(&a) {
pos.x -= normal.x * move_a;
pos.y -= normal.y * move_a;
}
if let Some(pos) = positions.get_mut(&b) {
pos.x += normal.x * move_b;
pos.y += normal.y * move_b;
}
}
}
let Some(va) = velocities.get(&a).copied() else {
return;
};
let Some(vb) = velocities.get(&b).copied() else {
return;
};
let rel_x = vb.x - va.x;
let rel_y = vb.y - va.y;
let rel_normal = rel_x * normal.x + rel_y * normal.y;
if rel_normal >= 0.0 {
return;
}
let total_inv = inv_mass_a + inv_mass_b;
if total_inv <= 0.0 {
return;
}
let normal_impulse = (-(1.0 + CONTACT_RESTITUTION) * rel_normal / total_inv)
.min(MAX_CONTACT_IMPULSE)
.max(0.0);
let impulse_x = normal.x * normal_impulse;
let impulse_y = normal.y * normal_impulse;
if let Some(vel) = velocities.get_mut(&a) {
vel.x -= impulse_x * inv_mass_a;
vel.y -= impulse_y * inv_mass_a;
}
if let Some(vel) = velocities.get_mut(&b) {
vel.x += impulse_x * inv_mass_b;
vel.y += impulse_y * inv_mass_b;
}
let tangent_x = rel_x - normal.x * rel_normal;
let tangent_y = rel_y - normal.y * rel_normal;
let tangent_len = (tangent_x * tangent_x + tangent_y * tangent_y).sqrt();
if tangent_len <= f32::EPSILON {
return;
}
let tx = tangent_x / tangent_len;
let ty = tangent_y / tangent_len;
let rel_tangent = rel_x * tx + rel_y * ty;
let friction_impulse = (-rel_tangent / total_inv).clamp(
-CONTACT_FRICTION * normal_impulse,
CONTACT_FRICTION * normal_impulse,
);
let friction_x = tx * friction_impulse;
let friction_y = ty * friction_impulse;
if let Some(vel) = velocities.get_mut(&a) {
vel.x -= friction_x * inv_mass_a;
vel.y -= friction_y * inv_mass_a;
}
if let Some(vel) = velocities.get_mut(&b) {
vel.x += friction_x * inv_mass_b;
vel.y += friction_y * inv_mass_b;
}
}