use crate::{
math::{HasPosition, Scalar, TransformTrait, Vector},
mesh::{
DefaultEdgePayload, DefaultFacePayload, Face3d, HalfEdge, MeshType3D, MeshTypeHalfEdge,
VertexPayload,
},
operations::{MeshExtrude, MeshLoft, MeshSubdivision},
primitives::polygon::Make2dShape,
};
use super::regular_polygon_sidelength;
fn circle_iter<
const D: usize,
S: Scalar,
Vec: Vector<S, D>,
VP: VertexPayload + HasPosition<D, Vec, S = S>,
>(
r: S,
n: usize,
shift: S,
y: S,
) -> impl Iterator<Item = VP> {
(0..n).map(move |i| {
let v = S::PI * (S::from_usize(2 * i) + shift) / S::from_usize(n);
VP::from_pos(Vec::from_xyz(r * v.cos(), y, -r * v.sin()))
})
}
pub trait MakePrismatoid<T: MeshTypeHalfEdge<Mesh = Self> + MeshType3D<Mesh = Self>>:
Make2dShape<T> + MeshExtrude<T> + MeshLoft<T> + MeshSubdivision<T>
where
T::EP: DefaultEdgePayload,
T::FP: DefaultFacePayload,
{
fn insert_prism(&mut self, vp: impl IntoIterator<Item = T::VP>, height: T::S) -> T::E {
let first = self.insert_polygon(vp);
let twin = self.edge(first).twin(self);
let f = twin.face(self).expect("The polygon must have a face");
let normal = Face3d::normal(f, self).normalize();
let e = self.extrude(first, T::Trans::from_translation(-normal * height));
e
}
fn prism(vp: impl IntoIterator<Item = T::VP>, height: T::S) -> Self {
let mut mesh = Self::default();
mesh.insert_prism(vp, height);
mesh
}
fn insert_antiprism(&mut self, vp: impl IntoIterator<Item = T::VP>, height: T::S) -> T::E {
let first = self.insert_polygon(vp);
let f = self
.edge(first)
.face(self)
.expect("The polygon must have a face");
let normal = f.normal(self).normalize();
let e = self.extrude_tri2(
self.edge(first).twin_id(),
T::Trans::from_translation(-normal * height),
);
e
}
fn antiprism(vp: impl IntoIterator<Item = T::VP>, height: T::S) -> Self {
let mut mesh = Self::default();
mesh.insert_antiprism(vp, height);
mesh
}
fn insert_antiprism_iter(
&mut self,
vp: impl IntoIterator<Item = T::VP>,
vp2: impl IntoIterator<Item = T::VP>,
) -> T::E {
let first = self.insert_polygon(vp);
let e = self.loft_tri_closed(first, vp2);
self.close_hole(e, Default::default(), false);
e
}
fn antiprism_iter(
vp: impl IntoIterator<Item = T::VP>,
vp2: impl IntoIterator<Item = T::VP>,
) -> Self {
let mut mesh = Self::default();
mesh.insert_antiprism_iter(vp, vp2);
mesh
}
fn insert_pyramid(&mut self, base: impl IntoIterator<Item = T::VP>, apex: T::VP) -> T::E {
let first = self.insert_polygon(base);
self.fill_hole_apex(first, apex);
self.edge(first).twin_id()
}
fn pyramid(base: impl IntoIterator<Item = T::VP>, apex: T::VP) -> Self {
let mut mesh = Self::default();
mesh.insert_pyramid(base, apex);
mesh
}
fn insert_frustum(
&mut self,
base: impl IntoIterator<Item = T::VP>,
top: impl IntoIterator<Item = T::VP>,
smooth: bool,
) -> T::E {
let first = self.insert_polygon(base);
let top_edge = self.loft_polygon(first, 2, 2, top);
self.close_hole(top_edge, Default::default(), false);
assert!(!smooth, "Smooth frustums not yet implemented");
top_edge
}
fn frustum(
base: impl IntoIterator<Item = T::VP>,
top: impl IntoIterator<Item = T::VP>,
smooth: bool,
) -> Self {
let mut mesh = Self::default();
mesh.insert_frustum(base, top, smooth);
mesh
}
fn cuboid(size: T::Vec) -> T::Mesh {
let p = size * T::S::HALF;
let mut mesh = Self::default();
let vp = |x, y, z| T::VP::from_pos(T::Vec::from_xyz(x, y, z));
let bottom_edge = mesh.insert_polygon([
vp(-p.x(), -p.y(), -p.z()),
vp(p.x(), -p.y(), -p.z()),
vp(p.x(), p.y(), -p.z()),
vp(-p.x(), p.y(), -p.z()),
]);
let top_edge = mesh.loft_polygon(
bottom_edge,
2,
2,
[
vp(-p.x(), -p.y(), p.z()),
vp(p.x(), -p.y(), p.z()),
vp(p.x(), p.y(), p.z()),
vp(-p.x(), p.y(), p.z()),
],
);
mesh.close_hole_default(top_edge);
mesh
}
fn cube(x: T::S) -> T::Mesh {
Self::cuboid(T::Vec::splat(x))
}
fn regular_hexahedron(l: T::S) -> T::Mesh {
Self::cube(l)
}
fn regular_pyramid(radius: T::S, height: T::S, n: usize) -> Self {
Self::pyramid(
circle_iter(radius, n, T::S::ZERO, T::S::ZERO),
T::VP::from_pos(T::Vec::from_xyz(T::S::ZERO, height, T::S::ZERO)),
)
}
fn cone(radius: T::S, height: T::S, n: usize) -> T::Mesh {
Self::pyramid(
circle_iter(radius, n, T::S::ZERO, T::S::ZERO),
T::VP::from_pos(T::Vec::from_xyz(T::S::ZERO, height, T::S::ZERO)),
)
}
fn regular_tetrahedron(l: T::S) -> T::Mesh {
let e = T::S::HALF * l;
let sq = e / T::S::TWO.sqrt();
let zero = T::S::ZERO;
Self::pyramid(
[
T::VP::from_pos(T::Vec::from_xyz(-e, zero, -sq)),
T::VP::from_pos(T::Vec::from_xyz(zero, -e, sq)),
T::VP::from_pos(T::Vec::from_xyz(e, zero, -sq)),
],
T::VP::from_pos(T::Vec::from_xyz(zero, e, sq)),
)
}
fn regular_octahedron(radius: T::S) -> T::Mesh {
let zero = T::S::ZERO;
let h = radius;
let mut mesh = Self::default();
let e = mesh.insert_pyramid(
circle_iter(radius, 4, T::S::ZERO, T::S::ZERO),
T::VP::from_pos(T::Vec::from_xyz(zero, h, zero)),
);
mesh.remove_face(mesh.edge(e).face_id());
mesh.fill_hole_apex(e, T::VP::from_pos(T::Vec::from_xyz(zero, -h, zero)));
mesh
}
fn regular_frustum(r1: T::S, r2: T::S, h: T::S, n: usize, smooth: bool) -> Self {
Self::frustum(
circle_iter(r1, n, T::S::ZERO, T::S::ZERO),
circle_iter(r2, n, T::S::ZERO, h),
smooth,
)
}
fn cylinder(radius: T::S, height: T::S, n: usize) -> T::Mesh {
Self::regular_frustum(radius, radius, height, n, true)
}
fn regular_prism(r: T::S, h: T::S, n: usize) -> Self {
Self::prism(circle_iter(r, n, T::S::ZERO, T::S::ZERO), h)
}
fn uniform_prism(r: T::S, n: usize) -> Self {
Self::regular_prism(r, regular_polygon_sidelength(r, n), n)
}
fn regular_antiprism(r: T::S, h: T::S, n: usize) -> Self {
Self::antiprism_iter(
circle_iter(r, n, T::S::ZERO, T::S::ZERO),
circle_iter(r, n, T::S::ONE, h),
)
}
fn uniform_antiprism(r: T::S, n: usize) -> Self {
Self::regular_antiprism(
r,
regular_polygon_sidelength(r, n) * T::S::THREE.sqrt() * T::S::HALF,
n,
)
}
}