@@ -21,6 +21,9 @@ use anyhow::{Context, Result};
2121use glam:: f32:: Vec3A ;
2222use 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