Skip to content

Commit

Permalink
Sum, Product implementations for Iterator<Vector*>
Browse files Browse the repository at this point in the history
Add implementations for `std::iter::Sum` and `std::iter::Product` for
the Vector (and IVector) types.

Note both operations are element-wise (this comes from glam).

Due to there being multiple Add and Mul implementations (ultimately in
glam), I noticed having to specify types explicitly in some
circumstances. I imagine this can probably be improved but I am not an
expert on generic programming.
  • Loading branch information
jamesmcm committed Apr 19, 2024
1 parent 7482941 commit f872e32
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
15 changes: 15 additions & 0 deletions godot-core/src/builtin/vectors/vector3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,21 @@ mod test {
assert_eq_approx!(vector1.slerp(vector2, 0.5).length(), real!(6.258_311));
}

#[test]
fn iter_sum() {
let vecs = vec![
Vector3::new(1.0, 2.0, 3.0),
Vector3::new(4.0, 5.0, 6.0),
Vector3::new(7.0, 8.0, 9.0),
];

let sum_refs = vecs.iter().sum();
let sum = vecs.into_iter().sum();

assert_eq_approx!(sum, Vector3::new(12.0, 15.0, 18.0));
assert_eq_approx!(sum_refs, Vector3::new(12.0, 15.0, 18.0));
}

#[cfg(feature = "serde")]
#[test]
fn serde_roundtrip() {
Expand Down
12 changes: 12 additions & 0 deletions godot-core/src/builtin/vectors/vector4i.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,16 @@ mod test {
Some(Vector4Axis::W),
);
}

#[test]
fn test_iter_elementwise_prod() {
// Test element-wise product from iter
let vecs = vec![Vector4i::new(1, 2, 3, 4), Vector4i::new(5, 6, 7, 8)];
let expected = Vector4i::new(5, 12, 21, 32);
let prod_refs: Vector4i = vecs.iter().product();
let prod: Vector4i = vecs.into_iter().product();

assert_eq!(prod_refs, expected);
assert_eq!(prod, expected);
}
}
30 changes: 30 additions & 0 deletions godot-core/src/builtin/vectors/vector_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,34 @@ macro_rules! impl_vector_scalar_assign_operator {
}
}

/// Implements a component-wise single infix binary operator between two vectors.
macro_rules! impl_iter_vector_reduction {
(
// Name of the vector type.
$Vector:ty,
// Name of the reduction trait: `Sum` or `Product`.
$Operator:ident,
// Operator symbol: `+` or `*`.
$symbol:tt,
// Name of the function on the operator trait, for example `add`.
$func:ident
) => {
impl std::iter::$Operator<Self> for $Vector {
fn $func<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(<$Vector>::ZERO, |x, y| x $symbol y)
}
}

impl<'a> std::iter::$Operator<&'a Self> for $Vector {
fn $func<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,{
iter.fold(<$Vector>::ZERO, |x, y| x $symbol *y)
}
}
}
}

/// Implements all common arithmetic operators on a built-in vector type.
macro_rules! impl_vector_operators {
(
Expand All @@ -181,6 +209,8 @@ macro_rules! impl_vector_operators {
impl_scalar_vector_binary_operator!($Vector, $Scalar, ($($components),*), Mul, mul);
impl_vector_vector_binary_operator!($Vector, $Scalar, ($($components),*), Div, div);
impl_vector_scalar_binary_operator!($Vector, $Scalar, ($($components),*), Div, div);
impl_iter_vector_reduction!($Vector, Sum, +, sum);
impl_iter_vector_reduction!($Vector, Product, *, product);
impl_vector_vector_assign_operator!($Vector, $Scalar, ($($components),*), AddAssign, add_assign);
impl_vector_vector_assign_operator!($Vector, $Scalar, ($($components),*), SubAssign, sub_assign);
impl_vector_vector_assign_operator!($Vector, $Scalar, ($($components),*), MulAssign, mul_assign);
Expand Down

0 comments on commit f872e32

Please sign in to comment.