mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Add spring setter
This commit is contained in:
parent
b44b9ef2ba
commit
3b22f6ccf9
4 changed files with 75 additions and 71 deletions
|
|
@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- `cleanup()` now accepts `thread` types.
|
- `cleanup()` now accepts `thread` types.
|
||||||
- Implicit effects to set children can now recursively create more implicit
|
- Implicit effects to set children can now recursively create more implicit
|
||||||
effects to set children.
|
effects to set children.
|
||||||
|
- `spring()` returns a second value, a setter to set position, velocity and
|
||||||
|
impulse.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ Returns a new source with a value always moving torwards the input source value.
|
||||||
source: () -> T & Animatable,
|
source: () -> T & Animatable,
|
||||||
period: number = 1,
|
period: number = 1,
|
||||||
damping_ratio: number = 1
|
damping_ratio: number = 1
|
||||||
): () -> T
|
): (() -> T, Setter<T>)
|
||||||
|
|
||||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3 | Rect
|
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3 | Rect
|
||||||
|
|
||||||
|
type Setter<T> = ({
|
||||||
|
position: T?,
|
||||||
|
velocity: T?,
|
||||||
|
impulse: T?
|
||||||
|
}) -> ()
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Details**
|
- **Details**
|
||||||
|
|
|
||||||
136
src/spring.luau
136
src/spring.luau
|
|
@ -1,23 +1,3 @@
|
||||||
--[[
|
|
||||||
|
|
||||||
Supported datatypes:
|
|
||||||
- number
|
|
||||||
- CFrame
|
|
||||||
- Color3
|
|
||||||
- UDim
|
|
||||||
- UDim2
|
|
||||||
- Vector2
|
|
||||||
- Vector3
|
|
||||||
- Rect
|
|
||||||
|
|
||||||
Unsupported datatypes:
|
|
||||||
- bool
|
|
||||||
- Vector2int16
|
|
||||||
- Vector3int16
|
|
||||||
- EnumItem
|
|
||||||
|
|
||||||
]]
|
|
||||||
|
|
||||||
local throw = require "./throw"
|
local throw = require "./throw"
|
||||||
local graph = require "./graph"
|
local graph = require "./graph"
|
||||||
type Node<T> = graph.Node<T>
|
type Node<T> = graph.Node<T>
|
||||||
|
|
@ -33,68 +13,68 @@ local UPDATE_RATE = 120
|
||||||
local TOLERANCE = 0.001
|
local TOLERANCE = 0.001
|
||||||
local TOLERANCE_VECTOR = vector.create(TOLERANCE, TOLERANCE, TOLERANCE)
|
local TOLERANCE_VECTOR = vector.create(TOLERANCE, TOLERANCE, TOLERANCE)
|
||||||
|
|
||||||
type Vec3 = Vector3
|
|
||||||
|
|
||||||
local function Vec3(x: number, y: number, z: number): Vec3
|
|
||||||
return vector.create(x, y, z)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ZERO = Vec3(0, 0, 0)
|
|
||||||
|
|
||||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
|
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
|
||||||
|
|
||||||
type SpringData<T> = {
|
--[[
|
||||||
|
Unsupported datatypes:
|
||||||
|
- bool
|
||||||
|
- Vector2int16
|
||||||
|
- Vector3int16
|
||||||
|
- EnumItem
|
||||||
|
]]
|
||||||
|
|
||||||
|
type SpringState<T> = {
|
||||||
k: number, -- spring constant
|
k: number, -- spring constant
|
||||||
c: number, -- damping coeff
|
c: number, -- damping coeff
|
||||||
|
|
||||||
-- dimensions 1-3
|
x0_123: vector, x0_456: vector, -- current position
|
||||||
x0_123: Vec3,
|
x1_123: vector, x1_456: vector, -- target position
|
||||||
x1_123: Vec3,
|
v_123: vector, v_456: vector, -- current velocity
|
||||||
v_123: Vec3,
|
|
||||||
|
|
||||||
-- dimensions 4-6
|
|
||||||
x0_456: Vec3,
|
|
||||||
x1_456: Vec3,
|
|
||||||
v_456: Vec3,
|
|
||||||
|
|
||||||
source_value: T -- current value of spring input source
|
source_value: T -- current value of spring input source
|
||||||
}
|
}
|
||||||
|
|
||||||
type TypeToVec6<T> = (T) -> (Vec3, Vec3)
|
type SpringSettings<T> = ({
|
||||||
type Vec6ToType<T> = (Vec3, Vec3) -> T
|
position: T?,
|
||||||
|
velocity: T?,
|
||||||
|
impulse: T?
|
||||||
|
}) -> ()
|
||||||
|
|
||||||
|
type TypeToVec6<T> = (T) -> (vector, vector)
|
||||||
|
type Vec6ToType<T> = (vector, vector) -> T
|
||||||
|
|
||||||
local type_to_vec6 = {
|
local type_to_vec6 = {
|
||||||
number = function(v)
|
number = function(v)
|
||||||
return Vec3(v, 0, 0), ZERO
|
return vector.create(v, 0, 0), vector.zero
|
||||||
end :: TypeToVec6<number>,
|
end :: TypeToVec6<number>,
|
||||||
|
|
||||||
CFrame = function(v)
|
CFrame = function(v)
|
||||||
return v.Position, Vec3(v:ToEulerAnglesXYZ())
|
return v.Position, vector.create(v:ToEulerAnglesXYZ())
|
||||||
end :: TypeToVec6<CFrame>,
|
end :: TypeToVec6<CFrame>,
|
||||||
|
|
||||||
Color3 = function(v)
|
Color3 = function(v)
|
||||||
-- todo: hsv, oklab?
|
-- todo: hsv, oklab?
|
||||||
return Vec3(v.R, v.G, v.B), ZERO
|
return vector.create(v.R, v.G, v.B), vector.zero
|
||||||
end :: TypeToVec6<Color3>,
|
end :: TypeToVec6<Color3>,
|
||||||
|
|
||||||
UDim = function(v)
|
UDim = function(v)
|
||||||
return Vec3(v.Scale, v.Offset, 0), ZERO
|
return vector.create(v.Scale, v.Offset, 0), vector.zero
|
||||||
end :: TypeToVec6<UDim>,
|
end :: TypeToVec6<UDim>,
|
||||||
|
|
||||||
UDim2 = function(v)
|
UDim2 = function(v)
|
||||||
return Vec3(v.X.Scale, v.X.Offset, v.Y.Scale), Vec3(v.Y.Offset, 0, 0)
|
return vector.create(v.X.Scale, v.X.Offset, v.Y.Scale), vector.create(v.Y.Offset, 0, 0)
|
||||||
end :: TypeToVec6<UDim2>,
|
end :: TypeToVec6<UDim2>,
|
||||||
|
|
||||||
Vector2 = function(v)
|
Vector2 = function(v)
|
||||||
return Vec3(v.X, v.Y, 0), ZERO
|
return vector.create(v.X, v.Y, 0), vector.zero
|
||||||
end :: TypeToVec6<Vector2>,
|
end :: TypeToVec6<Vector2>,
|
||||||
|
|
||||||
Vector3 = function(v)
|
Vector3 = function(v)
|
||||||
return v, ZERO
|
return v, vector.zero
|
||||||
end :: TypeToVec6<Vector3>,
|
end :: TypeToVec6<Vector3>,
|
||||||
|
|
||||||
Rect = function(v)
|
Rect = function(v)
|
||||||
return Vec3(v.Min.X, v.Min.Y, v.Max.X), Vec3(v.Max.Y, 0, 0)
|
return vector.create(v.Min.X, v.Min.Y, v.Max.X), vector.create(v.Max.Y, 0, 0)
|
||||||
end :: TypeToVec6<Rect>
|
end :: TypeToVec6<Rect>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,10 +123,10 @@ setmetatable(vec6_to_type, invalid_type)
|
||||||
|
|
||||||
-- maps spring data to its corresponding output node
|
-- maps spring data to its corresponding output node
|
||||||
-- lifetime of spring data is tied to output node
|
-- lifetime of spring data is tied to output node
|
||||||
local springs: { [SpringData<any>]: SourceNode<any> } = {}
|
local springs: { [SpringState<unknown>]: SourceNode<unknown> } = {}
|
||||||
setmetatable(springs, { __mode = "v" })
|
setmetatable(springs :: any, { __mode = "v" })
|
||||||
|
|
||||||
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T
|
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): (() -> T, SpringSettings<T>)
|
||||||
local owner = assert_stable_scope()
|
local owner = assert_stable_scope()
|
||||||
|
|
||||||
-- https://en.wikipedia.org/wiki/Damping
|
-- https://en.wikipedia.org/wiki/Damping
|
||||||
|
|
@ -164,17 +144,17 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
||||||
throw("spring damping too high, consider reducing damping or increasing period")
|
throw("spring damping too high, consider reducing damping or increasing period")
|
||||||
end
|
end
|
||||||
|
|
||||||
local data: SpringData<T> = {
|
local data: SpringState<T> = {
|
||||||
k = k,
|
k = k,
|
||||||
c = c,
|
c = c,
|
||||||
|
|
||||||
x0_123 = ZERO,
|
x0_123 = vector.zero,
|
||||||
x1_123 = ZERO,
|
x1_123 = vector.zero,
|
||||||
v_123 = ZERO,
|
v_123 = vector.zero,
|
||||||
|
|
||||||
x0_456 = ZERO,
|
x0_456 = vector.zero,
|
||||||
x1_456 = ZERO,
|
x1_456 = vector.zero,
|
||||||
v_456 = ZERO,
|
v_456 = vector.zero,
|
||||||
|
|
||||||
source_value = false :: any,
|
source_value = false :: any,
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +165,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
||||||
local value = source()
|
local value = source()
|
||||||
data.x1_123, data.x1_456 = type_to_vec6[typeof(value)](value)
|
data.x1_123, data.x1_456 = type_to_vec6[typeof(value)](value)
|
||||||
data.source_value = value
|
data.source_value = value
|
||||||
springs[data] = output -- todo: investigate why insertion is not O(1) at ~20k springs
|
springs[data] = output
|
||||||
return value
|
return value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -199,6 +179,28 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
||||||
-- set output to goal
|
-- set output to goal
|
||||||
output.cache = data.source_value
|
output.cache = data.source_value
|
||||||
|
|
||||||
|
local setter = function(p)
|
||||||
|
local x = p.position
|
||||||
|
local v = p.velocity
|
||||||
|
local dv = p.impulse
|
||||||
|
|
||||||
|
if x then
|
||||||
|
data.x0_123, data.x0_456 = type_to_vec6[typeof(x)](x)
|
||||||
|
end
|
||||||
|
|
||||||
|
if v then
|
||||||
|
data.v_123, data.v_456 = type_to_vec6[typeof(v)](v)
|
||||||
|
end
|
||||||
|
|
||||||
|
if dv then
|
||||||
|
local dv_123, dv_456 = type_to_vec6[typeof(dv)](dv)
|
||||||
|
data.v_123 += dv_123
|
||||||
|
data.v_456 += dv_456
|
||||||
|
end
|
||||||
|
|
||||||
|
springs[data] = output
|
||||||
|
end :: SpringSettings<T>
|
||||||
|
|
||||||
return function(...)
|
return function(...)
|
||||||
if select("#", ...) == 0 then -- no args were given
|
if select("#", ...) == 0 then -- no args were given
|
||||||
push_child_to_scope(output)
|
push_child_to_scope(output)
|
||||||
|
|
@ -210,8 +212,8 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
||||||
data.x0_123, data.x0_456 = type_to_vec6[typeof(v)](v)
|
data.x0_123, data.x0_456 = type_to_vec6[typeof(v)](v)
|
||||||
|
|
||||||
-- reset velocity
|
-- reset velocity
|
||||||
data.v_123 = ZERO
|
data.v_123 = vector.zero
|
||||||
data.v_456 = ZERO
|
data.v_456 = vector.zero
|
||||||
|
|
||||||
-- schedule spring
|
-- schedule spring
|
||||||
springs[data] = output
|
springs[data] = output
|
||||||
|
|
@ -220,7 +222,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
||||||
output.cache = v
|
output.cache = v
|
||||||
|
|
||||||
return v
|
return v
|
||||||
end
|
end, setter
|
||||||
end
|
end
|
||||||
|
|
||||||
local function step_springs(dt: number)
|
local function step_springs(dt: number)
|
||||||
|
|
@ -264,7 +266,7 @@ end
|
||||||
local remove_queue = {}
|
local remove_queue = {}
|
||||||
|
|
||||||
local function update_spring_sources()
|
local function update_spring_sources()
|
||||||
for data, output in next, springs do
|
for data, output in springs do
|
||||||
local x0_123, x1_123, v_123,
|
local x0_123, x1_123, v_123,
|
||||||
x0_456, x1_456, v_456 =
|
x0_456, x1_456, v_456 =
|
||||||
data.x0_123, data.x1_123, data.v_123,
|
data.x0_123, data.x1_123, data.v_123,
|
||||||
|
|
@ -280,7 +282,7 @@ local function update_spring_sources()
|
||||||
|
|
||||||
if max_difference == TOLERANCE_VECTOR then
|
if max_difference == TOLERANCE_VECTOR then
|
||||||
-- close enough to target, unshedule spring and set value to target
|
-- close enough to target, unshedule spring and set value to target
|
||||||
table.insert(remove_queue, data)
|
springs[data] = nil
|
||||||
output.cache = data.source_value
|
output.cache = data.source_value
|
||||||
else
|
else
|
||||||
output.cache = vec6_to_type[typeof(data.source_value)](x0_123, x0_456)
|
output.cache = vec6_to_type[typeof(data.source_value)](x0_123, x0_456)
|
||||||
|
|
@ -288,12 +290,6 @@ local function update_spring_sources()
|
||||||
|
|
||||||
update_descendants(output)
|
update_descendants(output)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, data in next, remove_queue do
|
|
||||||
springs[data] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
table.clear(remove_queue)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue