@quenty/spring 10.8.1 → 10.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.8.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/spring@10.8.1...@quenty/spring@10.8.2) (2025-04-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [10.8.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/spring@10.8.0...@quenty/spring@10.8.1) (2025-03-21)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/spring
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/spring",
3
- "version": "10.8.1",
3
+ "version": "10.8.2",
4
4
  "description": "Spring implementation for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -29,8 +29,8 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@quenty/ducktype": "^5.8.1",
33
- "@quenty/loader": "^10.8.0"
32
+ "@quenty/ducktype": "^5.8.2",
33
+ "@quenty/loader": "^10.8.1"
34
34
  },
35
- "gitHead": "6b7c3e15e60cdb185986207b574e2b5591261e7a"
35
+ "gitHead": "78c3ac0ab08dd18085b6e6e6e4f745e76ed99f68"
36
36
  }
@@ -1,3 +1,4 @@
1
+ --!nocheck
1
2
  --[=[
2
3
  Represents a value that can operate in linear space
3
4
 
@@ -12,6 +13,14 @@ local LinearValue = {}
12
13
  LinearValue.ClassName = "LinearValue"
13
14
  LinearValue.__index = LinearValue
14
15
 
16
+ export type LinearValue<T> = typeof(setmetatable(
17
+ {} :: {
18
+ _constructor: (...number) -> T,
19
+ _values: { number },
20
+ },
21
+ LinearValue
22
+ ))
23
+
15
24
  --[=[
16
25
  Constructs a new LinearValue object.
17
26
 
@@ -19,10 +28,10 @@ LinearValue.__index = LinearValue
19
28
  @param values ({ number })
20
29
  @return LinearValue<T>
21
30
  ]=]
22
- function LinearValue.new(constructor, values)
31
+ function LinearValue.new<T>(constructor: (...number) -> T, values: { number }): LinearValue<T>
23
32
  return setmetatable({
24
- _constructor = constructor;
25
- _values = values;
33
+ _constructor = constructor,
34
+ _values = values,
26
35
  }, LinearValue)
27
36
  end
28
37
 
@@ -32,28 +41,32 @@ end
32
41
  @param value any -- A value to check
33
42
  @return boolean -- True if a linear value, false otherwise
34
43
  ]=]
35
- function LinearValue.isLinear(value)
44
+ function LinearValue.isLinear(value: any): boolean
36
45
  return DuckTypeUtils.isImplementation(LinearValue, value)
37
46
  end
38
47
 
39
- local function convertUDim2(scaleX, offsetX, scaleY, offsetY)
48
+ local function convertUDim2(scaleX: number, offsetX: number, scaleY: number, offsetY: number): UDim2
40
49
  -- Roblox UDim2.new(0, 9.999, 0, 9.999) rounds to UDim2.new(0, 9, 0, 9) which means small floating point
41
50
  -- errors can cause shaking UI.
42
51
 
43
52
  return UDim2.new(scaleX, math.round(offsetX), scaleY, math.round(offsetY))
44
53
  end
45
54
 
46
- local function convertUDim(scale, offset)
55
+ local function convertUDim(scale: number, offset: number): UDim
47
56
  -- Roblox UDim.new(0, 9.999) rounds to UDim.new(0, 9) which means small floating point
48
57
  -- errors can cause shaking UI.
49
58
 
50
59
  return UDim.new(scale, math.round(offset))
51
60
  end
52
61
 
53
- local function convertBoolean(value)
62
+ local function convertBoolean(value: number): boolean
54
63
  return value ~= 0
55
64
  end
56
65
 
66
+ local function convertColor3(r: number, g: number, b: number): Color3
67
+ return Color3.new(r, g, b)
68
+ end
69
+
57
70
  --[=[
58
71
  Converts an arbitrary value to a LinearValue if Roblox has not defined this value
59
72
  for multiplication and addition.
@@ -61,13 +74,16 @@ end
61
74
  @param value T
62
75
  @return LinearValue<T> | T
63
76
  ]=]
64
- function LinearValue.toLinearIfNeeded(value)
77
+ function LinearValue.toLinearIfNeeded<T>(value: any): LinearValue<any>
65
78
  if typeof(value) == "Color3" then
66
- return LinearValue.new(Color3.new, {value.r, value.g, value.b})
79
+ return LinearValue.new(convertColor3, { value.R, value.G, value.B })
67
80
  elseif typeof(value) == "UDim2" then
68
- return LinearValue.new(convertUDim2, {value.X.Scale, math.round(value.X.Offset), value.Y.Scale, math.round(value.Y.Offset)})
81
+ return LinearValue.new(
82
+ convertUDim2,
83
+ { value.X.Scale, math.round(value.X.Offset), value.Y.Scale, math.round(value.Y.Offset) }
84
+ )
69
85
  elseif typeof(value) == "UDim" then
70
- return LinearValue.new(convertUDim, {value.Scale, math.round(value.Offset)})
86
+ return LinearValue.new(convertUDim, { value.Scale, math.round(value.Offset) })
71
87
  elseif type(value) == "boolean" then
72
88
  return LinearValue.new(convertBoolean, { value and 1 or 0 })
73
89
  else
@@ -81,7 +97,7 @@ end
81
97
  @param value LinearValue<T> | any
82
98
  @return T | any
83
99
  ]=]
84
- function LinearValue.fromLinearIfNeeded(value)
100
+ function LinearValue.fromLinearIfNeeded<T>(value: LinearValue<T> | any): any
85
101
  if LinearValue.isLinear(value) then
86
102
  return value:ToBaseValue()
87
103
  else
@@ -94,24 +110,24 @@ end
94
110
 
95
111
  @return T
96
112
  ]=]
97
- function LinearValue:ToBaseValue()
113
+ function LinearValue:ToBaseValue<T>(): T
98
114
  return self._constructor(unpack(self._values))
99
115
  end
100
116
 
101
- local function operation(func)
102
- return function(a, b)
117
+ local function operation(func: (number, number) -> number)
118
+ return function(a: LinearValue<any>, b: LinearValue<any>)
103
119
  if LinearValue.isLinear(a) and LinearValue.isLinear(b) then
104
120
  assert(a._constructor == b._constructor, "a is not the same type of linearValue as b")
105
121
 
106
122
  local values = {}
107
- for i=1, #a._values do
123
+ for i = 1, #a._values do
108
124
  values[i] = func(a._values[i], b._values[i])
109
125
  end
110
126
  return LinearValue.new(a._constructor, values)
111
127
  elseif LinearValue.isLinear(a) then
112
128
  if type(b) == "number" then
113
129
  local values = {}
114
- for i=1, #a._values do
130
+ for i = 1, #a._values do
115
131
  values[i] = func(a._values[i], b)
116
132
  end
117
133
  return LinearValue.new(a._constructor, values)
@@ -121,7 +137,7 @@ local function operation(func)
121
137
  elseif LinearValue.isLinear(b) then
122
138
  if type(a) == "number" then
123
139
  local values = {}
124
- for i=1, #b._values do
140
+ for i = 1, #b._values do
125
141
  values[i] = func(a, b._values[i])
126
142
  end
127
143
  return LinearValue.new(b._constructor, values)
@@ -139,11 +155,11 @@ end
139
155
 
140
156
  @return number -- The magnitude of the linear value.
141
157
  ]=]
142
- function LinearValue:GetMagnitude()
143
- local dot = 0
144
- for i=1, #self._values do
145
- local value = self._values[i]
146
- dot = dot + value*value
158
+ function LinearValue:GetMagnitude(): number
159
+ local dot: number = 0
160
+ for i = 1, #self._values do
161
+ local value: number = self._values[i]
162
+ dot = dot + value * value
147
163
  end
148
164
  return math.sqrt(dot)
149
165
  end
@@ -155,10 +171,10 @@ end
155
171
  @readonly
156
172
  @within LinearValue
157
173
  ]=]
158
- function LinearValue:__index(key)
174
+ function LinearValue.__index<T>(self: LinearValue<T>, key: string): any
159
175
  if LinearValue[key] then
