expy 0.0.2

Embeddable & extensible expression evaluator
Documentation
//! Vector math tests.

use expy::{Context, Error, ParseError, eval, eval_in};
use glam::*;


// (Used by randomized tests).
const MAX: f32 = 16.;
const TRIES: usize = 512;


#[test]
fn subscript() {
    let mut ctx = Context::new();
    ctx.set("v", Vec2::new(1., 2.));
    assert_eq!(eval_in(&mut ctx, "v[0]").unwrap().unwrap_float(), 1.);
    assert_eq!(eval_in(&mut ctx, "v[1]").unwrap().unwrap_float(), 2.);
}

#[test]
fn member_access() {
    let mut ctx = Context::new();
    ctx.set("v", Vec2::new(1., 2.));
    assert_eq!(eval_in(&mut ctx, "v.x").unwrap().unwrap_float(), 1.);
    assert_eq!(eval_in(&mut ctx, "v.y").unwrap().unwrap_float(), 2.);
}

#[test]
fn vector_literals() {
    assert_eq!(eval("[1., 2.]").unwrap().unwrap_vec2(), Vec2::new(1., 2.));
    assert_eq!(eval("[1., 2., 3.]").unwrap().unwrap_vec3(), Vec3::new(1., 2., 3.));
    assert_eq!(eval("[1., 2., 3., 4.]").unwrap().unwrap_vec4(), Vec4::new(1., 2., 3., 4.));
    assert_eq!(
        eval("[1., 2., 3., 4., 5.]").unwrap_err(),
        Error::Parse(ParseError::Unsupported("vector of length 5".into())));
}

#[test]
fn subscripting_literal_vectors() {
    assert_eq!(eval("[1., 2.][0]").unwrap().unwrap_float(), 1.);
    assert_eq!(eval("[1., 2., 3.][1]").unwrap().unwrap_float(), 2.);
    assert_eq!(eval("[1., 2., 3., 4.][2]").unwrap().unwrap_float(), 3.);
}

#[test]
fn accessing_members_of_literal_vectors() {
    assert_eq!(eval("[1., 2.].x").unwrap().unwrap_float(), 1.);
    assert_eq!(eval("[1., 2., 3.].y").unwrap().unwrap_float(), 2.);
    assert_eq!(eval("[1., 2., 3., 4.].z").unwrap().unwrap_float(), 3.);
}

#[test]
fn arithmetic_with_scalars() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let v = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        let c = fastrand::f32() * MAX;
        ctx.set("v", v);
        ctx.set("c", c);

        assert_eq!(eval_in(&mut ctx, "v * c").unwrap().unwrap_vec2(), v * c);
        assert_eq!(eval_in(&mut ctx, "c * v").unwrap().unwrap_vec2(), c * v);
        if c != 0. {
            assert_eq!(eval_in(&mut ctx, "v / c").unwrap().unwrap_vec2(), v / c);
        }
    }
}

#[test]
fn arithmetic_on_just_vectors() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let a = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        let b = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        ctx.set("a", a);
        ctx.set("b", b);

        assert_eq!(eval_in(&mut ctx, "a + b").unwrap().unwrap_vec2(), a + b);
        assert_eq!(eval_in(&mut ctx, "a - b").unwrap().unwrap_vec2(), a - b);
        assert_eq!(eval_in(&mut ctx, "a * b").unwrap().unwrap_vec2(), a * b);
        if b.cmpne(Vec2::ZERO).all() {
            assert_eq!(eval_in(&mut ctx, "a / b").unwrap().unwrap_vec2(), a / b);
        }
    }
}

#[test]
fn power() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let v = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        let x = fastrand::f32() * MAX;
        ctx.set("v", v);
        ctx.set("x", x);
        assert_eq!(eval_in(&mut ctx, "v ^ x").unwrap().unwrap_vec2(), v.powf(x));
    }
}

#[test]
fn abs() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let v = Vec2::new(-MAX + fastrand::f32() * 2. * MAX, -MAX +fastrand::f32() * 2. * MAX);
        ctx.set("v", v);
        assert_eq!(eval_in(&mut ctx, "abs(v)").unwrap().unwrap_vec2(), v.abs());
    }
}

#[test]
fn rounding_functions() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let v = Vec2::new(-MAX + fastrand::f32() * 2. * MAX, -MAX +fastrand::f32() * 2. * MAX);
        ctx.set("v", v);

        assert_eq!(eval_in(&mut ctx, "frac(v)").unwrap().unwrap_vec2(), v.fract());
        assert_eq!(eval_in(&mut ctx, "trunc(v)").unwrap().unwrap_vec2(), v.trunc());
        assert_eq!(eval_in(&mut ctx, "floor(v)").unwrap().unwrap_vec2(), v.floor());
        assert_eq!(eval_in(&mut ctx, "ceil(v)").unwrap().unwrap_vec2(), v.ceil());
        assert_eq!(eval_in(&mut ctx, "round(v)").unwrap().unwrap_vec2(), v.round());
    }
}

#[test]
fn length() {
    let mut ctx = Context::new();
    ctx.set("x", Vec2::X);
    ctx.set("y", Vec2::Y);
    assert_eq!(eval_in(&mut ctx, "len(x)").unwrap().unwrap_float(), 1.);
    assert_eq!(eval_in(&mut ctx, "len(y)").unwrap().unwrap_float(), 1.);
}

#[test]
fn length_alias() {
    let mut ctx = Context::new();
    ctx.set("x", Vec2::X);
    ctx.set("y", Vec2::Y);
    ctx.set("z", Vec3::Z);
    assert_eq!(eval_in(&mut ctx, "length(x)").unwrap().unwrap_float(), 1.);
    assert_eq!(eval_in(&mut ctx, "mag(y)").unwrap().unwrap_float(), 1.);
    assert_eq!(eval_in(&mut ctx, "magnitude(z)").unwrap().unwrap_float(), 1.);
}

#[test]
fn normalize() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let v = Vec2::new(-MAX + fastrand::f32() * 2. * MAX, -MAX +fastrand::f32() * 2. * MAX);
        if v.try_normalize().is_some() {
            ctx.set("v", v);

            let n = eval_in(&mut ctx, "normalize(v)").unwrap().unwrap_vec2();
            assert!(n.is_normalized());
        }
    }
}

#[test]
fn distance() {
    let mut ctx = Context::new();
    ctx.set("a", Vec2::new(5., 4.));
    ctx.set("b", Vec2::new(5., -3.));
    assert_eq!(eval_in(&mut ctx, "dist(a, b)").unwrap().unwrap_float(), 7.);
}

#[test]
fn distance_commutative() {
    let mut ctx = Context::new();
    for _ in 0..TRIES {
        let a = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        let b = Vec2::new(fastrand::f32() * MAX, fastrand::f32() * MAX);
        ctx.set("a", a);
        ctx.set("b", b);
        assert!(
            eval_in(&mut ctx, "dist(a, b) == dist(b, a)").unwrap().unwrap_bool(),
            "Distance between v_a ({}) and v_b ({}) is different than from v_b to v_a?!", a, b);
    }
}