#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloorSide {
Floor,
Ceiling,
}
#[derive(Debug, Clone)]
pub struct FloorConstraint {
pub plane_height: f32,
pub side: FloorSide,
pub use_rotation: bool,
pub influence: f32,
pub label: String,
}
pub fn new_floor_constraint(plane_height: f32, label: &str) -> FloorConstraint {
FloorConstraint {
plane_height,
side: FloorSide::Floor,
use_rotation: false,
influence: 1.0,
label: label.to_owned(),
}
}
pub fn apply_floor_constraint(c: &FloorConstraint, pos: [f32; 3]) -> [f32; 3] {
let target_y = match c.side {
FloorSide::Floor => pos[1].max(c.plane_height),
FloorSide::Ceiling => pos[1].min(c.plane_height),
};
let blended_y = pos[1] + (target_y - pos[1]) * c.influence;
[pos[0], blended_y, pos[2]]
}
pub fn is_satisfied(c: &FloorConstraint, pos: [f32; 3]) -> bool {
match c.side {
FloorSide::Floor => pos[1] >= c.plane_height,
FloorSide::Ceiling => pos[1] <= c.plane_height,
}
}
pub fn distance_to_plane(c: &FloorConstraint, pos: [f32; 3]) -> f32 {
(pos[1] - c.plane_height).abs()
}
pub fn floor_constraint_to_json(c: &FloorConstraint) -> String {
let side_str = match c.side {
FloorSide::Floor => "floor",
FloorSide::Ceiling => "ceiling",
};
format!(
r#"{{"label":"{}", "height":{:.4}, "side":"{}", "influence":{:.4}}}"#,
c.label, c.plane_height, side_str, c.influence
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn floor_at_zero_clamps_below() {
let c = new_floor_constraint(0.0, "ground");
let out = apply_floor_constraint(&c, [0.0, -2.0, 0.0]);
assert!(out[1] >= 0.0);
}
#[test]
fn floor_above_plane_unchanged() {
let c = new_floor_constraint(0.0, "ground");
let out = apply_floor_constraint(&c, [0.0, 3.0, 0.0]);
assert!((out[1] - 3.0).abs() < 1e-5);
}
#[test]
fn ceiling_clamps_above() {
let mut c = new_floor_constraint(5.0, "ceiling");
c.side = FloorSide::Ceiling;
let out = apply_floor_constraint(&c, [0.0, 8.0, 0.0]);
assert!(out[1] <= 5.0);
}
#[test]
fn is_satisfied_floor_above_plane() {
let c = new_floor_constraint(0.0, "g");
assert!(is_satisfied(&c, [0.0, 1.0, 0.0]));
}
#[test]
fn is_satisfied_floor_below_plane_false() {
let c = new_floor_constraint(0.0, "g");
assert!(!is_satisfied(&c, [0.0, -1.0, 0.0]));
}
#[test]
fn distance_to_plane_correct() {
let c = new_floor_constraint(2.0, "g");
assert!((distance_to_plane(&c, [0.0, 5.0, 0.0]) - 3.0).abs() < 1e-5);
}
#[test]
fn zero_influence_does_not_move_vertex() {
let mut c = new_floor_constraint(0.0, "g");
c.influence = 0.0;
let out = apply_floor_constraint(&c, [0.0, -5.0, 0.0]);
assert!((out[1] - (-5.0)).abs() < 1e-5);
}
#[test]
fn json_contains_label() {
let c = new_floor_constraint(0.0, "floorLabel");
assert!(floor_constraint_to_json(&c).contains("floorLabel"));
}
#[test]
fn json_side_field_is_floor() {
let c = new_floor_constraint(0.0, "g");
assert!(floor_constraint_to_json(&c).contains("floor"));
}
}