Every now and then, I try to find some time to work on my little visualization library, Visula.

When I posted about it last time - more than a year ago now - I mentioned that I was not too fond of using the macros in the definition of the Spheres object:

let spheres = Spheres::new(
    application,
    &SphereDelegate {
        position: delegate!(particle.position),
        radius: delegate!(1.0),
        color: delegate!(particle.position / 40.0 + vec3::<f32>(0.5, 0.5, 0.5)),
    },
)?;

I have finally gotten around to improving the syntax a bit. I have introduced a new Expression type, with support for conversion from types such as numbers and glam-based vectors. By further adding a few operator overloads, the above can now be rewritten as

let spheres = Spheres::new(
    &application.rendering_descriptor(),
    &SphereDelegate {
        position: particle.position.clone(),
        radius: 1.0.into(),
        color: particle.position.clone() / 40.0 + glam::Vec3::new(0.5, 0.5, 0.5),
    },
)?;

The delegate! macro is finally gone, but at the cost of a few calls to clone() and into(). Overall, I am pretty happy with the change. I can always add back the macro later if I find the new syntax too verbose.

The Expression type is unfortunately written in a way that does not give you type checking for these definitions at compile time. If you mistakenly add a Vec2 and Vec3, you will not see any error before you run your program. I am looking into ways to improve this, but I know I definitely want to keep the current version around. The dynamic typing is exactly what I need to allow expressions to be constructed based on user input.