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};

pub fn test_get_plane_xy<T>()
where
    T: GenericVector3 + From<T>,
{
    let _1: <T as HasXY>::Scalar = 1.0.into();
    let _ten: <T as HasXY>::Scalar = 10.0.into();
    let _0: <T as HasXY>::Scalar = 0.0.into();
    let _e = <T as HasXY>::Scalar::default_epsilon();
    let _u = <T as HasXY>::Scalar::default_max_ulps();

    let points: [T; 3] = [
        T::new_3d(_1, _0, _0),
        T::new_3d(_0, _1, _0),
        T::new_3d(_1, _1, _0),
    ];
    let aabb = <T as GenericVector3>::Aabb::from_points(points.iter());
    let plane = Plane::get_plane(&aabb).unwrap();
    assert_eq!(plane, Plane::XY);
    let p3d = T::new_3d(_ten, _1, _0);
    let p2d = plane.point_to_2d(p3d);
    let p3d_2 = plane.point_to_3d(p2d);
    assert!(p3d.is_ulps_eq(p3d_2, _e, _u));
    let points_2d: Vec<T::Vector2> = plane.points_to_2d(points.iter()).collect();
    let points_3d: Vec<T> = plane.points_to_3d(points_2d.iter()).collect();
    for (v1, v2) in points_3d.iter().zip(points.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
    let p2di: Vec<T::Vector2> = points.iter().map(|v| plane.point_to_2d(*v)).collect();
    for (v1, v2) in points_2d.iter().zip(p2di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
    let p3di: Vec<T> = p2di.iter().map(|v| plane.point_to_3d(*v)).collect();
    for (v1, v2) in points_3d.iter().zip(p3di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
}

pub fn test_get_plane_xz<T>()
where
    T: GenericVector3 + From<T>,
{
    let _1: <T as HasXY>::Scalar = 1.0.into();
    let _2: <T as HasXY>::Scalar = 2.0.into();
    let _ten: <T as HasXY>::Scalar = 10.0.into();
    let _0: <T as HasXY>::Scalar = 0.0.into();
    let _e = <T as HasXY>::Scalar::default_epsilon();
    let _u = <T as HasXY>::Scalar::default_max_ulps();

    let points: [T; 3] = [
        T::new_3d(_1, _0, _0),
        T::new_3d(_0, _0, _2),
        T::new_3d(_1, _0, _1),
    ];
    let aabb = <T as GenericVector3>::Aabb::from_points(&points);
    let plane = aabb.get_plane().unwrap();
    assert_eq!(plane, Plane::XZ);
    let p3d = T::new_3d(_ten, _0, _1);
    let p2d = plane.point_to_2d(p3d);
    let p3d_2 = plane.point_to_3d(p2d);

    assert!(p3d.is_ulps_eq(p3d_2, _e, _u));
    let points_2d: Vec<T::Vector2> = plane.points_to_2d(points.iter()).collect();
    let points_3d: Vec<T> = plane.points_to_3d(points_2d.iter()).collect();
    for (v1, v2) in points_3d.iter().zip(points.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }

    let p2di: Vec<T::Vector2> = points.iter().map(|v| plane.point_to_2d(*v)).collect();
    for (v1, v2) in points_2d.iter().zip(p2di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
    let p3di: Vec<T> = p2di.iter().map(|v| plane.point_to_3d(*v)).collect();
    for (v1, v2) in points_3d.iter().zip(p3di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
}

pub fn test_get_plane_yz<T>()
where
    T: GenericVector3 + From<T>,
{
    let _1: <T as HasXY>::Scalar = 1.0.into();
    let _2: <T as HasXY>::Scalar = 2.0.into();
    let _ten: <T as HasXY>::Scalar = 10.0.into();
    let _0: <T as HasXY>::Scalar = 0.0.into();
    let _e = <T as HasXY>::Scalar::default_epsilon();
    let _u = <T as HasXY>::Scalar::default_max_ulps();

    let points: [T; 3] = [
        T::new_3d(_0, _1, _0),
        T::new_3d(_0, _0, _2),
        T::new_3d(_0, _ten, _1),
    ];
    let aabb = <T as GenericVector3>::Aabb::from_points(&points);
    let plane = aabb.get_plane().unwrap();
    assert_eq!(plane, Plane::YZ);
    let p3d = T::new_3d(_0, _ten, _1);
    let p2d: T::Vector2 = plane.point_to_2d(p3d);
    let p2d_2: T::Vector2 = T::Vector2::new(_1, _ten);

    assert!(p2d.is_ulps_eq(p2d_2, _e, _u), "{p2d:?}!={p2d_2:?}");
    let p3d_2 = plane.point_to_3d(p2d);

    assert!(p3d.is_ulps_eq(p3d_2, _e, _u), "{p3d:?}!={p3d_2:?}");
    let points_2d: Vec<T::Vector2> = plane.points_to_2d(points.iter()).collect();
    let points_3d: Vec<T> = plane.points_to_3d(points_2d.iter()).collect();
    for (v1, v2) in points_3d.iter().zip(points.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
    #[allow(clippy::useless_conversion)]
    let points_3d: Vec<T> = plane.points_to_3d(points_2d.clone().into_iter()).collect();
    for (v1, v2) in points_3d.iter().zip(points.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u), "[points_3d:?] != [points:?]");
    }

    let p2di: Vec<T::Vector2> = points.iter().map(|v| plane.point_to_2d(*v)).collect();
    for (v1, v2) in points_2d.iter().zip(p2di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
    let p3di: Vec<T> = p2di.iter().map(|v| plane.point_to_3d(*v)).collect();
    for (v1, v2) in points_3d.iter().zip(p3di.iter()) {
        assert!(v1.is_ulps_eq(*v2, _e, _u));
    }
}

pub fn test_get_bad_plane<T>()
where
    T: GenericVector3 + From<T>,
{
    let _1: <T as HasXY>::Scalar = 1.0.into();
    let _0: <T as HasXY>::Scalar = 0.0.into();
    let aabb = <T as GenericVector3>::Aabb::default();
    assert_eq!(aabb.get_plane(), None);
    let points: [T; 3] = [
        T::new_3d(_0, _1, _0),
        T::new_3d(_0, _1, _0),
        T::new_3d(_0, _1, _1),
    ];
    let aabb = <T as GenericVector3>::Aabb::from_points(&points);
    assert_eq!(aabb.get_plane(), None);
    let points = vec![T::new_3d(_0, _1, _0), T::new_3d(_0, _1, _0)];
    let aabb = <T as GenericVector3>::Aabb::from_points(&points);
    assert_eq!(aabb.get_plane(), None);
}

pub fn test_get_plane_relaxed<T>()
where
    T: GenericVector3 + From<T>,
{
    let _1: <T as HasXY>::Scalar = 1.0.into();
    let _0: <T as HasXY>::Scalar = 0.0.into();

    let points: [T; 3] = [
        T::new_3d(_1, _0, _0),
        T::new_3d(_0, _1, _0),
        T::new_3d(_1, _1, _0),
    ];
    let aabb = T::Aabb::from_points(points.iter());
    let plane = aabb.get_plane_relaxed(0.00001.into(), 5);
    assert_eq!(plane, Some(Plane::XY));
}