Bevy-Rust-GPU / rust-gpu-sdf

Signed distance field library usable on both CPU and GPU.
Apache License 2.0
12 stars 1 forks source link

Configuration API for composed distance fields #6

Closed Shfty closed 1 year ago

Shfty commented 1 year ago

Currently constructuing everything via struct update pattern + free-floating default function.

Ideal would be an inline builder pattern like Translate::<Cube>::default().with_translation(Vec3::X).with_size(0.5), but a trivial deref-based implementation raises concerns about implicit conflicts in the returned pointer type.

Consider Translate<Cube>, which expands out to: Operator<Operator<Point, ElongateOp<Exact, Vec3>, Vec3>, TranslateOp<Vec3>, Vec3>

Since a deref implementation would point to the SDF field, we're specifically interested in the Operator -> Operator -> Point chain. Builder-style with_foo(&mut self, foo: T) -> &mut Self doesn't work here, since calling a method on one of the inner types would return a mutable reference that denies access to anything above it in the chain.

So it seems that type level trait chaining, whereby outer types defer to inner types for implementation but short-circuit the return chain, is in order.

However, this raises its own concerns; specifically, what would these traits be? It seems impractical to try generalizing parameters into traits that are shared between types (ex. trait Radius or trait Size<Dim>), since that would result in many small traits and eventually scale poorly in terms of boilerplate.

Thus, it seems like the remaining option is to implement one trait and virtualize parameter type through a generic param via marker structs. That way, wrappers can defer to their children from one callsite that generalizes over any type, and leaf types can expose parameters via marker traits.

Shfty commented 1 year ago

After some experimentation, it's looking like functional lenses (or some simplification thereof) are going to be the right way to make this work w.r.t. nested types; the type-tagged field approach works, but doesn't cover cases where the same struct is present at multiple nesting depths.

Shfty commented 1 year ago

Should be able to keep the type-tagged field idea, and compose it into a cons list path that can be traversed via trait.

ex. Translate::<Square>::default().with((FieldSdf, (FieldOp, ())), 0.5), where the cons list could be composed using a type-level macro like path!(FieldSdf, FieldOp).

May be able to store field tag types on associated types or consts in a trait, and use them for lookup purposes; primary motivation being something like Translate<Square>::default().with(path!(Translate::Inner, Square::Size), 0.5), or perhaps even getting a pre-wrapped Square::Size directly from Translate.

Shfty commented 1 year ago

Managed to make it work with all desired features, but need to setup a new repo to hold type-level path functionality, derive for all types, and implement per-shape const aliases before publishing.

Shfty commented 1 year ago

Done.