use core::iter::{once, Once};
use crate::layout::ArcStepIterator;
use super::iterators::{GridStepIterator, LineStepIterator};
pub use glam::Vec3;
#[derive(Debug, Clone)]
pub enum Shape3d {
Point(Vec3),
Line {
start: Vec3,
end: Vec3,
pixel_count: usize,
},
Grid {
start: Vec3,
horizontal_end: Vec3,
vertical_end: Vec3,
horizontal_pixel_count: usize,
vertical_pixel_count: usize,
serpentine: bool,
},
Arc {
center: Vec3,
axis_u: Vec3,
axis_v: Vec3,
start_angle_in_radians: f32,
end_angle_in_radians: f32,
pixel_count: usize,
},
}
impl Shape3d {
pub const fn pixel_count(&self) -> usize {
match *self {
Shape3d::Point(_) => 1,
Shape3d::Line { pixel_count, .. } => pixel_count,
Shape3d::Grid {
horizontal_pixel_count,
vertical_pixel_count,
..
} => horizontal_pixel_count * vertical_pixel_count,
Shape3d::Arc { pixel_count, .. } => pixel_count,
}
}
pub fn points(&self) -> Shape3dPointsIterator {
match *self {
Shape3d::Point(point) => once(point).into(),
Shape3d::Line {
start,
end,
pixel_count,
} => {
let step = (end - start) / ((pixel_count - 1) as f32).max(1.);
LineStepIterator::new(start, step, pixel_count).into()
}
Shape3d::Grid {
start,
horizontal_end,
vertical_end,
horizontal_pixel_count,
vertical_pixel_count,
serpentine,
} => {
let horizontal_step =
(horizontal_end - start) / (horizontal_pixel_count as f32 - 1.).max(1.);
let vertical_step =
(vertical_end - start) / (vertical_pixel_count as f32 - 1.).max(1.);
GridStepIterator::new(
start,
vertical_step,
horizontal_step,
horizontal_pixel_count,
vertical_pixel_count,
serpentine,
)
.into()
}
Shape3d::Arc {
center,
axis_u,
axis_v,
start_angle_in_radians,
end_angle_in_radians,
pixel_count,
} => ArcStepIterator::new(
center,
axis_u,
axis_v,
start_angle_in_radians,
end_angle_in_radians,
pixel_count,
)
.into(),
}
}
}
pub trait Layout3d {
const PIXEL_COUNT: usize;
fn shapes() -> impl Iterator<Item = Shape3d>;
fn points() -> impl Iterator<Item = Vec3> {
Self::shapes().flat_map(|s| s.points())
}
}
#[derive(Debug)]
pub enum Shape3dPointsIterator {
Point(Once<Vec3>),
Line(LineStepIterator<Vec3, f32>),
Grid(GridStepIterator<Vec3, f32>),
Arc(ArcStepIterator<Vec3>),
}
impl Iterator for Shape3dPointsIterator {
type Item = Vec3;
fn next(&mut self) -> Option<Self::Item> {
match self {
Shape3dPointsIterator::Point(iter) => iter.next(),
Shape3dPointsIterator::Line(iter) => iter.next(),
Shape3dPointsIterator::Grid(iter) => iter.next(),
Shape3dPointsIterator::Arc(iter) => iter.next(),
}
}
}
impl From<Once<Vec3>> for Shape3dPointsIterator {
fn from(value: Once<Vec3>) -> Self {
Shape3dPointsIterator::Point(value)
}
}
impl From<LineStepIterator<Vec3, f32>> for Shape3dPointsIterator {
fn from(value: LineStepIterator<Vec3, f32>) -> Self {
Shape3dPointsIterator::Line(value)
}
}
impl From<GridStepIterator<Vec3, f32>> for Shape3dPointsIterator {
fn from(value: GridStepIterator<Vec3, f32>) -> Self {
Shape3dPointsIterator::Grid(value)
}
}
impl From<ArcStepIterator<Vec3>> for Shape3dPointsIterator {
fn from(value: ArcStepIterator<Vec3>) -> Self {
Shape3dPointsIterator::Arc(value)
}
}
#[macro_export]
macro_rules! layout3d {
($(#[$attr:meta])* $vis:vis $name:ident, [$($shape:expr),* $(,)?]) => {
$(#[$attr])*
$vis struct $name;
impl $crate::layout::Layout3d for $name {
const PIXEL_COUNT: usize = 0 $(+ $shape.pixel_count())*;
fn shapes() -> impl Iterator<Item = $crate::layout::Shape3d> {
[$($shape),*].into_iter()
}
}
};
}