#[must_use]
pub fn braking_distance(velocity: f64, deceleration: f64) -> f64 {
if deceleration <= 0.0 {
return 0.0;
}
let speed = velocity.abs();
speed * speed / (2.0 * deceleration)
}
#[derive(Debug, Clone, Copy)]
pub struct MovementResult {
pub position: f64,
pub velocity: f64,
pub arrived: bool,
}
#[must_use]
pub fn tick_movement(
position: f64,
velocity: f64,
target_position: f64,
max_speed: f64,
acceleration: f64,
deceleration: f64,
dt: f64,
) -> MovementResult {
const EPSILON: f64 = 1e-9;
let displacement = target_position - position;
if displacement.abs() < EPSILON && velocity.abs() < EPSILON {
return MovementResult {
position: target_position,
velocity: 0.0,
arrived: true,
};
}
let sign = displacement.signum();
let distance_remaining = displacement.abs();
let speed = velocity.abs();
let stopping_distance = speed * speed / (2.0 * deceleration);
let new_velocity = if stopping_distance >= distance_remaining - EPSILON {
let v = (-deceleration * dt).mul_add(velocity.signum(), velocity);
if velocity > 0.0 && v < 0.0 || velocity < 0.0 && v > 0.0 {
0.0
} else {
v
}
} else if speed < max_speed {
let v = (acceleration * dt).mul_add(sign, velocity);
if v.abs() > max_speed {
sign * max_speed
} else {
v
}
} else {
sign * max_speed
};
let new_pos = new_velocity.mul_add(dt, position);
let new_displacement = target_position - new_pos;
if new_displacement.abs() < EPSILON || (new_displacement.signum() - sign).abs() > EPSILON {
return MovementResult {
position: target_position,
velocity: 0.0,
arrived: true,
};
}
MovementResult {
position: new_pos,
velocity: new_velocity,
arrived: false,
}
}