vector-traits 0.6.2

Rust traits for 2D and 3D vector types.
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2023, 2025 lacklustr@protonmail.com https://github.com/eadf

// This file is part of vector-traits.

use crate::prelude::*;
use approx::{AbsDiffEq, UlpsEq};
use num_traits::{AsPrimitive, float::FloatCore};

pub fn test_xy<T: HasXY + Approx>(x: T::Scalar, y: T::Scalar) {
    assert_eq!(x, T::Scalar::from_bits(x.to_bits()));
    assert_eq!(y, T::Scalar::from_bits(y.to_bits()));

    let v0 = T::new_2d(x, y);
    assert_eq!(v0.x(), x);
    assert_eq!(v0.y(), y);
    let mut v1 = v0;
    let mult = 6.0.into();
    *v1.x_mut() = x * mult;
    *v1.y_mut() = y * mult;
    assert_eq!(v1.x(), x * mult);
    assert_eq!(v1.y(), y * mult);

    let mult = 3.0.into();
    v1.set_x(x * mult);
    v1.set_y(y * mult);
    assert_eq!(v1.x(), x * mult);
    assert_eq!(v1.y(), y * mult);

    let n = T::Scalar::INFINITY;
    assert!(!n.is_normal());
    assert!(!n.is_finite());
    let _5: T::Scalar = 5.0.into();
    let _6: T::Scalar = 6.0.into();
    let _8: T::Scalar = 8.0.into();

    assert_eq!(_6.clamp(_5, _8), _6);
    assert_eq!(_5.clamp(_6, _8), _6);

    let v0 = T::new_2d(T::Scalar::ONE, T::Scalar::TWO);
    let v1 = T::new_2d(T::Scalar::ONE, T::Scalar::TWO);
    assert!(v0.is_ulps_eq(
        v1,
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(v0.is_abs_diff_eq(v1, T::Scalar::default_epsilon()));
}

pub fn test_gxy<T: GenericVector2>(x: T::Scalar, y: T::Scalar, z: T::Scalar) {
    let mut v0 = T::new_2d(x, y);
    assert_eq!(v0.x(), x);
    assert_eq!(v0.y(), y);
    let mut v1 = v0;
    let mult = 6.0.into();
    v1.set_x(x * mult);
    v1.set_y(y * mult);
    assert_eq!(v1.x(), x * mult);
    assert_eq!(v1.y(), y * mult);
    let v2 = v0.to_3d(z) * mult;
    assert_eq!(v2.x(), x * mult);
    assert_eq!(v2.y(), y * mult);
    assert_eq!(v2.z(), z * mult);
    let v2 = (v0 * mult).to_3d(z).to_2d() / mult;
    assert_eq!(v2.x(), x);
    assert_eq!(v2.y(), y);

    let s: T::Scalar = 4.0.into();
    assert_eq!(s, 4_f32.into());
    assert_eq!(s, 4_u16.into());
    assert_eq!(s, 4_i16.into());
    assert_eq!(s, 4_u8.into());
    assert_eq!(s, 4_i8.into());

    let a: T::Scalar = 4.0.into();
    assert_eq!(4_u32, a.as_(),);
    assert_eq!(4_usize, a.as_());
    assert_eq!(4_f32, a.as_());
    assert_eq!(4_f64, a.as_());
    assert_eq!(4_usize, a.as_());
    assert_eq!(4_isize, a.as_());
    assert_eq!(4_u64, a.as_());
    assert_eq!(4_i64, a.as_());
    assert_eq!(4_u32, a.as_());
    assert_eq!(4_i32, a.as_());
    assert_eq!(4_u16, a.as_());
    assert_eq!(4_i16, a.as_());
    assert_eq!(4_u8, a.as_());
    assert_eq!(4_i8, a.as_());

    v0 += -v1 - v1 + v1 + v1;

    let _7: T::Scalar = 7.0.into();
    let v0 = T::new_2d(_7, T::Scalar::nan());
    assert!(!v0.is_finite());
    let v0 = T::new_2d(T::Scalar::nan(), _7);
    assert!(!v0.is_finite());
    let v0 = T::new_2d(_7, T::Scalar::infinity());
    assert!(!v0.is_finite());
    let v0 = T::new_2d(T::Scalar::infinity(), _7);
    assert!(!v0.is_finite());
}

pub fn test_generic_xy<T: GenericVector2>(
    x: T::Scalar,
    y: T::Scalar,
    z: T::Scalar,
    epsilon: T::Scalar,
) {
    let v0 = T::new_2d(x, y);
    assert_eq!(v0.x(), x);
    assert_eq!(v0.y(), y);

    let mut v1 = v0;
    let mult: T::Scalar = 6.0.into();
    v1.set_x(v1.x() * mult);
    v1.set_y(v1.y() * mult);
    assert_eq!(v1.x(), x * mult);
    assert_eq!(v1.y(), y * mult);

    assert!(!v0.is_ulps_eq(
        v1,
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(!v0.is_abs_diff_eq(v1, T::Scalar::default_epsilon()));

    let v2 = v0.to_3d(z) * mult;
    assert_eq!(v2.x(), x * mult);
    assert_eq!(v2.y(), y * mult);
    assert_eq!(v2.z(), z * mult);

    let v3 = (v0 * mult).to_3d(z).to_2d() / mult;
    assert_eq!(v3.x(), x);
    assert_eq!(v3.y(), y);

    // Test magnitude and magnitude_sq
    let magnitude = v0.magnitude();
    let magnitude_sq = v0.magnitude_sq();
    assert!(
        (magnitude * magnitude - magnitude_sq).abs() < epsilon,
        "{} != {}",
        magnitude * magnitude,
        magnitude_sq
    );

    // Test dot product
    let dot = v0.dot(v1);
    assert_eq!(dot, (x * x * mult + y * y * mult));

    // Test perp_dot (the result will vary based on specific types and values)
    let _perp_dot = v0.perp_dot(v1);

    // Test distance and distance_sq
    let distance = v0.distance(v1);
    let distance_sq = v0.distance_sq(v1);
    assert!(
        (distance * distance - distance_sq).abs() < epsilon,
        "{} != {}",
        distance * distance,
        distance_sq
    );

    // Test normalize
    let normalized = v0.normalize();
    assert!(
        (normalized.magnitude() - T::Scalar::ONE) < epsilon,
        "{} != {}",
        normalized.magnitude(),
        T::Scalar::ONE
    );

    // Test try_normalize
    if let Some(v) = v0.try_normalize(T::Scalar::EPSILON) {
        assert!(
            (v.magnitude() - T::Scalar::ONE) < epsilon,
            "{} != {}",
            v.magnitude(),
            T::Scalar::from(1.0)
        )
    };

    let v0 = T::new_2d(T::Scalar::ZERO, T::Scalar::ZERO);
    assert!(v0.try_normalize(T::Scalar::EPSILON).is_none());
    assert!(v0.is_ulps_eq(
        v0,
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(v0.is_abs_diff_eq(v0, T::Scalar::default_epsilon()));

    // Test min()
    let x = v0.x();
    let y = v0.y();

    let min_value = T::new_2d(1.0.into(), 2.0.into());
    let max_value = T::new_2d(8.0.into(), 9.0.into());
    let min_vec = v0.min(min_value);
    assert!(min_vec.x().ulps_eq(
        &x.min(min_value.x()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(min_vec.y().ulps_eq(
        &y.min(min_value.y()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));

    // Test max()
    let max_vec = v0.max(max_value);
    assert!(max_vec.x().ulps_eq(
        &x.max(max_value.x()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(max_vec.y().ulps_eq(
        &y.max(max_value.y()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));

    // Test clamp()
    let clamped_vec = v0.clamp(min_value, max_value);
    assert!(clamped_vec.x().ulps_eq(
        &x.clamp(min_value.x(), max_value.x()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    assert!(clamped_vec.y().ulps_eq(
        &y.clamp(min_value.y(), max_value.y()),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));
    let v0 = T::splat(T::Scalar::THREE);
    assert!(v0.is_ulps_eq(
        T::new(T::Scalar::THREE, T::Scalar::THREE),
        T::Scalar::default_epsilon(),
        T::Scalar::default_max_ulps()
    ));

    let mut v0 = T::new_2d(x, y);
    let five = 5.0.into();
    v0[0] *= five;
    v0[1] *= five;

    assert_eq!(v0[0], x * five);
    assert_eq!(v0[1], y * five);
}