Unified animation curves
Resolves #87, resolves #193, resolves #194, resolves #187, resolves #21, resolves #161. Related to #55 and #11.
This is the PR for Fusion 0.3's unified animation system. As per #87, this will replace the existing animation system with a much more programmable and flexible system.
The following is a pseudocode overview of the theoretical concepts underpinning the API being implemented. Note that this does not represent actual API code. Implementation details, naming etc may be very different.
Animation curves and families
The basic atom in the new animation system are animation curves. These are functions of time evaluating to a vector of numbers, representing linearly independent components of a higher dimensional type.
local function curve(time)
return {1, 2, 3, 4, 5}
end
To express differentiable animation curves, a vector of curves is used. Displacement is always given. Most of the time, only displacement and velocity are provided, though further derivatives may be provided for completeness.
local curve = {
function(time) return displacements end,
function(time) return velocities end,
function(time) return accelerations end,
-- etc...
}
A family of differentiable animation curves is expressed as a function of the initial value. The initial values are given as an array. Displacement is always given.
local function family(initialValues)
local initialDisplacement = initialValues[1]
local initialVelocity = initialValues[2] or table.create(0, #initialDisplacement)
local initialAcceleration = initialValues[3] or table.create(0, #initialDisplacement)
-- etc...
return {
function(time) return displacements end,
function(time) return velocities end,
function(time) return accelerations end,
-- etc...
}
end
Displacement spaces
The zero-th derivative of a differentiable animation curve can be either displacement or position. This is a semantic distinction rather than a logical difference; both kinds of curve have identical representation under this system.
Conceptually, displacement can only exist inside of a 'displacement space' which defines how positions and displacements relate to each other. This is because displacement is calculated differently in certain cases, for example when dealing with angular or nonlinear quantities. A displacement space is entirely defined by two functions which convert between positions and displacements relative to a reference point. The functions operate on vectors of numbers, similarly to above:
local linear = {
toDisplacement = function(positions, relativeTo)
local displacements = table.create(#positions)
for index, position in positions do
displacements[index] = position - relativeTo[index]
end
return displacements
end,
toPosition = function(displacements, relativeTo)
local positions = table.create(#displacements)
for index, displacement in displacements do
positions[index] = displacement + relativeTo[index]
end
return positions
end,
}
Type composition
To allow for easy animation of complex types such as Vector3 or CFrame, compose and decompose functions are provided for a variety of types. Some types such as Color3 have multiple ways to compose and decompose them, for example utilising linear vs perceptual colour space. An appropriate default displacement space is provided for the type.
local Vector3 = {
compose = function(nums)
return Vector3.new(nums[1], nums[2], nums[3])
end,
decompose = function(vec)
return vec.X, vec.Y, vec.Z
end,
space = linear
}
Converging animation curve families
A special case of animation curve family, e.g. spring, tween, bezier, acceltween. These attempt to reduce displacement relative to a goal value over time. Typically differentiable. The goal value and displacement space are curried so they can be reconfigured separately from the 'artistic' properties. When all derivatives are within epsilon, the animation can snap to the goal and sleep.
local function Spring(speed, damping)
speed = speed or 10
damping = damping or 1
return function(goal, space)
return {
-- differentiable family...
}
end
end
TODO: Reactive interfaces, interpolant animation