Fix and clean up typing-related flaws with the Math category of nodes

This commit is contained in:
Keavon Chambers
2025-09-18 16:37:01 -07:00
parent 7e3ab78ea4
commit 9d15e56ce1
6 changed files with 112 additions and 96 deletions
@@ -1060,6 +1060,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
if reference == "Instance Index" && inputs_count == 0 { if reference == "Instance Index" && inputs_count == 0 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
document.network_interface.set_display_name(node_id, "Instance Index".to_string(), network_path);
let mut node_path = network_path.to_vec(); let mut node_path = network_path.to_vec();
node_path.push(*node_id); node_path.push(*node_id);
+1 -1
View File
@@ -338,7 +338,7 @@ async fn wrap_graphic<T: Into<Graphic> + 'n>(
/// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table. /// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table.
/// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired. /// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
#[node_macro::node(category("Type Conversion"))] #[node_macro::node(category("General"))]
async fn to_graphic<T: Into<Table<Graphic>> + 'n>( async fn to_graphic<T: Into<Table<Graphic>> + 'n>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
+21 -7
View File
@@ -9,9 +9,9 @@ use crate::vector::Vector;
use crate::{Context, Ctx}; use crate::{Context, Ctx};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
#[node_macro::node(category("Type Conversion"))] #[node_macro::node(category("Debug"))]
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(bool, f64, u32, u64, DVec2, DAffine2, String)] value: T) -> String { fn to_string(_: impl Ctx, value: String) -> String {
format!("{value:?}") value
} }
#[node_macro::node(category("Text"))] #[node_macro::node(category("Text"))]
@@ -34,10 +34,24 @@ fn string_replace(_: impl Ctx, string: String, from: TextArea, to: TextArea) ->
#[node_macro::node(category("Text"))] #[node_macro::node(category("Text"))]
fn string_slice(_: impl Ctx, string: String, start: f64, end: f64) -> String { fn string_slice(_: impl Ctx, string: String, start: f64, end: f64) -> String {
let start = if start < 0. { string.len() - start.abs() as usize } else { start as usize }; let total_chars = string.chars().count();
let end = if end <= 0. { string.len() - end.abs() as usize } else { end as usize };
let n = end.saturating_sub(start); let start = if start < 0. {
string.char_indices().skip(start).take(n).map(|(_, c)| c).collect() total_chars.saturating_sub(start.abs() as usize)
} else {
(start as usize).min(total_chars)
};
let end = if end <= 0. {
total_chars.saturating_sub(end.abs() as usize)
} else {
(end as usize).min(total_chars)
};
if start >= end {
return String::new();
}
string.chars().skip(start).take(end - start).collect()
} }
// TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs. // TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs.
+1 -2
View File
@@ -1,6 +1,5 @@
use graphene_core_shaders::Ctx;
use crate::Node; use crate::Node;
use graphene_core_shaders::Ctx;
use std::marker::PhantomData; use std::marker::PhantomData;
// TODO: Rename to "Passthrough" // TODO: Rename to "Passthrough"
+84 -86
View File
@@ -33,24 +33,24 @@ impl ValueProvider for MathNodeContext {
/// Calculates a mathematical expression with input values "A" and "B" /// Calculates a mathematical expression with input values "A" and "B"
#[node_macro::node(category("Math: Arithmetic"), properties("math_properties"))] #[node_macro::node(category("Math: Arithmetic"), properties("math_properties"))]
fn math<U: num_traits::float::Float>( fn math<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The value of "A" when calculating the expression /// The value of "A" when calculating the expression
#[implementations(f64, f32)] #[implementations(f64, f32)]
operand_a: U, operand_a: T,
/// A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2" /// A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"
#[default(A + B)] #[default(A + B)]
expression: String, expression: String,
/// The value of "B" when calculating the expression /// The value of "B" when calculating the expression
#[implementations(f64, f32)] #[implementations(f64, f32)]
#[default(1.)] #[default(1.)]
operand_b: U, operand_b: T,
) -> U { ) -> T {
let (node, _unit) = match ast::Node::try_parse_from_str(&expression) { let (node, _unit) = match ast::Node::try_parse_from_str(&expression) {
Ok(expr) => expr, Ok(expr) => expr,
Err(e) => { Err(e) => {
warn!("Invalid expression: `{expression}`\n{e:?}"); warn!("Invalid expression: `{expression}`\n{e:?}");
return U::from(0.).unwrap(); return T::from(0.).unwrap();
} }
}; };
let context = EvalContext::new( let context = EvalContext::new(
@@ -65,14 +65,14 @@ fn math<U: num_traits::float::Float>(
Ok(value) => value, Ok(value) => value,
Err(e) => { Err(e) => {
warn!("Expression evaluation error: {e:?}"); warn!("Expression evaluation error: {e:?}");
return U::from(0.).unwrap(); return T::from(0.).unwrap();
} }
}; };
let Value::Number(num) = value; let Value::Number(num) = value;
match num { match num {
Number::Real(val) => U::from(val).unwrap(), Number::Real(val) => T::from(val).unwrap(),
Number::Complex(c) => U::from(c.re).unwrap(), Number::Complex(c) => T::from(c.re).unwrap(),
} }
} }
@@ -109,11 +109,11 @@ fn subtract<U: Sub<T>, T>(
fn multiply<U: Mul<T>, T>( fn multiply<U: Mul<T>, T>(
_: impl Ctx, _: impl Ctx,
/// The left-hand side of the multiplication operation. /// The left-hand side of the multiplication operation.
#[implementations(f64, f32, u32, f64, DVec2, DVec2, DAffine2)] #[implementations(f64, f32, u32, DVec2, f64, DVec2, DAffine2)]
multiplier: U, multiplier: U,
/// The right-hand side of the multiplication operation. /// The right-hand side of the multiplication operation.
#[default(1.)] #[default(1.)]
#[implementations(f64, f32, u32, DVec2, f64, DVec2, DAffine2)] #[implementations(f64, f32, u32, DVec2, DVec2, f64, DAffine2)]
multiplicand: T, multiplicand: T,
) -> <U as Mul<T>>::Output { ) -> <U as Mul<T>>::Output {
multiplier * multiplicand multiplier * multiplicand
@@ -126,11 +126,11 @@ fn multiply<U: Mul<T>, T>(
fn divide<U: Div<T> + Default + PartialEq, T: Default + PartialEq>( fn divide<U: Div<T> + Default + PartialEq, T: Default + PartialEq>(
_: impl Ctx, _: impl Ctx,
/// The left-hand side of the division operation. /// The left-hand side of the division operation.
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, DVec2, f64)] #[implementations(f64, f32, u32, DVec2, DVec2, f64)]
numerator: U, numerator: U,
/// The right-hand side of the division operation. /// The right-hand side of the division operation.
#[default(1.)] #[default(1.)]
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, f64, DVec2)] #[implementations(f64, f32, u32, DVec2, f64, DVec2)]
denominator: T, denominator: T,
) -> <U as Div<T>>::Output ) -> <U as Div<T>>::Output
where where
@@ -162,58 +162,58 @@ fn modulo<U: Rem<T, Output: Add<T, Output: Rem<T, Output = U::Output>>>, T: Copy
/// The exponent operation (^) calculates the result of raising a number to a power. /// The exponent operation (^) calculates the result of raising a number to a power.
#[node_macro::node(category("Math: Arithmetic"))] #[node_macro::node(category("Math: Arithmetic"))]
fn exponent<U: Pow<T>, T>( fn exponent<T: Pow<T>>(
_: impl Ctx, _: impl Ctx,
/// The base number that will be raised to the power. /// The base number that will be raised to the power.
#[implementations(f64, f32, u32)] #[implementations(f64, f32, u32)]
base: U, base: T,
/// The power to which the base number will be raised. /// The power to which the base number will be raised.
#[default(2.)]
#[implementations(f64, f32, u32)] #[implementations(f64, f32, u32)]
#[default(2.)]
power: T, power: T,
) -> <U as num_traits::Pow<T>>::Output { ) -> <T as num_traits::Pow<T>>::Output {
base.pow(power) base.pow(power)
} }
/// The square root operation (√) calculates the nth root of a number, equivalent to raising the number to the power of 1/n. /// The square root operation (√) calculates the nth root of a number, equivalent to raising the number to the power of 1/n.
#[node_macro::node(category("Math: Arithmetic"))] #[node_macro::node(category("Math: Arithmetic"))]
fn root<U: num_traits::float::Float>( fn root<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The number for which the nth root will be calculated. /// The number for which the nth root will be calculated.
#[default(2.)] #[default(2.)]
#[implementations(f64, f32)] #[implementations(f64, f32)]
radicand: U, radicand: T,
/// The degree of the root to be calculated. Square root is 2, cube root is 3, and so on. /// The degree of the root to be calculated. Square root is 2, cube root is 3, and so on.
#[default(2.)] #[default(2.)]
#[implementations(f64, f32)] #[implementations(f64, f32)]
degree: U, degree: T,
) -> U { ) -> T {
if degree == U::from(2.).unwrap() { if degree == T::from(2.).unwrap() {
radicand.sqrt() radicand.sqrt()
} else if degree == U::from(3.).unwrap() { } else if degree == T::from(3.).unwrap() {
radicand.cbrt() radicand.cbrt()
} else { } else {
radicand.powf(U::from(1.).unwrap() / degree) radicand.powf(T::from(1.).unwrap() / degree)
} }
} }
/// The logarithmic function (log) calculates the logarithm of a number with a specified base. If the natural logarithm function (ln) is desired, set the base to "e". /// The logarithmic function (log) calculates the logarithm of a number with a specified base. If the natural logarithm function (ln) is desired, set the base to "e".
#[node_macro::node(category("Math: Arithmetic"))] #[node_macro::node(category("Math: Arithmetic"))]
fn logarithm<U: num_traits::float::Float>( fn logarithm<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The number for which the logarithm will be calculated. /// The number for which the logarithm will be calculated.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
/// The base of the logarithm, such as 2 (binary), 10 (decimal), and e (natural logarithm). /// The base of the logarithm, such as 2 (binary), 10 (decimal), and e (natural logarithm).
#[default(2.)] #[default(2.)]
#[implementations(f64, f32)] #[implementations(f64, f32)]
base: U, base: T,
) -> U { ) -> T {
if base == U::from(2.).unwrap() { if base == T::from(2.).unwrap() {
value.log2() value.log2()
} else if base == U::from(10.).unwrap() { } else if base == T::from(10.).unwrap() {
value.log10() value.log10()
} else if base - U::from(std::f64::consts::E).unwrap() < U::epsilon() * U::from(1e6).unwrap() { } else if base - T::from(std::f64::consts::E).unwrap() < T::epsilon() * T::from(1e6).unwrap() {
value.ln() value.ln()
} else { } else {
value.log(base) value.log(base)
@@ -222,66 +222,66 @@ fn logarithm<U: num_traits::float::Float>(
/// The sine trigonometric function (sin) calculates the ratio of the angle's opposite side length to its hypotenuse length. /// The sine trigonometric function (sin) calculates the ratio of the angle's opposite side length to its hypotenuse length.
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn sine<U: num_traits::float::Float>( fn sine<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The given angle. /// The given angle.
#[implementations(f64, f32)] #[implementations(f64, f32)]
theta: U, theta: T,
/// Whether the given angle should be interpreted as radians instead of degrees. /// Whether the given angle should be interpreted as radians instead of degrees.
radians: bool, radians: bool,
) -> U { ) -> T {
if radians { theta.sin() } else { theta.to_radians().sin() } if radians { theta.sin() } else { theta.to_radians().sin() }
} }
/// The cosine trigonometric function (cos) calculates the ratio of the angle's adjacent side length to its hypotenuse length. /// The cosine trigonometric function (cos) calculates the ratio of the angle's adjacent side length to its hypotenuse length.
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn cosine<U: num_traits::float::Float>( fn cosine<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The given angle. /// The given angle.
#[implementations(f64, f32)] #[implementations(f64, f32)]
theta: U, theta: T,
/// Whether the given angle should be interpreted as radians instead of degrees. /// Whether the given angle should be interpreted as radians instead of degrees.
radians: bool, radians: bool,
) -> U { ) -> T {
if radians { theta.cos() } else { theta.to_radians().cos() } if radians { theta.cos() } else { theta.to_radians().cos() }
} }
/// The tangent trigonometric function (tan) calculates the ratio of the angle's opposite side length to its adjacent side length. /// The tangent trigonometric function (tan) calculates the ratio of the angle's opposite side length to its adjacent side length.
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn tangent<U: num_traits::float::Float>( fn tangent<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The given angle. /// The given angle.
#[implementations(f64, f32)] #[implementations(f64, f32)]
theta: U, theta: T,
/// Whether the given angle should be interpreted as radians instead of degrees. /// Whether the given angle should be interpreted as radians instead of degrees.
radians: bool, radians: bool,
) -> U { ) -> T {
if radians { theta.tan() } else { theta.to_radians().tan() } if radians { theta.tan() } else { theta.to_radians().tan() }
} }
/// The inverse sine trigonometric function (asin) calculates the angle whose sine is the specified value. /// The inverse sine trigonometric function (asin) calculates the angle whose sine is the specified value.
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn sine_inverse<U: num_traits::float::Float>( fn sine_inverse<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN. /// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
/// Whether the resulting angle should be given in as radians instead of degrees. /// Whether the resulting angle should be given in as radians instead of degrees.
radians: bool, radians: bool,
) -> U { ) -> T {
if radians { value.asin() } else { value.asin().to_degrees() } if radians { value.asin() } else { value.asin().to_degrees() }
} }
/// The inverse cosine trigonometric function (acos) calculates the angle whose cosine is the specified value. /// The inverse cosine trigonometric function (acos) calculates the angle whose cosine is the specified value.
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn cosine_inverse<U: num_traits::float::Float>( fn cosine_inverse<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN. /// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
/// Whether the resulting angle should be given in as radians instead of degrees. /// Whether the resulting angle should be given in as radians instead of degrees.
radians: bool, radians: bool,
) -> U { ) -> T {
if radians { value.acos() } else { value.acos().to_degrees() } if radians { value.acos() } else { value.acos().to_degrees() }
} }
@@ -289,16 +289,16 @@ fn cosine_inverse<U: num_traits::float::Float>(
/// atan: the angle whose tangent is the specified scalar number. /// atan: the angle whose tangent is the specified scalar number.
/// atan2: the angle of a ray from the origin to the specified vec2. /// atan2: the angle of a ray from the origin to the specified vec2.
/// ///
/// The resulting angle is always in the range [0°, 180°] or, in radians, [-π/2, π/2]. /// The resulting angle is always in the range [-90°, 90°] or, in radians, [-π/2, π/2].
#[node_macro::node(category("Math: Trig"))] #[node_macro::node(category("Math: Trig"))]
fn tangent_inverse<U: TangentInverse>( fn tangent_inverse<T: TangentInverse>(
_: impl Ctx, _: impl Ctx,
/// The given value for which the angle will be calculated. /// The given value for which the angle will be calculated.
#[implementations(f64, f32, DVec2)] #[implementations(f64, f32, DVec2)]
value: U, value: T,
/// Whether the resulting angle should be given in as radians instead of degrees. /// Whether the resulting angle should be given in as radians instead of degrees.
radians: bool, radians: bool,
) -> U::Output { ) -> T::Output {
value.atan(radians) value.atan(radians)
} }
@@ -327,88 +327,86 @@ impl TangentInverse for DVec2 {
/// The random function (rand) converts a seed into a random number within the specified range, inclusive of the minimum and exclusive of the maximum. The minimum and maximum values are automatically swapped if they are reversed. /// The random function (rand) converts a seed into a random number within the specified range, inclusive of the minimum and exclusive of the maximum. The minimum and maximum values are automatically swapped if they are reversed.
#[node_macro::node(category("Math: Numeric"))] #[node_macro::node(category("Math: Numeric"))]
fn random<U: num_traits::float::Float>( fn random(
_: impl Ctx, _: impl Ctx,
_primary: (), _primary: (),
/// Seed to determine the unique variation of which number will be generated. /// Seed to determine the unique variation of which number will be generated.
seed: u64, seed: u64,
/// The smaller end of the range within which the random number will be generated. /// The smaller end of the range within which the random number will be generated.
#[implementations(f64, f32)]
#[default(0.)] #[default(0.)]
min: U, min: f64,
/// The larger end of the range within which the random number will be generated. /// The larger end of the range within which the random number will be generated.
#[implementations(f64, f32)]
#[default(1.)] #[default(1.)]
max: U, max: f64,
) -> f64 { ) -> f64 {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed); let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let result = rng.random::<f64>(); let result = rng.random::<f64>();
let (min, max) = if min < max { (min, max) } else { (max, min) }; let (min, max) = if min < max { (min, max) } else { (max, min) };
let (min, max) = (min.to_f64().unwrap(), max.to_f64().unwrap());
result * (max - min) + min result * (max - min) + min
} }
/// Convert a number to an integer of the type u32, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented. // TODO: Test that these are no longer needed in all circumstances, then remove them and add a migration to convert these into Passthrough nodes. Note: these act more as type annotations than as identity functions.
#[node_macro::node(name("To u32"), category("Type Conversion"))] /// Convert a number to an integer of the type u32, which may be the required type for certain node inputs.
fn to_u32<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> u32 { #[node_macro::node(name("To u32"), category("Debug"))]
let value = U::clamp(value, U::from(0.).unwrap(), U::from(u32::MAX as f64).unwrap()); fn to_u32(_: impl Ctx, value: u32) -> u32 {
value.to_u32().unwrap() value
} }
/// Convert a number to an integer of the type u64, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented. // TODO: Test that these are no longer needed in all circumstances, then remove them and add a migration to convert these into Passthrough nodes. Note: these act more as type annotations than as identity functions.
#[node_macro::node(name("To u64"), category("Type Conversion"))] /// Convert a number to an integer of the type u64, which may be the required type for certain node inputs.
fn to_u64<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> u64 { #[node_macro::node(name("To u64"), category("Debug"))]
let value = U::clamp(value, U::from(0.).unwrap(), U::from(u64::MAX as f64).unwrap()); fn to_u64(_: impl Ctx, value: u64) -> u64 {
value.to_u64().unwrap() value
} }
/// Convert an integer to a decimal number of the type f64, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented. // TODO: Test that these are no longer needed in all circumstances, then remove them and add a migration to convert these into Passthrough nodes. Note: these act more as type annotations than as identity functions.
#[node_macro::node(name("To f64"), category("Type Conversion"))] /// Convert an integer to a decimal number of the type f64, which may be the required type for certain node inputs.
fn to_f64<U: num_traits::int::PrimInt>(_: impl Ctx, #[implementations(u32, u64)] value: U) -> f64 { #[node_macro::node(name("To f64"), category("Debug"))]
value.to_f64().unwrap() fn to_f64(_: impl Ctx, value: f64) -> f64 {
value
} }
/// The rounding function (round) maps an input value to its nearest whole number. Halfway values are rounded away from zero. /// The rounding function (round) maps an input value to its nearest whole number. Halfway values are rounded away from zero.
#[node_macro::node(category("Math: Numeric"))] #[node_macro::node(category("Math: Numeric"))]
fn round<U: num_traits::float::Float>( fn round<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The number which will be rounded. /// The number which will be rounded.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
) -> U { ) -> T {
value.round() value.round()
} }
/// The floor function (floor) rounds down an input value to the nearest whole number, unless the input number is already whole. /// The floor function (floor) rounds down an input value to the nearest whole number, unless the input number is already whole.
#[node_macro::node(category("Math: Numeric"))] #[node_macro::node(category("Math: Numeric"))]
fn floor<U: num_traits::float::Float>( fn floor<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The number which will be rounded down. /// The number which will be rounded down.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
) -> U { ) -> T {
value.floor() value.floor()
} }
/// The ceiling function (ceil) rounds up an input value to the nearest whole number, unless the input number is already whole. /// The ceiling function (ceil) rounds up an input value to the nearest whole number, unless the input number is already whole.
#[node_macro::node(category("Math: Numeric"))] #[node_macro::node(category("Math: Numeric"))]
fn ceiling<U: num_traits::float::Float>( fn ceiling<T: num_traits::float::Float>(
_: impl Ctx, _: impl Ctx,
/// The number which will be rounded up. /// The number which will be rounded up.
#[implementations(f64, f32)] #[implementations(f64, f32)]
value: U, value: T,
) -> U { ) -> T {
value.ceil() value.ceil()
} }
/// The absolute value function (abs) removes the negative sign from an input value, if present. /// The absolute value function (abs) removes the negative sign from an input value, if present.
#[node_macro::node(category("Math: Numeric"))] #[node_macro::node(category("Math: Numeric"))]
fn absolute_value<U: num_traits::float::Float>( fn absolute_value<T: num_traits::sign::Signed>(
_: impl Ctx, _: impl Ctx,
/// The number which will be made positive. /// The number which will be made positive.
#[implementations(f64, f32)] #[implementations(f64, f32, i32, i64)]
value: U, value: T,
) -> U { ) -> T {
value.abs() value.abs()
} }
@@ -540,28 +538,28 @@ fn binary_gcd<T: num_traits::int::PrimInt + std::ops::ShrAssign<i32> + std::ops:
/// The equality operation (==) compares two values and returns true if they are equal, or false if they are not. /// The equality operation (==) compares two values and returns true if they are equal, or false if they are not.
#[node_macro::node(category("Math: Logic"))] #[node_macro::node(category("Math: Logic"))]
fn equals<U: std::cmp::PartialEq<T>, T>( fn equals<T: std::cmp::PartialEq<T>>(
_: impl Ctx, _: impl Ctx,
/// One of the two numbers to compare for equality. /// One of the two numbers to compare for equality.
#[implementations(f64, f32, u32, DVec2, &str, String)] #[implementations(f64, f32, u32, DVec2, &str, String)]
value: T, value: T,
/// The other of the two numbers to compare for equality. /// The other of the two numbers to compare for equality.
#[implementations(f64, f32, u32, DVec2, &str, String)] #[implementations(f64, f32, u32, DVec2, &str, String)]
other_value: U, other_value: T,
) -> bool { ) -> bool {
other_value == value other_value == value
} }
/// The inequality operation (!=) compares two values and returns true if they are not equal, or false if they are. /// The inequality operation (!=) compares two values and returns true if they are not equal, or false if they are.
#[node_macro::node(category("Math: Logic"))] #[node_macro::node(category("Math: Logic"))]
fn not_equals<U: std::cmp::PartialEq<T>, T>( fn not_equals<T: std::cmp::PartialEq<T>>(
_: impl Ctx, _: impl Ctx,
/// One of the two numbers to compare for inequality. /// One of the two numbers to compare for inequality.
#[implementations(f64, f32, u32, DVec2, &str)] #[implementations(f64, f32, u32, DVec2, &str)]
value: T, value: T,
/// The other of the two numbers to compare for inequality. /// The other of the two numbers to compare for inequality.
#[implementations(f64, f32, u32, DVec2, &str)] #[implementations(f64, f32, u32, DVec2, &str)]
other_value: U, other_value: T,
) -> bool { ) -> bool {
other_value != value other_value != value
} }
@@ -57,6 +57,10 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
into_node!(from: &WasmEditorApi, to: &WgpuExecutor), into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
convert_node!(from: String, to: String), convert_node!(from: String, to: String),
convert_node!(from: bool, to: String),
convert_node!(from: DVec2, to: String),
convert_node!(from: IVec2, to: String),
convert_node!(from: DAffine2, to: String),
// ============= // =============
// MONITOR NODES // MONITOR NODES
// ============= // =============