gemath 0.1.0

Type-safe game math with type-level units/spaces, typed angles, and explicit fallible ops (plus optional geometry/collision).
Documentation
use criterion::{criterion_group, criterion_main, Criterion, black_box};
use gemath::{vec2::Vec2f32, vec3::Vec3f32, vec4::Vec4f32};
use glam::{Vec2 as GVec2, Vec3 as GVec3, Vec4 as GVec4};

fn _bench_vec2(c: &mut Criterion) {
    let v1 = Vec2f32::new(1.0, 2.0);
    let v2 = Vec2f32::new(3.0, 4.0);
    // glam benchmarks
    let gv1 = GVec2::new(1.0, 2.0);
    let gv2 = GVec2::new(3.0, 4.0);
    
    let scalar = 2.5;
    c.bench_function("Vec2::new", |b| b.iter(|| Vec2f32::new(black_box(1.0), black_box(2.0))));
    c.bench_function("glam::Vec2::new", |b| b.iter(|| GVec2::new(black_box(1.0), black_box(2.0))));

    c.bench_function("Vec2 add", |b| b.iter(|| black_box(v1) + black_box(v2)));
    c.bench_function("glam::Vec2 add", |b| b.iter(|| black_box(gv1) + black_box(gv2)));

    c.bench_function("Vec2 sub", |b| b.iter(|| black_box(v1) - black_box(v2)));
    c.bench_function("glam::Vec2 sub", |b| b.iter(|| black_box(gv1) - black_box(gv2)));

    c.bench_function("Vec2 neg", |b| b.iter(|| -black_box(v1)));
    c.bench_function("glam::Vec2 neg", |b| b.iter(|| -black_box(gv1)));

    c.bench_function("Vec2 mul scalar", |b| b.iter(|| black_box(v1) * black_box(scalar)));
    c.bench_function("glam::Vec2 mul scalar", |b| b.iter(|| black_box(gv1) * black_box(scalar)));

    c.bench_function("Vec2 mul hadamard", |b| b.iter(|| black_box(v1) * black_box(v2)));
    c.bench_function("glam::Vec2 mul hadamard", |b| b.iter(|| black_box(gv1) * black_box(gv2)));

    c.bench_function("Vec2 div scalar", |b| b.iter(|| black_box(v1) / black_box(scalar)));
    c.bench_function("glam::Vec2 div scalar", |b| b.iter(|| black_box(gv1) / black_box(scalar)));

    c.bench_function("Vec2 div hadamard", |b| b.iter(|| black_box(v1) / black_box(v2)));
    c.bench_function("glam::Vec2 div hadamard", |b| b.iter(|| black_box(gv1) / black_box(gv2)));

    c.bench_function("Vec2 dot", |b| b.iter(|| black_box(v1).dot(black_box(v2))));
    c.bench_function("glam::Vec2 dot", |b| b.iter(|| black_box(gv1).dot(black_box(gv2))));

    c.bench_function("Vec2 cross", |b| b.iter(|| black_box(v1).cross(black_box(v2))));
    c.bench_function("glam::Vec2 cross (perp_dot)", |b| b.iter(|| black_box(gv1).perp_dot(black_box(gv2))));

    c.bench_function("Vec2 length", |b| b.iter(|| black_box(v1).length()));
    c.bench_function("glam::Vec2 length", |b| b.iter(|| black_box(gv1).length()));

    c.bench_function("Vec2 normalize", |b| b.iter(|| black_box(v1).normalize()));
    c.bench_function("glam::Vec2 normalize", |b| b.iter(|| black_box(gv1).normalize()));

    c.bench_function("Vec2 lerp", |b| b.iter(|| black_box(v1).lerp(black_box(v2), black_box(0.5))));
    c.bench_function("glam::Vec2 lerp", |b| b.iter(|| black_box(gv1).lerp(black_box(gv2), black_box(0.5))));

    c.bench_function("Vec2 angle_between", |b| b.iter(|| black_box(v1).angle_between(black_box(v2))));
    // c.bench_function("glam::Vec2 angle_between", |b| b.iter(|| black_box(gv1).angle_between(black_box(gv2))));

    c.bench_function("Vec2 project_onto", |b| b.iter(|| black_box(v1).project_onto(black_box(v2))));
    c.bench_function("glam::Vec2 project_onto", |b| b.iter(|| black_box(gv1).project_onto(black_box(gv2))));

    c.bench_function("Vec2 distance", |b| b.iter(|| black_box(v1).distance(black_box(v2))));
    c.bench_function("glam::Vec2 distance", |b| b.iter(|| black_box(gv1).distance(black_box(gv2))));

    c.bench_function("Vec2 reflect", |b| b.iter(|| black_box(v1).reflect(black_box(v2))));
    c.bench_function("glam::Vec2 reflect", |b| b.iter(|| black_box(gv1).reflect(black_box(gv2.normalize()))));

    c.bench_function("Vec2 refract", |b| b.iter(|| black_box(v1).refract(black_box(v2), black_box(1.33))));
    c.bench_function("glam::Vec2 refract", |b| b.iter(|| black_box(gv1).refract(black_box(gv2.normalize()), black_box(1.33))));
}

