Skip to content

Commit

Permalink
OrderedFloat refactor (#918)
Browse files Browse the repository at this point in the history
* Move egui/util/float_ord.rs -> epaint/util/ordered_float.rs

* Implement Hash on OrderedFloat

* Generic OrderedFloat<T>; impl Hash; documentation
  • Loading branch information
Bromeon authored Dec 11, 2021
1 parent c85eca6 commit 5ec1486
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 68 deletions.
64 changes: 0 additions & 64 deletions egui/src/util/float_ord.rs

This file was deleted.

1 change: 0 additions & 1 deletion egui/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion egui/src/widgets/plot/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
4 changes: 2 additions & 2 deletions egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
12 changes: 12 additions & 0 deletions epaint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,15 @@ pub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {
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);
}
}
4 changes: 4 additions & 0 deletions epaint/src/util.rs → epaint/src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
127 changes: 127 additions & 0 deletions epaint/src/util/ordered_float.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit 5ec1486

Please sign in to comment.