bitshifter / glam-rs

A simple and fast linear algebra library for games and graphics
Apache License 2.0
1.5k stars 152 forks source link

Mutable Swizzles #516

Open the-ssd opened 4 months ago

the-ssd commented 4 months ago

The main Idea is to be able to do this

let mut test_vec = Vec3::ZERO;
*test_vec.xy_mut() = Vec2::ONE;
assert_eq!(test_vec, Vec3::new(1.0, 1.0, 0.0))

Here is something that works, but only done for xy_mut()

trait MutSwizzleVec3<'a> {
    fn xy_mut(&'a mut self) -> Vec3XY<'a>;
}

impl<'a> MutSwizzleVec3<'a> for Vec3 {
    fn xy_mut(&'a mut self) -> Vec3XY<'a> {
        let xy = self.xy();
        Vec3XY {
            inner: self,
            thing: xy,
        }
    }
}

struct Vec3XY<'a> {
    inner: &'a mut Vec3,
    thing: Vec2,
}

impl<'a> core::ops::Deref for Vec3XY<'a> {
    type Target = Vec2;

    fn deref(&self) -> &Self::Target {
        &self.thing
    }
}

impl<'a> core::ops::DerefMut for Vec3XY<'a> {
    fn deref_mut(&mut self) -> &mut Vec2 {
        &mut self.thing
    }
}

impl Drop for Vec3XY<'_> {
    fn drop(&mut self) {
        self.inner.x = self.thing.x;
        self.inner.y = self.thing.y;
    }
}

#[test]
fn _test() {
    let mut test_vec = Vec3::ZERO;
    *test_vec.xy_mut() = Vec2::ONE;
    assert_eq!(test_vec, Vec3::new(1.0, 1.0, 0.0))
}
the-ssd commented 4 months ago

Alternatively, it can be implemented like this. (But this doesn't allow passing &mut Vec2 to a function, for example)

pub trait Vec3SwizzleSet {
    fn set_xz(&mut self, thing: Vec2);
    fn set_xy(&mut self, thing: Vec2);
    fn set_zy(&mut self, thing: Vec2);
    fn set_yz(&mut self, thing: Vec2);
}

impl Vec3SwizzleSet for Vec3 {
    fn set_xz(&mut self, thing: Vec2) {
        self.x = thing.x;
        self.z = thing.y;
    }

    fn set_xy(&mut self, thing: Vec2) {
        self.x = thing.x;
        self.y = thing.y;
    }

    fn set_zy(&mut self, thing: Vec2) {
        self.z = thing.x;
        self.y = thing.y;
    }

    fn set_yz(&mut self, thing: Vec2) {
        self.y = thing.x;
        self.z = thing.y;
    }
}
bitshifter commented 4 months ago

I was thinking about this when adding the with_x etc. methods, which would be an alternative to a set that mutates,

So rather than fn set_zy(&mut self, v: Vec2) it would be fn with_zy(self, v: Vec2) -> Self, although both have their uses.

The swizzles are all generated code which does reduce the amount of typing required but that particular bit of codegen is reasonably complex.

One complication is it would be good to support Vec3 and Vec3A as the thing vector which will probably make the trait slightly more complex.

the-ssd commented 4 months ago

What about making it impl Into<Vec3>? If both have their use cases, then maybe have both?

I saw, and I was trying to understand what was going on, but failed. I made a marco which generates Vec3SwizzleSet. But AFAIK macros will slow down compilation compared to the current approach. (maybe with proc-marco2 the codegen can be modified to output the generated code into a file?)