Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions node-graph/nodes/math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,15 +1473,28 @@ fn cross_product(
Item::from_parts(value.perp_dot(*other_value.element()), attributes)
}

/// Calculates the angle swept between two vectors.
/// Calculates the angle swept between two vec2s.
///
/// The value is always positive and ranges from 0° (both vectors point the same direction) to 180° (both vectors point opposite directions).
/// The angle ranges from -180° to 180° (or -π to π radians) and its sign gives the sweep direction from the "Direction From" input to the "Direction To" input: positive for clockwise, negative for counterclockwise, as drawn in the viewport and matching the direction convention of the Transform node's rotation.
#[node_macro::node(category("Math: Vec2"))]
fn angle_between(_: impl Ctx, vector_a: Item<DVec2>, vector_b: Item<DVec2>, radians: Item<bool>) -> Item<f64> {
let (vector_a, attributes) = vector_a.into_parts();
fn angle_between(
_: impl Ctx,
/// The direction the angle is measured from.
direction_from: Item<DVec2>,
/// The direction the angle is measured to.
#[default(1., 0.)]
direction_to: Item<DVec2>,
/// Whether the resulting angle should be given in radians instead of degrees.
radians: Item<bool>,
) -> Item<f64> {
let (direction_from, attributes) = direction_from.into_parts();
let direction_to = *direction_to.element();

if direction_from == DVec2::ZERO || direction_to == DVec2::ZERO {
return Item::from_parts(0., attributes);
}

let dot_product = vector_a.normalize_or_zero().dot(vector_b.element().normalize_or_zero());
let angle = dot_product.acos();
let angle = direction_from.angle_to(direction_to);
let result = if *radians.element() { angle } else { angle.to_degrees() };
Item::from_parts(result, attributes)
}
Expand Down Expand Up @@ -1653,6 +1666,21 @@ mod test {
assert!(lerp_between(5., -0., 1.).is_sign_negative());
}

#[test]
pub fn angle_between_signed() {
let right = DVec2::new(1., 0.);
let down = DVec2::new(0., 1.);
let angle = |a, b, radians| angle_between((), Item::new_from_element(a), Item::new_from_element(b), Item::new_from_element(radians)).into_element();
assert_eq!(angle(right, down, false), 90.);
assert_eq!(angle(down, right, false), -90.);
}

#[test]
pub fn angle_between_zero_vector() {
let (zero, right) = (Item::new_from_element(DVec2::ZERO), Item::new_from_element(DVec2::new(1., 0.)));
assert_eq!(angle_between((), zero, right, Item::new_from_element(false)).into_element(), 0.);
}

#[test]
pub fn clamp_vec2_within_swapped_bounds() {
let vec2 = |x, y| Item::new_from_element(DVec2::new(x, y));
Expand Down
Loading