Skip to content

Commit

Permalink
Clean Up + Bug Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
LunaticWyrm467 committed Jan 18, 2024
1 parent 83a86c8 commit ec42811
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 57 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
"powi",
"signum",
"slerp"
],
"rust-analyzer.linkedProjects": [
".\\Cargo.toml"
]
}
22 changes: 17 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,25 @@
//
//===================================================================================================================================================================================//

//!
//! Created by LunaticWyrm467
//!
//?
//? The root file of the library.
//? Created by LunaticWyrm467 and others.
//?
//? All code is licensed under the MIT license.
//? Feel free to reproduce, modify, and do whatever.
//?

//!
//! The root file of the library. Nothing special here.
//!
/// The scalar module contains traits and functions that are added to basic primitive types.
/// While you can import from this module, scalar traits will automatically be imported from the `prelude` module.
pub mod scalar;
pub mod vector;

/// Generally speaking, you'll want to use the prelude module to get all of the traits and functions you'll need.
/// This does not include any of the individual types. If you want those, import from the `vector` and `scalar` modules.
pub mod prelude {
pub use crate::vector::{ IntVector, SignedVector, FloatVector };
pub use crate::scalar::{ IntScalar, SignedScalar, FloatScalar };
}
6 changes: 3 additions & 3 deletions src/scalar/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use approx::RelativeEq;
use approx::{RelativeEq, AbsDiffEq};
use num_traits::{ Num, Signed, Float, FloatConst, PrimInt, FromPrimitive, ToPrimitive };


Expand Down Expand Up @@ -124,7 +124,7 @@ pub trait SignedScalar: Scalar + Signed {
}

/// Implements unique operations for all floating point primitives.
pub trait FloatScalar: SignedScalar + Float + FloatConst + RelativeEq {}
pub trait FloatScalar: SignedScalar + Float + FloatConst + RelativeEq + AbsDiffEq<Epsilon = Self> {}

