1use glam::{Vec3, vec3};
2use itertools::Itertools;
3
4use crate::MeshGraph;
5
6#[derive(Debug, Clone, Copy)]
8pub struct Triangle(pub Vec3, pub Vec3, pub Vec3);
9
10#[derive(Debug, Clone, Copy)]
12pub struct Quad(pub Vec3, pub Vec3, pub Vec3, pub Vec3);
13
14#[derive(Debug, Clone, Copy)]
16pub struct IcoSphere {
17 pub radius: f32,
18 pub subdivisions: u8,
19}
20
21impl From<Triangle> for MeshGraph {
22 fn from(triangle: Triangle) -> Self {
23 let Triangle(a, b, c) = triangle;
24 MeshGraph::indexed_triangles(&[a, b, c], &[0, 1, 2])
25 }
26}
27
28impl From<Quad> for MeshGraph {
29 fn from(quad: Quad) -> Self {
30 let Quad(a, b, c, d) = quad;
31 MeshGraph::indexed_triangles(&[a, b, c, d], &[0, 1, 2, 0, 2, 3])
32 }
33}
34
35impl From<IcoSphere> for MeshGraph {
36 fn from(isosphere: IcoSphere) -> Self {
37 let IcoSphere {
38 radius,
39 subdivisions,
40 } = isosphere;
41
42 let t = (1.0 + 5_f32.sqrt()) / 2.0;
43
44 let mut vertices = vec![
46 vec3(-1.0, t, 0.0),
47 vec3(1.0, t, 0.0),
48 vec3(-1.0, -t, 0.0),
49 vec3(1.0, -t, 0.0),
50 vec3(0.0, -1.0, t),
51 vec3(0.0, 1.0, t),
52 vec3(0.0, -1.0, -t),
53 vec3(0.0, 1.0, -t),
54 vec3(t, 0.0, -1.0),
55 vec3(t, 0.0, 1.0),
56 vec3(-t, 0.0, -1.0),
57 vec3(-t, 0.0, 1.0),
58 ];
59
60 for v in &mut vertices {
62 *v = v.normalize();
63 }
64
65 let mut faces = vec![
67 [0, 11, 5],
69 [0, 5, 1],
70 [0, 1, 7],
71 [0, 7, 10],
72 [0, 10, 11],
73 [1, 5, 9],
75 [5, 11, 4],
76 [11, 10, 2],
77 [10, 7, 6],
78 [7, 1, 8],
79 [3, 9, 4],
81 [3, 4, 2],
82 [3, 2, 6],
83 [3, 6, 8],
84 [3, 8, 9],
85 [4, 9, 5],
87 [2, 4, 11],
88 [6, 2, 10],
89 [8, 6, 7],
90 [9, 8, 1],
91 ];
92
93 for _ in 0..subdivisions {
94 let mut new_faces = Vec::new();
95
96 let mut midpoints = std::collections::HashMap::new();
98
99 for face in &faces {
100 let v1 = face[0];
102 let v2 = face[1];
103 let v3 = face[2];
104
105 let mid12_idx = {
107 let edge = if v1 < v2 { (v1, v2) } else { (v2, v1) };
108 if let Some(&idx) = midpoints.get(&edge) {
109 idx
110 } else {
111 let mid_point = (vertices[v1] + vertices[v2]).normalize();
112 let idx = vertices.len();
113 vertices.push(mid_point);
114 midpoints.insert(edge, idx);
115 idx
116 }
117 };
118
119 let mid23_idx = {
120 let edge = if v2 < v3 { (v2, v3) } else { (v3, v2) };
121 if let Some(&idx) = midpoints.get(&edge) {
122 idx
123 } else {
124 let mid_point = (vertices[v2] + vertices[v3]).normalize();
125 let idx = vertices.len();
126 vertices.push(mid_point);
127 midpoints.insert(edge, idx);
128 idx
129 }
130 };
131
132 let mid31_idx = {
133 let edge = if v3 < v1 { (v3, v1) } else { (v1, v3) };
134 if let Some(&idx) = midpoints.get(&edge) {
135 idx
136 } else {
137 let mid_point = (vertices[v3] + vertices[v1]).normalize();
138 let idx = vertices.len();
139 vertices.push(mid_point);
140 midpoints.insert(edge, idx);
141 idx
142 }
143 };
144
145 new_faces.push([v1, mid12_idx, mid31_idx]);
147 new_faces.push([v2, mid23_idx, mid12_idx]);
148 new_faces.push([v3, mid31_idx, mid23_idx]);
149 new_faces.push([mid12_idx, mid23_idx, mid31_idx]);
150 }
151
152 faces = new_faces;
153 }
154
155 MeshGraph::indexed_triangles(
156 &vertices.into_iter().map(|v| v * radius).collect_vec(),
157 &faces.into_iter().flatten().collect_vec(),
158 )
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_triangle_conversion() {
168 let triangle = Triangle(
169 Vec3::new(0.0, 0.0, 0.0),
170 Vec3::new(1.0, 0.0, 0.0),
171 Vec3::new(0.0, 1.0, 0.0),
172 );
173 let mesh_graph = MeshGraph::from(triangle);
174
175 assert_eq!(mesh_graph.vertices.len(), 3);
176 assert_eq!(mesh_graph.faces.len(), 1);
177 assert_eq!(mesh_graph.halfedges.len(), 3);
178 }
179
180 #[test]
181 fn test_quad_conversion() {
182 let quad = Quad(
183 Vec3::new(0.0, 0.0, 0.0),
184 Vec3::new(1.0, 0.0, 0.0),
185 Vec3::new(1.0, 1.0, 0.0),
186 Vec3::new(0.0, 1.0, 0.0),
187 );
188 let mesh_graph = MeshGraph::from(quad);
189
190 assert_eq!(mesh_graph.vertices.len(), 4);
191 assert_eq!(mesh_graph.faces.len(), 2);
192 assert_eq!(mesh_graph.halfedges.len(), 6);
193 }
194}