fleabitdev / glsp

The GameLisp scripting language
https://gamelisp.rs/
Apache License 2.0
395 stars 13 forks source link

Common GameLisp Math library? #21

Closed cedric-h closed 4 years ago

cedric-h commented 4 years ago

I vaguely remember something like this being addressed in the docs, but I searched for it and couldn't find what I was thinking of. Since GameLisp is domain specific -- it's for games -- and games happen to often involve some degree of math, I think it would make sense to have two to three dimensional vectors built into the std. (Okay, maybe four for quaternions?)

How those should be represented raises some questions. I've implemented a bunch of rfns that take simple lists of two numbers, but then simple operations like addition, subtraction, etc. can't conflict with the built in + - /, etc. and you don't have a method namespace for them. (Not to mention the unnecessary heap allocations caused by using entire lists for these when you know you only need two elements).

    #[derive(Copy, Clone)]
    struct V(f32, f32);
    impl ToVal for V {
        fn to_val(&self) -> GResult<Val> {
            ToVal::to_val(&(self.0, self.1))
        }
    }
    impl FromVal for V {
        fn from_val(val: &Val) -> GResult<Self> {
            let (x, y) = <(Num, Num)>::from_val(val)?; 
            Ok(V(x.into_f32(), y.into_f32()))
        }
    }

    glsp::bind_rfn("delta", rfn!(delta))?;
    glsp::bind_rfn("sub", rfn!(delta))?;
    fn delta(V(x1, y1): V, V(x2, y2): V) -> V {
        V(x1 - x2, y1 - y2)
    }

    glsp::bind_rfn("dot", rfn!(dot))?;
    fn dot(V(x1, y1): V, V(x2, y2): V) -> f32 {
        (x1 * x2) + (y1 * y2)
    }

    glsp::bind_rfn("magn2", rfn!(magn2))?;
    fn magn2(v: V) -> f32 {
        dot(v, v)
    }

    glsp::bind_rfn("magn", rfn!(magn))?;
    fn magn(v: V) -> f32 {
        magn2(v).sqrt()
    }

    glsp::bind_rfn("scale", rfn!(scale))?;
    fn scale(V(x1, y1): V, scale: f32) -> V {
        V(x1 * scale, y1 * scale)
    }

    glsp::bind_rfn("norm", rfn!(norm))?;
    fn norm(v: V) -> V {
        scale(v, 1.0 / magn(v))
    }

I regret going taking this route, and think I should've made a Vec2 rdata instead, so I could do (.+ v1 v2). But I don't believe I'm the only person who would benefit from having easy to use Vec2s in gamelisp (nor do I even believe that I am the first, but if I have to make my own I might as well make it available for everyone else as well).

If @fleabitdev isn't inclined to add anything of the sort to gamelisp's std, how should we proceed? We could make a Rust crate for this? Perhaps it could return mint Vec2s for easy conversion? (perhaps with cargo features with direct support for certain vectors, so the overhead for conversion is lower?)

cedric-h commented 4 years ago

Something I just encountered -- it's quite convenient if your vector operators operate on lists, like the built in +, -, etc. operators

fleabitdev commented 4 years ago

GameLisp deliberately lacks a standard geometry library for the same reasons that it lacks a standard input/output library. Creating a good one-size-fits-all solution would be very difficult, and hand-rolling a custom solution for each project should be pretty straightforward (modulo issue #15, and GameLisp's lack of overloading for mathematical operators). In your case, I agree that defining an rdata ! { struct Vec2 ... } would probably be the right approach.

You're more than welcome to publish a 2D geometry crate for GameLisp if you like, but please proceed with caution - making a crate which is worth using would be a real challenge.

In general, my instinct is that Rust's culture of publishing lots of small reusable crates might not be a good fit for GameLisp. Because API bindings are quite low-effort to write, in many cases it would be better to just publish a pure-Rust crate, and let each GameLisp programmer devise a binding for the crate which works best for their own project. (An exception would be when the API surface is very large - for example, I could envisage a glsp-imgui crate which is a fairly straight translation of the C++ interface.)