use glam::{Vec3, vec3};
use itertools::Itertools;
use crate::MeshGraph;
#[derive(Debug, Clone, Copy)]
pub struct Triangle(pub Vec3, pub Vec3, pub Vec3);
#[derive(Debug, Clone, Copy)]
pub struct Quad(pub Vec3, pub Vec3, pub Vec3, pub Vec3);
#[derive(Debug, Clone, Copy)]
pub struct IcoSphere {
pub radius: f32,
pub subdivisions: u8,
}
impl From<Triangle> for MeshGraph {
fn from(triangle: Triangle) -> Self {
let Triangle(a, b, c) = triangle;
MeshGraph::indexed_triangles(&[a, b, c], &[0, 1, 2])
}
}
impl From<Quad> for MeshGraph {
fn from(quad: Quad) -> Self {
let Quad(a, b, c, d) = quad;
MeshGraph::indexed_triangles(&[a, b, c, d], &[0, 1, 2, 0, 2, 3])
}
}
impl From<IcoSphere> for MeshGraph {
fn from(isosphere: IcoSphere) -> Self {
let IcoSphere {
radius,
subdivisions,
} = isosphere;
let t = (1.0 + 5_f32.sqrt()) / 2.0;
let mut vertices = vec![
vec3(-1.0, t, 0.0),
vec3(1.0, t, 0.0),
vec3(-1.0, -t, 0.0),
vec3(1.0, -t, 0.0),
vec3(0.0, -1.0, t),
vec3(0.0, 1.0, t),
vec3(0.0, -1.0, -t),
vec3(0.0, 1.0, -t),
vec3(t, 0.0, -1.0),
vec3(t, 0.0, 1.0),
vec3(-t, 0.0, -1.0),
vec3(-t, 0.0, 1.0),
];
for v in &mut vertices {
*v = v.normalize();
}
let mut faces = 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],
];
for _ in 0..subdivisions {
let mut new_faces = Vec::new();
let mut midpoints = std::collections::HashMap::new();
for face in &faces {
let v1 = face[0];
let v2 = face[1];
let v3 = face[2];
let mid12_idx = {
let edge = if v1 < v2 { (v1, v2) } else { (v2, v1) };
if let Some(&idx) = midpoints.get(&edge) {
idx
} else {
let mid_point = (vertices[v1] + vertices[v2]).normalize();
let idx = vertices.len();
vertices.push(mid_point);
midpoints.insert(edge, idx);
idx
}
};
let mid23_idx = {
let edge = if v2 < v3 { (v2, v3) } else { (v3, v2) };
if let Some(&idx) = midpoints.get(&edge) {
idx
} else {
let mid_point = (vertices[v2] + vertices[v3]).normalize();
let idx = vertices.len();
vertices.push(mid_point);
midpoints.insert(edge, idx);
idx
}
};
let mid31_idx = {
let edge = if v3 < v1 { (v3, v1) } else { (v1, v3) };
if let Some(&idx) = midpoints.get(&edge) {
idx
} else {
let mid_point = (vertices[v3] + vertices[v1]).normalize();
let idx = vertices.len();
vertices.push(mid_point);
midpoints.insert(edge, idx);
idx
}
};
new_faces.push([v1, mid12_idx, mid31_idx]);
new_faces.push([v2, mid23_idx, mid12_idx]);
new_faces.push([v3, mid31_idx, mid23_idx]);
new_faces.push([mid12_idx, mid23_idx, mid31_idx]);
}
faces = new_faces;
}
MeshGraph::indexed_triangles(
&vertices.into_iter().map(|v| v * radius).collect_vec(),
&faces.into_iter().flatten().collect_vec(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_triangle_conversion() {
let triangle = Triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let mesh_graph = MeshGraph::from(triangle);
assert_eq!(mesh_graph.vertices.len(), 3);
assert_eq!(mesh_graph.faces.len(), 1);
assert_eq!(mesh_graph.halfedges.len(), 6);
}
#[test]
fn test_quad_conversion() {
let quad = Quad(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let mesh_graph = MeshGraph::from(quad);
assert_eq!(mesh_graph.vertices.len(), 4);
assert_eq!(mesh_graph.faces.len(), 2);
assert_eq!(mesh_graph.halfedges.len(), 10);
}
}