Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,26 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "JohnnyMorganz.stylua"
},

"stylua.targetReleaseVersion": "latest",

"luau-lsp.fflags.enableNewSolver": true,
"luau-lsp.completion.imports.enabled": true,

"editor.detectIndentation": false,
"editor.insertSpaces": false,
"prettier.useTabs": true,

"luau-lsp.completion.imports.stringRequires.enabled": true,

"luau-lsp.types.definitionFiles": [ "types/globals.d.luau" ],

"luau-lsp.types.definitionFiles": [
"types/globals.d.luau"
],
"luau-lsp.sourcemap.autogenerate": false,
"luau-lsp.sourcemap.enabled": true,
"luau-lsp.sourcemap.includeNonScripts": true,

"luau-lsp.sourcemap.sourcemapFile": "sourcemap.json",

"luau-lsp.ignoreGlobs": [
"**/_Index/**",
"build/**",
"vendor/**",
"types/**"
],
"editor.formatOnSave": true
}
"editor.formatOnSave": true,
"luau-lsp.platform.type": "roblox"
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Added `fluid.tween(input, tween_info)` by @godmothersfire
- Added animation support for `CFrame`, `ColorSequenceKeypoint`, `DateTime`,
`NumberRange`, `NumberSequenceKeypoint`, `PhysicalProperties`, `Ray`, `Rect`,
`Region3`, `Vector2`, and `Vector3` by @godmothersfire
- Exported `Animatable` type by @godmothersfire

## 0.6.4

### Fixes
Expand Down
19 changes: 19 additions & 0 deletions src/anim/anim_types.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type Animatable =
| number
| CFrame
| Color3
| ColorSequenceKeypoint
| DateTime
| NumberRange
| NumberSequenceKeypoint
| PhysicalProperties
| Ray
| Rect
| Region3
| UDim
| UDim2
| Vector2
| Vector3
| vector

return nil
122 changes: 90 additions & 32 deletions src/anim/lerp.luau
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,99 @@ local types = require("../reactive/types")

local active_animations = {}

local function vector_lerp(a: vector, b: vector, t: number): vector
return a + (b - a) * t
local lerp_functions: { [string]: (any, any, number) -> any } = {
number = math.lerp,
vector = vector.lerp,
Vector2 = vector.lerp,
Vector3 = vector.lerp,
UDim2 = UDim2.new().Lerp,
}

function lerp_functions.CFrame(initial: CFrame, goal: CFrame, ratio: number)
return initial:Lerp(goal, ratio)
end

local function general_lerp(a: any, b: any, t: number): any
return a + (b - a) * t
function lerp_functions.Color3(initial: Color3, goal: Color3, ratio: number)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u just put everything inside the table for lerp_functions rather than doing function.x

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

local initial_vector = oklab.from_srgb(vector.create(initial.R, initial.G, initial.B))
local goal_vector = oklab.from_srgb(vector.create(goal.R, goal.G, goal.B))
local interpolated_vector = vector.lerp(initial_vector, goal_vector, ratio)
local result_vector = vector.clamp(oklab.to_srgb(interpolated_vector), vector.zero, vector.one)
return Color3.new(result_vector.x, result_vector.y, result_vector.z)
end

local lerp_functions: { [string]: (a: any, b: any, t: number) -> any } = {
number = math.lerp,
Vector3 = vector_lerp,
Vector2 = general_lerp,
Color3 = function(a: Color3, b: Color3, t)
local initial_vector = oklab.from_srgb(vector.create(a.R, a.G, a.B))
local goal_vector = oklab.from_srgb(vector.create(b.R, b.G, b.B))
local interpolated_vector = vector_lerp(initial_vector, goal_vector, t)
local result_vector =
vector.clamp(oklab.to_srgb(interpolated_vector), vector.zero, vector.one)

return Color3.new(result_vector.x, result_vector.y, result_vector.z)
end,
CFrame = CFrame.identity.Lerp,
Rect = function(a: Rect, b: Rect, t: number)
return Rect.new(
general_lerp(a.Min, b.Min, t) :: Vector2,
general_lerp(a.Max, b.Max, t) :: Vector2
)
end,
UDim = function(a: UDim, b: UDim, t: number)
return UDim.new(math.lerp(a.Scale, b.Scale, t), math.lerp(a.Offset, b.Offset, t))
end,
UDim2 = UDim2.new().Lerp,
}
function lerp_functions.ColorSequenceKeypoint(
initial: ColorSequenceKeypoint,
goal: ColorSequenceKeypoint,
ratio: number
)
return ColorSequenceKeypoint.new(
math.lerp(initial.Time, goal.Time, ratio),
lerp_functions.Color3(initial.Value, goal.Value, ratio)
)
end

function lerp_functions.NumberRange(initial: NumberRange, goal: NumberRange, ratio: number)
return NumberRange.new(
math.lerp(initial.Min, goal.Min, ratio),
math.lerp(initial.Max, goal.Max, ratio)
)
end

function lerp_functions.NumberSequenceKeypoint(
initial: NumberSequenceKeypoint,
goal: NumberSequenceKeypoint,
ratio: number
)
return NumberSequenceKeypoint.new(
math.lerp(initial.Time, goal.Time, ratio),
math.lerp(initial.Value, goal.Value, ratio)
)
end

function lerp_functions.PhysicalProperties(
initial: PhysicalProperties,
goal: PhysicalProperties,
ratio: number
)
return PhysicalProperties.new(
math.lerp(initial.Density, goal.Density, ratio),
math.lerp(initial.Friction, goal.Friction, ratio),
math.lerp(initial.Elasticity, goal.Elasticity, ratio),
math.lerp(initial.FrictionWeight, goal.FrictionWeight, ratio),
math.lerp(initial.ElasticityWeight, goal.ElasticityWeight, ratio),
math.lerp(initial.AcousticAbsorption, goal.AcousticAbsorption, ratio)
)
end

