From 28faafdc417be1a24ed649fc1c30048ee543fa3f Mon Sep 17 00:00:00 2001 From: Robin KAY Date: Sun, 25 Aug 2024 15:11:58 +0100 Subject: [PATCH] Fix tiny seam in Annulus geometry. (#14913) # Objective There is a tiny seam at the top of the annulus caused by normal floating-point error in calculating the coordinates. When generating the last pair of triangles, given `n == i` then `(TAU / n) * i` does not equal `TAU` exactly. Fixes https://github.com/komadori/bevy_mod_outline/issues/42 ## Solution This can be fixed by changing the calculation so that `(TAU / n) * (i % n) == 0.0`, which is equivalent for trigonometric purposes. ## Testing Added the unit test `bevy_render::mesh::primitives::dim2::tests::test_annulus`. --- .../bevy_render/src/mesh/primitives/dim2.rs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/bevy_render/src/mesh/primitives/dim2.rs b/crates/bevy_render/src/mesh/primitives/dim2.rs index 3bbd2be6d2..3b2d42b6c9 100644 --- a/crates/bevy_render/src/mesh/primitives/dim2.rs +++ b/crates/bevy_render/src/mesh/primitives/dim2.rs @@ -598,7 +598,7 @@ impl MeshBuilder for AnnulusMeshBuilder { let start_angle = FRAC_PI_2; let step = std::f32::consts::TAU / self.resolution as f32; for i in 0..=self.resolution { - let theta = start_angle + i as f32 * step; + let theta = start_angle + (i % self.resolution) as f32 * step; let (sin, cos) = theta.sin_cos(); let inner_pos = [cos * inner_radius, sin * inner_radius, 0.]; let outer_pos = [cos * outer_radius, sin * outer_radius, 0.]; @@ -1005,9 +1005,33 @@ impl From for Mesh { #[cfg(test)] mod tests { - use bevy_math::primitives::RegularPolygon; + use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd}; + use bevy_utils::HashSet; - use crate::mesh::{Mesh, VertexAttributeValues}; + use crate::mesh::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; + + fn count_distinct_positions(points: &[[f32; 3]]) -> usize { + let mut map = HashSet::new(); + for point in points { + map.insert(point.map(FloatOrd)); + } + map.len() + } + + #[test] + fn test_annulus() { + let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build(); + + assert_eq!( + 32, + count_distinct_positions( + mesh.attribute(Mesh::ATTRIBUTE_POSITION) + .unwrap() + .as_float3() + .unwrap() + ) + ); + } /// Sin/cos and multiplication computations result in numbers like 0.4999999. /// Round these to numbers we expect like 0.5.