use crate::{DistancePixel, GeometryBuffer, Image, RenderConfig, TileSizesRef};
use fidget_core::{
eval::Function,
render::{CancelToken, ImageSize, ThreadPool, TileSizes, VoxelSize},
shape::{Shape, ShapeVars},
};
use nalgebra::{Const, Matrix3, Matrix4, OPoint, Point2, Vector2};
pub struct ImageRenderConfig<'a> {
pub image_size: ImageSize,
pub world_to_model: Matrix3<f32>,
pub pixel_perfect: bool,
pub tile_sizes: TileSizes,
pub threads: Option<&'a ThreadPool>,
pub cancel: CancelToken,
}
impl Default for ImageRenderConfig<'_> {
fn default() -> Self {
Self {
image_size: ImageSize::from(512),
tile_sizes: TileSizes::new(&[128, 32, 8]).unwrap(),
world_to_model: Matrix3::identity(),
pixel_perfect: false,
threads: Some(&ThreadPool::Global),
cancel: CancelToken::new(),
}
}
}
impl RenderConfig for ImageRenderConfig<'_> {
fn width(&self) -> u32 {
self.image_size.width()
}
fn height(&self) -> u32 {
self.image_size.height()
}
fn threads(&self) -> Option<&ThreadPool> {
self.threads
}
fn tile_sizes(&self) -> TileSizesRef<'_> {
let max_size = self.width().max(self.height()) as usize;
TileSizesRef::new(&self.tile_sizes, max_size)
}
fn is_cancelled(&self) -> bool {
self.cancel.is_cancelled()
}
}
impl ImageRenderConfig<'_> {
pub fn run<F: Function>(
&self,
shape: Shape<F>,
) -> Option<Image<DistancePixel>> {
self.run_with_vars::<F>(shape, &ShapeVars::new())
}
pub fn run_with_vars<F: Function>(
&self,
shape: Shape<F>,
vars: &ShapeVars<f32>,
) -> Option<Image<DistancePixel>> {
crate::render2d::<F>(shape, vars, self)
}
pub fn mat(&self) -> Matrix3<f32> {
self.world_to_model * self.image_size.screen_to_world()
}
}
pub struct VoxelRenderConfig<'a> {
pub image_size: VoxelSize,
pub world_to_model: Matrix4<f32>,
pub tile_sizes: TileSizes,
pub threads: Option<&'a ThreadPool>,
pub cancel: CancelToken,
}
impl Default for VoxelRenderConfig<'_> {
fn default() -> Self {
Self {
image_size: VoxelSize::from(512),
tile_sizes: TileSizes::new(&[128, 64, 32, 16, 8]).unwrap(),
world_to_model: Matrix4::identity(),
threads: Some(&ThreadPool::Global),
cancel: CancelToken::new(),
}
}
}
impl RenderConfig for VoxelRenderConfig<'_> {
fn width(&self) -> u32 {
self.image_size.width()
}
fn height(&self) -> u32 {
self.image_size.height()
}
fn threads(&self) -> Option<&ThreadPool> {
self.threads
}
fn tile_sizes(&self) -> TileSizesRef<'_> {
let max_size = self.width().max(self.height()) as usize;
TileSizesRef::new(&self.tile_sizes, max_size)
}
fn is_cancelled(&self) -> bool {
self.cancel.is_cancelled()
}
}
impl VoxelRenderConfig<'_> {
pub fn run<F: Function>(&self, shape: Shape<F>) -> Option<GeometryBuffer> {
self.run_with_vars::<F>(shape, &ShapeVars::new())
}
pub fn run_with_vars<F: Function>(
&self,
shape: Shape<F>,
vars: &ShapeVars<f32>,
) -> Option<GeometryBuffer> {
crate::render3d::<F>(shape, vars, self)
}
pub fn mat(&self) -> Matrix4<f32> {
self.world_to_model * self.image_size.screen_to_world()
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct Tile<const N: usize> {
pub corner: OPoint<usize, Const<N>>,
}
impl<const N: usize> Tile<N> {
#[inline]
pub(crate) fn new(corner: OPoint<usize, Const<N>>) -> Tile<N> {
Tile { corner }
}
pub(crate) fn add(&self, pos: Vector2<usize>) -> Point2<usize> {
let corner = Point2::new(self.corner[0], self.corner[1]);
corner + pos
}
}
#[cfg(test)]
mod test {
use super::*;
use fidget_core::render::ImageSize;
#[test]
fn test_default_render_config() {
let config = ImageRenderConfig {
image_size: ImageSize::from(512),
..Default::default()
};
let mat = config.mat();
assert_eq!(
mat.transform_point(&Point2::new(0.0, -1.0)),
Point2::new(-1.0, 1.0)
);
assert_eq!(
mat.transform_point(&Point2::new(512.0, -1.0)),
Point2::new(1.0, 1.0)
);
assert_eq!(
mat.transform_point(&Point2::new(512.0, 511.0)),
Point2::new(1.0, -1.0)
);
let config = ImageRenderConfig {
image_size: ImageSize::from(575),
..Default::default()
};
let mat = config.mat();
assert_eq!(
mat.transform_point(&Point2::new(0.0, -1.0)),
Point2::new(-1.0, 1.0)
);
assert_eq!(
mat.transform_point(&Point2::new(
config.image_size.width() as f32,
-1.0
)),
Point2::new(1.0, 1.0)
);
assert_eq!(
mat.transform_point(&Point2::new(
config.image_size.width() as f32,
config.image_size.height() as f32 - 1.0,
)),
Point2::new(1.0, -1.0)
);
}
}