function lerp_functions.Ray(initial: Ray, goal: Ray, ratio: number)
return Ray.new(
vector.lerp(initial.Origin :: any, goal.Origin :: any, ratio) :: any,
vector.lerp(initial.Direction :: any, goal.Direction :: any, ratio) :: any
)
end

function lerp_functions.Rect(initial: Rect, goal: Rect, ratio: number)
return Rect.new(
vector.lerp(initial.Min :: any, goal.Min :: any, ratio) :: any,
vector.lerp(initial.Max :: any, goal.Max :: any, ratio) :: any
)
end

function lerp_functions.Region3(initial: Region3, goal: Region3, ratio: number)
-- FUTURE: support rotated Region3s if/when they become constructable
local position = initial.CFrame.Position:Lerp(goal.CFrame.Position, ratio)
local halfSize = initial.Size:Lerp(goal.Size, ratio) / 2
return Region3.new(position - halfSize, position + halfSize)
end

function lerp_functions.UDim(initial: UDim, goal: UDim, ratio: number)
return UDim.new(
math.lerp(initial.Scale, goal.Scale, ratio),
math.lerp(initial.Offset, goal.Offset, ratio)
)
end

local lerp = {}
local lerp = { functions = lerp_functions }

function lerp.create<T>(goal: types.UsedAs<T>, takes: types.UsedAs<number>)
graph.set_group(graph.new_group("lerp"))
Expand All @@ -61,7 +119,7 @@ function lerp.create<T>(goal: types.UsedAs<T>, takes: types.UsedAs<number>)
graph.clear_group()

return function()
graph.push_dependency(output)
graph.push_dependency(output :: T)
return output.cached_value
end
end
Expand All @@ -78,7 +136,7 @@ function lerp.step(delta_time: number)
end

local interpolated = fn(data.start, data.goal, t)
graph.update_source_node(output, interpolated)
graph.update_source_node(output :: any, interpolated)
end
end

Expand Down
115 changes: 115 additions & 0 deletions src/anim/pack_type.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
-- Based on https://github.com/dphfox/Fusion/blob/2790f7b6272bdf7cd0bbfee259a2f9d79ea20810/src/Animation/unpackType.luau

local oklab = require("../utils/oklab")

local function pack_type(value: unknown): (string, { number })
local type_string = typeof(value)
if type_string == "number" then
value = value :: number
return type_string, { value }
elseif type_string == "CFrame" then
value = value :: CFrame
-- FUTURE: is there a better way of doing this? doing distance
-- calculations on `angle` may be incorrect
local axis, angle = value:ToAxisAngle()
return type_string, { value.X, value.Y, value.Z, axis.X, axis.Y, axis.Z, angle }
elseif type_string == "Color3" then
value = value :: Color3
-- oklab conversion
local intermediate_vector = oklab.from_srgb(vector.create(value.R, value.G, value.B))

return type_string,
{
intermediate_vector.x,
intermediate_vector.y,
intermediate_vector.z,
}
elseif type_string == "ColorSequenceKeypoint" then
value = value :: ColorSequenceKeypoint

-- oklab conversion
local intermediate_vector =
oklab.from_srgb(vector.create(value.Value.R, value.Value.G, value.Value.B))

return type_string,
{
intermediate_vector.x,
intermediate_vector.y,
intermediate_vector.z,
}
elseif type_string == "DateTime" then
value = value :: DateTime
return type_string, { value.UnixTimestampMillis }
elseif type_string == "NumberRange" then
value = value :: NumberRange
return type_string, { value.Min, value.Max }
elseif type_string == "NumberSequenceKeypoint" then
value = value :: NumberSequenceKeypoint
return type_string, { value.Value, value.Time, value.Envelope }
elseif type_string == "PhysicalProperties" then
value = value :: PhysicalProperties
return type_string,
{
value.Density,
value.Friction,
value.Elasticity,
value.FrictionWeight,
value.ElasticityWeight,
}
elseif type_string == "Ray" then
value = value :: Ray
return type_string,
{
value.Origin.X,
value.Origin.Y,
value.Origin.Z,
value.Direction.X,
value.Direction.Y,
value.Direction.Z,
}
elseif type_string == "Rect" then
value = value :: Rect
return type_string, { value.Min.X, value.Min.Y, value.Max.X, value.Max.Y }
elseif type_string == "Region3" then
value = value :: Region3
-- FUTURE: support rotated Region3s if/when they become constructable
return type_string,
{
value.CFrame.X,
value.CFrame.Y,
value.CFrame.Z,
value.Size.X,
value.Size.Y,
value.Size.Z,
}
elseif type_string == "Region3int16" then
value = value :: Region3int16
return type_string,
{ value.Min.X, value.Min.Y, value.Min.Z, value.Max.X, value.Max.Y, value.Max.Z }
elseif type_string == "UDim" then
value = value :: UDim
return type_string, { value.Scale, value.Offset }
elseif type_string == "UDim2" then
value = value :: UDim2
return type_string, { value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset }
elseif type_string == "Vector2" then
value = value :: Vector2
return type_string, { value.X, value.Y }
elseif type_string == "Vector2int16" then
value = value :: Vector2int16
return type_string, { value.X, value.Y }
elseif type_string == "Vector3" then
value = value :: Vector3
return type_string, { value.X, value.Y, value.Z }
elseif type_string == "Vector3int16" then
value = value :: Vector3int16
return type_string, { value.X, value.Y, value.Z }
elseif type_string == "vector" then
value = value :: vector
return type_string, { value.x, value.y, value.z }
else
return type_string, {}
end
end

return pack_type
Loading