glam-rs icon indicating copy to clipboard operation
glam-rs copied to clipboard

Mutable Swizzles

Open the-ssd opened this issue 1 year ago • 3 comments

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 avatar May 26 '24 18:05 the-ssd

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;
    }
}

the-ssd avatar May 26 '24 20:05 the-ssd

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.

bitshifter avatar May 26 '24 22:05 bitshifter

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?)

the-ssd avatar May 27 '24 19:05 the-ssd

Fixed in #584

bitshifter avatar Mar 19 '25 08:03 bitshifter