1use crate::{DistancePixel, GeometryBuffer, Image, RenderConfig, TileSizesRef};
2use fidget_core::{
3 eval::Function,
4 render::{CancelToken, ImageSize, ThreadPool, TileSizes, VoxelSize},
5 shape::{Shape, ShapeVars},
6};
7use nalgebra::{Const, Matrix3, Matrix4, OPoint, Point2, Vector2};
8
9pub struct ImageRenderConfig<'a> {
11 pub image_size: ImageSize,
13
14 pub world_to_model: Matrix3<f32>,
16
17 pub pixel_perfect: bool,
19
20 pub tile_sizes: TileSizes,
26
27 pub threads: Option<&'a ThreadPool>,
32
33 pub cancel: CancelToken,
35}
36
37impl Default for ImageRenderConfig<'_> {
38 fn default() -> Self {
39 Self {
40 image_size: ImageSize::from(512),
41 tile_sizes: TileSizes::new(&[128, 32, 8]).unwrap(),
42 world_to_model: Matrix3::identity(),
43 pixel_perfect: false,
44 threads: Some(&ThreadPool::Global),
45 cancel: CancelToken::new(),
46 }
47 }
48}
49
50impl RenderConfig for ImageRenderConfig<'_> {
51 fn width(&self) -> u32 {
52 self.image_size.width()
53 }
54 fn height(&self) -> u32 {
55 self.image_size.height()
56 }
57 fn threads(&self) -> Option<&ThreadPool> {
58 self.threads
59 }
60 fn tile_sizes(&self) -> TileSizesRef<'_> {
61 let max_size = self.width().max(self.height()) as usize;
62 TileSizesRef::new(&self.tile_sizes, max_size)
63 }
64 fn is_cancelled(&self) -> bool {
65 self.cancel.is_cancelled()
66 }
67}
68
69impl ImageRenderConfig<'_> {
70 pub fn run<F: Function>(
72 &self,
73 shape: Shape<F>,
74 ) -> Option<Image<DistancePixel>> {
75 self.run_with_vars::<F>(shape, &ShapeVars::new())
76 }
77
78 pub fn run_with_vars<F: Function>(
80 &self,
81 shape: Shape<F>,
82 vars: &ShapeVars<f32>,
83 ) -> Option<Image<DistancePixel>> {
84 crate::render2d::<F>(shape, vars, self)
85 }
86
87 pub fn mat(&self) -> Matrix3<f32> {
89 self.world_to_model * self.image_size.screen_to_world()
90 }
91}
92
93pub struct VoxelRenderConfig<'a> {
95 pub image_size: VoxelSize,
101
102 pub world_to_model: Matrix4<f32>,
104
105 pub tile_sizes: TileSizes,
111
112 pub threads: Option<&'a ThreadPool>,
117
118 pub cancel: CancelToken,
120}
121
122impl Default for VoxelRenderConfig<'_> {
123 fn default() -> Self {
124 Self {
125 image_size: VoxelSize::from(512),
126 tile_sizes: TileSizes::new(&[128, 64, 32, 16, 8]).unwrap(),
127 world_to_model: Matrix4::identity(),
128 threads: Some(&ThreadPool::Global),
129 cancel: CancelToken::new(),
130 }
131 }
132}
133
134impl RenderConfig for VoxelRenderConfig<'_> {
135 fn width(&self) -> u32 {
136 self.image_size.width()
137 }
138 fn height(&self) -> u32 {
139 self.image_size.height()
140 }
141 fn threads(&self) -> Option<&ThreadPool> {
142 self.threads
143 }
144 fn tile_sizes(&self) -> TileSizesRef<'_> {
145 let max_size = self.width().max(self.height()) as usize;
146 TileSizesRef::new(&self.tile_sizes, max_size)
147 }
148 fn is_cancelled(&self) -> bool {
149 self.cancel.is_cancelled()
150 }
151}
152
153impl VoxelRenderConfig<'_> {
154 pub fn run<F: Function>(&self, shape: Shape<F>) -> Option<GeometryBuffer> {
163 self.run_with_vars::<F>(shape, &ShapeVars::new())
164 }
165
166 pub fn run_with_vars<F: Function>(
168 &self,
169 shape: Shape<F>,
170 vars: &ShapeVars<f32>,
171 ) -> Option<GeometryBuffer> {
172 crate::render3d::<F>(shape, vars, self)
173 }
174
175 pub fn mat(&self) -> Matrix4<f32> {
177 self.world_to_model * self.image_size.screen_to_world()
178 }
179}
180
181#[derive(Copy, Clone, Debug)]
184pub(crate) struct Tile<const N: usize> {
185 pub corner: OPoint<usize, Const<N>>,
187}
188
189impl<const N: usize> Tile<N> {
190 #[inline]
192 pub(crate) fn new(corner: OPoint<usize, Const<N>>) -> Tile<N> {
193 Tile { corner }
194 }
195
196 pub(crate) fn add(&self, pos: Vector2<usize>) -> Point2<usize> {
200 let corner = Point2::new(self.corner[0], self.corner[1]);
201 corner + pos
202 }
203}
204
205#[cfg(test)]
208mod test {
209 use super::*;
210 use fidget_core::render::ImageSize;
211
212 #[test]
213 fn test_default_render_config() {
214 let config = ImageRenderConfig {
215 image_size: ImageSize::from(512),
216 ..Default::default()
217 };
218 let mat = config.mat();
219 assert_eq!(
220 mat.transform_point(&Point2::new(0.0, -1.0)),
221 Point2::new(-1.0, 1.0)
222 );
223 assert_eq!(
224 mat.transform_point(&Point2::new(512.0, -1.0)),
225 Point2::new(1.0, 1.0)
226 );
227 assert_eq!(
228 mat.transform_point(&Point2::new(512.0, 511.0)),
229 Point2::new(1.0, -1.0)
230 );
231
232 let config = ImageRenderConfig {
233 image_size: ImageSize::from(575),
234 ..Default::default()
235 };
236 let mat = config.mat();
237 assert_eq!(
238 mat.transform_point(&Point2::new(0.0, -1.0)),
239 Point2::new(-1.0, 1.0)
240 );
241 assert_eq!(
242 mat.transform_point(&Point2::new(
243 config.image_size.width() as f32,
244 -1.0
245 )),
246 Point2::new(1.0, 1.0)
247 );
248 assert_eq!(
249 mat.transform_point(&Point2::new(
250 config.image_size.width() as f32,
251 config.image_size.height() as f32 - 1.0,
252 )),
253 Point2::new(1.0, -1.0)
254 );
255 }
256}