#vector-math #matrix-operations #2d-3d #gamedev #physics-simulation #math #utility

no-std swift_vec

A convenient and comfortable vector maths crate that supports 2D, 3D, and 4D matrices and vectors - along with additional scalar math

8 unstable releases (3 breaking)

0.4.1 Jun 16, 2024
0.4.0 Jun 5, 2024
0.3.0 Jun 3, 2024
0.2.0 Jan 22, 2024
0.1.3 Jan 19, 2024

#262 in Algorithms

Download history 186/week @ 2024-06-15 5/week @ 2024-06-22 17/week @ 2024-06-29 4/week @ 2024-07-27 17/week @ 2024-09-21 40/week @ 2024-09-28

57 downloads per month

MIT/Apache

140KB
2.5K SLoC

SwiftVec

Static Badge Crates.io Version Static Badge Crates.io License

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.

⚠️WARNING⚠️
This crate is in its infancy! 4D vectors are missing, along with other planned functionality. Beware of bugs!

Getting Started!

Simply either run cargo add swift_vec at the terminal directed towards the directory of your project, or add swift_vec = X.X to your cargo.toml file.

To show off some basic functionality of what this crate allows for;

use core::f32::consts::TAU;

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

    // Supports tuple destructuring and field indexing.
    let Vec2(x, y): Vec2<i32> = Vec2(1, 0);
    match Vec2(x, y) {
        Vec2( 0,  1) => println!("Up"),
        Vec2( 0, -1) => println!("Down"),
        Vec2(-1,  0) => println!("Left"),
        Vec2( 1,  0) => println!("Right"),
        _            => println!("Other")
    }

    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, X);
    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: Vec3<f32> = (vec_i32.cast().unwrap() + vec_u32.cast().unwrap() + vec_usize.cast().unwrap() + vec_f64.cast().unwrap()) / 4.0;
    
    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 = 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, 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);

    assert_eq!(c, Vec2(2.0, 2.5));
    
    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)));

    /*
     * Matrices are also supported!
     */

    // Create a matrix from a scale.
    let control: Mat3<u32> = Mat3::new(
        2, 0, 0,
        0, 4, 0,
        0, 0, 8
    );

    let scale: Vec3<u32> = Vec3(2, 4, 8);
    let mat:   Mat3<u32> = Mat3::from_scale(scale);

    assert_eq!(mat, control);
    
    // Rotate the matrix.
    // You can use `rotated_free()` to specify a custom axis.
    let mut mat: Mat3<f32> = mat.cast().unwrap();
            mat            = mat.rotated(TAU / 2.0, SignedAxis3::YPos);
            mat            = mat.rotated(TAU / 4.0, SignedAxis3::XPos);
    
    assert!(mat.get_scale().approx_eq(Vec3(2.0, 4.0, 8.0)));
    
    // Matrix inversion is suppported.
    let control: Mat3<f32> = Mat3::new(
         0.452055,  0.041096, -0.39726,
        -0.054795, -0.09589,   0.260274,
        -0.041096,  0.178082, -0.054795
    );
    
    let mat: Mat3<f32> = Mat3::new(
        3.0, 5.0, 2.0,
        1.0, 3.0, 7.0,
        1.0, 6.0, 3.0
    );
    
    assert!(mat.inverse().approx_eq(&control));

    // So is normalization.
    let mut mat: Mat3<f32> = Mat3::IDENTITY.scaled(Vec3(3.0, 4.0, 6.0));
            mat            = mat.rotated(TAU, SignedAxis3::YPos);
	        mat            = mat.rotated(TAU, SignedAxis3::XPos);
	        mat            = mat.orthonormalized();
    
    assert!(Vec3(
        mat.x.length(),   // You can also index Matrices by using `Axis3`.
        mat.y.length(),
        mat.z.length()
    ).approx_eq(1.0.vec3()));

    // And a bunch of other stuff...
}

We also provide functionality for rectangles and their associated geometric functions;

use swift_vec::prelude::*;
use swift_vec::vector::{ Vec2, Axis2 };
use swift_vec::rect::{ Rect2, Side2 };

fn main() {

    // Just like vectors, rectangles can be destructured and indexed.
    let Rect2(position, dimensions): Rect2<i32> = Rect2(Vec2(1, 1), Vec2(3, 6));
    let rect:                        Rect2<i32> = Rect2(position, dimensions);

    let longest_axis:   Axis2 = rect.longest_axis();
    let longest_length: i32   = rect.longest_axis_length();

    assert_eq!(longest_axis,   Axis2::Y);
    assert_eq!(longest_length, 6);

    // There are checks in place for determining whether rectangles intersect, and to allow for the
    // computation of their cross-section.
    let rect_a: Rect2<f32> = Rect2::from_offsets(-5.0, -5.0, 5.0, 5.0);
    let rect_b: Rect2<f32> = Rect2::from_components(-10.0, -10.0, 7.5, 7.5);

    assert_eq!(rect_a.intersects(&rect_b, false), true);   // `include_borders` is set to false - not that it matters here.
    assert_eq!(rect_a.intersection(&rect_b).unwrap(), Rect2(Vec2(-5.0, -5.0), Vec2(2.5, 2.5)));

    let smaller_rect: Rect2<isize> = Rect2::unit();
    let bigger_rect:  Rect2<i64>   = Rect2(Vec2(-32, -32), Vec2(64, 64));

    assert_eq!(bigger_rect.encompasses(&smaller_rect.cast()), true);   // Casting is supported.
    assert_eq!(smaller_rect.encompasses(&bigger_rect.cast()), false);

    // Rectangles can be checked to see if they contain a point.
    let platform: Rect2<i16> = Rect2(Vec2(0, 0), Vec2(100, 100));
    let point:    Vec2<i16>  = Vec2(50, 50);

    assert_eq!(platform.encompasses_point(point), true);

    // Rectangles can be merged and their shape can be manipulated.
    let rect_a: Rect2<i32> = Rect2::from_components(-3, -3, 3, 3);
    let rect_b: Rect2<i32> = Rect2::from_components(3, 3, 3, 3);
    let merged: Rect2<i32> = rect_a.merge(&rect_b);
    
    assert_eq!(merged, Rect2(Vec2(-3, -3), Vec2(9, 9)));

    let base_rect: Rect2<i32> = Rect2::unit();
    let mod_rect:  Rect2<i32> = base_rect.grow_side(Side2::Top, 5);

    assert_eq!(mod_rect, Rect2(Vec2(0, -5), Vec2(1, 6)));
}

Features

  • ℹ️ Simple yet intuitive syntax. No messy constructors!
  • ➕ Standard vector arithmetic and operations.
  • ✨ Matrix operation support!
  • ⛛ Trigonometric functions and angle manipulation.
  • ↗️ Standard vector operations such as magnitude(), normalize(), dot(), cross(), etc.
  • 🪞 Reflection and refraction functions.
  • 🌍 Geometric comparisons and operations such as distance_to(), slide(), etc.
  • 🐌 Different interpolation methods such as lerp(), bezier_sample(), cubic_interpolate(), and cubic_interpolate_in_time().
  • 📚 Aliases for common functions, such as length() for magnitude().

Dependencies

~0.5–1.4MB
~37K SLoC