/// Adds some additional operations featured in rust that are not available in the standard PrimInt trait for some odd reason.
pub trait IntUnique<T: IntScalar<T>> {
Expand All @@ -141,7 +141,7 @@ pub trait IntUnique<T: IntScalar<T>> {
impl <T: Clone + Copy + Num + Default + PartialOrd + std::fmt::Display + std::fmt::Debug + FromPrimitive + ToPrimitive> Scalar for T {}
impl <T: Scalar + Ord + PrimInt + IntUnique<T>> IntScalar<T> for T {}
impl <T: Scalar + Signed> SignedScalar for T {}
impl <T: SignedScalar + Float + FloatConst + RelativeEq> FloatScalar for T {}
impl <T: SignedScalar + Float + FloatConst + RelativeEq + AbsDiffEq<Epsilon = Self>> FloatScalar for T {}

impl IntUnique<u8> for u8 {
fn ilog(self, base: u8) -> u8 {
Expand Down
33 changes: 19 additions & 14 deletions src/vector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@
//
//===================================================================================================================================================================================//

//!
//! Created by LunaticWyrm467
//!
//?
//? Contains the Vector trait which is used for all vectors (tuples).
//? Created by LunaticWyrm467 and others.
//?
//? All code is licensed under the MIT license.
//? Feel free to reproduce, modify, and do whatever.
//?

pub mod v2d;
//!
//! The vector module contains the definitions of the various vector types, such as `Vec2`, `Vec3`, and `Vec4`.
//! Also contains global traits and functions that are shared between all vector types.
//!
mod v2d;

use std::{ ops::{ Add, Sub, Mul, Div, Rem, Neg, AddAssign, SubAssign, MulAssign, DivAssign, RemAssign }, f32::consts::FRAC_PI_2 };

use approx::relative_eq;

use crate::scalar::{ Scalar, FloatScalar, SignedScalar, IntScalar };
pub use v2d::{ Axis2, Vec2 };


/*
Expand Down Expand Up @@ -115,8 +120,8 @@ where
/// Returns the sign of the vector.
/// For example:
/// ```rust
/// use swift_vec::vector::v2d::Vec2;
/// use swift_vec::vector::SignedVector;
/// use swift_vec::vector::Vec2;
/// use swift_vec::prelude::SignedVector;
///
/// let sign: Vec2<f32> = Vec2(-1.0, 1.0).signum();
/// assert_eq!(sign, Vec2(-1f32, 1f32));
Expand Down Expand Up @@ -149,7 +154,7 @@ pub trait IntVector<T: IntScalar<T>, V: IntVector<T, V, A>, A>: Vector<T, V, A>
pub trait FloatVector<T: FloatScalar, V: FloatVector<T, V, A>, A>: SignedVector<T, V, A> {

//=====// Trigonometry //=====//
/// Initializes a vector from an angle.
/// Initializes a vector from an angle in radians.
fn from_angle(angle: T) -> V;

/// Calculates the angle of a vector in respect to the positive x-axis.
Expand Down Expand Up @@ -353,7 +358,7 @@ pub trait FloatVector<T: FloatScalar, V: FloatVector<T, V, A>, A>: SignedVector<

/// Similar to `cubic_interpolate`, but it has additional time parameters `terminal_t`, `pre_start_t`, and `post_terminal_t`.
/// This can be smoother than `cubic_interpolate` in certain instances.
fn cubic_interpolate_in_time(&self, terminal: &V, pre_start: &V, post_terminal: &V, t0: T, terminal_t: &V, pre_start_t: &V, post_terminal_t: &V) -> V;
fn cubic_interpolate_in_time(&self, terminal: &V, pre_start: &V, post_terminal: &V, t0: T, terminal_t: T, pre_start_t: T, post_terminal_t: T) -> V;

/// Spherically interpolates between two vectors.
/// This interpolation is focused on the length or magnitude of the vectors. If the magnitudes are equal,
Expand Down Expand Up @@ -450,11 +455,11 @@ pub trait FloatVector<T: FloatScalar, V: FloatVector<T, V, A>, A>: SignedVector<
/// Returns whether this vector is normalized.
fn is_normalized(&self) -> bool {
let magnitude: T = self.magnitude();
relative_eq!(magnitude, T::one()) // TODO: Figure out type constraints on epsilon.
relative_eq!(magnitude, T::one(), epsilon = T::epsilon())
}

/// Returns if this vector is approximately zero.
fn is_zero_approx(&self) -> bool;


fn is_zero_approx(&self) -> bool {
self.approx_eq(&Self::zeros_like())
}
}
53 changes: 33 additions & 20 deletions src/vector/v2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@
//
//===================================================================================================================================================================================//

//!
//! Created by LunaticWyrm467
//!
//?
//? Contains the implementations for all of the Vector2D types.
//? Created by LunaticWyrm467 and others.
//?
//? All code is licensed under the MIT license.
//? Feel free to reproduce, modify, and do whatever.
//?

//!
//! A private submodule for the vector module that contains all of the implementations
//! for any of the non-shared behaviours of the 2D vector.
//!
use super::*;
use crate::scalar::{ Scalar, SignedScalar };

Expand All @@ -35,7 +39,9 @@ pub enum Axis2 {
Y
}


/// A 2D Vector with an X and Y component.
/// Contains behaviours and methods for allowing for algebraic, geometric, and trigonometric operations,
/// as well as interpolation and other common vector operations.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct Vec2<T: Scalar>(pub T, pub T);

Expand Down Expand Up @@ -129,7 +135,7 @@ impl <T: FloatScalar> FloatVector<T, Vec2<T>, Axis2> for Vec2<T> {
}

fn angle(&self) -> T {
self.x().atan2(self.y())
self.y().atan2(self.x())
}

fn rotated(&self, angle: T) -> Vec2<T> {
Expand Down Expand Up @@ -201,10 +207,10 @@ impl <T: FloatScalar> FloatVector<T, Vec2<T>, Axis2> for Vec2<T> {
)
}

fn cubic_interpolate_in_time(&self, terminal: &Vec2<T>, pre_start: &Vec2<T>, post_terminal: &Vec2<T>, t0: T, terminal_t: &Vec2<T>, pre_start_t: &Vec2<T>, post_terminal_t: &Vec2<T>) -> Vec2<T> {
fn cubic_interpolate_in_time(&self, terminal: &Vec2<T>, pre_start: &Vec2<T>, post_terminal: &Vec2<T>, t0: T, terminal_t: T, pre_start_t: T, post_terminal_t: T) -> Vec2<T> {
Vec2(
self.x().cubic_interpolate_in_time(terminal.x(), pre_start.x(), post_terminal.x(), t0, terminal_t.x(), pre_start_t.x(), post_terminal_t.x()),
self.y().cubic_interpolate_in_time(terminal.y(), pre_start.y(), post_terminal.y(), t0, terminal_t.y(), pre_start_t.y(), post_terminal_t.y())
self.x().cubic_interpolate_in_time(terminal.x(), pre_start.x(), post_terminal.x(), t0, terminal_t, pre_start_t, post_terminal_t),
self.y().cubic_interpolate_in_time(terminal.y(), pre_start.y(), post_terminal.y(), t0, terminal_t, pre_start_t, post_terminal_t)
)
}

Expand Down Expand Up @@ -244,12 +250,9 @@ impl <T: FloatScalar> FloatVector<T, Vec2<T>, Axis2> for Vec2<T> {
Vec2(self.x().round(), self.y().round())
}

fn approx_eq(&self, other: &Vec2<T>) -> bool { // TODO: Figure out epsilon trait requirements.
relative_eq!(self.x(), other.x()) && approx::relative_eq!(self.y(), other.y())
}

fn is_zero_approx(&self) -> bool { // TODO: Figure out epsilon trait requirements.
relative_eq!(self.x(), T::zero()) && approx::relative_eq!(self.y(), T::zero())
fn approx_eq(&self, other: &Vec2<T>) -> bool {
let eps: T = T::epsilon() * T::from(4).unwrap();
relative_eq!(self.x(), other.x(), epsilon = eps) && approx::relative_eq!(self.y(), other.y(), epsilon = eps)
}
}

Expand All @@ -264,6 +267,11 @@ impl <T: Scalar> Vec2<T> {
pub fn right() -> Vec2<T> {
Vec2(T::one(), T::zero())
}

/// Initializes a vector from a scalar.
pub fn of(scalar: T) -> Vec2<T> {
Vec2(scalar, scalar)
}

/// Gets the x component of the vector.
pub fn x(&self) -> T {
Expand All @@ -276,13 +284,18 @@ impl <T: Scalar> Vec2<T> {
}

/// Gets the x and y components of this vector in order as a tuple.
pub fn xy(&self) -> (T, T) {
pub fn raw(&self) -> (T, T) {
(self.x(), self.y())
}

/// Gets the x and y components of this vector in inverse order as a tuple.
pub fn yx(&self) -> (T, T) {
(self.y(), self.x())
/// An alias for the identity function.
pub fn xy(&self) -> Vec2<T> {
self.identity().to_owned()
}

/// Gets the x and y components of this vector in inverse order as a vector of 2.
pub fn yx(&self) -> Vec2<T> {
Vec2(self.y(), self.x())
}

/// Calculates the aspect ratio of this vector.
Expand Down
65 changes: 50 additions & 15 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::f32::consts as f32consts;
use std::f64::consts as f64consts;

use swift_vec;
use swift_vec::prelude::*;
use approx::assert_relative_eq;

#[test]
fn vec2_angle_to() {

use swift_vec::vector::v2d::Vec2;
use swift_vec::vector::FloatVector;
use swift_vec::vector::Vec2;

// Create four directional vectors
let up: Vec2<f32> = Vec2::up();
Expand All @@ -26,31 +25,67 @@ fn vec2_angle_to() {
#[test]
fn vec2_rotate() {

use swift_vec::vector::v2d::Vec2;
use swift_vec::vector::FloatVector;
use swift_vec::vector::Vec2;

// Create a northbound vector
let north: Vec2<f64> = Vec2::up();

// Rotate the vector 90 degrees counterclockwise.
// Create a northbound vector and rotate the vector 90 degrees counterclockwise.
// Check that the vector is now westbound.
let north: Vec2<f64> = Vec2::up();
let rotated: Vec2<f64> = north.rotated(f64consts::FRAC_PI_2);

// Check that the vector is now westbound.
assert!(rotated.approx_eq(&Vec2::left()));

// Create a vector from the rotation of 180 degrees and rotate the vector 90 degrees clockwise.
// Check that the vector is now of a 90 degree angle.
let rotated_180: Vec2<f64> = Vec2::from_angle(180f64.to_radians());
let rotated_90: Vec2<f64> = rotated_180.rotated(-f64consts::FRAC_PI_2);

assert_relative_eq!(rotated_90.angle().to_degrees(), 90f64);
}

#[test]
fn vec2_log() {

use swift_vec::vector::v2d::Vec2;
use swift_vec::vector::IntVector;
use swift_vec::vector::Vec2;

// Create a vector of 10.
// Create a vector of 10,
// then compute the logarithm of base 10 of the vector.
let vec: Vec2<usize> = Vec2(10, 10);

// Compute the logarithm of base 10 of the vector.
let log: Vec2<usize> = vec.log(10);

// Check that the result is 1.
assert_eq!(log, Vec2(1, 1));
}

#[test]
fn vec2_interpolation() {

use swift_vec::vector::Vec2;

// Cubically interpolate between two vectors.
let a: Vec2<f32> = Vec2::of(1.0);
let b: Vec2<f32> = Vec2::of(2.0);

let pre_start: Vec2<f32> = Vec2::of(-5.0);
let post_terminal: Vec2<f32> = Vec2::of( 3.0);

let c_025: Vec2<f32> = a.cubic_interpolate(&b, &pre_start, &post_terminal, 0.25);
let c_05: Vec2<f32> = a.cubic_interpolate(&b, &pre_start, &post_terminal, 0.50);
let c_075: Vec2<f32> = a.cubic_interpolate(&b, &pre_start, &post_terminal, 0.75);

assert!(c_025.approx_eq(&Vec2(1.601563, 1.601563)));
assert!(c_05.approx_eq( &Vec2(1.8125, 1.8125 )));
assert!(c_075.approx_eq(&Vec2(1.867188, 1.867188)));

// Test the interpolation in time.
let terminal_t: f32 = -3.0;
let pre_start_t: f32 = 0.0;
let post_terminal_t: f32 = 2.0;

let ct_025: Vec2<f32> = a.cubic_interpolate_in_time(&b, &pre_start, &post_terminal, 0.25, terminal_t, pre_start_t, post_terminal_t);
let ct_05: Vec2<f32> = a.cubic_interpolate_in_time(&b, &pre_start, &post_terminal, 0.50, terminal_t, pre_start_t, post_terminal_t);
let ct_075: Vec2<f32> = a.cubic_interpolate_in_time(&b, &pre_start, &post_terminal, 0.75, terminal_t, pre_start_t, post_terminal_t);

assert!(ct_025.approx_eq(&Vec2(-2.378125, -2.378125)));
assert!(ct_05.approx_eq( &Vec2(-0.425, -0.425 )));
assert!(ct_075.approx_eq(&Vec2( 0.990625, 0.990625)));
}

0 comments on commit ec42811

Please sign in to comment.