voxell-tech/bevy_motiongfx

Implement easier pattern for `vello` graphics creation (quality of life improvement)

Closed this issue · 0 comments

The Problem

At the moment, creating a simple rect is extremely verbose:

fn xxx_system(mut commands: Commands, mut scenes: ResMut<Assets<VelloScene>>) {
    //...
    commands.spawn(VelloRectBundle {
        rect: VelloRect::anchor_center(DVec2::new(100.0, 100.0), DVec4::splat(10.0)),
        fill: FillStyle::from_brush(Color::WHITE),
        stroke: StrokeStyle::from_brush(Color::WHITE).with_style(4.0),
        scene_bundle: VelloSceneBundle {
            scene: scenes.add(VelloScene::default()),
            transform: Transform::from_xyz(-200.0, 0.0, 0.0),
            ..default()
        },
    });
    //...
}

By default, all of the Components inside the Bundle implements the Default trait, this means that if the user is not interested in setting for example the color and transform of the rect, they can do something like this:

commands.spawn(VelloRectBundle {
    rect: VelloRect::anchor_center(DVec2::new(100.0, 100.0), DVec4::splat(10.0)),
    scene_bundle: VelloSceneBundle {
        scene: scenes.add(VelloScene::default()),
        ..default()
    },
    ..default()
});

Although it makes the code shorter, setting up simple graphics like a rect should be even more compact. Notice also that users will always need to add the scene_bundle component manually as it contains an asset handle that needs to be created for the vello renderer to pick up. Furthermore, users only have the choice to pick and choose which Component inside the Bundle to be left out. For example, in the VelloRect component, users are required to choose the anchor style, define the size of the rect, and the radius of the rect. Wouldn't it be nice if users get to choose which one of those to be left out for default values?

Proposed Pattern

The builder pattern is what first come to my mind. The idea is to provide a good default value for places where it is not needed and allow for customization only where the user intends.

Here is a mock up on how it might look like:

create_rect(100.0, 100.0)
    .radius(10.0)
    .fill(Color::WHITE)
    .build(&mut commands, &mut scenes);

As you can see, the user can now pick and choose individual values of the rect to be left out or customized. The final build function is then called to spawn the actual rect using commands and scenes. If the user is not interested in any customizations, they would only need to fill in the required data for a rect creation:

create_rect(100.0, 100.0).build(&mut commands, &mut scenes);

An even better API proposal might be to create trait functions for the Command struct so that we can omit the &mut commands input for the build function:

commands.create_rect(100.0, 100.0).build(&mut scenes);