1#![warn(missing_docs)]
7use fidget_core::{
8 eval::Function,
9 render::{ImageSize, RenderHandle, ThreadPool, TileSizes},
10 shape::{Shape, ShapeVars},
11};
12use nalgebra::{Const, OPoint, Point2, Vector2};
13use rayon::prelude::*;
14use zerocopy::{Immutable, IntoBytes};
15
16pub mod effects;
17pub mod pixel;
18pub mod voxel;
19
20#[derive(Copy, Clone, Debug)]
21pub(crate) struct Tile<const N: usize> {
22 pub corner: OPoint<usize, Const<N>>,
24}
25
26impl<const N: usize> Tile<N> {
27 #[inline]
29 pub(crate) fn new(corner: OPoint<usize, Const<N>>) -> Tile<N> {
30 Tile { corner }
31 }
32
33 pub(crate) fn add(&self, pos: Vector2<usize>) -> Point2<usize> {
37 let corner = Point2::new(self.corner[0], self.corner[1]);
38 corner + pos
39 }
40}
41
42#[derive(Copy, Clone)]
47pub(crate) struct TileSizesRef<'a>(&'a [usize]);
48
49impl<'a> std::ops::Index<usize> for TileSizesRef<'a> {
50 type Output = usize;
51
52 fn index(&self, i: usize) -> &Self::Output {
53 &self.0[i]
54 }
55}
56
57impl TileSizesRef<'_> {
58 fn new(tiles: &TileSizes, max_size: usize) -> TileSizesRef<'_> {
60 let i = tiles
61 .iter()
62 .position(|t| *t < max_size)
63 .unwrap_or(tiles.len())
64 .saturating_sub(1);
65 TileSizesRef(&tiles[i..])
66 }
67
68 pub fn last(&self) -> usize {
70 *self.0.last().unwrap()
71 }
72
73 pub fn get(&self, i: usize) -> Option<usize> {
75 self.0.get(i).copied()
76 }
77
78 #[inline]
83 pub(crate) fn pixel_offset(&self, pos: Point2<usize>) -> usize {
84 let x = pos.x % self.0[0];
86 let y = pos.y % self.0[0];
87
88 x + y * self.0[0]
90 }
91}
92
93pub(crate) fn render_tiles<'a, F: Function, W: RenderWorker<'a, F>>(
100 shape: Shape<F>,
101 vars: &'a ShapeVars<f32>,
102 config: &'a W::Config,
103 tile_sizes: TileSizesRef<'a>,
104) -> Option<Vec<(Tile<2>, W::Output)>>
105where
106 W::Config: Send + Sync,
107{
108 use rayon::prelude::*;
109
110 let mut tiles = vec![];
111 let t = tile_sizes[0];
112 let width = config.width() as usize;
113 let height = config.height() as usize;
114 for i in 0..width.div_ceil(t) {
115 for j in 0..height.div_ceil(t) {
116 tiles.push(Tile::new(Point2::new(
117 i * tile_sizes[0],
118 j * tile_sizes[0],
119 )));
120 }
121 }
122
123 let mut rh = RenderHandle::new(shape);
124
125 let _ = rh.i_tape(&mut vec![]); let ts = tile_sizes;
127 let init = || {
128 let rh = rh.clone();
129 let worker = W::new(config, ts, vars);
130 (worker, rh)
131 };
132
133 match config.threads() {
134 None => {
135 let mut worker = W::new(config, tile_sizes, vars);
136 tiles
137 .into_iter()
138 .map(|tile| {
139 if config.is_cancelled() {
140 Err(())
141 } else {
142 let pixels = worker.render_tile(&mut rh, tile);
143 Ok((tile, pixels))
144 }
145 })
146 .collect::<Result<Vec<_>, ()>>()
147 .ok()
148 }
149
150 Some(p) => p.run(|| {
151 tiles
152 .into_par_iter()
153 .map_init(init, |(w, rh), tile| {
154 if config.is_cancelled() {
155 Err(())
156 } else {
157 let pixels = w.render_tile(rh, tile);
158 Ok((tile, pixels))
159 }
160 })
161 .collect::<Result<Vec<_>, ()>>()
162 .ok()
163 }),
164 }
165}
166
167pub(crate) trait RenderConfig: RenderSize {
169 fn threads(&self) -> Option<&ThreadPool>;
170 fn is_cancelled(&self) -> bool;
171}
172
173pub trait RenderSize {
175 fn width(&self) -> u32;
177 fn height(&self) -> u32;
179}
180
181pub(crate) trait RenderWorker<'a, F: Function> {
183 type Config: RenderConfig;
184 type Output: Send;
185
186 fn new(
190 cfg: &'a Self::Config,
191 tile_sizes: TileSizesRef<'a>,
192 vars: &'a ShapeVars<f32>,
193 ) -> Self;
194
195 fn render_tile(
197 &mut self,
198 shape: &mut RenderHandle<F>,
199 tile: Tile<2>,
200 ) -> Self::Output;
201}
202
203#[derive(Clone)]
220pub struct Image<P, S = ImageSize> {
221 data: Vec<P>,
222 size: S,
223}
224
225impl RenderSize for pixel::RenderSize {
226 fn width(&self) -> u32 {
227 self.width()
228 }
229 fn height(&self) -> u32 {
230 self.height()
231 }
232}
233
234impl RenderSize for voxel::RenderSize {
235 fn width(&self) -> u32 {
236 self.width()
237 }
238 fn height(&self) -> u32 {
239 self.height()
240 }
241}
242
243impl<P: Send, S: RenderSize + Sync> Image<P, S> {
244 pub fn apply_effect<F: Fn(usize, usize) -> P + Send + Sync>(
249 &mut self,
250 f: F,
251 threads: Option<&ThreadPool>,
252 ) {
253 let r = |(y, row): (usize, &mut [P])| {
254 for (x, v) in row.iter_mut().enumerate() {
255 *v = f(x, y);
256 }
257 };
258
259 if let Some(threads) = threads {
260 threads.run(|| {
261 self.data
262 .par_chunks_mut(self.size.width() as usize)
263 .enumerate()
264 .for_each(r)
265 })
266 } else {
267 self.data
268 .chunks_mut(self.size.width() as usize)
269 .enumerate()
270 .for_each(r)
271 }
272 }
273}
274
275impl<P: IntoBytes + Immutable, S: RenderSize> Image<P, S> {
276 pub fn as_bytes(&self) -> &[u8] {
278 self.data.as_bytes()
279 }
280}
281
282impl<P, S: Default> Default for Image<P, S> {
283 fn default() -> Self {
284 Image {
285 data: vec![],
286 size: S::default(),
287 }
288 }
289}
290
291impl<P: Default + Clone, S: RenderSize> Image<P, S> {
292 pub fn new(size: S) -> Self {
294 Self {
295 data: vec![
296 P::default();
297 size.width() as usize * size.height() as usize
298 ],
299 size,
300 }
301 }
302}
303
304impl<P, S: Clone> Image<P, S> {
305 pub fn size(&self) -> S {
307 self.size.clone()
308 }
309
310 pub fn map<T, F: Fn(&P) -> T>(&self, f: F) -> Image<T, S> {
312 let data = self.data.iter().map(f).collect();
313 Image {
314 data,
315 size: self.size.clone(),
316 }
317 }
318
319 pub fn as_slice(&self) -> &[P] {
321 &self.data
322 }
323
324 pub fn take(self) -> (Vec<P>, S) {
326 (self.data, self.size)
327 }
328}
329
330impl<P, S: RenderSize> Image<P, S> {
331 pub fn width(&self) -> usize {
333 self.size.width() as usize
334 }
335
336 pub fn height(&self) -> usize {
338 self.size.height() as usize
339 }
340
341 fn decode_position(&self, pos: (usize, usize)) -> usize {
345 let (row, col) = pos;
346 assert!(
347 row < self.height(),
348 "row ({row}) must be less than image height ({})",
349 self.height()
350 );
351 assert!(
352 col < self.width(),
353 "column ({col}) must be less than image width ({})",
354 self.width()
355 );
356 row * self.width() + col
357 }
358
359 pub fn build(data: Vec<P>, size: S) -> Result<Self, BadPixelCount> {
363 let expected = u64::from(size.width()) * u64::from(size.height());
364 let actual = data.len();
365 if expected != actual as u64 {
366 return Err(BadPixelCount {
367 expected,
368 actual,
369 width: size.width(),
370 height: size.height(),
371 });
372 }
373 Ok(Self { data, size })
374 }
375}
376
377impl<P, S> Image<P, S> {
378 pub fn iter(&self) -> impl Iterator<Item = &P> + '_ {
380 self.data.iter()
381 }
382
383 pub fn len(&self) -> usize {
385 self.data.len()
386 }
387
388 pub fn is_empty(&self) -> bool {
390 self.data.is_empty()
391 }
392}
393
394impl<'a, P: 'a, S> IntoIterator for &'a Image<P, S> {
395 type Item = &'a P;
396 type IntoIter = std::slice::Iter<'a, P>;
397 fn into_iter(self) -> Self::IntoIter {
398 self.data.iter()
399 }
400}
401
402impl<P, S> IntoIterator for Image<P, S> {
403 type Item = P;
404 type IntoIter = std::vec::IntoIter<P>;
405 fn into_iter(self) -> Self::IntoIter {
406 self.data.into_iter()
407 }
408}
409
410impl<P, S> std::ops::Index<usize> for Image<P, S> {
411 type Output = P;
412 fn index(&self, index: usize) -> &Self::Output {
413 &self.data[index]
414 }
415}
416
417impl<P, S> std::ops::IndexMut<usize> for Image<P, S> {
418 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
419 &mut self.data[index]
420 }
421}
422
423macro_rules! define_image_index {
424 ($ty:ty) => {
425 impl<P, S> std::ops::Index<$ty> for Image<P, S> {
426 type Output = [P];
427 fn index(&self, index: $ty) -> &Self::Output {
428 &self.data[index]
429 }
430 }
431
432 impl<P, S> std::ops::IndexMut<$ty> for Image<P, S> {
433 fn index_mut(&mut self, index: $ty) -> &mut Self::Output {
434 &mut self.data[index]
435 }
436 }
437 };
438}
439
440define_image_index!(std::ops::Range<usize>);
441define_image_index!(std::ops::RangeTo<usize>);
442define_image_index!(std::ops::RangeFrom<usize>);
443define_image_index!(std::ops::RangeInclusive<usize>);
444define_image_index!(std::ops::RangeToInclusive<usize>);
445define_image_index!(std::ops::RangeFull);
446
447impl<P, S: RenderSize> std::ops::Index<(usize, usize)> for Image<P, S> {
449 type Output = P;
450 fn index(&self, pos: (usize, usize)) -> &Self::Output {
451 let index = self.decode_position(pos);
452 &self.data[index]
453 }
454}
455
456impl<P, S: RenderSize> std::ops::IndexMut<(usize, usize)> for Image<P, S> {
457 fn index_mut(&mut self, pos: (usize, usize)) -> &mut Self::Output {
458 let index = self.decode_position(pos);
459 &mut self.data[index]
460 }
461}
462
463impl<P: Default + Copy + Clone> Image<P, voxel::RenderSize> {
464 pub fn depth(&self) -> usize {
466 self.size.depth() as usize
467 }
468}
469
470pub type ColorImage = Image<[u8; 3]>;
472
473#[derive(thiserror::Error, Debug, PartialEq)]
475#[error(
476 "bad pixel count: expected {expected} ({width} × {height}), got {actual}"
477)]
478pub struct BadPixelCount {
479 pub expected: u64,
481 pub actual: usize,
483 pub width: u32,
485 pub height: u32,
487}
488
489#[cfg(test)]
490mod test {
491 use super::*;
492
493 #[test]
494 fn image_construction() {
495 let i = Image::build(vec![1, 2, 3, 4, 5, 6], ImageSize::new(2, 3));
496 assert!(i.is_ok());
497
498 let i = Image::build(vec![1, 2, 3, 4, 5, 6], ImageSize::new(3, 2));
499 assert!(i.is_ok());
500
501 let i = Image::build(vec![1, 2, 3, 4, 5], ImageSize::new(2, 3));
502 let Err(e) = i else {
503 panic!("expected error, got valid image");
504 };
505 assert_eq!(
506 e,
507 BadPixelCount {
508 expected: 6,
509 actual: 5,
510 width: 2,
511 height: 3,
512 }
513 );
514 }
515}