unitforge 0.3.18

A library for unit and quantity consistent computations in Rust
Documentation
use crate::PhysicsQuantity;
use ndarray::{Array1, ArrayView1};
use num_traits::Zero;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::{
    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Vector2<T: PhysicsQuantity> {
    pub data: [T; 2],
}

impl<T> Neg for Vector2<T>
where
    T: PhysicsQuantity + Neg<Output = T>,
{
    type Output = Self;

    fn neg(self) -> Self::Output {
        Vector2 {
            data: [self.data[0].neg(), self.data[1].neg()],
        }
    }
}

impl<T: PhysicsQuantity + Zero> Zero for Vector2<T> {
    fn zero() -> Self {
        Vector2::new([T::zero(), T::zero()])
    }

    fn is_zero(&self) -> bool {
        self.data[0].is_zero() && self.data[1].is_zero()
    }
}

impl<T: PhysicsQuantity + Display> Display for Vector2<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}\n{}", self.data[0], self.data[1])
    }
}

impl<T, U, V> Mul<U> for Vector2<T>
where
    T: PhysicsQuantity + Mul<U, Output = V>,
    U: PhysicsQuantity,
    V: PhysicsQuantity,
{
    type Output = Vector2<V>;

    fn mul(self, scalar: U) -> Vector2<V> {
        Vector2 {
            data: [self.data[0] * scalar, self.data[1] * scalar],
        }
    }
}

impl<T, U, V> Mul<Vector2<U>> for Vector2<T>
where
    T: PhysicsQuantity + Mul<U, Output = V>,
    U: PhysicsQuantity,
    V: PhysicsQuantity,
{
    type Output = Vector2<V>;

    fn mul(self, other: Vector2<U>) -> Vector2<V> {
        Vector2 {
            data: [self.data[0] * other.data[0], self.data[1] * other.data[1]],
        }
    }
}

impl<T: PhysicsQuantity> MulAssign<f64> for Vector2<T> {
    fn mul_assign(&mut self, scalar: f64) {
        self.data[0] *= scalar;
        self.data[1] *= scalar;
    }
}

impl<T, U, V> Div<U> for Vector2<T>
where
    T: PhysicsQuantity + Div<U, Output = V>,
    U: PhysicsQuantity,
    V: PhysicsQuantity,
{
    type Output = Vector2<V>;

    fn div(self, scalar: U) -> Vector2<V> {
        Vector2 {
            data: [self.data[0] / scalar, self.data[1] / scalar],
        }
    }
}

impl<T: PhysicsQuantity> DivAssign<f64> for Vector2<T> {
    fn div_assign(&mut self, scalar: f64) {
        self.data[0] /= scalar;
        self.data[1] /= scalar;
    }
}

impl<T: PhysicsQuantity> Add for Vector2<T> {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {
            data: [self.data[0] + other.data[0], self.data[1] + other.data[1]],
        }
    }
}

impl<T: PhysicsQuantity> AddAssign for Vector2<T> {
    fn add_assign(&mut self, other: Self) {
        self.data[0] += other.data[0];
        self.data[1] += other.data[1];
    }
}

impl<T: PhysicsQuantity> Sub for Vector2<T> {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Self {
            data: [self.data[0] - other.data[0], self.data[1] - other.data[1]],
        }
    }
}

impl<T: PhysicsQuantity> SubAssign for Vector2<T> {
    fn sub_assign(&mut self, other: Self) {
        self.data[0] -= other.data[0];
        self.data[1] -= other.data[1];
    }
}

impl<T: PhysicsQuantity> Vector2<T> {
    pub fn new(data: [T; 2]) -> Self {
        Self { data }
    }

    pub fn zero() -> Self {
        Self {
            data: [T::zero(); 2],
        }
    }

    pub fn data(&self) -> [T; 2] {
        self.data
    }

    pub fn from_f64(data: [f64; 2]) -> Self {
        let mut quantity_data = [T::zero(); 2];
        quantity_data[0] = T::from_raw(data[0]);
        quantity_data[1] = T::from_raw(data[1]);
        Self::new(quantity_data)
    }

    pub fn to_ndarray(&self) -> Array1<T> {
        Array1::from_vec(self.data.to_vec())
    }

    pub fn from_ndarray(array: ArrayView1<T>) -> Result<Self, String> {
        if array.len() == 2 {
            Ok(Vector2 {
                data: [array[0], array[1]],
            })
        } else {
            Err(format!("Array length is not 2, it is {}", array.len()))
        }
    }

    pub fn norm(&self) -> T {
        T::from_raw((self.data[0].as_f64().powi(2) + self.data[1].as_f64().powi(2)).sqrt())
    }

    pub fn abs(&self) -> Self {
        Self {
            data: [self.data[0].abs(), self.data[1].abs()],
        }
    }

    pub fn to_unit_vector(&self) -> Vector2<f64> {
        let len = self.norm();
        if len.is_zero() {
            return Vector2::zero();
        }
        self.as_f64() / len.as_f64()
    }

    pub fn as_f64(&self) -> Vector2<f64> {
        Vector2::new([self.data[0].as_f64(), self.data[1].as_f64()])
    }

    pub fn from_raw(raw: Vector2<f64>) -> Self {
        Self {
            data: [T::from_raw(raw[0]), T::from_raw(raw[1])],
        }
    }

    pub fn optimize(&mut self) {
        for element in &mut self.data {
            element.optimize();
        }
    }

    pub fn dot_vct<U, V>(&self, other: &Vector2<U>) -> V
    where
        T: Mul<U, Output = V>,
        U: PhysicsQuantity,
        V: PhysicsQuantity + Add<Output = V>,
    {
        self.data[0] * other.data[0] + self.data[1] * other.data[1]
    }
}

impl Vector2<f64> {
    pub fn x() -> Self {
        Self { data: [1.0, 0.0] }
    }

    pub fn y() -> Self {
        Self { data: [0.0, 1.0] }
    }
}

impl<T: PhysicsQuantity> Index<usize> for Vector2<T> {
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        &self.data[index]
    }
}

impl<T: PhysicsQuantity> IndexMut<usize> for Vector2<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.data[index]
    }
}