Skip to content

Commit

Permalink
Added Vec3 and changed indexing.
Browse files Browse the repository at this point in the history
  • Loading branch information
LunaticWyrm467 committed Jun 3, 2024
1 parent a168029 commit ae043e5
Show file tree
Hide file tree
Showing 14 changed files with 1,373 additions and 315 deletions.
10 changes: 0 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ license = "MIT OR Apache-2.0"

[dependencies]
num-traits = { version = "0.2.17", default-features = false, features = ["libm"] }
approx = "0.5.1"

[features]
default = ["alloc"]
Expand Down
82 changes: 48 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# SwiftVec
![Crates.io License](https://img.shields.io/crates/l/swift_vec?color=green)
[![Crates.io Version](https://img.shields.io/crates/v/swift_vec)](https://crates.io/crates/swift_vec)
[![Documentation](https://docs.rs/swift_vec/badge.svg)](https://docs.rs/swift_vec)
[![Static Badge](https://img.shields.io/badge/GITHUB-LunaticWyrm467%2Fswift-vec-LunaticWyrm467%2Fswift-vec?style=for-the-badge&logo=github)](https://github.com/LunaticWyrm467/swift-vec)
[![Crates.io Version](https://img.shields.io/crates/v/swift-vec?style=for-the-badge&logo=rust)](https://crates.io/crates/swift-vec)
[![Static Badge](https://img.shields.io/badge/DOCS.RS-swift-vec-66c2a5?style=for-the-badge&logo=docs.rs)](https://docs.rs/swift-vec)
![Crates.io License](https://img.shields.io/crates/l/swift-vec?color=green&style=for-the-badge)

**SwiftVec** is an easy-to-use, intuitive vector maths library with a ton
of functionality for game development, physics simulations, and other potential use cases.
Expand All @@ -17,8 +18,9 @@ or add `swift_vec = X.X` to your `cargo.toml` file.
To show off some basic functionality of what this crate allows for;
```rust
use swift_vec::prelude::*;
use swift_vec::vector::{ Vec2, Axis2 };

use swift_vec::vector::{ Vec2, Vec3, Axis2 };
use Axis2::*; // It is recommended to import from the Axis enums if you're going to be
// indexing a lot.
fn main() {

// Supports tuple destructuring and field indexing.
Expand All @@ -31,44 +33,56 @@ fn main() {
_ => println!("Other")
}

let test_vec: Vec2<i32> = Vec2(1, 0);
let argmax_axis: Axis2 = test_vec.argmax();
let argmax_val: i32 = test_vec.get(argmax_axis);
let mut test_vec: Vec2<i32> = Vec2(1, 0);
let argmax_axis: Axis2 = test_vec.argmax();
let argmax_val: i32 = test_vec[argmax_axis];

test_vec[X] = 2; // You could always use tuple fields (`test_vec.0`) but this is more readable.
test_vec[Y] = 3;

assert_eq!(argmax_axis, Axis2::X);
assert_eq!(argmax_val, 1);

// Vectors support all primitive numerical types.
let vec_i32: Vec2<i32> = Vec2::ones_like();
let vec_u32: Vec2<u32> = Vec2::of(2);
let vec_usize: Vec2<usize> = Vec2::of(3);
let vec_f64: Vec2<f64> = Vec2::of(4.0);

assert_eq!(argmax_val, 1);
assert_eq!(test_vec, Vec2(2, 3));

// Vectors support all primitive numerical types and support multiple construction methods.
let vec_i32: Vec3<i32> = 1.vec3();
let vec_u32: Vec3<u32> = (1, 2, 3).vec3();
let vec_usize: Vec3<usize> = (3, Vec2(2, 3)).vec3();
let vec_f64: Vec3<f64> = (Vec2(5.0, 6.0), 4.0).vec3();

// Vectors can be cast to other types, and can be manipulated just like any other numerical data.
let avg: Vec2<f32> = (vec_i32.cast() + vec_u32.cast() + vec_usize.cast() + vec_f64.cast()) / 4.0;
let avg: Vec3<f32> = (vec_i32.cast().unwrap() + vec_u32.cast().unwrap() + vec_usize.cast().unwrap() + vec_f64.cast().unwrap()) / 4.0;

assert_eq!(avg, Vec2(2.5, 2.5));
assert_eq!(avg, Vec3(2.5, 2.75, 2.75));

// Several operations are implemented, such as dot/cross products, magnitude/normalization, etc.
let dot: f64 = Vec2(5.0, 3.0).dot(&Vec2(1.0, 2.0));
let mag: f64 = Vec2(3.0, 4.0).magnitude();
let dot: f64 = Vec3(5.0, 4.0, 3.0).dot(Vec3(1.0, 2.0, 3.0));
let mag: f64 = Vec3(3.0, 4.0, 5.0).magnitude();

assert_eq!(dot, 11.0);
assert_eq!(mag, 5.0);
assert_eq!(Vec2(3.0, 4.0).normalized().magnitude(), 1.0);
// Interpolation is added to both scalar and vector types.
assert_eq!(dot, 22.0);
assert_eq!(mag, 5.0 * 2.0f64.sqrt());
assert!(Vec3(3.0, 4.0, 5.0).normalized().magnitude().approx_eq(1.0));

// Interpolation is added to vector types.
let a: Vec2<f32> = Vec2(1.0, 2.0);
let b: Vec2<f32> = Vec2(3.0, 3.0);
let c: Vec2<f32> = a.lerp(&b, 0.5);
let c: Vec2<f32> = a.lerp(b, 0.5);

assert_eq!(c, Vec2(2.0, 2.5));
assert_eq!(1.0.lerp(2.0, 0.5), 1.5);

// min(), max(), and clamp() functions are also implemented for floating point scalars.
assert_eq!(1.0.min(2.0), 1.0);
assert_eq!(1.0.max(2.0), 2.0);
assert_eq!(1.0.clamp(0.0, 2.0), 1.0);

let a: Vec2<f32> = 1.0.vec2();
let b: Vec2<f32> = 2.0.vec2();

let pre_a: Vec2<f32> = -5.0.vec2();
let post_b: Vec2<f32> = 3.0.vec2();

let c_025: Vec2<f32> = a.cubic_interpolate(b, pre_a, post_b, 0.25);
let c_050: Vec2<f32> = a.cubic_interpolate(b, pre_a, post_b, 0.50);
let c_075: Vec2<f32> = a.cubic_interpolate(b, pre_a, post_b, 0.75);

assert!(c_025.approx_eq(Vec2(1.601563, 1.601563)));
assert!(c_050.approx_eq(Vec2(1.8125, 1.8125 )));
assert!(c_075.approx_eq(Vec2(1.867188, 1.867188)));
}
```

Expand Down
26 changes: 25 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ pub mod rect;
/// This does not include any of the individual types. If you want those, import from the `vector` and/or `rect` modules.
pub mod prelude {
pub use crate::scalar::{ Scalar, IntScalar, SignedScalar, FloatScalar };
pub use crate::vector::{ Vector, IntVector, SignedVector, FloatVector };
pub use crate::vector::{ Vector, IntVector, SignedVector, FloatVector, Vectorized2D, Vectorized3D };
pub use crate::rect::{ Rect, SignedRect, FloatRect };
}


/*
Vectorized
Trait
*/

/// We expose this private trait to the whole library so our library's generic traits can use it,
/// but we don't allow it to be used outside of the library since there are more readable options
/// such as `Vectorized2D`, `Vectorized3D`, and `Vectorized4D` whose functions are more
/// approachable.
mod vectorized {
use crate::vector::VectorAbstract;
use crate::scalar::Scalar;

pub trait Vectorized<T: Scalar + Vectorized<T, V>, V: VectorAbstract<T, V>>: Clone + Copy {

/// Returns a scalar if the type is a scalar, otherwise returns None.
fn attempt_get_scalar(self) -> Option<T>;

/// Converts a type or tuple of types to a suitable Vector representation.
fn dvec(self) -> V;
}
}
5 changes: 3 additions & 2 deletions src/rect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ mod r2d;

use core::fmt::{ self, Debug, Display, Formatter };

use crate::vectorized::Vectorized;
use crate::scalar::{ Scalar, FloatScalar, SignedScalar };
use crate::vector::{ FloatVector, SignedVector, Vector, VectorAbstract, Vectorized };
use crate::vector::{ FloatVector, SignedVector, Vector, VectorAbstract };

pub use r2d::{ Side2, Rect2 };

Expand Down Expand Up @@ -237,7 +238,7 @@ pub trait SignedRect<T: SignedScalar + Vectorized<T, V>, V: SignedVector<T, V, A
}
}

pub trait FloatRect<T: FloatScalar + Vectorized<T, V>, V: FloatVector<T, V, A>, R: FloatRect<T, V, R, A, S>, A, S>: SignedRect<T, V, R, A, S> {
pub trait FloatRect<T: FloatScalar + Vectorized<T, V>, V: FloatVector<T, V, A, C>, R: FloatRect<T, V, R, A, S, C>, A, S, C: Vectorized<T, V>>: SignedRect<T, V, R, A, S> {

/// Returns whether this rectangle is approximately equal to another.
fn approx_eq(&self, other: &R) -> bool {
Expand Down
70 changes: 33 additions & 37 deletions src/rect/r2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//!
use super::*;
use crate::vector::{ Vec2, Axis2 };
use crate::vector::{ Vec2, Axis2::{ self, X, Y } };


/*
Expand Down Expand Up @@ -54,10 +54,10 @@ impl <T: Scalar> Rect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {
}

fn encompass_points(points: &[Vec2<T>]) -> Rect2<T> {
let top: T = points.iter().map(|p| p.y()).reduce(T::min).unwrap();
let bottom: T = points.iter().map(|p| p.y()).reduce(T::max).unwrap();
let left: T = points.iter().map(|p| p.x()).reduce(T::min).unwrap();
let right: T = points.iter().map(|p| p.x()).reduce(T::max).unwrap();
let top: T = points.iter().map(|p| p[Y]).reduce(T::min).unwrap();
let bottom: T = points.iter().map(|p| p[Y]).reduce(T::max).unwrap();
let left: T = points.iter().map(|p| p[X]).reduce(T::min).unwrap();
let right: T = points.iter().map(|p| p[X]).reduce(T::max).unwrap();

Rect2::from_offsets(left, top, right, bottom)
}
Expand Down Expand Up @@ -101,25 +101,21 @@ impl <T: Scalar> Rect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {
}

fn longest_axis(&self) -> Axis2 {
if self.size().y() > self.size().x() {
return Axis2::Y;
if self.size()[Y] > self.size()[X] {
return Y;
}
Axis2::X
X
}

fn shortest_axis(&self) -> Axis2 {
if self.size().y() < self.size().x() {
return Axis2::Y;
if self.size()[Y] < self.size()[X] {
return Y;
}
Axis2::X
X
}

fn axis_length(&self, axis: Axis2) -> T {
match axis {
Axis2::X => self.size().x(),
Axis2::Y => self.size().y(),
Axis2::None => panic!("Axis cannot be None.")
}
self.size()[axis]
}

fn expand_to_include(&self, point: Vec2<T>) -> Rect2<T> {
Expand All @@ -130,18 +126,18 @@ impl <T: Scalar> Rect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {

// Check each component of the origin and end and compare it to that of the given point.
// If a component of a vector is out of bounds, then update the respective component of either the origin or end.
if point.x() < origin.x() {
origin.set_x(point.x());
if point[X] < origin[X] {
origin[X] = point[X];
}
if point.y() < origin.y() {
origin.set_y(point.y());
if point[Y] < origin[Y] {
origin[Y] = point[Y];
}

if point.x() > end.x() {
end.set_x(point.x());
if point[X] > end[X] {
end[X] = point[X];
}
if point.y() > end.y() {
end.set_y(point.y());
if point[Y] > end[Y] {
end[Y] = point[Y];
}

Rect2(origin, end)
Expand All @@ -158,29 +154,29 @@ impl <T: Scalar> Rect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {

fn intersects(&self, other: &Rect2<T>, including_borders: bool) -> bool {
if including_borders {
if self.position().x() > other.position().x() + other.size().x() {
if self.position()[X] > other.position()[X] + other.size()[X] {
return false;
}
if self.position().x() + self.size().x() < other.position().x() {
if self.position()[X] + self.size()[X] < other.position()[X] {
return false;
}
if self.position().y() > other.position().y() + other.size().y() {
if self.position()[Y] > other.position()[Y] + other.size()[Y] {
return false;
}
if self.position().y() + self.size().y() < other.position().y() {
if self.position()[Y] + self.size()[Y] < other.position()[Y] {
return false;
}
} else {
if self.position().x() >= other.position().x() + other.size().x() {
if self.position()[X] >= other.position()[X] + other.size()[X] {
return false;
}
if self.position().x() + self.size().x() <= other.position().x() {
if self.position()[X] + self.size()[X] <= other.position()[X] {
return false;
}
if self.position().y() >= other.position().y() + other.size().y() {
if self.position()[Y] >= other.position()[Y] + other.size()[Y] {
return false;
}
if self.position().y() + self.size().y() <= other.position().y() {
if self.position()[Y] + self.size()[Y] <= other.position()[Y] {
return false;
}
}
Expand All @@ -191,7 +187,7 @@ impl <T: Scalar> Rect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {

impl <T: SignedScalar> SignedRect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {}

impl <T: FloatScalar> FloatRect<T, Vec2<T>, Rect2<T>, Axis2, Side2> for Rect2<T> {}
impl <T: FloatScalar> FloatRect<T, Vec2<T>, Rect2<T>, Axis2, Side2, T> for Rect2<T> {}

impl <T: Scalar> Rect2<T> {

Expand All @@ -216,22 +212,22 @@ impl <T: Scalar> Rect2<T> {

/// Returns the x component of the origin of the `Rect2`.
pub fn x(&self) -> T {
self.0.x()
self.0[X]
}

/// Returns the y component of the origin of the `Rect2`.
pub fn y(&self) -> T {
self.0.y()
self.0[Y]
}

/// Returns the width of the `Rect2`.
pub fn width(&self) -> T {
self.1.x()
self.1[X]
}

/// Returns the height of the `Rect2`.
pub fn height(&self) -> T {
self.1.y()
self.1[Y]
}
}

Expand Down
Loading

0 comments on commit ae043e5

Please sign in to comment.