OrderedFloat refactor (#918)
* Move egui/util/float_ord.rs -> epaint/util/ordered_float.rs * Implement Hash on OrderedFloat * Generic OrderedFloat<T>; impl Hash; documentation
This commit is contained in:
parent
c85eca6eaa
commit
5ec14867c8
7 changed files with 146 additions and 68 deletions
|
@ -1,64 +0,0 @@
|
||||||
//! Total order on floating point types, assuming absence of NaN.
|
|
||||||
//! 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);
|
|
||||||
|
|
||||||
impl Eq for OrderedFloat {}
|
|
||||||
|
|
||||||
impl PartialEq<Self> for OrderedFloat {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
// NaNs are considered equal (equivalent when it comes to ordering
|
|
||||||
if self.0.is_nan() {
|
|
||||||
other.0.is_nan()
|
|
||||||
} else {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<Self> for OrderedFloat {
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
match self.0.partial_cmp(&other.0) {
|
|
||||||
Some(ord) => Some(ord),
|
|
||||||
None => Some(self.0.is_nan().cmp(&other.0.is_nan())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for OrderedFloat {
|
|
||||||
#[inline]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match self.partial_cmp(other) {
|
|
||||||
Some(ord) => ord,
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extension trait to provide `ord` method
|
|
||||||
pub(crate) trait FloatOrd {
|
|
||||||
/// Type to provide total order, useful as key in sorted contexts.
|
|
||||||
fn ord(self) -> OrderedFloat;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatOrd for f32 {
|
|
||||||
#[inline]
|
|
||||||
fn ord(self) -> OrderedFloat {
|
|
||||||
OrderedFloat(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO ordering may break down at least significant digits due to f64 -> f32 conversion
|
|
||||||
// Possible solutions: generic OrderedFloat<T>, always OrderedFloat(f64)
|
|
||||||
impl FloatOrd for f64 {
|
|
||||||
#[inline]
|
|
||||||
fn ord(self) -> OrderedFloat {
|
|
||||||
OrderedFloat(self as f32)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub(crate) mod fixed_cache;
|
pub(crate) mod fixed_cache;
|
||||||
pub(crate) mod float_ord;
|
|
||||||
mod history;
|
mod history;
|
||||||
pub mod id_type_map;
|
pub mod id_type_map;
|
||||||
pub mod undoer;
|
pub mod undoer;
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use epaint::util::FloatOrd;
|
||||||
use epaint::Mesh;
|
use epaint::Mesh;
|
||||||
|
|
||||||
use crate::util::float_ord::FloatOrd;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use super::{PlotBounds, ScreenTransform};
|
use super::{PlotBounds, ScreenTransform};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Simple plotting library.
|
//! Simple plotting library.
|
||||||
|
|
||||||
use crate::util::float_ord::FloatOrd;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use color::Hsva;
|
|
||||||
use epaint::ahash::AHashSet;
|
use epaint::ahash::AHashSet;
|
||||||
|
use epaint::color::Hsva;
|
||||||
|
use epaint::util::FloatOrd;
|
||||||
use items::PlotItem;
|
use items::PlotItem;
|
||||||
use legend::LegendWidget;
|
use legend::LegendWidget;
|
||||||
use transform::{PlotBounds, ScreenTransform};
|
use transform::{PlotBounds, ScreenTransform};
|
||||||
|
|
|
@ -196,3 +196,15 @@ pub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {
|
||||||
f.to_bits().hash(state);
|
f.to_bits().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn f64_hash<H: std::hash::Hasher>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
mod ordered_float;
|
||||||
|
|
||||||
|
pub use ordered_float::*;
|
||||||
|
|
||||||
/// Hash the given value with a predictable hasher.
|
/// Hash the given value with a predictable hasher.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash(value: impl std::hash::Hash) -> u64 {
|
pub fn hash(value: impl std::hash::Hash) -> u64 {
|
127
epaint/src/util/ordered_float.rs
Normal file
127
epaint/src/util/ordered_float.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
//! Total order on floating point types.
|
||||||
|
//! Can be used for sorting, min/max computation, and other collection algorithms.
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
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`].
|
||||||
|
pub struct OrderedFloat<T>(T);
|
||||||
|
|
||||||
|
impl<T: Float> Eq for OrderedFloat<T> {}
|
||||||
|
|
||||||
|
impl<T: Float> PartialEq<Self> for OrderedFloat<T> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// NaNs are considered equal (equivalent) when it comes to ordering
|
||||||
|
if self.0.is_nan() {
|
||||||
|
other.0.is_nan()
|
||||||
|
} else {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Float> PartialOrd<Self> for OrderedFloat<T> {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match self.0.partial_cmp(&other.0) {
|
||||||
|
Some(ord) => Some(ord),
|
||||||
|
None => Some(self.0.is_nan().cmp(&other.0.is_nan())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Float> Ord for OrderedFloat<T> {
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match self.partial_cmp(other) {
|
||||||
|
Some(ord) => ord,
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Float> Hash for OrderedFloat<T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// 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<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatOrd for f32 {
|
||||||
|
#[inline]
|
||||||
|
fn ord(self) -> OrderedFloat<f32> {
|
||||||
|
OrderedFloat(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatOrd for f64 {
|
||||||
|
#[inline]
|
||||||
|
fn ord(self) -> OrderedFloat<f64> {
|
||||||
|
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<H: Hasher>(&self, state: &mut H);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatImpl for f32 {
|
||||||
|
#[inline]
|
||||||
|
fn is_nan(&self) -> bool {
|
||||||
|
f32::is_nan(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: Hasher>(&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<H: Hasher>(&self, state: &mut H) {
|
||||||
|
crate::f64_hash(state, *self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue