use glam::{Vec2, Vec3};
#[inline(always)]
fn dist_to_seg(px: f32, py: f32, ax: f32, ay: f32, bx: f32, by: f32) -> f32 {
let dx = bx - ax;
let dy = by - ay;
let len_sq = dx.mul_add(dx, dy * dy);
let t = if len_sq > 0.0 {
(px - ax).mul_add(dx, (py - ay) * dy) / len_sq
} else {
0.0
};
let t = t.clamp(0.0, 1.0);
let cx = dx.mul_add(t, ax);
let cy = dy.mul_add(t, ay);
(px - cx).hypot(py - cy)
}
#[inline(always)]
fn cross_2d(px: f32, py: f32, ax: f32, ay: f32, bx: f32, by: f32) -> f32 {
(px - ax).mul_add(by - ay, -((py - ay) * (bx - ax)))
}
#[inline(always)]
pub fn sdf_parallelogram(p: Vec3, width: f32, para_height: f32, skew: f32, half_depth: f32) -> f32 {
let px = p.x;
let py = p.y;
let he = para_height;
let wi = width;
let sk = skew;
let (dx, dy) = (wi - sk, -he);
let (ax, ay) = (wi + sk, he);
let (bx, by) = (-wi + sk, he);
let (cx, cy) = (-wi - sk, -he);
let d1 = dist_to_seg(px, py, dx, dy, ax, ay); let d2 = dist_to_seg(px, py, ax, ay, bx, by); let d3 = dist_to_seg(px, py, bx, by, cx, cy); let d4 = dist_to_seg(px, py, cx, cy, dx, dy);
let d_unsigned = d1.min(d2).min(d3).min(d4);
let c1 = cross_2d(px, py, dx, dy, ax, ay);
let c2 = cross_2d(px, py, ax, ay, bx, by);
let c3 = cross_2d(px, py, bx, by, cx, cy);
let c4 = cross_2d(px, py, cx, cy, dx, dy);
let inside = c1 <= 0.0 && c2 <= 0.0 && c3 <= 0.0 && c4 <= 0.0;
let d_2d = if inside { -d_unsigned } else { d_unsigned };
let d_z = p.z.abs() - half_depth;
let w = Vec2::new(d_2d.max(0.0), d_z.max(0.0));
d_2d.max(d_z).min(0.0) + w.length()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parallelogram_center_inside() {
let d = sdf_parallelogram(Vec3::ZERO, 1.0, 0.5, 0.3, 0.5);
assert!(d < 0.0, "Center should be inside, got {}", d);
}
#[test]
fn test_parallelogram_far_outside() {
let d = sdf_parallelogram(Vec3::new(5.0, 0.0, 0.0), 1.0, 0.5, 0.3, 0.5);
assert!(d > 0.0, "Far point should be outside, got {}", d);
}
#[test]
fn test_parallelogram_symmetry_z() {
let d1 = sdf_parallelogram(Vec3::new(0.3, 0.1, 0.2), 1.0, 0.5, 0.3, 0.5);
let d2 = sdf_parallelogram(Vec3::new(0.3, 0.1, -0.2), 1.0, 0.5, 0.3, 0.5);
assert!((d1 - d2).abs() < 0.001, "Should be symmetric in Z");
}
#[test]
fn test_parallelogram_scale() {
let d1 = sdf_parallelogram(Vec3::ZERO, 1.0, 0.5, 0.3, 0.5);
let d2 = sdf_parallelogram(Vec3::ZERO, 2.0, 1.0, 0.6, 1.0);
assert!(d1 < 0.0 && d2 < 0.0, "Both should be inside");
assert!(
d2 < d1,
"Larger shape should have more negative distance at center"
);
}
}