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 std::fmt::Debug;

/// A workaround for Rust's limitations where external traits cannot be implemented for external types.
///
/// The `Approx` trait provides methods for performing approximate equality comparisons on types.
/// It serves as a workaround for Rust's limitations, allowing you to implement approximate
/// equality checks for types not originally designed with this capability.
///
/// This trait leverages the `approx` crate and its traits to perform approximate equality
/// comparisons. The methods in this trait are wrappers around the corresponding methods provided
/// by the `approx` crate.
///
pub trait Approx: HasXY {
    /// Checks if two instances are nearly equal within a specified tolerance in ULPs (Units in the Last Place).
    ///
    /// This method delegates to the `approx::UlpsEq::ulps_eq` method, performing approximate equality checks
    /// one time per coordinate axis.
    fn is_ulps_eq(
        self,
        other: Self,
        epsilon: <Self::Scalar as approx::AbsDiffEq>::Epsilon,
        max_ulps: u32,
    ) -> bool;

    /// Checks if two instances are nearly equal within a specified absolute difference tolerance.
    ///
    /// This method delegates to the `approx::AbsDiffEq::abs_diff_eq` method, performing approximate equality checks
    /// one time per coordinate axis.
    fn is_abs_diff_eq(
        self,
        other: Self,
        epsilon: <Self::Scalar as approx::AbsDiffEq>::Epsilon,
    ) -> bool;
}

/// A basic two-dimensional vector trait, designed for flexibility in precision.
///
/// The `HasXY` trait abstracts over two-dimensional vectors, allowing for easy
/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
/// significant codebase modifications. It only provides the most basic vector interface.
/// It is intended to be used in situations where you need a custom storage type of vectors.
/// For example an FFI type.
///
/// Implementors of this trait can benefit from the ability to switch between different
/// precision representations seamlessly, making it ideal for applications where varying
/// precision levels might be desirable at different stages or configurations.
///
/// The associated `Scalar` type represents the scalar type (e.g., `f32` or `f64`) used
/// by the vector.
///
pub trait HasXY: Sync + Send + Copy + Debug + Sized {
    type Scalar: GenericScalar;
    /// create a new instance of Self, note that this
    /// creates a 3d vector if the instanced type is a 3d type
    fn new_2d(x: Self::Scalar, y: Self::Scalar) -> Self;
    fn x(self) -> Self::Scalar;
    fn x_mut(&mut self) -> &mut Self::Scalar;
    fn set_x(&mut self, val: Self::Scalar);
    fn y(self) -> Self::Scalar;
    fn y_mut(&mut self) -> &mut Self::Scalar;
    fn set_y(&mut self, val: Self::Scalar);
}

/// A generic two-dimensional vector trait.
///
/// The `GenericVector2` trait abstracts over two-dimensional vectors, allowing for easy
/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
/// significant codebase modifications. It provides the common operations one would expect
/// for 2D vectors, such as dot products, cross products, and normalization.
///
/// Implementors of this trait can benefit from the ability to switch between different
/// precision representations seamlessly, making it ideal for applications where varying
/// precision levels might be desirable at different stages or configurations.
///
/// The associated `Scalar` type represents the scalar type (e.g., `f32` or `f64`) used
/// by the vector, and `Vector3` is the corresponding three-dimensional vector type.
///
/// Note: The actual trait functionality might vary based on the concrete implementations.
pub trait GenericVector2:
    HasXY
    + Copy
    + Approx
    + PartialEq
    + std::ops::Index<usize, Output = Self::Scalar>
    + std::ops::IndexMut<usize, Output = Self::Scalar>
    + std::ops::AddAssign
    + std::ops::SubAssign
    + std::ops::Neg<Output = Self>
    + std::ops::Add<Self, Output = Self>
    + std::ops::Sub<Self, Output = Self>
    + std::ops::Mul<Self::Scalar, Output = Self>
    + std::ops::Div<Self::Scalar, Output = Self>
    + Into<[Self::Scalar; 2]>
    + From<[Self::Scalar; 2]>
{
    // Associated constants
    const ZERO: Self;
    const ONE: Self;

    type Vector3: GenericVector3<Scalar = Self::Scalar, Vector2 = Self>;
    type Affine: Affine2D<Vector2 = Self>;
    type Aabb: Aabb2<Vector = Self> + Into<Vec<Self>>;

    fn new(x: Self::Scalar, y: Self::Scalar) -> Self;
    fn splat(value: Self::Scalar) -> Self;
    fn to_3d(self, z: Self::Scalar) -> Self::Vector3;
    fn magnitude(self) -> Self::Scalar;
    fn magnitude_sq(self) -> Self::Scalar;
    fn dot(self, other: Self) -> Self::Scalar;
    fn perp_dot(self, rhs: Self) -> Self::Scalar;
    fn distance(self, rhs: Self) -> Self::Scalar;
    fn distance_sq(self, rhs: Self) -> Self::Scalar;
    fn normalize(self) -> Self;
    fn try_normalize(self, epsilon: Self::Scalar) -> Option<Self>;
    fn min(self, rhs: Self) -> Self;
    fn max(self, rhs: Self) -> Self;
    /// Clamps the vector's components to be within the range defined by `min` and `max`.
    ///
    /// # Note
    /// This implementation does **not** follow `nalgebra`'s convention for `clamp`.
    /// In `nalgebra`, `clamp` is symmetric around zero.
    /// In this implementation, `clamp` ensures that `self` is bounded by `min` and `max`
    /// in a non-symmetric way: `self.min(max).max(min)`.
    fn clamp(self, min: Self, max: Self) -> Self;
    /// returns false on all inf or NaN values
    fn is_finite(self) -> bool;
}