160
176
  return LinearValue[key]
161
- elseif key == "magnitude" then
177
+ elseif key == "magnitude" or key == "Magnitude" then
162
178
  return self:GetMagnitude()
163
179
  else
164
180
  return nil
@@ -181,7 +197,7 @@ LinearValue.__div = operation(function(a, b)
181
197
  return a / b
182
198
  end)
183
199
 
184
- function LinearValue:__eq(a, b)
200
+ function LinearValue:__eq<T>(a: LinearValue<T>, b: LinearValue<T>): boolean
185
201
  if LinearValue.isLinear(a) and LinearValue.isLinear(b) then
186
202
  if #a._values ~= #b._values then
187
203
  return false
@@ -1,3 +1,4 @@
1
+ --!strict
1
2
  --[=[
2
3
  A physical model of a spring, useful in many applications.
3
4
 
@@ -27,6 +28,30 @@
27
28
  @class Spring
28
29
  ]=]
29
30
  local Spring = {}
31
+ Spring.__index = Spring
32
+
33
+ export type SpringClock = () -> number
34
+
35
+ export type Spring<T> = typeof(setmetatable(
36
+ {} :: {
37
+ Position: T,
38
+ Velocity: T,
39
+ Target: T,
40
+ Damper: number,
41
+ Speed: number,
42
+ Clock: SpringClock,
43
+
44
+ _position0: T,
45
+ _velocity0: T,
46
+ _time0: number,
47
+ _target: T,
48
+ _damper: number,
49
+ _speed: number,
50
+ _clock: SpringClock,
51
+ _positionVelocity: (self: Spring<T>, now: number) -> (T, T),
52
+ },
53
+ Spring
54
+ ))
30
55
 
31
56
  --[=[
32
57
  Constructs a new Spring at the position and target specified, of type T.
@@ -46,18 +71,19 @@ local Spring = {}
46
71
  @param clock? () -> number -- The clock function is optional, and is used to update the spring
47
72
  @return Spring<T>
48
73
  ]=]
49
- function Spring.new(initial, clock)
50
- local target = initial or 0
51
- clock = clock or os.clock
74
+ function Spring.new<T>(initial: T?, clock: SpringClock?): Spring<T>
75
+ local p0 = initial or 0
76
+ local springClock = clock or os.clock
77
+
52
78
  return setmetatable({
53
- _clock = clock;
54
- _time0 = clock();
55
- _position0 = target;
56
- _velocity0 = 0*target;
57
- _target = target;
58
- _damper = 1;
59
- _speed = 1;
60
- }, Spring)
79
+ _clock = springClock,
80
+ _time0 = springClock(),
81
+ _position0 = p0,
82
+ _velocity0 = 0 * (p0 :: any),
83
+ _target = p0,
84
+ _damper = 1,
85
+ _speed = 1,
86
+ }, Spring) :: any
61
87
  end
62
88
 
63
89
  --[=[
@@ -67,8 +93,8 @@ end
67
93
  @param velocity T -- The velocity to impulse with
68
94
  @return ()
69
95
  ]=]
70
- function Spring:Impulse(velocity)
71
- self.Velocity = self.Velocity + velocity
96
+ function Spring.Impulse<T>(self: Spring<T>, velocity: T)
97
+ self.Velocity = (self.Velocity :: any) + velocity
72
98
  end
73
99
 
74
100
  --[=[
@@ -76,9 +102,9 @@ end
76
102
  @param delta number -- Time to skip forwards
77
103
  @return ()
78
104
  ]=]
79
- function Spring:TimeSkip(delta)
105
+ function Spring.TimeSkip<T>(self: Spring<T>, delta: number)
80
106
  local now = self._clock()
81
- local position, velocity = self:_positionVelocity(now+delta)
107
+ local position, velocity = self:_positionVelocity(now + delta)
82
108
  self._position0 = position
83
109
  self._velocity0 = velocity
84
110
  self._time0 = now
@@ -90,11 +116,11 @@ end
90
116
  @param value T -- The target to set
