From 85fa2a7ad1808d3c33828691fe27f683fa734f58 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 29 Nov 2021 19:58:03 +0100 Subject: [PATCH 1/3] Move egui/util/float_ord.rs -> epaint/util/ordered_float.rs --- egui/src/util/mod.rs | 1 - egui/src/widgets/plot/items/mod.rs | 2 +- egui/src/widgets/plot/mod.rs | 4 ++-- epaint/src/{util.rs => util/mod.rs} | 4 ++++ .../util/float_ord.rs => epaint/src/util/ordered_float.rs | 6 +++--- 5 files changed, 10 insertions(+), 7 deletions(-) rename epaint/src/{util.rs => util/mod.rs} (90%) rename egui/src/util/float_ord.rs => epaint/src/util/ordered_float.rs (92%) diff --git a/egui/src/util/mod.rs b/egui/src/util/mod.rs index 0a327f0baf9..ea83ea618f6 100644 --- a/egui/src/util/mod.rs +++ b/egui/src/util/mod.rs @@ -2,7 +2,6 @@ pub mod cache; pub(crate) mod fixed_cache; -pub(crate) mod float_ord; mod history; pub mod id_type_map; pub mod undoer; diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 9f935ca7ed8..30a2be70c29 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -2,9 +2,9 @@ use std::ops::RangeInclusive; +use epaint::util::FloatOrd; use epaint::Mesh; -use crate::util::float_ord::FloatOrd; use crate::*; use super::{PlotBounds, ScreenTransform}; diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 768c9f18b8f..b37b0447486 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -1,9 +1,9 @@ //! Simple plotting library. -use crate::util::float_ord::FloatOrd; use crate::*; -use color::Hsva; use epaint::ahash::AHashSet; +use epaint::color::Hsva; +use epaint::util::FloatOrd; use items::PlotItem; use legend::LegendWidget; use transform::{PlotBounds, ScreenTransform}; diff --git a/epaint/src/util.rs b/epaint/src/util/mod.rs similarity index 90% rename from epaint/src/util.rs rename to epaint/src/util/mod.rs index a321113d1d2..726dcfe3b7a 100644 --- a/epaint/src/util.rs +++ b/epaint/src/util/mod.rs @@ -1,3 +1,7 @@ +mod ordered_float; + +pub use ordered_float::*; + /// Hash the given value with a predictable hasher. #[inline] pub fn hash(value: impl std::hash::Hash) -> u64 { diff --git a/egui/src/util/float_ord.rs b/epaint/src/util/ordered_float.rs similarity index 92% rename from egui/src/util/float_ord.rs rename to epaint/src/util/ordered_float.rs index d4fb1b2d5d5..178035d210c 100644 --- a/egui/src/util/float_ord.rs +++ b/epaint/src/util/ordered_float.rs @@ -1,11 +1,11 @@ -//! Total order on floating point types, assuming absence of NaN. +//! Total order on floating point types. //! Can be used for sorting, min/max computation, and other collection algorithms. use std::cmp::Ordering; /// Totally orderable floating-point value /// For not `f32` is supported; could be made generic if necessary. -pub(crate) struct OrderedFloat(f32); +pub struct OrderedFloat(f32); impl Eq for OrderedFloat {} @@ -42,7 +42,7 @@ impl Ord for OrderedFloat { } /// Extension trait to provide `ord` method -pub(crate) trait FloatOrd { +pub trait FloatOrd { /// Type to provide total order, useful as key in sorted contexts. fn ord(self) -> OrderedFloat; } From e44e020ebd21e6c9ed79b5fb1449372738b0b30b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 29 Nov 2021 20:05:00 +0100 Subject: [PATCH 2/3] Implement Hash on OrderedFloat --- epaint/src/util/ordered_float.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/epaint/src/util/ordered_float.rs b/epaint/src/util/ordered_float.rs index 178035d210c..68d58a86b72 100644 --- a/epaint/src/util/ordered_float.rs +++ b/epaint/src/util/ordered_float.rs @@ -2,6 +2,7 @@ //! Can be used for sorting, min/max computation, and other collection algorithms. use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; /// Totally orderable floating-point value /// For not `f32` is supported; could be made generic if necessary. @@ -41,6 +42,14 @@ impl Ord for OrderedFloat { } } +impl Hash for OrderedFloat { + fn hash(&self, state: &mut H) { + crate::f32_hash(state, self.0); + } +} + +// ---------------------------------------------------------------------------- + /// Extension trait to provide `ord` method pub trait FloatOrd { /// Type to provide total order, useful as key in sorted contexts. From 4e97dda1c120c3392c38e6a79f8c5c10669e992c Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 29 Nov 2021 22:29:20 +0100 Subject: [PATCH 3/3] Generic OrderedFloat; impl Hash; documentation --- epaint/src/lib.rs | 12 +++++ epaint/src/util/ordered_float.rs | 88 ++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/epaint/src/lib.rs b/epaint/src/lib.rs index 046045e3a23..e4d75ba3cf1 100644 --- a/epaint/src/lib.rs +++ b/epaint/src/lib.rs @@ -196,3 +196,15 @@ pub(crate) fn f32_hash(state: &mut H, f: f32) { 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); + } +} diff --git a/epaint/src/util/ordered_float.rs b/epaint/src/util/ordered_float.rs index 68d58a86b72..8bbcef22dd5 100644 --- a/epaint/src/util/ordered_float.rs +++ b/epaint/src/util/ordered_float.rs @@ -4,16 +4,18 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; -/// Totally orderable floating-point value -/// For not `f32` is supported; could be made generic if necessary. -pub struct OrderedFloat(f32); +/// Wraps a floating-point value to add total order and hash. +/// Possible types for `T` are `f32` and `f64`. +/// +/// See also [`FloatOrd`]. +pub struct OrderedFloat(T); -impl Eq for OrderedFloat {} +impl Eq for OrderedFloat {} -impl PartialEq for OrderedFloat { +impl PartialEq for OrderedFloat { #[inline] fn eq(&self, other: &Self) -> bool { - // NaNs are considered equal (equivalent when it comes to ordering + // NaNs are considered equal (equivalent) when it comes to ordering if self.0.is_nan() { other.0.is_nan() } else { @@ -22,7 +24,7 @@ impl PartialEq for OrderedFloat { } } -impl PartialOrd for OrderedFloat { +impl PartialOrd for OrderedFloat { #[inline] fn partial_cmp(&self, other: &Self) -> Option { match self.0.partial_cmp(&other.0) { @@ -32,7 +34,7 @@ impl PartialOrd for OrderedFloat { } } -impl Ord for OrderedFloat { +impl Ord for OrderedFloat { #[inline] fn cmp(&self, other: &Self) -> Ordering { match self.partial_cmp(other) { @@ -42,32 +44,84 @@ impl Ord for OrderedFloat { } } -impl Hash for OrderedFloat { +impl Hash for OrderedFloat { fn hash(&self, state: &mut H) { - crate::f32_hash(state, self.0); + self.0.hash(state); } } // ---------------------------------------------------------------------------- -/// Extension trait to provide `ord` method +/// Extension trait to provide `ord()` method. +/// +/// Example with `f64`: +/// ``` +/// use epaint::util::FloatOrd; +/// +/// 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 { /// Type to provide total order, useful as key in sorted contexts. - fn ord(self) -> OrderedFloat; + fn ord(self) -> OrderedFloat + where + Self: Sized; } impl FloatOrd for f32 { #[inline] - fn ord(self) -> OrderedFloat { + fn ord(self) -> OrderedFloat { OrderedFloat(self) } } -// TODO ordering may break down at least significant digits due to f64 -> f32 conversion -// Possible solutions: generic OrderedFloat, always OrderedFloat(f64) impl FloatOrd for f64 { #[inline] - fn ord(self) -> OrderedFloat { - OrderedFloat(self as f32) + 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::*; + + pub trait FloatImpl { + fn is_nan(&self) -> bool; + fn hash(&self, state: &mut H); + } + + impl FloatImpl for f32 { + #[inline] + fn is_nan(&self) -> bool { + f32::is_nan(*self) + } + + #[inline] + fn hash(&self, state: &mut H) { + crate::f32_hash(state, *self); + } + } + + impl FloatImpl for f64 { + #[inline] + fn is_nan(&self) -> bool { + f64::is_nan(*self) + } + + #[inline] + fn hash(&self, state: &mut H) { + crate::f64_hash(state, *self); + } } }