Open gregsn opened 1 year ago
my two cents:
First question: How do we want to work?
both options make sense.
It's interesting to notice that in kairos both these last options could make sense, depending on the scenario. example: First keyframe: Constant Value Keyframe Second keyframe: Live Value Keyframe
both control options are interesting!
in the case of the control points being offsets, one could argue: "how the hell I'm supposed to edit (grab and move around) C3 if it's an offset over an unpredictable value that potentially moves every frame? It would be a videogame, just to grab it!"
we could easily solve this by imagining an edit mode in which C4 (which is unpredictable) gets temporarily replaced by a dummy keyframe where the codomain value is fixed at the center of the track, just to allow us "design" the offset from it.
This option doesn't seem to be problematic,: it would offer always the same editing possibilities, independently from the "kind" of keyframes we are dealing with, since this curve is not affected by the keyframes (potentially dynamic) value, only being a representation of the transition behavior, the scalar of the lerp function.
Given these two bezier "styles", let's pass to analyze how they could be implemented and edited in each value type.
We already covered the Float32 case scenario.
what happens with Vectors? being vectors an abstraction over a collection of values (eg. Vector3 = X value + Y value + Z value), I think the user would expect to be able to adjust the bezier curves potentially for each component of the vector.
the same is true for RGBA. In this case there's another level of complexity given by the fact that an RGBA value can be interpreted as a Vector4 but with different meanings: is it RGBA or HSV or HSL or... each color space introduces a new interpretation of the RGBA value. the user might want to operate in one specific color space.
Other exotic types (like String, matrix, or even skia layer or stride entity) can't properly be used to draw a 2d bezier curve, so would exclude Option 1. you would be only allowed to design the transition curve (Option 2).
Great! Let's look at the possible implementations of each variant:
InverseBezier(t) -> w
needs to be computed for each component of a 3d Vector. t -> (wx -> vx), (wx -> vy), (wz -> vz)
: A vector3 implementation splits the components, calls InverseDomainBezier(t) and CoDomainBezier(w) for each component.t -> (wx, wx, wz) -> (vx, vy, vz)
: A vector3 implementation calls InverseDomainBezier(t) once which returns a vector that can be used to call CoDomainBezier(w). The Sample value would be a Vector. here the two-step idea #244 would be great. It would allows us to compute the Lerp Scalar once. The restricted variant would be the fastest, but also the free Transition Bezier only needs to call into InverseDomainBezier(t) and CoDomainBezier(w) only once.
It's the most versatile. It would work for spreads or even for many tracks. One curve for many tracks would be awesome...
To me, it still sounds like the Free Value Bezier is the most wanted candidate, even if it might be the most complicated and potentially slowest one.
Maybe we should go for Option A and abstract everything away behind an adaptive node FreeValueBezier(t, cp1, cp2, cp3, cp4) -> v
which is responsible to do everything. So per type we need to offer the implementations. The implementations shall be able to delegate the hard tasks to helper nodes InverseDomainBezier(t, t1, t2, t3, t4) -> w
(..) which they can use per component. Abstracting it this way via adaptive operation doesn't allow the implementations to keep state around, but maybe let's do the non-optimized yet most flexible approach first (no caching, no binary search, just solving the cubic formula each time) as this approach also works for live value keyframes.
Uhm. I think I got the last part wrong. For a Vector3 there is not such a thing as a CP2 of type Vector3. We have 3 CP2 with each (x,y). That's 6 values.
Maybe we should think about having at least t restricted for all the components. CP2 =(t2,(x2,y2,z2))
This way we could compute w once for the whole type and implementing per type wouldn't be necessary at all (as we have adaptive Bezer already).
For the UI this would mean: You can move around all your controlpoints freely, but when moving (t2, y2) in time direction you also move (t2, z2), since there is only one t2.
We have 4 different ideas for Bezier Interpolation. Can we pick one or shall we allow all 4 different styles?
CP1 = (0, V1), CP2 = (1/3, V2), CP3 = (2/3, V3), CP4 = (1, V4)
CP1 = (t1, V1), CP2 = (t2, V2), CP3 = (t3, V3), CP4 = (t4, V4)
Independently of this we have the question whether we operate on data directly or just in normalized sampling space. see #244