91
117
  @param doNotAnimate boolean? -- Whether or not to animate
92
118
  ]=]
93
- function Spring:SetTarget(value, doNotAnimate)
119
+ function Spring.SetTarget<T>(self: Spring<T>, value: T, doNotAnimate: boolean?)
94
120
  if doNotAnimate then
95
121
  local now = self._clock()
96
122
  self._position0 = value
97
- self._velocity0 = 0*value
123
+ self._velocity0 = 0 * (value :: any)
98
124
  self._target = value
99
125
  self._time0 = now
100
126
  else
@@ -183,7 +209,7 @@ end
183
209
  @prop Clock () -> number
184
210
  @within Spring
185
211
  ]=]
186
- function Spring:__index(index)
212
+ (Spring :: any).__index = function<T>(self: Spring<T>, index: any): any
187
213
  if Spring[index] then
188
214
  return Spring[index]
189
215
  elseif index == "Value" or index == "Position" or index == "p" then
@@ -205,7 +231,7 @@ function Spring:__index(index)
205
231
  end
206
232
  end
207
233
 
208
- function Spring:__newindex(index, value)
234
+ function Spring.__newindex<T>(self: Spring<T>, index, value)
209
235
  local now = self._clock()
210
236
 
211
237
  if index == "Value" or index == "Position" or index == "p" then
@@ -247,43 +273,43 @@ function Spring:__newindex(index, value)
247
273
  end
248
274
  end
249
275
 
250
- function Spring:_positionVelocity(now)
276
+ function Spring._positionVelocity<T>(self: Spring<T>, now: number): (T, T)
251
277
  local p0 = self._position0
252
278
  local v0 = self._velocity0
253
279
  local p1 = self._target
254
- local d = self._damper
255
- local s = self._speed
280
+ local d: number = self._damper
281
+ local s: number = self._speed
256
282
 
257
- local t = s*(now - self._time0)
258
- local d2 = d*d
283
+ local t: number = s * (now - self._time0)
284
+ local d2 = d * d
259
285
 
260
286
  local h, si, co
261
287
  if d2 < 1 then
262
288
  h = math.sqrt(1 - d2)
263
- local ep = math.exp(-d*t)/h
264
- co, si = ep*math.cos(h*t), ep*math.sin(h*t)
289
+ local ep = math.exp(-d * t) / h
290
+ co, si = ep * math.cos(h * t), ep * math.sin(h * t)
265
291
  elseif d2 == 1 then
266
292
  h = 1
267
- local ep = math.exp(-d*t)/h
268
- co, si = ep, ep*t
293
+ local ep = math.exp(-d * t) / h
294
+ co, si = ep, ep * t
269
295
  else
270
296
  h = math.sqrt(d2 - 1)
271
- local u = math.exp((-d + h)*t)/(2*h)
272
- local v = math.exp((-d - h)*t)/(2*h)
297
+ local u = math.exp((-d + h) * t) / (2 * h)
298
+ local v = math.exp((-d - h) * t) / (2 * h)
273
299
  co, si = u + v, u - v
274
300
  end
275
301
 
276
- local a0 = h*co + d*si
277
- local a1 = 1 - (h*co + d*si)
278
- local a2 = si/s
302
+ local a0: any = h * co + d * si
303
+ local a1: any = 1 - (h * co + d * si)
304
+ local a2: any = si / s
279
305
 
280
- local b0 = -s*si
281
- local b1 = s*si
282
- local b2 = h*co - d*si
306
+ local b0: any = -s * si
307
+ local b1: any = s * si
308
+ local b2: any = h * co - d * si
283
309
 
284
- return
285
- a0*p0 + a1*p1 + a2*v0,
286
- b0*p0 + b1*p1 + b2*v0
310
+ -- stylua: ignore
311
+ return a0 * p0 + a1 * p1 + a2 * v0,
312
+ b0 * p0 + b1 * p1 + b2 * v0
287
313
  end
288
314
 
289
315
  return Spring
