1use crate::config::Tile;
6use fidget_core::{
7 eval::Function,
8 render::{ImageSize, RenderHandle, ThreadPool, TileSizes, VoxelSize},
9 shape::{Shape, ShapeVars},
10};
11use nalgebra::Point2;
12use rayon::prelude::*;
13use zerocopy::{FromBytes, Immutable, IntoBytes};
14
15mod config;
16mod render2d;
17mod render3d;
18
19pub mod effects;
20pub use config::{ImageRenderConfig, VoxelRenderConfig};
21pub use render2d::DistancePixel;
22
23use render2d::render as render2d;
24use render3d::render as render3d;
25
26pub(crate) struct TileSizesRef<'a>(&'a [usize]);
31
32impl<'a> std::ops::Index<usize> for TileSizesRef<'a> {
33 type Output = usize;
34
35 fn index(&self, i: usize) -> &Self::Output {
36 &self.0[i]
37 }
38}
39
40impl TileSizesRef<'_> {
41 fn new(tiles: &TileSizes, max_size: usize) -> TileSizesRef<'_> {
43 let i = tiles
44 .iter()
45 .position(|t| *t < max_size)
46 .unwrap_or(tiles.len())
47 .saturating_sub(1);
48 TileSizesRef(&tiles[i..])
49 }
50
51 pub fn last(&self) -> usize {
53 *self.0.last().unwrap()
54 }
55
56 pub fn get(&self, i: usize) -> Option<usize> {
58 self.0.get(i).copied()
59 }
60
61 #[inline]
66 pub(crate) fn pixel_offset(&self, pos: Point2<usize>) -> usize {
67 let x = pos.x % self.0[0];
69 let y = pos.y % self.0[0];
70
71 x + y * self.0[0]
73 }
74}
75
76pub(crate) fn render_tiles<'a, F: Function, W: RenderWorker<'a, F, T>, T>(
83 shape: Shape<F, T>,
84 vars: &ShapeVars<f32>,
85 config: &'a W::Config,
86) -> Option<Vec<(Tile<2>, W::Output)>>
87where
88 W::Config: Send + Sync,
89 T: Sync,
90{
91 use rayon::prelude::*;
92
93 let tile_sizes = config.tile_sizes();
94
95 let mut tiles = vec![];
96 let t = tile_sizes[0];
97 let width = config.width() as usize;
98 let height = config.height() as usize;
99 for i in 0..width.div_ceil(t) {
100 for j in 0..height.div_ceil(t) {
101 tiles.push(Tile::new(Point2::new(
102 i * tile_sizes[0],
103 j * tile_sizes[0],
104 )));
105 }
106 }
107
108 let mut rh = RenderHandle::new(shape);
109
110 let _ = rh.i_tape(&mut vec![]); let init = || {
112 let rh = rh.clone();
113 let worker = W::new(config);
114 (worker, rh)
115 };
116
117 match config.threads() {
118 None => {
119 let mut worker = W::new(config);
120 tiles
121 .into_iter()
122 .map(|tile| {
123 if config.is_cancelled() {
124 Err(())
125 } else {
126 let pixels = worker.render_tile(&mut rh, vars, tile);
127 Ok((tile, pixels))
128 }
129 })
130 .collect::<Result<Vec<_>, ()>>()
131 .ok()
132 }
133
134 Some(p) => p.run(|| {
135 tiles
136 .into_par_iter()
137 .map_init(init, |(w, rh), tile| {
138 if config.is_cancelled() {
139 Err(())
140 } else {
141 let pixels = w.render_tile(rh, vars, tile);
142 Ok((tile, pixels))
143 }
144 })
145 .collect::<Result<Vec<_>, ()>>()
146 .ok()
147 }),
148 }
149}
150
151pub(crate) trait RenderConfig {
153 fn width(&self) -> u32;
154 fn height(&self) -> u32;
155 fn tile_sizes(&self) -> TileSizesRef<'_>;
156 fn threads(&self) -> Option<&ThreadPool>;
157 fn is_cancelled(&self) -> bool;
158}
159
160pub(crate) trait RenderWorker<'a, F: Function, T> {
162 type Config: RenderConfig;
163 type Output: Send;
164
165 fn new(cfg: &'a Self::Config) -> Self;
169
170 fn render_tile(
172 &mut self,
173 shape: &mut RenderHandle<F, T>,
174 vars: &ShapeVars<f32>,
175 tile: config::Tile<2>,
176 ) -> Self::Output;
177}
178
179#[derive(Clone)]
193pub struct Image<P, S = ImageSize> {
194 data: Vec<P>,
195 size: S,
196}
197
198pub trait ImageSizeLike {
200 fn width(&self) -> u32;
202 fn height(&self) -> u32;
204}
205
206impl ImageSizeLike for ImageSize {
207 fn width(&self) -> u32 {
208 self.width()
209 }
210 fn height(&self) -> u32 {
211 self.height()
212 }
213}
214
215impl ImageSizeLike for VoxelSize {
216 fn width(&self) -> u32 {
217 self.width()
218 }
219 fn height(&self) -> u32 {
220 self.height()
221 }
222}
223
224impl<P: Send, S: ImageSizeLike + Sync> Image<P, S> {
225 pub fn apply_effect<F: Fn(usize, usize) -> P + Send + Sync>(
230 &mut self,
231 f: F,
232 threads: Option<&ThreadPool>,
233 ) {
234 let r = |(y, row): (usize, &mut [P])| {
235 for (x, v) in row.iter_mut().enumerate() {
236 *v = f(x, y);
237 }
238 };
239
240 if let Some(threads) = threads {
241 threads.run(|| {
242 self.data
243 .par_chunks_mut(self.size.width() as usize)
244 .enumerate()
245 .for_each(r)
246 })
247 } else {
248 self.data
249 .chunks_mut(self.size.width() as usize)
250 .enumerate()
251 .for_each(r)
252 }
253 }
254}
255
256impl<P, S: Default> Default for Image<P, S> {
257 fn default() -> Self {
258 Image {
259 data: vec![],
260 size: S::default(),
261 }
262 }
263}
264
265impl<P: Default + Clone, S: ImageSizeLike> Image<P, S> {
266 pub fn new(size: S) -> Self {
268 Self {
269 data: vec![
270 P::default();
271 size.width() as usize * size.height() as usize
272 ],
273 size,
274 }
275 }
276}
277
278impl<P, S: Clone> Image<P, S> {
279 pub fn size(&self) -> S {
281 self.size.clone()
282 }
283
284 pub fn map<T, F: Fn(&P) -> T>(&self, f: F) -> Image<T, S> {
286 let data = self.data.iter().map(f).collect();
287 Image {
288 data,
289 size: self.size.clone(),
290 }
291 }
292
293 pub fn take(self) -> (Vec<P>, S) {
295 (self.data, self.size)
296 }
297}
298
299impl<P, S: ImageSizeLike> Image<P, S> {
300 pub fn width(&self) -> usize {
302 self.size.width() as usize
303 }
304
305 pub fn height(&self) -> usize {
307 self.size.height() as usize
308 }
309
310 fn decode_position(&self, pos: (usize, usize)) -> usize {
314 let (row, col) = pos;
315 assert!(
316 row < self.height(),
317 "row ({row}) must be less than image height ({})",
318 self.height()
319 );
320 assert!(
321 col < self.width(),
322 "column ({col}) must be less than image width ({})",
323 self.width()
324 );
325 row * self.width() + col
326 }
327}
328
329impl<P, S> Image<P, S> {
330 pub fn iter(&self) -> impl Iterator<Item = &P> + '_ {
332 self.data.iter()
333 }
334
335 pub fn len(&self) -> usize {
337 self.data.len()
338 }
339
340 pub fn is_empty(&self) -> bool {
342 self.data.is_empty()
343 }
344}
345
346impl<'a, P: 'a, S> IntoIterator for &'a Image<P, S> {
347 type Item = &'a P;
348 type IntoIter = std::slice::Iter<'a, P>;
349 fn into_iter(self) -> Self::IntoIter {
350 self.data.iter()
351 }
352}
353
354impl<P, S> IntoIterator for Image<P, S> {
355 type Item = P;
356 type IntoIter = std::vec::IntoIter<P>;
357 fn into_iter(self) -> Self::IntoIter {
358 self.data.into_iter()
359 }
360}
361
362impl<P, S> std::ops::Index<usize> for Image<P, S> {
363 type Output = P;
364 fn index(&self, index: usize) -> &Self::Output {
365 &self.data[index]
366 }
367}
368
369impl<P, S> std::ops::IndexMut<usize> for Image<P, S> {
370 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
371 &mut self.data[index]
372 }
373}
374
375macro_rules! define_image_index {
376 ($ty:ty) => {
377 impl<P, S> std::ops::Index<$ty> for Image<P, S> {
378 type Output = [P];
379 fn index(&self, index: $ty) -> &Self::Output {
380 &self.data[index]
381 }
382 }
383
384 impl<P, S> std::ops::IndexMut<$ty> for Image<P, S> {
385 fn index_mut(&mut self, index: $ty) -> &mut Self::Output {
386 &mut self.data[index]
387 }
388 }
389 };
390}
391
392define_image_index!(std::ops::Range<usize>);
393define_image_index!(std::ops::RangeTo<usize>);
394define_image_index!(std::ops::RangeFrom<usize>);
395define_image_index!(std::ops::RangeInclusive<usize>);
396define_image_index!(std::ops::RangeToInclusive<usize>);
397define_image_index!(std::ops::RangeFull);
398
399impl<P, S: ImageSizeLike> std::ops::Index<(usize, usize)> for Image<P, S> {
401 type Output = P;
402 fn index(&self, pos: (usize, usize)) -> &Self::Output {
403 let index = self.decode_position(pos);
404 &self.data[index]
405 }
406}
407
408impl<P, S: ImageSizeLike> std::ops::IndexMut<(usize, usize)> for Image<P, S> {
409 fn index_mut(&mut self, pos: (usize, usize)) -> &mut Self::Output {
410 let index = self.decode_position(pos);
411 &mut self.data[index]
412 }
413}
414
415#[repr(C)]
419#[derive(Debug, Default, Copy, Clone, IntoBytes, FromBytes, Immutable)]
420pub struct GeometryPixel {
421 pub depth: u32, pub normal: [f32; 3],
425}
426
427impl GeometryPixel {
428 pub fn to_color(&self) -> [u8; 3] {
430 let [dx, dy, dz] = self.normal;
431 let s = (dx.powi(2) + dy.powi(2) + dz.powi(2)).sqrt();
432 if s != 0.0 {
433 let scale = u8::MAX as f32 / s;
434 [
435 (dx.abs() * scale) as u8,
436 (dy.abs() * scale) as u8,
437 (dz.abs() * scale) as u8,
438 ]
439 } else {
440 [0; 3]
441 }
442 }
443}
444
445pub type GeometryBuffer = Image<GeometryPixel, VoxelSize>;
447
448impl<P: Default + Copy + Clone> Image<P, VoxelSize> {
449 pub fn depth(&self) -> usize {
451 self.size.depth() as usize
452 }
453}
454
455pub type ColorImage = Image<[u8; 3]>;