use crate::{
math::{HasPosition, IndexType, Scalar, Vector},
mesh::{
DefaultEdgePayload, DefaultFacePayload, HalfEdge, HalfEdgeSemiBuilder, MeshType3D,
MeshTypeHalfEdge, SlerpVertexInterpolator,
},
operations::{MeshExtrude, MeshLoft, MeshSubdivision, SubdivisionDescription},
primitives::{Make2dShape, MakePrismatoid},
};
pub fn icosahedron_r2a<S: Scalar>(r: S) -> S {
S::FOUR * r / (S::TEN + S::TWO * S::FIVE.sqrt()).sqrt()
}
pub fn icosahedron_a2r<S: Scalar>(a: S) -> S {
(S::TEN + S::TWO * S::FIVE.sqrt()).sqrt() * a / S::FOUR
}
pub trait MakeSphere<T: MeshTypeHalfEdge<Mesh = Self> + MeshType3D<Mesh = Self>>:
MeshLoft<T> + MeshExtrude<T> + MeshSubdivision<T> + MakePrismatoid<T>
where
T::EP: DefaultEdgePayload,
T::FP: DefaultFacePayload,
{
fn uv_sphere(radius: T::S, n: usize, m: usize) -> Self {
assert!(n >= 2);
assert!(m >= 3);
let mut mesh = Self::default();
let sn = T::S::from_usize(n);
let sm = T::S::from_usize(m);
let make_vp = |i, j| {
let phi = T::S::PI * T::S::from_usize(i) / sn;
let theta = -T::S::PI * T::S::from_usize(2 * j + 4) / sm;
T::VP::from_pos(T::Vec::from_xyz(
radius * phi.sin() * theta.cos(),
radius * phi.cos(),
radius * phi.sin() * theta.sin(),
))
};
let mut prev = mesh.insert_loop((0..m).map(|j| make_vp(1, j)));
mesh.fill_hole_apex(mesh.edge(prev).twin_id(), make_vp(0, 0));
for i in 1..(n - 1) {
prev = mesh.loft_polygon_back(prev, 2, 2, (0..m).map(|j| make_vp(i + 1, j)));
}
mesh.fill_hole_apex(prev, make_vp(n, 0));
mesh
}
fn dodecahedron(radius: T::S) -> Self {
let phi = radius * T::S::PHI;
let iphi = radius / T::S::PHI;
let one = radius;
let zero = T::S::ZERO;
let make_vp = |x, y, z| T::VP::from_pos(T::Vec::from_xyz(x, y, z));
let mut mesh: Self = Make2dShape::polygon([
make_vp(one, one, one), make_vp(zero, phi, iphi), make_vp(-one, one, one), make_vp(-iphi, zero, phi), make_vp(iphi, zero, phi), ]);
let start = mesh.shared_edge_id(T::V::new(1), T::V::new(0)).unwrap();
let start_middle = mesh.loft_polygon_back(
start,
3,
2,
[
make_vp(phi, iphi, zero), make_vp(one, one, -one), make_vp(zero, phi, -iphi), make_vp(-one, one, -one), make_vp(-phi, iphi, zero), make_vp(-phi, -iphi, zero), make_vp(-one, -one, one), make_vp(zero, -phi, iphi), make_vp(one, -one, one), make_vp(phi, -iphi, zero), ],
);
let start_bottom = mesh.loft_polygon_back(
mesh.edge(start_middle).next_id(),
2,
3,
[
make_vp(one, -one, -one), make_vp(iphi, zero, -phi), make_vp(-iphi, zero, -phi), make_vp(-one, -one, -one), make_vp(zero, -phi, -iphi), ],
);
mesh.close_hole(start_bottom, Default::default(), false);
mesh
}
fn regular_icosahedron(l: T::S) -> Self {
let long = l * T::S::PHI * T::S::HALF;
let short = l * T::S::HALF;
let zero = T::S::ZERO;
let make_vp = |x, y, z| T::VP::from_pos(T::Vec::from_xyz(x, y, z));
let mut mesh = Self::default();
let start = mesh.insert_loop([
make_vp(zero, long, -short),
make_vp(long, short, zero),
make_vp(short, zero, long),
make_vp(-short, zero, long),
make_vp(-long, short, zero),
]);
mesh.fill_hole_apex(start, make_vp(zero, long, short));
let end = mesh.loft_tri_closed(
mesh.edge(start).twin_id(),
[
make_vp(short, zero, -long),
make_vp(long, -short, zero),
make_vp(zero, -long, short),
make_vp(-long, -short, zero),
make_vp(-short, zero, -long),
],
);
mesh.fill_hole_apex(end, make_vp(zero, -long, -short));
mesh
}
fn icosphere(radius: T::S, n: usize) -> Self
where
T::Mesh: HalfEdgeSemiBuilder<T>,
{
Self::geodesic_icosahedron(radius, n)
}
fn geodesic_icosahedron(radius: T::S, n: usize) -> Self
where
T::Mesh: HalfEdgeSemiBuilder<T>,
{
let mut mesh = Self::regular_icosahedron(icosahedron_r2a(radius));
debug_assert!(mesh.centroid().is_about(&T::Vec::zero(), T::S::EPS));
mesh.subdivision_frequency(
SubdivisionDescription::new(n, 0),
SlerpVertexInterpolator::new(T::Vec::zero(), radius),
);
mesh
}
fn geodesic_tetrahedron(radius: T::S, n: usize) -> Self
where
T::Mesh: HalfEdgeSemiBuilder<T>,
{
let mut mesh = Self::regular_tetrahedron(radius);
debug_assert!(mesh.centroid().is_about(&T::Vec::zero(), T::S::EPS));
mesh.subdivision_frequency(
SubdivisionDescription::new(n, 0),
SlerpVertexInterpolator::new(T::Vec::zero(), radius),
);
mesh
}
fn geodesic_octahedron(radius: T::S, n: usize) -> Self
where
T::Mesh: HalfEdgeSemiBuilder<T>,
{
let mut mesh = Self::regular_octahedron(radius);
debug_assert!(mesh.centroid().is_about(&T::Vec::zero(), T::S::EPS));
mesh.subdivision_frequency(
SubdivisionDescription::new(n, 0),
SlerpVertexInterpolator::new(T::Vec::zero(), radius),
);
mesh
}
}