Fix the 'Divide' node dividing partially-zero Vec2 denominators and the 'Logarithm' node misdetecting base e (#4359)

This commit is contained in:
Keavon Chambers
2026-07-22 18:41:28 -07:00
committed by Dennis Kobert
parent 0af05aa5ff
commit 5dbcdaf0c2

View File

@@ -10,7 +10,7 @@ use math_parser::context::{EvalContext, NothingMap, ValueProvider};
use math_parser::value::{Number, Value}; use math_parser::value::{Number, Value};
use num_traits::Pow; use num_traits::Pow;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use std::ops::{Add, Div, Mul, Rem, Sub}; use std::ops::{Add, Mul, Rem, Sub};
use vector_types::Gradient; use vector_types::Gradient;
use vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod as SpreadMethodAttr}; use vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod as SpreadMethodAttr};
@@ -121,11 +121,52 @@ fn multiply<A: Mul<B>, B>(
multiplier * multiplicand multiplier * multiplicand
} }
pub trait SafeDivide<Rhs = Self> {
type Output;
fn safe_divide(self, denominator: Rhs) -> Self::Output;
}
impl SafeDivide for f64 {
type Output = f64;
fn safe_divide(self, denominator: f64) -> f64 {
if denominator == 0. { 0. } else { self / denominator }
}
}
impl SafeDivide for f32 {
type Output = f32;
fn safe_divide(self, denominator: f32) -> f32 {
if denominator == 0. { 0. } else { self / denominator }
}
}
impl SafeDivide for u32 {
type Output = u32;
fn safe_divide(self, denominator: u32) -> u32 {
self.checked_div(denominator).unwrap_or(0)
}
}
impl SafeDivide for DVec2 {
type Output = DVec2;
fn safe_divide(self, denominator: DVec2) -> DVec2 {
DVec2::new(self.x.safe_divide(denominator.x), self.y.safe_divide(denominator.y))
}
}
impl SafeDivide<f64> for DVec2 {
type Output = DVec2;
fn safe_divide(self, denominator: f64) -> DVec2 {
DVec2::new(self.x.safe_divide(denominator), self.y.safe_divide(denominator))
}
}
impl SafeDivide<DVec2> for f64 {
type Output = DVec2;
fn safe_divide(self, denominator: DVec2) -> DVec2 {
DVec2::new(self.safe_divide(denominator.x), self.safe_divide(denominator.y))
}
}
/// The division operation (`÷`) calculates the quotient of two scalar numbers or vectors. /// The division operation (`÷`) calculates the quotient of two scalar numbers or vectors.
/// ///
/// Produces 0 if the denominator is 0. /// Produces 0 for any division by 0. With vec2 inputs, this applies separately to the X and Y components.
#[node_macro::node(category("Math: Arithmetic"))] #[node_macro::node(category("Math: Arithmetic"))]
fn divide<A: Div<B> + Default + PartialEq, B: Default + PartialEq>( fn divide<A: SafeDivide<B>, B>(
_: impl Ctx, _: impl Ctx,
/// The left-hand side of the division operation. /// The left-hand side of the division operation.
#[implementations(f64, f32, u32, DVec2, DVec2, f64)] #[implementations(f64, f32, u32, DVec2, DVec2, f64)]
@@ -134,14 +175,8 @@ fn divide<A: Div<B> + Default + PartialEq, B: Default + PartialEq>(
#[default(1.)] #[default(1.)]
#[implementations(f64, f32, u32, DVec2, f64, DVec2)] #[implementations(f64, f32, u32, DVec2, f64, DVec2)]
denominator: B, denominator: B,
) -> <A as Div<B>>::Output ) -> <A as SafeDivide<B>>::Output {
where numerator.safe_divide(denominator)
<A as Div<B>>::Output: Default,
{
if denominator == B::default() {
return <A as Div<B>>::Output::default();
}
numerator / denominator
} }
/// The reciprocal operation (`1/x`) calculates the multiplicative inverse of a number. /// The reciprocal operation (`1/x`) calculates the multiplicative inverse of a number.
@@ -235,7 +270,7 @@ fn logarithm<T: num_traits::float::Float>(
value.log2() value.log2()
} else if base == T::from(10.).unwrap() { } else if base == T::from(10.).unwrap() {
value.log10() value.log10()
} else if base - T::from(std::f64::consts::E).unwrap() < T::epsilon() * T::from(1e6).unwrap() { } else if (base - T::from(std::f64::consts::E).unwrap()).abs() < T::epsilon() * T::from(1e6).unwrap() {
value.ln() value.ln()
} else { } else {
value.log(base) value.log(base)
@@ -1051,6 +1086,11 @@ mod test {
assert_eq!(super::divide(&(), DVec2::ONE, 2_f64), DVec2::ONE / 2.); assert_eq!(super::divide(&(), DVec2::ONE, 2_f64), DVec2::ONE / 2.);
} }
#[test]
pub fn divide_vector_by_partially_zero_vector() {
assert_eq!(super::divide(&(), DVec2::new(1., 2.), DVec2::new(2., 0.)), DVec2::new(0.5, 0.));
}
#[test] #[test]
pub fn modulo_positive() { pub fn modulo_positive() {
assert_eq!(super::modulo(&(), -5_f64, 2_f64, true), 1_f64); assert_eq!(super::modulo(&(), -5_f64, 2_f64, true), 1_f64);