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