1use crabslab::SlabItem;
6use glam::Vec2;
7#[derive(Clone, Copy, SlabItem)]
16pub struct CircleDescriptor {
17 pub center: Vec2,
18 pub radius: f32,
19}
20
21impl CircleDescriptor {
22 pub fn distance(&self, point: Vec2) -> f32 {
23 let p = point - self.center;
24 p.length() - self.radius
25 }
26}
27
28#[derive(Clone, Copy, SlabItem)]
29pub struct Box {
30 pub center: Vec2,
31 pub half_extent: Vec2,
32}
33
34impl Box {
35 pub fn distance(&self, point: Vec2) -> f32 {
36 let p = point - self.center;
37 let component_edge_distance = p.abs() - self.half_extent;
38 let outside = component_edge_distance.max(Vec2::ZERO).length();
39 let inside = component_edge_distance
40 .x
41 .max(component_edge_distance.y)
42 .min(0.0);
43 inside + outside
44 }
45}
46
47#[cfg(test)]
48mod test {
49 use super::*;
50
51 #[test]
52 fn sdf_circle_sanity() {
53 let mut img = image::ImageBuffer::<image::Luma<f32>, Vec<f32>>::new(32, 32);
54
55 let circle = CircleDescriptor {
56 center: Vec2::new(12.0, 12.0),
57 radius: 4.0,
58 };
59
60 img.enumerate_pixels_mut().for_each(|(x, y, p)| {
61 let distance = circle.distance(Vec2::new(x as f32 + 0.5, y as f32 + 0.5));
62 p.0[0] = distance / circle.radius;
63 });
64
65 img_diff::assert_img_eq(
66 "sdf/circle_sanity.png",
67 image::DynamicImage::from(img).into_rgb8(),
68 );
69 }
70
71 #[test]
72 fn sdf_box_sanity() {
73 let mut img = image::ImageBuffer::<image::Luma<f32>, Vec<f32>>::new(32, 32);
74
75 let bx = Box {
76 center: Vec2::new(12.0, 12.0),
77 half_extent: Vec2::new(4.0, 6.0),
78 };
79
80 img.enumerate_pixels_mut().for_each(|(x, y, p)| {
81 let distance = bx.distance(Vec2::new(x as f32 + 0.5, y as f32 + 0.5));
82 p.0[0] = distance / bx.half_extent.max_element();
83 });
84
85 img_diff::assert_img_eq(
86 "sdf/box_sanity.png",
87 image::DynamicImage::from(img).into_rgb8(),
88 );
89 }
90}