fn _bench_vec3(c: &mut Criterion) {
    let v1 = Vec3f32::new(1.0, 2.0, 3.0);
    let v2 = Vec3f32::new(4.0, 5.0, 6.0);
    // glam benchmarks
    let gv1 = GVec3::new(1.0, 2.0, 3.0);
    let gv2 = GVec3::new(4.0, 5.0, 6.0);
    let scalar = 2.5;
    // Mix each pair of Vec3 and glam::Vec3 benchmarks alongside for direct comparison

    c.bench_function("Vec3::new", |b| b.iter(|| Vec3f32::new(black_box(1.0), black_box(2.0), black_box(3.0))));
    c.bench_function("glam::Vec3::new", |b| b.iter(|| GVec3::new(black_box(1.0), black_box(2.0), black_box(3.0))));

    c.bench_function("Vec3 add", |b| b.iter(|| black_box(v1) + black_box(v2)));
    c.bench_function("glam::Vec3 add", |b| b.iter(|| black_box(gv1) + black_box(gv2)));

    c.bench_function("Vec3 sub", |b| b.iter(|| black_box(v1) - black_box(v2)));
    c.bench_function("glam::Vec3 sub", |b| b.iter(|| black_box(gv1) - black_box(gv2)));

    c.bench_function("Vec3 neg", |b| b.iter(|| -black_box(v1)));
    c.bench_function("glam::Vec3 neg", |b| b.iter(|| -black_box(gv1)));

    c.bench_function("Vec3 mul scalar", |b| b.iter(|| black_box(v1) * black_box(scalar)));
    c.bench_function("glam::Vec3 mul scalar", |b| b.iter(|| black_box(gv1) * black_box(scalar)));

    c.bench_function("Vec3 mul hadamard", |b| b.iter(|| black_box(v1) * black_box(v2)));
    c.bench_function("glam::Vec3 mul hadamard", |b| b.iter(|| black_box(gv1) * black_box(gv2)));

    c.bench_function("Vec3 div scalar", |b| b.iter(|| black_box(v1) / black_box(scalar)));
    c.bench_function("glam::Vec3 div scalar", |b| b.iter(|| black_box(gv1) / black_box(scalar)));

    c.bench_function("Vec3 div hadamard", |b| b.iter(|| black_box(v1) / black_box(v2)));
    c.bench_function("glam::Vec3 div hadamard", |b| b.iter(|| black_box(gv1) / black_box(gv2)));

    c.bench_function("Vec3 dot", |b| b.iter(|| black_box(v1).dot(black_box(v2))));
    c.bench_function("glam::Vec3 dot", |b| b.iter(|| black_box(gv1).dot(black_box(gv2))));

    c.bench_function("Vec3 cross", |b| b.iter(|| black_box(v1).cross(black_box(v2))));
    c.bench_function("glam::Vec3 cross", |b| b.iter(|| black_box(gv1).cross(black_box(gv2))));

    c.bench_function("Vec3 length", |b| b.iter(|| black_box(v1).length()));
    c.bench_function("glam::Vec3 length", |b| b.iter(|| black_box(gv1).length()));

    c.bench_function("Vec3 normalize", |b| b.iter(|| black_box(v1).normalize()));
    c.bench_function("glam::Vec3 normalize", |b| b.iter(|| black_box(gv1).normalize()));

    c.bench_function("Vec3 lerp", |b| b.iter(|| black_box(v1).lerp(black_box(v2), black_box(0.5))));
    c.bench_function("glam::Vec3 lerp", |b| b.iter(|| black_box(gv1).lerp(black_box(gv2), black_box(0.5))));

    c.bench_function("Vec3 angle_between", |b| b.iter(|| black_box(v1).angle_between(black_box(v2))));
    c.bench_function("glam::Vec3 angle_between", |b| b.iter(|| black_box(gv1).angle_between(black_box(gv2))));

    c.bench_function("Vec3 project_onto", |b| b.iter(|| black_box(v1).project_onto(black_box(v2))));
    c.bench_function("glam::Vec3 project_onto", |b| b.iter(|| black_box(gv1).project_onto(black_box(gv2))));

    c.bench_function("Vec3 distance", |b| b.iter(|| black_box(v1).distance(black_box(v2))));
    c.bench_function("glam::Vec3 distance", |b| b.iter(|| black_box(gv1).distance(black_box(gv2))));

    c.bench_function("Vec3 reflect", |b| b.iter(|| black_box(v1).reflect(black_box(v2))));
    c.bench_function("glam::Vec3 reflect", |b| b.iter(|| black_box(gv1).reflect(black_box(gv2.normalize()))));

    c.bench_function("Vec3 refract", |b| b.iter(|| black_box(v1).refract(black_box(v2), black_box(1.33))));
    c.bench_function("glam::Vec3 refract", |b| b.iter(|| black_box(gv1).refract(black_box(gv2.normalize()), black_box(1.33))));
}

