Add support for ColorSequence in Tween & Spring
I want to be able to animate my UIGradients, so I added support for the ColorSequence datatype in all the relevant places.
The main problem here is that ColorSequence does not meet the criteria for animatability:
If a data type can reasonably be represented as a fixed-length array of numbers, then it is animatable.
http://elttob.uk/fusion-new-website/api-reference/animation/animatable/
A ColorSequence is not fixed size. To represent each stop of the gradient, we can use a fixed size type (ColorSequenceKeypoint, which is indeed animatable), but there could be an arbitrary number of stops, meaning an arbitrary number of numerical components would be needed to represent the entire type.
This breaks our contracts about animatability, because you could try to animate between two gradients with different numbers of stops. I do not want to special case this contract just for sequence types.
It is possible to specifically interpolate gradients by interpolating extra stops, but this would probably be better served by a dedicated contract for interpolatability, given that this does not give us a full coordinate space to represent all possible permutations in. A full coordinate space is required for things such as spring simulation which are defined componentwise rather than as a ratio of two values.
I think there could be merit to adding a less broad interpolation case where it makes sense to interpolate two values, but no sense to define a coordinate space for them.
I will think about this.
Related to #87
I see. However, I don't think that is grounds to eliminate entire datatypes. There are cases that don't work, but that doesn't mean there aren't case that do. We already have a block that checks validity (ie: same type) so we can expand that to check contract alignment. This way we can at least animate these types in scenarios that are aligned.
However, I don't think that is grounds to eliminate entire datatypes. There are cases that don't work, but that doesn't mean there aren't case that do.
I don't intend to eliminate ColorSequence tweening at all - I perfectly support the case for this feature! All that I intend to say is that, if we need to break the contract we've already made about animatable types, then perhaps we need more specific types to describe it, and that might be worth taking extra time to consider to ensure we implement it sensibly.
The question is not whether this is a good thing to implement - because it is clearly worth doing; the question is about how to implement it in an internally consistent way.
Preferably, it should make concrete these relevant subclasses of animatability which are related to ColorSequence animation:
- you can have multiple animatable spaces which may be animated within, but not between - relevant for free animation between ColorSequences of identical stop count
- there might only be animatability between values (i.e. you may only express ratios of two values) - relevant for interpolation between ColorSequences of different stop count
This has extremely important implications when it comes to behavioural differences between Tweens and Springs, which currently act in a unified manner - even though they're really on two diverging sides of this animatability problem. We could probably superficially address this right now by adding new types, SuperAnimatable or Interpolatable, to act as a counterpart to Animatable which semantically highlights the difference for users, but that is basically cosmetic.
I do have some new thoughts on what the proper, eventual solution to this could look like.
After thinking on this for a long while, I think that the answer to this problem is intimately tied with how we approach non-componentwise animation generally. Componentwise animation assumes that all animation can happen in one unrestricted coordinate space, i.e. that there are a fixed number of always-valid numeric components, whereas the animation described here between ColorSequences require multiple coordinate spaces of different dimensionality.
I plan to implement componentwise animation in the rework on top of non-componentwise APIs, perhaps as a helper function that takes in a componentwise mover function and returns a function that can deal with the full data types. It is not hard to conceptualise specifying a variant of that helper function that can constrain incoming and outgoing ColorSequences to a certain stop count so as to fix the number of components being processed, thus restricting you to one animatable space.
Having non-componentwise animation as the foundational abstraction like this also opens up the door for modelling values which can only be animated as a ratio of two other values, since we can provide helpers there to interpolate between any two ColorSequences linearly by adding extra stops where required.
On top of these basic ideas, we can then proceed to implement spring motion as a simple componentwise mover, and tween motion as a simple 'ratiowise' mover. This would allow tweens to encompass the more general behaviour you propose here, while allowing springs to naturally remain more restricted, without imposing any extra burden on the definitions of either. The burden is handled by the system's definitions itself.
The above is probably not clear to anyone but myself right now, since I'm using a lot of jargon - I would like to make a prototype or video at some point, but currently I'm a little swamped with work on Vanilla since Roblox unexpectedly broke that. The details of my above proposals don't matter too much right now.
On the question of timing - v0.2 is currently feature frozen outside of anything critical (destructors, at the moment, are considered critical), and v0.3 was going to be when the animation rework made its debut. We could maybe slip your PR in before the animation rework gets implemented, but is that really useful if you're going to be throwing out the old system's way of thinking anyway? We may end up with some API surfaces that share the same parameters (e.g. you might still have Tween or Spring functions that return smoothed state), but the mental model might be different so as to make this a non-issue. I will revisit this once v0.2 is out, but for the time being I think v0.2 should stay focused on getting destructors done.
This work is blocked by 0.3's animation rework changes, which will start after we process through our backlog from 0.2.
Closing this PR for the time being, because there's no concrete design for this yet, and the implementation of this PR is simple enough that it doesn't need to be kept around for whenever the API design is finalised.