use std::collections::HashMap;
use nalgebra::SVector;
use cartan_manifolds::euclidean::Euclidean;
use cartan_manifolds::sphere::Sphere;
use crate::mesh::Mesh;
use crate::mesh_quality::{is_well_centered, make_delaunay, make_well_centered};
pub fn icosphere(manifold: &Sphere<3>, level: usize, well_centered: bool) -> Mesh<Sphere<3>, 3, 2> {
let (mut vertices, mut triangles) = icosahedron_seed();
for _ in 0..level {
let (v, t) = subdivide_sphere(&vertices, &triangles);
vertices = v;
triangles = t;
}
let points: Vec<SVector<f64, 3>> = vertices
.iter()
.map(|v| SVector::<f64, 3>::new(v[0], v[1], v[2]))
.collect();
let mut mesh = Mesh::from_simplices_generic(manifold, points, triangles);
if well_centered {
mesh = make_delaunay(mesh, manifold);
if !is_well_centered(&mesh, manifold) {
mesh = make_well_centered(mesh, manifold, 50, 1e-8);
}
}
mesh
}
pub fn torus(
manifold: &Euclidean<3>,
major_radius: f64,
minor_radius: f64,
n_major: usize,
n_minor: usize,
well_centered: bool,
) -> Mesh<Euclidean<3>, 3, 2> {
let nv = n_major * n_minor;
let mut vertices = Vec::with_capacity(nv);
for i in 0..n_major {
let theta = 2.0 * std::f64::consts::PI * i as f64 / n_major as f64;
for j in 0..n_minor {
let phi = 2.0 * std::f64::consts::PI * j as f64 / n_minor as f64;
let x = (major_radius + minor_radius * phi.cos()) * theta.cos();
let y = (major_radius + minor_radius * phi.cos()) * theta.sin();
let z = minor_radius * phi.sin();
vertices.push(SVector::<f64, 3>::new(x, y, z));
}
}
let mut triangles = Vec::with_capacity(2 * nv);
for i in 0..n_major {
for j in 0..n_minor {
let v00 = i * n_minor + j;
let v10 = ((i + 1) % n_major) * n_minor + j;
let v01 = i * n_minor + (j + 1) % n_minor;
let v11 = ((i + 1) % n_major) * n_minor + (j + 1) % n_minor;
triangles.push([v00, v10, v01]);
triangles.push([v10, v11, v01]);
}
}
let mut mesh = Mesh::from_simplices_generic(manifold, vertices, triangles);
if well_centered {
mesh = make_delaunay(mesh, manifold);
if !is_well_centered(&mesh, manifold) {
mesh = make_well_centered(mesh, manifold, 50, 1e-8);
}
}
mesh
}
pub fn torus_gaussian_curvature(
major_radius: f64,
minor_radius: f64,
n_major: usize,
n_minor: usize,
) -> Vec<f64> {
let mut curvatures = Vec::with_capacity(n_major * n_minor);
for _i in 0..n_major {
for j in 0..n_minor {
let phi = 2.0 * std::f64::consts::PI * j as f64 / n_minor as f64;
let k = phi.cos() / (minor_radius * (major_radius + minor_radius * phi.cos()));
curvatures.push(k);
}
}
curvatures
}
fn icosahedron_seed() -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
let norm = (1.0 + phi * phi).sqrt();
let a = 1.0 / norm;
let b = phi / norm;
let vertices = vec![
[-a, b, 0.0],
[a, b, 0.0],
[-a, -b, 0.0],
[a, -b, 0.0],
[0.0, -a, b],
[0.0, a, b],
[0.0, -a, -b],
[0.0, a, -b],
[b, 0.0, -a],
[b, 0.0, a],
[-b, 0.0, -a],
[-b, 0.0, a],
];
let triangles = vec![
[0, 11, 5],
[0, 5, 1],
[0, 1, 7],
[0, 7, 10],
[0, 10, 11],
[1, 5, 9],
[5, 11, 4],
[11, 10, 2],
[10, 7, 6],
[7, 1, 8],
[3, 9, 4],
[3, 4, 2],
[3, 2, 6],
[3, 6, 8],
[3, 8, 9],
[4, 9, 5],
[2, 4, 11],
[6, 2, 10],
[8, 6, 7],
[9, 8, 1],
];
(vertices, triangles)
}
fn subdivide_sphere(
vertices: &[[f64; 3]],
triangles: &[[usize; 3]],
) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
let mut new_verts = vertices.to_vec();
let mut midpoint_cache: HashMap<(usize, usize), usize> = HashMap::new();
let mut new_tris = Vec::with_capacity(triangles.len() * 4);
for &[v0, v1, v2] in triangles {
let a = get_midpoint(v0, v1, &mut new_verts, &mut midpoint_cache);
let b = get_midpoint(v1, v2, &mut new_verts, &mut midpoint_cache);
let c = get_midpoint(v2, v0, &mut new_verts, &mut midpoint_cache);
new_tris.push([v0, a, c]);
new_tris.push([v1, b, a]);
new_tris.push([v2, c, b]);
new_tris.push([a, b, c]);
}
(new_verts, new_tris)
}
fn get_midpoint(
v0: usize,
v1: usize,
verts: &mut Vec<[f64; 3]>,
cache: &mut HashMap<(usize, usize), usize>,
) -> usize {
let key = if v0 < v1 { (v0, v1) } else { (v1, v0) };
if let Some(&idx) = cache.get(&key) {
return idx;
}
let mid = [
(verts[v0][0] + verts[v1][0]) / 2.0,
(verts[v0][1] + verts[v1][1]) / 2.0,
(verts[v0][2] + verts[v1][2]) / 2.0,
];
let len = (mid[0] * mid[0] + mid[1] * mid[1] + mid[2] * mid[2]).sqrt();
let projected = [mid[0] / len, mid[1] / len, mid[2] / len];
let idx = verts.len();
verts.push(projected);
cache.insert(key, idx);
idx
}