Kairos icon indicating copy to clipboard operation
Kairos copied to clipboard

Bezier Interpolation styles

Open gregsn opened this issue 2 years ago • 3 comments

We have 4 different ideas for Bezier Interpolation. Can we pick one or shall we allow all 4 different styles?

  • Restricted Handles CP1 = (0, V1), CP2 = (1/3, V2), CP3 = (2/3, V3), CP4 = (1, V4)
  • Free Handles CP1 = (t1, V1), CP2 = (t2, V2), CP3 = (t3, V3), CP4 = (t4, V4)

grafik

Independently of this we have the question whether we operate on data directly or just in normalized sampling space. see #244

gregsn avatar Jan 11 '23 18:01 gregsn

my two cents:

First question: How do we want to work?

  • Option 1: we want to design the curve that represents the final value across time
  • Option 2: we want to design the curve that represents the transition (the scalar of the lerp function)

image

both options make sense.

Option 1 - "Value Bezier"

image

  • All the control points represent values in time and codomain
  • C1 and C4 are the known values from the keyframes
  • What about C2 and C2? How do we want to control them? In other terms, how do we model them? there are at least two options:
    • They are absolute positions (like C1 and C4). In this case we would store their position in time (probably still normalized from C1.time-C4.time to 0-1) and the codomain value (normalized between codomain min-max)
    • They are relative positions. In this case we interpret them as offset vectors, so we store the Vector2 offset that applied to C1 gives C2, and from C4 gives C3

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

image

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.

image

Option 2 - "Transition Brezier"

image

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.

Value Types considerations

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

natan-sinigaglia avatar Jan 12 '23 22:01 natan-sinigaglia

Great! Let's look at the possible implementations of each variant:

Restricted Value Bezier

  • DeCasteljau (3+2+1 Lerp) on the CoDomain as this allows to use of all types of T that come with a Lerp grafik
  • But we could also split complex types into their components and use the polynomial approach
  • UI: have 4 value editors linearly spreaded

Free Value Bezier

  • InverseBezier(t) -> w needs to be computed for each component of a 3d Vector.
  • Inside the Interpolate operation of the FreeValueBezier we do both InverseDomainBezier(t) and CoDomainBezier(w). However, there is no single w. Each type needs to treat each component separately.
    • Option A t -> (wx -> vx), (wx -> vy), (wz -> vz): A vector3 implementation splits the components, calls InverseDomainBezier(t) and CoDomainBezier(w) for each component.
    • Option B 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.
  • UI:
    • Bezier handles for each component (float, Vector) which can be moved freely
    • just 4 value editors of type T that can be moved freely (e.g. color, string)

Transition Bezier

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...

Opinion

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.

gregsn avatar Jan 13 '23 21:01 gregsn

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.

gregsn avatar Jan 13 '23 22:01 gregsn