@@ -1,15 +1,18 @@
1
+ --!strict
1
2
  --[=[
2
3
  Utility functions that are related to the Spring object
3
4
  @class SpringUtils
4
5
  ]=]
5
6
 
6
- local EPSILON = 1e-6
7
-
8
7
  local require = require(script.Parent.loader).load(script)
8
+
9
9
  local LinearValue = require("LinearValue")
10
+ local _Spring = require("Spring")
10
11
 
11
12
  local SpringUtils = {}
12
13
 
14
+ local EPSILON = 1e-6
15
+
13
16
  --[=[
14
17
  Utility function that returns whether or not a spring is animating based upon
15
18
  velocity and closeness to target, and as the second value, the value that should be
@@ -19,21 +22,21 @@ local SpringUtils = {}
19
22
  @param epsilon number? -- Optional epsilon
20
23
  @return boolean, T
21
24
  ]=]
22
- function SpringUtils.animating(spring, epsilon)
23
- epsilon = epsilon or EPSILON
25
+ function SpringUtils.animating<T>(spring: _Spring.Spring<T>, epsilon: number?): (boolean, T)
26
+ local thisEpsilon = epsilon or EPSILON
24
27
 
25
28
  local position = spring.Position
26
29
  local target = spring.Target
27
30
 
28
31
  local animating
29
32
  if type(target) == "number" then
30
- animating = math.abs(spring.Position - spring.Target) > epsilon
31
- or math.abs(spring.Velocity) > epsilon
33
+ animating = math.abs((spring :: any).Position - (spring :: any).Target) > thisEpsilon
34
+ or math.abs((spring :: any).Velocity) > thisEpsilon
32
35
  else
33
36
  local rbxtype = typeof(target)
34
37
  if rbxtype == "Vector3" or rbxtype == "Vector2" or LinearValue.isLinear(target) then
35
- animating = (spring.Position - spring.Target).magnitude > epsilon
36
- or spring.Velocity.magnitude > epsilon
38
+ animating = ((spring :: any).Position - (spring :: any).Target).magnitude > thisEpsilon
39
+ or (spring :: any).Velocity.magnitude > thisEpsilon
37
40
  else
38
41
  error("Unknown type")
39
42
  end
@@ -42,7 +45,7 @@ function SpringUtils.animating(spring, epsilon)
42
45
  if animating then
43
46
  return true, position
44
47
  else
45
- -- We need to return the target so we use the actual target value (i.e. pretend like the spring is asleep)
48
+ -- We need to return the target so we use the actual target value (i.e. pretend like the (spring :: any) is asleep)
46
49
  return false, target
47
50
  end
48
51
  end
@@ -55,12 +58,12 @@ end
55
58
  @param speed number
56
59
  @return T
57
60
  ]=]
58
- function SpringUtils.getVelocityAdjustment(velocity, dampen, speed)
61
+ function SpringUtils.getVelocityAdjustment<T>(velocity: T, dampen: number, speed: number): T
59
62
  assert(velocity, "Bad velocity")
60
63
  assert(dampen, "Bad dampen")
61
64
  assert(speed, "Bad speed")
62
65
 
63
- return velocity*(2*dampen/speed)
66
+ return (velocity :: any) * (2 * dampen / speed)
64
67
  end
65
68
 
66
69
  --[=[
@@ -70,7 +73,7 @@ end
70
73
  @param value T
71
74
  @return LinearValue<T> | T
72
75
  ]=]
73
- function SpringUtils.toLinearIfNeeded(value)
76
+ function SpringUtils.toLinearIfNeeded<T>(value: T): LinearValue.LinearValue<T> | T
74
77
  return LinearValue.toLinearIfNeeded(value)
75
78
  end
76
79
 
@@ -80,7 +83,7 @@ end
80
83
  @param value LinearValue<T> | any
81
84
  @return T | any
82
85
  ]=]
83
- function SpringUtils.fromLinearIfNeeded(value)
86
+ function SpringUtils.fromLinearIfNeeded<T>(value: LinearValue.LinearValue<T> | T): T
84
87
  return LinearValue.fromLinearIfNeeded(value)
85
88
  end
86
89