use bevy::prelude::*;
pub const DEFAULT_DELTA_F32: f32 = 0.1;
pub const DEFAULT_DELTA_F64: f64 = 0.1;
pub trait CloseTo: Sized {
fn default_delta() -> Self;
fn is_close_with_delta(&self, b: &Self, epsilon: &Self) -> bool;
fn is_close(&self, b: &Self) -> bool {
Self::is_close_with_delta(self, b, &Self::default_delta())
}
}
impl CloseTo for f32 {
fn default_delta() -> Self { DEFAULT_DELTA_F32 }
fn is_close_with_delta(&self, b: &Self, epsilon: &Self) -> bool {
is_close_f32(*self, *b, *epsilon)
}
}
impl CloseTo for f64 {
fn default_delta() -> Self { DEFAULT_DELTA_F64 }
fn is_close_with_delta(&self, b: &Self, epsilon: &Self) -> bool {
is_close_f64(*self, *b, *epsilon)
}
}
pub fn is_close_f32(a: f32, b: f32, delta: f32) -> bool {
abs_diff(a, b) < delta
}
pub fn is_close_f64(a: f64, b: f64, delta: f64) -> bool {
abs_diff(a, b) < delta
}
pub fn abs_diff<T>(a: T, b: T) -> T
where
T: PartialOrd + std::ops::Sub<Output = T>,
{
if a > b { a - b } else { b - a }
}
impl CloseTo for Vec2 {
fn default_delta() -> Self {
Vec2::new(DEFAULT_DELTA_F32, DEFAULT_DELTA_F32)
}
fn is_close_with_delta(&self, b: &Self, delta: &Self) -> bool {
is_close_f32(self.x, b.x, delta.x) && is_close_f32(self.y, b.y, delta.y)
}
}
impl CloseTo for Vec3 {
fn default_delta() -> Self {
Vec3::new(DEFAULT_DELTA_F32, DEFAULT_DELTA_F32, DEFAULT_DELTA_F32)
}
fn is_close_with_delta(&self, b: &Self, delta: &Self) -> bool {
is_close_f32(self.x, b.x, delta.x)
&& is_close_f32(self.y, b.y, delta.y)
&& is_close_f32(self.z, b.z, delta.z)
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Deref, Component)]
struct Foo(pub Vec3);
#[test]
fn vec3() {
Vec3::ZERO.xpect_close(Vec3::ZERO);
Vec3::ZERO.xnot().xpect_close(Vec3::ONE);
Foo(Vec3::ZERO).xpect_close(Vec3::ZERO);
Foo(Vec3::ZERO)
.0
.xnot()
.xpect_close(Vec3::new(0.2, 0.2, 0.2));
}
}