From 6fae65a3fabaf442cbbc96a76cbf70fe2fee16ee Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 8 Aug 2025 12:04:51 +0200 Subject: [PATCH] Add `emath::fast_midpoint` (#7435) --- Cargo.toml | 2 +- crates/emath/src/align.rs | 2 +- crates/emath/src/lib.rs | 15 +++++++++++++++ crates/emath/src/rect.rs | 6 +++--- crates/emath/src/smart_aim.rs | 4 +++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67ee191b..d2944971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -300,7 +300,7 @@ zero_sized_map_values = "warn" # doc_comment_double_space_linebreaks = "warn" # elidable_lifetime_names = "warn" # ignore_without_reason = "warn" -# manual_midpoint = "warn" +# manual_midpoint = "warn" # NOTE `midpoint` is often a lot slower for floats, so we have our own `emath::fast_midpoint` function. # non_std_lazy_statics = "warn" # precedence_bits = "warn" # return_and_then = "warn" diff --git a/crates/emath/src/align.rs b/crates/emath/src/align.rs index 9604855e..395323d4 100644 --- a/crates/emath/src/align.rs +++ b/crates/emath/src/align.rs @@ -134,7 +134,7 @@ impl Align { if size == f32::INFINITY { Rangef::new(f32::NEG_INFINITY, f32::INFINITY) } else { - let left = f32::midpoint(min, max) - size / 2.0; + let left = crate::fast_midpoint(min, max) - size / 2.0; Rangef::new(left, left + size) } } diff --git a/crates/emath/src/lib.rs b/crates/emath/src/lib.rs index 2d9dfb2f..476b36d0 100644 --- a/crates/emath/src/lib.rs +++ b/crates/emath/src/lib.rs @@ -112,6 +112,21 @@ where (T::ONE - t) * *range.start() + t * *range.end() } +/// This is a faster version of [`f32::midpoint`] which doesn't handle overflow. +/// +/// ``` +/// # use emath::fast_midpoint; +/// assert_eq!(fast_midpoint(1.0, 5.0), 3.0); +/// ``` +#[inline(always)] +pub fn fast_midpoint(a: R, b: R) -> R +where + R: Copy + Add + Div + One, +{ + let two = R::ONE + R::ONE; + (a + b) / two +} + /// Where in the range is this value? Returns 0-1 if within the range. /// /// Returns <0 if before and >1 if after. diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 7731a5ce..b46fc43c 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{Div, Mul, NumExt as _, Pos2, Rangef, Rot2, Vec2, lerp, pos2, vec2}; +use crate::{Div, Mul, NumExt as _, Pos2, Rangef, Rot2, Vec2, fast_midpoint, lerp, pos2, vec2}; use std::ops::{BitOr, BitOrAssign}; /// A rectangular region of space. @@ -331,8 +331,8 @@ impl Rect { #[inline(always)] pub fn center(&self) -> Pos2 { Pos2 { - x: f32::midpoint(self.min.x, self.max.x), - y: f32::midpoint(self.min.y, self.max.y), + x: fast_midpoint(self.min.x, self.max.x), + y: fast_midpoint(self.min.y, self.max.y), } } diff --git a/crates/emath/src/smart_aim.rs b/crates/emath/src/smart_aim.rs index a3181647..8d083917 100644 --- a/crates/emath/src/smart_aim.rs +++ b/crates/emath/src/smart_aim.rs @@ -1,5 +1,7 @@ //! Find "simple" numbers is some range. Used by sliders. +use crate::fast_midpoint; + const NUM_DECIMALS: usize = 15; /// Find the "simplest" number in a closed range [min, max], i.e. the one with the fewest decimal digits. @@ -43,7 +45,7 @@ pub fn best_in_range_f64(min: f64, max: f64) -> f64 { if min_exponent.floor() != max_exponent.floor() { // pick the geometric center of the two: - let exponent = f64::midpoint(min_exponent, max_exponent); + let exponent = fast_midpoint(min_exponent, max_exponent); return 10.0_f64.powi(exponent.round() as i32); }