fn bench_vec4(c: &mut Criterion) {
    let v1 = Vec4f32::new(1.0, 2.0, 3.0, 4.0);
    let v2 = Vec4f32::new(5.0, 6.0, 7.0, 8.0);
    // glam benchmarks
    let gv1 = GVec4::new(1.0, 2.0, 3.0, 4.0);
    let gv2 = GVec4::new(5.0, 6.0, 7.0, 8.0);
    let scalar = 2.5;
    // Mix each pair of Vec4 and glam::Vec4 benchmarks together for direct comparison

    c.bench_function("Vec4::new", |b| b.iter(|| Vec4f32::new(black_box(1.0), black_box(2.0), black_box(3.0), black_box(4.0))));
    c.bench_function("glam::Vec4::new", |b| b.iter(|| GVec4::new(black_box(1.0), black_box(2.0), black_box(3.0), black_box(4.0))));

    c.bench_function("Vec4 add", |b| b.iter(|| black_box(v1) + black_box(v2)));
    c.bench_function("glam::Vec4 add", |b| b.iter(|| black_box(gv1) + black_box(gv2)));

    c.bench_function("Vec4 sub", |b| b.iter(|| black_box(v1) - black_box(v2)));
    c.bench_function("glam::Vec4 sub", |b| b.iter(|| black_box(gv1) - black_box(gv2)));

    c.bench_function("Vec4 neg", |b| b.iter(|| -black_box(v1)));
    c.bench_function("glam::Vec4 neg", |b| b.iter(|| -black_box(gv1)));

    c.bench_function("Vec4 mul scalar", |b| b.iter(|| black_box(v1) * black_box(scalar)));
    c.bench_function("glam::Vec4 mul scalar", |b| b.iter(|| black_box(gv1) * black_box(scalar)));

    c.bench_function("Vec4 mul hadamard", |b| b.iter(|| black_box(v1) * black_box(v2)));
    c.bench_function("glam::Vec4 mul hadamard", |b| b.iter(|| black_box(gv1) * black_box(gv2)));

    c.bench_function("Vec4 div scalar", |b| b.iter(|| black_box(v1) / black_box(scalar)));
    c.bench_function("glam::Vec4 div scalar", |b| b.iter(|| black_box(gv1) / black_box(scalar)));

    c.bench_function("Vec4 div hadamard", |b| b.iter(|| black_box(v1) / black_box(v2)));
    c.bench_function("glam::Vec4 div hadamard", |b| b.iter(|| black_box(gv1) / black_box(gv2)));

    c.bench_function("Vec4 dot", |b| b.iter(|| black_box(v1).dot(black_box(v2))));
    c.bench_function("glam::Vec4 dot", |b| b.iter(|| black_box(gv1).dot(black_box(gv2))));

    c.bench_function("Vec4 length", |b| b.iter(|| black_box(v1).length()));
    c.bench_function("glam::Vec4 length", |b| b.iter(|| black_box(gv1).length()));

    c.bench_function("Vec4 normalize", |b| b.iter(|| black_box(v1).normalize()));
    c.bench_function("glam::Vec4 normalize", |b| b.iter(|| black_box(gv1).normalize()));

    c.bench_function("Vec4 lerp", |b| b.iter(|| black_box(v1).lerp(black_box(v2), black_box(0.5))));
    c.bench_function("glam::Vec4 lerp", |b| b.iter(|| black_box(gv1).lerp(black_box(gv2), black_box(0.5))));

    c.bench_function("Vec4 angle_between", |b| b.iter(|| black_box(v1).angle_between(black_box(v2))));
    c.bench_function("glam::Vec4 angle_between", |b| b.iter(|| {
        let dot = black_box(gv1).dot(black_box(gv2));
        let len_product = black_box(gv1).length() * black_box(gv2).length();
        if len_product == 0.0 {
            0.0
        } else {
            (dot / len_product).clamp(-1.0, 1.0).acos()
        }
    }));

    c.bench_function("Vec4 project_onto", |b| b.iter(|| black_box(v1).project_onto(black_box(v2))));
    c.bench_function("glam::Vec4 project_onto", |b| b.iter(|| black_box(gv1).project_onto(black_box(gv2))));

    c.bench_function("Vec4 distance", |b| b.iter(|| black_box(v1).distance(black_box(v2))));
    c.bench_function("glam::Vec4 distance", |b| b.iter(|| black_box(gv1).distance(black_box(gv2))));

    c.bench_function("Vec4 reflect", |b| b.iter(|| black_box(v1).reflect(black_box(v2))));
    c.bench_function("glam::Vec4 reflect", |b| b.iter(|| black_box(gv1).reflect(black_box(gv2.normalize()))));

    c.bench_function("Vec4 refract", |b| b.iter(|| black_box(v1).refract(black_box(v2), black_box(1.33))));
    c.bench_function("glam::Vec4 refract", |b| b.iter(|| black_box(gv1).refract(black_box(gv2.normalize()), black_box(1.33))));
}

criterion_group!(benches, 
    // bench_vec2, bench_vec3, 
    bench_vec4);
criterion_main!(benches);