1#![allow(non_snake_case)]
2use {
3 crate::{
4 solver::{
5 Argmax2D, adf::{ADF, quadtree::Quadtree}
6 },
7 geometry::{
8 self, BoundingBox, Shape,
9 PixelSpace, WorldSpace, DistPoint,
10 Translation, Rotation, Scale
11 },
12 sdf::SDF
13 },
14 euclid::{Box2D, Point2D, Size2D, Vector2D as V2},
15 image::{
16 ImageBuffer, Luma, Rgba, Pixel, RgbaImage
17 },
18 num_traits::{Float, AsPrimitive, Signed}
19};
20
21mod impl_draw_rgbaimage;
22#[cfg(test)] mod tests;
23
24pub trait Draw<Float, Backend>: Shape<Float> {
25 fn draw(&self, image: &mut Backend);
26}
27
28static MSG: &str = "Draw is only implemented for Texture";
29
30impl <B, S, P> Draw<P, B> for Translation<S, P> where Translation<S, P>: Shape<P> {
32 fn draw(&self, _: &mut B) { unreachable!("{}", MSG) } }
33impl <B, S, P> Draw<P, B> for Rotation<S, P> where Rotation<S, P>: Shape<P> {
34 fn draw(&self, _: &mut B) { unreachable!("{}", MSG) } }
35impl <B, S, P> Draw<P, B> for Scale<S, P> where Scale<S, P>: Shape<P> {
36 fn draw(&self, _: &mut B) { unreachable!("{}", MSG) } }
37
38impl <B, P> Draw<P, B> for geometry::Line<P> where geometry::Line<P>: Shape<P> {
39 fn draw(&self, _: &mut B) { unreachable!("{}", MSG) } }
40impl <B, P, U> Draw<P, B> for geometry::Polygon<U> where P: Float, U: AsRef<[Point2D<P, WorldSpace>]> {
41 fn draw(&self, _: &mut B) { unreachable!("{}", MSG) } }
42
43#[derive(Debug, Copy, Clone)]
44pub struct Texture<S, T> {
45 pub shape: S,
46 pub texture: T
47}
48impl <P, S, T> SDF<P> for Texture<S, T> where S: SDF<P> {
49 fn sdf(&self, pixel: Point2D<P, WorldSpace>) -> P { self.shape.sdf(pixel) } }
50impl <P, S, T> BoundingBox<P> for Texture<S, T> where S: BoundingBox<P> {
51 fn bounding_box(&self) -> Box2D<P, WorldSpace> { self.shape.bounding_box() } }
52
53fn rescale_bounding_box(
55 bounding_box: Box2D<f64, WorldSpace>,
56 resolution: Size2D<u32, PixelSpace>
57) -> (
58 Option<Box2D<u32, PixelSpace>>, V2<f64, PixelSpace>, f64 ) {
62 let min_side = resolution.width.min(resolution.height) as f64;
63 let offset = (resolution.to_vector().to_f64() - V2::splat(min_side)) / 2.0;
64 let bounding_box = bounding_box
65 .scale(min_side, min_side).cast_unit()
66 .round_out()
67 .translate(offset)
68 .intersection(&Box2D::from_size(resolution.to_f64()))
69 .map(|x| x.cast::<u32>());
70 (bounding_box, offset, min_side)
71}
72
73pub fn draw_parallel<Float, Backend, Sh>(
76 framebuffer: &mut Backend,
77 shapes: impl rayon::iter::ParallelIterator<Item =Sh>
78) -> &mut Backend
79 where Backend: Sync + Send,
80 Sh: AsRef<dyn Draw<Float, Backend> + Send + Sync>
81{
82 let ptr = framebuffer as *mut _ as usize;
83 shapes.for_each(|shape|
84 shape.as_ref().draw(unsafe { &mut *(ptr as *mut Backend) })
85 );
86 framebuffer
87}
88
89pub fn display_sdf(sdf: impl Fn(Point2D<f64, WorldSpace>) -> f64, image: &mut RgbaImage, brightness: f64) {
90 let resolution = image.width();
91 let Δp = 1.0 / resolution as f64;
92
93 image.enumerate_pixels_mut()
95 .for_each(|(x, y, pixel)| {
96 let pixel_world = Point2D::new(x, y).to_f64() / resolution as f64;
97 let sdf = sdf(pixel_world);
98 let mut alpha = (Δp - sdf.abs()).clamp(0.0, Δp) / Δp;
99 alpha *= (x > 0 && y > 0) as u8 as f64;
100 let mut color = Luma([
101 ((sdf * brightness).powf(1.0) * 255.0) as u8
102 ]).to_rgba();
103 color.blend(&Rgba([255, 0, 0, (alpha * 128.0) as u8]));
104 *pixel = color;
105 });
106}
107
108impl Argmax2D {
109 pub fn display_debug(&self) -> image::RgbImage {
110 let mut image = ImageBuffer::<image::Rgb<u8>, _>::new(
111 self.dist_map.resolution as u32,
112 self.dist_map.resolution as u32
113 );
114 let max_dist = self.find_max().distance;
115 self.dist_map.pixels().for_each(|DistPoint { distance, point }| {
116 let color = Luma::from([(distance / max_dist * 255.0) as u8]);
117 *image.get_pixel_mut(point.x as u32, point.y as u32) = color.to_rgb();
118 });
119 image
120 }
121}
122
123impl <Data, _Float: Float> Quadtree<Data, _Float> {
124 pub fn draw_layout(&self, image: &mut RgbaImage) -> &Self {
125 use geometry::Line;
126
127 let px = 1.0 / image.width() as f64;
128 self.traverse(&mut |node| {
129 if node.children.is_some() { return Ok(()) };
130
131 let rect = node.rect.cast();
132 let lines = [
133 [[0.0, 0.0], [rect.size.width, 0.0]],
134 [[rect.size.width, 0.0], rect.size.into()],
135 [rect.size.into(), [0.0, rect.size.height]],
136 [[0.0, rect.size.height], [0.0, 0.0]]
137 ];
138 let alpha = 1.0 - (node.depth as f64 / self.max_depth as f64);
139 lines.iter().for_each(|&[a, b]| {
140 Line { a: a.into(), b: b.into(), thickness: px }
141 .translate(rect.origin.to_vector())
142 .texture(Rgba([
143 ((1.0 - alpha).powi(2) * 255.0) as u8,
144 0,
145 128,
146 ((1.0 - alpha).powf(0.5) * 255.0) as u8])
147 )
148 .draw(image);
149 });
150 Ok(())
151 }).ok();
152 self
153 }
154
155 pub fn draw_bounding(&self, domain: euclid::Rect<_Float, WorldSpace>, image: &mut RgbaImage) -> &Self {
156 self.traverse(&mut |node| {
157 if node.children.is_none() && node.rect.intersects(&domain) {
158 let rect = node.rect.cast();
159 geometry::Rect {
160 size: rect.size.to_vector().to_point()
161 } .translate(rect.origin.to_vector() + rect.size.to_vector() * 0.5)
162 .texture(Rgba([0xFF, 0, 0, 0x7F]))
163 .draw(image)
164 }
165 Ok(())
166 }).ok();
167 self
168 }
169}
170
171impl <_Float: Float + Signed + AsPrimitive<f64>> ADF<_Float> {
172 pub fn display_sdf(&self, image: &mut RgbaImage, brightness: f64) -> &Self {
173 display_sdf(|p| self.sdf(p.cast()).to_f64().unwrap(), image, brightness);
174 self
175 }
176 pub fn draw_bucket_weights(&self, image: &mut RgbaImage) -> &Self {
177 self.tree.traverse(&mut |node| {
178 if node.children.is_none() {
179 let rect = node.rect;
180 let alpha = (((node.data.len() - 1) as f64 / 3.0).powf(1.75)
181 * 0.33 * 255.0) as u8;
182 geometry::Rect {
183 size: rect.size.to_vector().to_point()
184 } .translate(rect.origin.to_vector() + rect.size.to_vector() * _Float::from(0.5).unwrap())
185 .texture(Rgba([0x7F, 0xFF, 0, alpha]))
186 .draw(image)
187 }
188 Ok(())
189 }).ok();
190 self
191 }
192}