mesh_graph/
primitives.rs

1use glam::{Vec3, vec3};
2use itertools::Itertools;
3
4use crate::MeshGraph;
5
6/// Single triangle
7#[derive(Debug, Clone, Copy)]
8pub struct Triangle(pub Vec3, pub Vec3, pub Vec3);
9
10/// Single quad consisting of two triangles
11#[derive(Debug, Clone, Copy)]
12pub struct Quad(pub Vec3, pub Vec3, pub Vec3, pub Vec3);
13
14/// Subdivided icosahedron to approximate a sphere
15#[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        // Create initial vertices of an icosahedron
45        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        // Normalize vertices to make them lie on the sphere surface
61        for v in &mut vertices {
62            *v = v.normalize();
63        }
64
65        // Define the 20 faces of the icosahedron
66        let mut faces = vec![
67            // 5 faces around point 0
68            [0, 11, 5],
69            [0, 5, 1],
70            [0, 1, 7],
71            [0, 7, 10],
72            [0, 10, 11],
73            // 5 adjacent faces
74            [1, 5, 9],
75            [5, 11, 4],
76            [11, 10, 2],
77            [10, 7, 6],
78            [7, 1, 8],
79            // 5 faces around point 3
80            [3, 9, 4],
81            [3, 4, 2],
82            [3, 2, 6],
83            [3, 6, 8],
84            [3, 8, 9],
85            // 5 adjacent faces
86            [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            // Temporary map to store midpoints of edges
97            let mut midpoints = std::collections::HashMap::new();
98
99            for face in &faces {
100                // Get the three vertices of the face
101                let v1 = face[0];
102                let v2 = face[1];
103                let v3 = face[2];
104
105                // Get or create the midpoints
106                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                // Create 4 new triangular faces
146                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}