Skip to content

Commit 8cb5c16

Browse files
committed
Renormalize weights
1 parent 589316a commit 8cb5c16

File tree

1 file changed

+27
-20
lines changed

1 file changed

+27
-20
lines changed

src/icosphere.rs

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ use anyhow::{Context, Result};
2121
use glam::f32::Vec3A;
2222
use hexasphere::AdjacencyBuilder;
2323

24+
/// Surface area of a unit sphere.
25+
const UNIT_SPHERE_AREA: f64 = 4.0 * PI;
26+
2427
/// Make icosphere with at least `min_points` surface points (vertices).
2528
///
2629
/// This is done by iteratively subdividing the faces of an icosahedron
@@ -89,31 +92,35 @@ pub fn make_weights(icosphere: &IcoSphere) -> Vec<f64> {
8992

9093
// Loop over each neighboring face to i'th vertex
9194
for (i, neighbors) in adjency.finish().iter().enumerate() {
92-
let mut areas = Vec::with_capacity(neighbors.len());
95+
let mut area = 0.0;
9396
// Handle the face made of i with the first and last neighboring vertices
94-
areas.push(spherical_face_area(
97+
area += spherical_face_area(
9598
&vertices[i],
9699
&vertices[*neighbors.first().unwrap()],
97100
&vertices[*neighbors.last().unwrap()],
98-
));
101+
);
99102
// Handle the faces made of i with each remaining pairs of neighboring vertices
100103
for j in 0..neighbors.len() - 1 {
101-
let area = spherical_face_area(
104+
area += spherical_face_area(
102105
&vertices[i],
103106
&vertices[neighbors[j]],
104107
&vertices[neighbors[j + 1]],
105108
);
106-
areas.push(area);
107109
}
108-
let avg_area = areas.iter().sum::<f64>() / areas.len() as f64;
109-
weights.push(avg_area);
110+
// Faces contribute w. 1/3 of their area to a single vertex weight
111+
weights.push(area / 3.0);
110112
}
111-
assert_eq!(weights.len(), vertices.len());
113+
debug_assert_eq!(weights.len(), vertices.len());
114+
115+
// The sum of all vertex contributions should add up to 4π,
116+
// the surface area of a unit sphere
117+
let total_area = weights.iter().sum::<f64>();
118+
approx::assert_relative_eq!(total_area, UNIT_SPHERE_AREA, epsilon = 1e-4);
112119

113-
// Normalize the weights so that they fluctuate around 1.0
120+
// Normalize the weights so that they fluctuate around 1
114121
// (this has no effect on final results)
115-
let scale = vertices.len() as f64 / weights.iter().sum::<f64>();
116-
weights.iter_mut().for_each(|w| *w *= scale);
122+
let ideal_vertex_area = UNIT_SPHERE_AREA / vertices.len() as f64;
123+
weights.iter_mut().for_each(|w| *w /= ideal_vertex_area);
117124
weights
118125
}
119126

@@ -194,10 +201,10 @@ mod tests {
194201
.sum::<f64>()
195202
.sqrt()
196203
/ (weights.len() as f64).sqrt();
197-
assert_relative_eq!(min_weight, 0.9538671732382897, epsilon = 1e-6);
198-
assert_relative_eq!(max_weight, 1.0184529616689328, epsilon = 1e-6);
204+
assert_relative_eq!(min_weight, 0.8327130552002484, epsilon = 1e-6);
205+
assert_relative_eq!(max_weight, 1.0669150648124996, epsilon = 1e-6);
199206
assert_relative_eq!(mean_weight, 0.9999999999999999, epsilon = 1e-6);
200-
assert_relative_eq!(weight_stddev, 0.0291766407712738, epsilon = 1e-6);
207+
assert_relative_eq!(weight_stddev, 0.10580141129416724, epsilon = 1e-6);
201208

202209
let icosphere = make_icosphere(92).unwrap();
203210
let weights = make_weights(&icosphere);
@@ -210,10 +217,10 @@ mod tests {
210217
.sum::<f64>()
211218
.sqrt()
212219
/ (weights.len() as f64).sqrt();
213-
assert_relative_eq!(min_weight, 0.9399785391170831, epsilon = 1e-6);
214-
assert_relative_eq!(max_weight, 1.0320120385450426, epsilon = 1e-5);
215-
assert_relative_eq!(mean_weight, 0.9999999999999999, epsilon = 1e-6);
216-
assert_relative_eq!(weight_stddev, 0.028536625575550475, epsilon = 1e-6);
220+
assert_relative_eq!(min_weight, 0.7996549501752724, epsilon = 1e-6);
221+
assert_relative_eq!(max_weight, 1.053539416842339, epsilon = 1e-5);
222+
assert_relative_eq!(mean_weight, 1.0, epsilon = 1e-3);
223+
assert_relative_eq!(weight_stddev, 0.07941105694522466, epsilon = 1e-6);
217224
}
218225

219226
#[test]
@@ -242,7 +249,7 @@ mod tests {
242249
};
243250
let total_area: f64 = icosahedron.get_all_indices().chunks(3).map(to_area).sum();
244251
assert_eq!(vertices.len(), 12);
245-
assert_relative_eq!(total_area, 4.0 * PI, epsilon = 1e-5);
252+
assert_relative_eq!(total_area, UNIT_SPHERE_AREA, epsilon = 1e-5);
246253
}
247254
#[test]
248255
fn test_subdivided_icosahedron_face_areas() {
@@ -259,6 +266,6 @@ mod tests {
259266
};
260267
let total_area: f64 = icosahedron.get_all_indices().chunks(3).map(to_area).sum();
261268
assert_eq!(vertices.len(), 92);
262-
assert_relative_eq!(total_area, 4.0 * PI, epsilon = 1e-4);
269+
assert_relative_eq!(total_area, UNIT_SPHERE_AREA, epsilon = 1e-4);
263270
}
264271
}

0 commit comments

Comments
 (0)