From 87b294534e4339fe7fe55f23b2b04df2e13f3f0e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 21 Apr 2024 20:36:32 +0200 Subject: [PATCH] Add `emath::OrderedFloat` (moved from `epaint::util::OrderedFloat`) (#4389) It makes much more sense in `emath` --- Cargo.lock | 1 + crates/ecolor/src/rgba.rs | 1 + crates/egui/Cargo.toml | 1 + crates/egui/src/load.rs | 20 ++++----- crates/egui/src/widgets/image.rs | 8 ++-- crates/egui_plot/src/items/mod.rs | 2 +- crates/egui_plot/src/lib.rs | 3 +- crates/emath/src/lib.rs | 4 +- .../src/util => emath/src}/ordered_float.rs | 41 +++++++++++-------- crates/epaint/src/lib.rs | 26 ------------ crates/epaint/src/stroke.rs | 2 +- crates/epaint/src/text/fonts.rs | 23 ++--------- crates/epaint/src/text/text_layout_types.rs | 10 ++--- crates/epaint/src/util/mod.rs | 5 +-- 14 files changed, 57 insertions(+), 90 deletions(-) rename crates/{epaint/src/util => emath/src}/ordered_float.rs (79%) diff --git a/Cargo.lock b/Cargo.lock index 8698db26e..d64d54397 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,6 +1242,7 @@ dependencies = [ "ahash", "backtrace", "document-features", + "emath", "epaint", "log", "nohash-hasher", diff --git a/crates/ecolor/src/rgba.rs b/crates/ecolor/src/rgba.rs index 2a61d311e..36cb33bd5 100644 --- a/crates/ecolor/src/rgba.rs +++ b/crates/ecolor/src/rgba.rs @@ -26,6 +26,7 @@ impl std::ops::IndexMut for Rgba { } } +/// Deterministically hash an `f32`, treating all NANs as equal, and ignoring the sign of zero. #[inline] pub(crate) fn f32_hash(state: &mut H, f: f32) { if f == 0.0 { diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index 347dbeda7..87b25a7ea 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -81,6 +81,7 @@ unity = ["epaint/unity"] [dependencies] +emath = { workspace = true, default-features = false } epaint = { workspace = true, default-features = false } ahash.workspace = true diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index 4d527de63..71d9d086f 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -55,23 +55,21 @@ mod bytes_loader; mod texture_loader; -use std::borrow::Cow; -use std::fmt::Debug; -use std::ops::Deref; -use std::{fmt::Display, sync::Arc}; +use std::{ + borrow::Cow, + fmt::{Debug, Display}, + ops::Deref, + sync::Arc, +}; use ahash::HashMap; -use epaint::mutex::Mutex; -use epaint::util::FloatOrd; -use epaint::util::OrderedFloat; -use epaint::TextureHandle; -use epaint::{textures::TextureOptions, ColorImage, TextureId, Vec2}; +use emath::{Float, OrderedFloat}; +use epaint::{mutex::Mutex, textures::TextureOptions, ColorImage, TextureHandle, TextureId, Vec2}; use crate::Context; -pub use self::bytes_loader::DefaultBytesLoader; -pub use self::texture_loader::DefaultTextureLoader; +pub use self::{bytes_loader::DefaultBytesLoader, texture_loader::DefaultTextureLoader}; /// Represents a failed attempt at loading an image. #[derive(Clone, Debug)] diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index 945c497dd..15b9dcddb 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -1,12 +1,12 @@ use std::borrow::Cow; -use crate::load::TextureLoadResult; +use emath::{Float as _, Rot2}; +use epaint::RectShape; + use crate::{ - load::{Bytes, SizeHint, SizedTexture, TexturePoll}, + load::{Bytes, SizeHint, SizedTexture, TextureLoadResult, TexturePoll}, *, }; -use emath::Rot2; -use epaint::{util::FloatOrd, RectShape}; /// A widget which displays an image. /// diff --git a/crates/egui_plot/src/items/mod.rs b/crates/egui_plot/src/items/mod.rs index ddf09b985..56098b366 100644 --- a/crates/egui_plot/src/items/mod.rs +++ b/crates/egui_plot/src/items/mod.rs @@ -3,7 +3,7 @@ use std::ops::RangeInclusive; -use epaint::{emath::Rot2, util::FloatOrd, Mesh}; +use epaint::{emath::Rot2, Mesh}; use crate::*; diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 9de1f7ce7..2e31f8312 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -17,7 +17,8 @@ use std::{cmp::Ordering, ops::RangeInclusive, sync::Arc}; use egui::ahash::HashMap; use egui::*; -use epaint::{util::FloatOrd, Hsva}; +use emath::Float as _; +use epaint::Hsva; pub use crate::{ axis::{Axis, AxisHints, HPlacement, Placement, VPlacement}, diff --git a/crates/emath/src/lib.rs b/crates/emath/src/lib.rs index 911fb52a0..d3e2f5e03 100644 --- a/crates/emath/src/lib.rs +++ b/crates/emath/src/lib.rs @@ -30,6 +30,7 @@ use std::ops::{Add, Div, Mul, RangeInclusive, Sub}; pub mod align; mod history; mod numeric; +mod ordered_float; mod pos2; mod range; mod rect; @@ -40,10 +41,11 @@ mod ts_transform; mod vec2; mod vec2b; -pub use { +pub use self::{ align::{Align, Align2}, history::History, numeric::*, + ordered_float::*, pos2::*, range::Rangef, rect::*, diff --git a/crates/epaint/src/util/ordered_float.rs b/crates/emath/src/ordered_float.rs similarity index 79% rename from crates/epaint/src/util/ordered_float.rs rename to crates/emath/src/ordered_float.rs index 72f06a20a..950ec16ac 100644 --- a/crates/epaint/src/util/ordered_float.rs +++ b/crates/emath/src/ordered_float.rs @@ -7,9 +7,12 @@ use std::hash::{Hash, Hasher}; /// Wraps a floating-point value to add total order and hash. /// Possible types for `T` are `f32` and `f64`. /// -/// See also [`FloatOrd`]. +/// All NaNs are considered equal to each other. +/// The size of zero is ignored. +/// +/// See also [`Float`]. #[derive(Clone, Copy)] -pub struct OrderedFloat(T); +pub struct OrderedFloat(pub T); impl OrderedFloat { #[inline] @@ -68,44 +71,34 @@ impl From for OrderedFloat { /// /// Example with `f64`: /// ``` -/// use epaint::util::FloatOrd; +/// use emath::Float as _; /// /// let array = [1.0, 2.5, 2.0]; /// let max = array.iter().max_by_key(|val| val.ord()); /// /// assert_eq!(max, Some(&2.5)); /// ``` -pub trait FloatOrd { +pub trait Float: PartialOrd + PartialEq + private::FloatImpl { /// Type to provide total order, useful as key in sorted contexts. fn ord(self) -> OrderedFloat where Self: Sized; } -impl FloatOrd for f32 { +impl Float for f32 { #[inline] fn ord(self) -> OrderedFloat { OrderedFloat(self) } } -impl FloatOrd for f64 { +impl Float for f64 { #[inline] fn ord(self) -> OrderedFloat { OrderedFloat(self) } } -// ---------------------------------------------------------------------------- - -/// Internal abstraction over floating point types -#[doc(hidden)] -pub trait Float: PartialOrd + PartialEq + private::FloatImpl {} - -impl Float for f32 {} - -impl Float for f64 {} - // Keep this trait in private module, to avoid exposing its methods as extensions in user code mod private { use super::*; @@ -124,7 +117,13 @@ mod private { #[inline] fn hash(&self, state: &mut H) { - crate::f32_hash(state, *self); + if *self == 0.0 { + state.write_u8(0); + } else if self.is_nan() { + state.write_u8(1); + } else { + self.to_bits().hash(state); + } } } @@ -136,7 +135,13 @@ mod private { #[inline] fn hash(&self, state: &mut H) { - crate::f64_hash(state, *self); + if *self == 0.0 { + state.write_u8(0); + } else if self.is_nan() { + state.write_u8(1); + } else { + self.to_bits().hash(state); + } } } } diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index 182984846..f7ee04cb7 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -152,32 +152,6 @@ macro_rules! epaint_assert { } } -// ---------------------------------------------------------------------------- - -#[inline(always)] -pub(crate) fn f32_hash(state: &mut H, f: f32) { - if f == 0.0 { - state.write_u8(0); - } else if f.is_nan() { - state.write_u8(1); - } else { - use std::hash::Hash; - f.to_bits().hash(state); - } -} - -#[inline(always)] -pub(crate) fn f64_hash(state: &mut H, f: f64) { - if f == 0.0 { - state.write_u8(0); - } else if f.is_nan() { - state.write_u8(1); - } else { - use std::hash::Hash; - f.to_bits().hash(state); - } -} - // --------------------------------------------------------------------------- /// Was epaint compiled with the `rayon` feature? diff --git a/crates/epaint/src/stroke.rs b/crates/epaint/src/stroke.rs index 15ddd231f..9dafef31d 100644 --- a/crates/epaint/src/stroke.rs +++ b/crates/epaint/src/stroke.rs @@ -48,7 +48,7 @@ impl std::hash::Hash for Stroke { #[inline(always)] fn hash(&self, state: &mut H) { let Self { width, color } = *self; - crate::f32_hash(state, width); + emath::OrderedFloat(width).hash(state); color.hash(state); } } diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 557d71733..394b89829 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -8,7 +8,7 @@ use crate::{ }, TextureAtlas, }; -use emath::NumExt as _; +use emath::{NumExt as _, OrderedFloat}; // ---------------------------------------------------------------------------- @@ -56,7 +56,7 @@ impl std::hash::Hash for FontId { #[inline(always)] fn hash(&self, state: &mut H) { let Self { size, family } = self; - crate::f32_hash(state, *size); + emath::OrderedFloat(*size).hash(state); family.hash(state); } } @@ -567,21 +567,6 @@ impl FontsAndCache { // ---------------------------------------------------------------------------- -#[derive(Clone, Copy, Debug, PartialEq)] -struct HashableF32(f32); - -#[allow(clippy::derived_hash_with_manual_eq)] -impl std::hash::Hash for HashableF32 { - #[inline(always)] - fn hash(&self, state: &mut H) { - crate::f32_hash(state, self.0); - } -} - -impl Eq for HashableF32 {} - -// ---------------------------------------------------------------------------- - /// The collection of fonts used by `epaint`. /// /// Required in order to paint text. @@ -591,7 +576,7 @@ pub struct FontsImpl { definitions: FontDefinitions, atlas: Arc>, font_impl_cache: FontImplCache, - sized_family: ahash::HashMap<(HashableF32, FontFamily), Font>, + sized_family: ahash::HashMap<(OrderedFloat, FontFamily), Font>, } impl FontsImpl { @@ -641,7 +626,7 @@ impl FontsImpl { let FontId { size, family } = font_id; self.sized_family - .entry((HashableF32(*size), family.clone())) + .entry((OrderedFloat(*size), family.clone())) .or_insert_with(|| { let fonts = &self.definitions.families.get(family); let fonts = fonts diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 70317f771..5e4a56d95 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -185,7 +185,7 @@ impl std::hash::Hash for LayoutJob { text.hash(state); sections.hash(state); wrap.hash(state); - crate::f32_hash(state, *first_row_min_height); + emath::OrderedFloat(*first_row_min_height).hash(state); break_on_newline.hash(state); halign.hash(state); justify.hash(state); @@ -214,7 +214,7 @@ impl std::hash::Hash for LayoutSection { byte_range, format, } = self; - crate::f32_hash(state, *leading_space); + OrderedFloat(*leading_space).hash(state); byte_range.hash(state); format.hash(state); } @@ -293,9 +293,9 @@ impl std::hash::Hash for TextFormat { valign, } = self; font_id.hash(state); - crate::f32_hash(state, *extra_letter_spacing); + emath::OrderedFloat(*extra_letter_spacing).hash(state); if let Some(line_height) = *line_height { - crate::f32_hash(state, line_height); + emath::OrderedFloat(line_height).hash(state); } color.hash(state); background.hash(state); @@ -375,7 +375,7 @@ impl std::hash::Hash for TextWrapping { break_anywhere, overflow_character, } = self; - crate::f32_hash(state, *max_width); + emath::OrderedFloat(*max_width).hash(state); max_rows.hash(state); break_anywhere.hash(state); overflow_character.hash(state); diff --git a/crates/epaint/src/util/mod.rs b/crates/epaint/src/util/mod.rs index 2aa70ee04..383b94567 100644 --- a/crates/epaint/src/util/mod.rs +++ b/crates/epaint/src/util/mod.rs @@ -1,6 +1,5 @@ -mod ordered_float; - -pub use ordered_float::*; +#[deprecated = "Use emath::OrderedFloat instead"] +pub use emath::OrderedFloat; /// Hash the given value with a predictable hasher. #[inline]