1use std::collections::HashMap;
4
5#[cfg(test)]
6pub fn create_icosphere(subdivisions: usize) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
14 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
15
16 let mut vertices: Vec<[f64; 3]> = vec![
18 [-1.0, phi, 0.0], [ 1.0, phi, 0.0], [-1.0, -phi, 0.0], [ 1.0, -phi, 0.0],
19 [ 0.0, -1.0, phi], [ 0.0, 1.0, phi], [ 0.0, -1.0, -phi], [ 0.0, 1.0, -phi],
20 [ phi, 0.0, -1.0], [ phi, 0.0, 1.0], [-phi, 0.0, -1.0], [-phi, 0.0, 1.0],
21 ];
22
23 let norm = (vertices[0][0].powi(2) + vertices[0][1].powi(2) + vertices[0][2].powi(2)).sqrt();
25 for v in vertices.iter_mut() {
26 v[0] /= norm;
27 v[1] /= norm;
28 v[2] /= norm;
29 }
30
31 let mut faces: Vec<[usize; 3]> = vec![
33 [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11],
34 [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8],
35 [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9],
36 [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1],
37 ];
38
39 for _ in 0..subdivisions {
41 let (new_vertices, new_faces) = subdivide_icosphere(&vertices, &faces);
42 vertices = new_vertices;
43 faces = new_faces;
44 }
45
46 (vertices, faces)
47}
48
49#[cfg(test)]
50fn subdivide_icosphere(vertices: &[[f64; 3]], faces: &[[usize; 3]]) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
52 let mut new_vertices: Vec<[f64; 3]> = vertices.to_vec();
53 let mut edge_midpoints: HashMap<(usize, usize), usize> = HashMap::new();
54 let mut new_faces: Vec<[usize; 3]> = Vec::with_capacity(faces.len() * 4);
55
56 let mut get_midpoint = |i1: usize, i2: usize, verts: &mut Vec<[f64; 3]>| -> usize {
57 let key = if i1 < i2 { (i1, i2) } else { (i2, i1) };
58 if let Some(&idx) = edge_midpoints.get(&key) {
59 return idx;
60 }
61
62 let v1 = verts[i1];
63 let v2 = verts[i2];
64 let mut mid = [
65 (v1[0] + v2[0]) / 2.0,
66 (v1[1] + v2[1]) / 2.0,
67 (v1[2] + v2[2]) / 2.0,
68 ];
69
70 let norm = (mid[0].powi(2) + mid[1].powi(2) + mid[2].powi(2)).sqrt();
72 mid[0] /= norm;
73 mid[1] /= norm;
74 mid[2] /= norm;
75
76 let idx = verts.len();
77 verts.push(mid);
78 edge_midpoints.insert(key, idx);
79 idx
80 };
81
82 for &[v0, v1, v2] in faces {
83 let m01 = get_midpoint(v0, v1, &mut new_vertices);
84 let m12 = get_midpoint(v1, v2, &mut new_vertices);
85 let m20 = get_midpoint(v2, v0, &mut new_vertices);
86
87 new_faces.push([v0, m01, m20]);
88 new_faces.push([v1, m12, m01]);
89 new_faces.push([v2, m20, m12]);
90 new_faces.push([m01, m12, m20]);
91 }
92
93 (new_vertices, new_faces)
94}
95
96pub fn bet2_icosa(n: usize) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
104 const TAU: f64 = 0.8506508084;
105 const ONE: f64 = 0.5257311121;
106 let mut pts: Vec<[f64; 3]> = vec![
108 [TAU, ONE, 0.0], [-TAU, ONE, 0.0], [-TAU, -ONE, 0.0], [TAU, -ONE, 0.0],
109 [ONE, 0.0, TAU], [ONE, 0.0, -TAU], [-ONE, 0.0, -TAU], [-ONE, 0.0, TAU],
110 [0.0, TAU, ONE], [0.0, -TAU, ONE], [0.0, -TAU, -ONE], [0.0, TAU, -ONE],
111 ];
112 let (za, zb, zc, zd, ya, yb, yc, yd, xa, xb, xc, xd) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
113 let mut tris: Vec<[usize; 3]> = vec![
114 [ya, xa, yd], [ya, yd, xb], [yb, yc, xd], [yb, xc, yc], [za, ya, zd],
115 [za, zd, yb], [zc, yd, zb], [zc, zb, yc], [xa, za, xd], [xa, xd, zb],
116 [xb, xc, zd], [xb, zc, xc], [xa, ya, za], [xd, za, yb], [ya, xb, zd],
117 [yb, zd, xc], [yd, xa, zb], [yc, zb, xd], [yd, zc, xb], [yc, xc, zc],
118 ];
119 for t in tris.iter_mut() {
120 t.swap(1, 2); }
122 for _ in 1..n {
123 let mut added: HashMap<[u64; 3], usize> = HashMap::new();
124 let mut new_tris = Vec::with_capacity(tris.len() * 4);
125 let mid = |a: [f64; 3], b: [f64; 3]| [(a[0] + b[0]) / 2.0, (a[1] + b[1]) / 2.0, (a[2] + b[2]) / 2.0];
126 for &[v0, v1, v2] in &tris {
127 let pt0 = mid(pts[v1], pts[v2]);
128 let pt2 = mid(pts[v0], pts[v1]);
129 let pt1 = mid(pts[v0], pts[v2]);
130 let mut get = |pt: [f64; 3], pts: &mut Vec<[f64; 3]>| -> usize {
131 let key = [pt[0].to_bits(), pt[1].to_bits(), pt[2].to_bits()];
133 *added.entry(key).or_insert_with(|| {
134 pts.push(pt);
135 pts.len() - 1
136 })
137 };
138 let p0 = get(pt0, &mut pts);
139 let p1 = get(pt1, &mut pts);
140 let p2 = get(pt2, &mut pts);
141 new_tris.push([p2, p0, p1]);
142 new_tris.push([p1, v0, p2]);
143 new_tris.push([p0, v2, p1]);
144 new_tris.push([p2, v1, p0]);
145 }
146 tris = new_tris;
147 for p in pts.iter_mut() {
148 let norm = (p[0] * p[0] + p[1] * p[1] + p[2] * p[2]).sqrt();
149 let inv = 1.0 / norm;
150 *p = [p[0] * inv, p[1] * inv, p[2] * inv];
151 }
152 }
153 (pts, tris)
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_icosphere_subdivisions() {
162 let (v0, f0) = create_icosphere(0);
164 assert_eq!(v0.len(), 12);
165 assert_eq!(f0.len(), 20);
166
167 let (v1, f1) = create_icosphere(1);
169 assert_eq!(v1.len(), 42);
170 assert_eq!(f1.len(), 80);
171
172 let (v4, f4) = create_icosphere(4);
174 assert_eq!(v4.len(), 2562);
175 assert_eq!(f4.len(), 5120);
176 }
177
178 #[test]
179 fn test_vertices_on_unit_sphere() {
180 let (vertices, _) = create_icosphere(2);
181 for v in vertices {
182 let norm = (v[0].powi(2) + v[1].powi(2) + v[2].powi(2)).sqrt();
183 assert!((norm - 1.0).abs() < 1e-10, "Vertex not on unit sphere: norm = {}", norm);
184 }
185 }
186}