@px-petals/roku.motion-web 0.0.0-stage → 1.1.0

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.
Files changed (99) hide show
  1. package/LICENSE +201 -0
  2. package/audio/audio.cues.js +27 -0
  3. package/audio/audio.js +259 -0
  4. package/bands/bands.js +392 -0
  5. package/bundle/bundle.fonts.js +55 -0
  6. package/bundle/bundle.js +131 -0
  7. package/focus/focus.direction.js +46 -0
  8. package/focus/focus.dom.js +158 -0
  9. package/focus/focus.js +277 -0
  10. package/focus/focus.tree.js +146 -0
  11. package/hit/hit.geometry.js +113 -0
  12. package/hit/hit.js +129 -0
  13. package/list/list.flex.js +181 -0
  14. package/list/list.items.js +80 -0
  15. package/list/list.js +518 -0
  16. package/lottie/lottie.js +124 -0
  17. package/lottie/lottie.layers.js +416 -0
  18. package/lottie/lottie.paint.js +238 -0
  19. package/lottie/lottie.paths.js +474 -0
  20. package/lottie/lottie.props.js +265 -0
  21. package/lottie/lottie.shapes.js +259 -0
  22. package/mixer/mixer.js +307 -0
  23. package/mixer/mixer.mix.js +107 -0
  24. package/mixer/mixer.stage.js +431 -0
  25. package/motion/motion.convert.js +404 -0
  26. package/motion/motion.flow.js +421 -0
  27. package/motion/motion.keys.js +150 -0
  28. package/motion/motion.machine.js +382 -0
  29. package/motion/motion.nested.js +345 -0
  30. package/motion/motion.values.js +368 -0
  31. package/motion.js +1155 -0
  32. package/package.json +47 -4
  33. package/player/overlay/overlay.js +325 -0
  34. package/player/overlay/overlay.layout.js +208 -0
  35. package/player/player.fit.js +101 -0
  36. package/player/player.js +421 -0
  37. package/player/player.raster.js +127 -0
  38. package/pointer/pointer.js +292 -0
  39. package/readme.md +345 -0
  40. package/script/luau/lib/base/base.js +202 -0
  41. package/script/luau/lib/base/base.table.js +189 -0
  42. package/script/luau/lib/lib.args.js +152 -0
  43. package/script/luau/lib/lib.js +23 -0
  44. package/script/luau/lib/math/math.bit32.js +144 -0
  45. package/script/luau/lib/math/math.js +288 -0
  46. package/script/luau/lib/math/math.vector.js +125 -0
  47. package/script/luau/lib/string/string.format.js +143 -0
  48. package/script/luau/lib/string/string.js +160 -0
  49. package/script/luau/lib/string/string.pattern.js +505 -0
  50. package/script/luau/load/load.js +362 -0
  51. package/script/luau/load/load.ops.js +79 -0
  52. package/script/luau/luau.convert.js +259 -0
  53. package/script/luau/luau.js +132 -0
  54. package/script/luau/luau.md +111 -0
  55. package/script/luau/vm/vm.js +167 -0
  56. package/script/luau/vm/vm.meta.js +401 -0
  57. package/script/luau/vm/vm.num.js +236 -0
  58. package/script/luau/vm/vm.run.js +842 -0
  59. package/script/luau/vm/vm.value.js +406 -0
  60. package/script/play/play.js +199 -0
  61. package/script/play/play.objects.js +295 -0
  62. package/script/play/play.scope.js +91 -0
  63. package/script/rive/api/api.statics.js +31 -0
  64. package/script/rive/api/api.type.js +188 -0
  65. package/script/rive/api/artboard/artboard.js +89 -0
  66. package/script/rive/api/assets/assets.js +72 -0
  67. package/script/rive/api/data/data.value.js +99 -0
  68. package/script/rive/api/draw/draw.contours.js +163 -0
  69. package/script/rive/api/draw/draw.measure.js +196 -0
  70. package/script/rive/api/draw/draw.paint.js +235 -0
  71. package/script/rive/api/draw/draw.path.js +153 -0
  72. package/script/rive/api/draw/draw.renderer.js +117 -0
  73. package/script/rive/api/math/math.color.js +96 -0
  74. package/script/rive/api/math/math.mat2d.js +180 -0
  75. package/script/rive/api/math/math.vector.js +121 -0
  76. package/script/rive/api/media/media.audio.js +113 -0
  77. package/script/rive/api/media/media.draw.js +73 -0
  78. package/script/rive/api/media/media.image.js +61 -0
  79. package/script/rive/api/media/media.mesh.js +50 -0
  80. package/script/rive/api/pointer/pointer.event.js +64 -0
  81. package/script/rive/api/pointer/pointer.route.js +69 -0
  82. package/script/rive/api/text/text.font.js +129 -0
  83. package/script/rive/api/text/text.js +278 -0
  84. package/script/rive/api/text/text.layout.js +259 -0
  85. package/script/rive/context/context.js +76 -0
  86. package/script/rive/context/context.memory.js +196 -0
  87. package/script/rive/context/context.viewmodel.js +302 -0
  88. package/script/rive/context/lists/lists.js +160 -0
  89. package/script/rive/context/lists/lists.values.js +281 -0
  90. package/script/rive/host/host.globals.js +44 -0
  91. package/script/rive/host/host.js +533 -0
  92. package/script/rive/host/host.node.js +45 -0
  93. package/script/rive/rive.js +23 -0
  94. package/script/rive/rive.md +180 -0
  95. package/script/script.js +11 -0
  96. package/skin/skin.deform.js +514 -0
  97. package/skin/skin.ik.js +260 -0
  98. package/skin/skin.js +297 -0
  99. package/README.md +0 -3
@@ -0,0 +1,382 @@
1
+ /**
2
+ * The state machine engine for dotLottie machines, as Roku's Motion runs it
3
+ * (runtime/roku/components/motion/state/state.bs): it answers "which state
4
+ * next?" and runs actions against a values table. It holds no state of its
5
+ * own; the flow owns the current state, the values and the pending events.
6
+ */
7
+
8
+ /**
9
+ * @typedef {{ initial: string, states: any[], inputs?: any[], interactions?: any[], layers?: any[] }} Graph
10
+ * @typedef {Record<string, any>} Values input name → value
11
+ * @typedef {Record<string, boolean>} Events pending events, name → true
12
+ * @typedef {{ changed: boolean, events: string[], focus: any[] }} Effects `focus`: the focus actions (FocusTarget, FocusMove, FocusClear) to run once the values are set
13
+ * @typedef {{ elapsed: number, length: number }} Clock how long the current state has played, and how long its timeline is, in ms
14
+ * @typedef {import('../script/play/play.js').Scripts} Scripts the scripts a bundle runs; without them a Script guard fails and a RunScript action does nothing
15
+ */
16
+
17
+ /**
18
+ * @param {any} graph
19
+ * @returns {boolean}
20
+ */
21
+ export function exists(graph) {
22
+ return !!graph && Array.isArray(graph.states) && typeof graph.initial === 'string'
23
+ }
24
+
25
+ /**
26
+ * @param {Graph} graph
27
+ * @param {string} name
28
+ */
29
+ export function find(graph, name) {
30
+ for (let n = 0; n < graph.states.length; n++) if (graph.states[n].name === name) return graph.states[n]
31
+ return null
32
+ }
33
+
34
+ /**
35
+ * The graphs of a machine's layers, each with the machine's inputs and
36
+ * interactions, which layers share. A machine without `layers` is one layer.
37
+ *
38
+ * @param {Graph} graph
39
+ * @returns {Graph[]}
40
+ */
41
+ export function layersOf(graph) {
42
+ const layers = graph.layers
43
+ if (!layers || layers.length < 2) return [ graph ]
44
+ return layers.map((/** @type {any} */ layer, /** @type {number} */ n) => (n === 0 ? graph : { initial: layer.initial, states: layer.states, inputs: graph.inputs, interactions: graph.interactions }))
45
+ }
46
+
47
+ /**
48
+ * Every input's authored value; events have none.
49
+ *
50
+ * @param {Graph} graph
51
+ * @returns {Values}
52
+ */
53
+ export function defaults(graph) {
54
+ /** @type {Values} */
55
+ const values = {}
56
+ const inputs = graph.inputs || []
57
+ for (let n = 0; n < inputs.length; n++) if (inputs[n].type !== 'Event') values[inputs[n].name] = inputs[n].value
58
+ return values
59
+ }
60
+
61
+ /**
62
+ * @param {Graph} graph
63
+ * @param {string} name
64
+ */
65
+ export function isEvent(graph, name) {
66
+ const inputs = graph.inputs || []
67
+ for (let n = 0; n < inputs.length; n++) if (inputs[n].name === name) return inputs[n].type === 'Event'
68
+ return false
69
+ }
70
+
71
+ // Transitions
72
+ // ■■■■■■■■■■■
73
+
74
+ /**
75
+ * Global states' transitions are checked first, then the current state's;
76
+ * the first whose guards all pass wins. A transition back into the current
77
+ * state from a global one is skipped, or it would fire forever.
78
+ *
79
+ * @param {Graph} graph
80
+ * @param {string} current
81
+ * @param {Values} values
82
+ * @param {Events} events
83
+ * @param {Clock | null} [clock] the current state's time, for exit times; without it exit times are not checked
84
+ * @param {Scripts | null} [scripts]
85
+ * @returns {string} the next state, or ""
86
+ */
87
+ export function nextState(graph, current, values, events, clock, scripts) {
88
+ const transition = takeTransition(graph, current, values, events, clock || null, scripts || null)
89
+ return transition ? transition.toState : ''
90
+ }
91
+
92
+ /**
93
+ * The transition `nextState` takes, whole: its `durationMs` is how long the
94
+ * outgoing and incoming states cross-fade.
95
+ *
96
+ * @param {Graph} graph
97
+ * @param {string} current
98
+ * @param {Values} values
99
+ * @param {Events} events
100
+ * @param {Clock | null} clock
101
+ * @param {Scripts | null} [scripts]
102
+ * @returns {any} the transition, or null
103
+ */
104
+ export function takeTransition(graph, current, values, events, clock, scripts) {
105
+ for (let n = 0; n < graph.states.length; n++) {
106
+ const state = graph.states[n]
107
+ if (state.type !== 'GlobalState') continue
108
+ const transition = firstPassing(state.transitions, values, events, current, clock, scripts || null)
109
+ if (transition) return transition
110
+ }
111
+
112
+ const state = find(graph, current)
113
+ return state ? firstPassing(state.transitions, values, events, '', clock, scripts || null) : null
114
+ }
115
+
116
+ /**
117
+ * When a transition's exit time is reached, in ms into its state.
118
+ *
119
+ * @param {any} transition
120
+ * @param {number} length the state's timeline, in ms
121
+ * @returns {number} 0 for a transition without an exit time
122
+ */
123
+ export function exitMs(transition, length) {
124
+ const exit = transition.exitTime
125
+ if (!exit) return 0
126
+ return typeof exit.ms === 'number' ? exit.ms : (exit.fraction || 0) * length
127
+ }
128
+
129
+ /**
130
+ * The earliest exit time among a state's transitions after `after`, in ms.
131
+ *
132
+ * @param {any} state
133
+ * @param {number} length the state's timeline, in ms
134
+ * @param {number} after an exit time already reached, or -1
135
+ * @returns {number} -1 when no exit time is left
136
+ */
137
+ export function nextExit(state, length, after) {
138
+ let next = -1
139
+ const list = (state && state.transitions) || []
140
+ for (let n = 0; n < list.length; n++) {
141
+ if (!list[n].exitTime) continue
142
+ const at = exitMs(list[n], length)
143
+ if (at > after && (next < 0 || at < next)) next = at
144
+ }
145
+ return next
146
+ }
147
+
148
+ /**
149
+ * @param {any[]} transitions
150
+ * @param {Values} values
151
+ * @param {Events} events
152
+ * @param {string} skip a target to pass over, or ""
153
+ * @param {Clock | null} clock
154
+ * @param {Scripts | null} scripts
155
+ */
156
+ function firstPassing(transitions, values, events, skip, clock, scripts) {
157
+ const list = transitions || []
158
+ for (let n = 0; n < list.length; n++) {
159
+ const transition = list[n]
160
+ if (transition.toState === skip) continue
161
+
162
+ // with a clock, an exit time is checked exactly and replaces the guard that waits for the whole timeline
163
+ const isTimed = !!clock && !!transition.exitTime
164
+ if (isTimed && clock.elapsed < exitMs(transition, clock.length)) continue
165
+ if (guardsPass(transition.guards, values, events, isTimed, scripts)) return transition
166
+ }
167
+ return null
168
+ }
169
+
170
+ /**
171
+ * @param {any[]} guards no guards always pass
172
+ * @param {Values} values
173
+ * @param {Events} events
174
+ * @param {boolean} isTimed the transition's exit time is checked apart; its `exit` guard is skipped
175
+ * @param {Scripts | null} scripts
176
+ */
177
+ function guardsPass(guards, values, events, isTimed, scripts) {
178
+ const list = guards || []
179
+ for (let n = 0; n < list.length; n++) {
180
+ if (isTimed && list[n].exit === true) continue
181
+ if (!guardPasses(list[n], values, events, scripts)) return false
182
+ }
183
+ return true
184
+ }
185
+
186
+ /**
187
+ * A guard compares only values of its own type, as the Roku engine must.
188
+ *
189
+ * @param {any} guard
190
+ * @param {Values} values
191
+ * @param {Events} events
192
+ * @param {Scripts | null} scripts
193
+ */
194
+ function guardPasses(guard, values, events, scripts) {
195
+ if (guard.type === 'Event') return events[guard.inputName] === true
196
+ // a scripted condition: the script says; with no way to run it, it does not pass
197
+ if (guard.type === 'Script') return !!scripts && scripts.evaluate(guard, values)
198
+
199
+ const value = values[guard.inputName]
200
+ const expected = resolve(guard.compareTo, values)
201
+ const condition = guard.conditionType
202
+
203
+ const isNumeric = guard.type === 'Numeric' && typeof value === 'number' && typeof expected === 'number'
204
+ if (isNumeric) return compare(value, condition, expected)
205
+
206
+ const isBoolean = guard.type === 'Boolean' && typeof value === 'boolean' && typeof expected === 'boolean'
207
+ const isString = guard.type === 'String' && typeof value === 'string' && typeof expected === 'string'
208
+ if (!isBoolean && !isString) return false
209
+
210
+ return condition === 'NotEqual' ? value !== expected : value === expected
211
+ }
212
+
213
+ /**
214
+ * @param {number} a
215
+ * @param {string} condition
216
+ * @param {number} b
217
+ */
218
+ function compare(a, condition, b) {
219
+ switch (condition) {
220
+ case 'NotEqual': return a !== b
221
+ case 'GreaterThan': return a > b
222
+ case 'GreaterThanOrEqual': return a >= b
223
+ case 'LessThan': return a < b
224
+ case 'LessThanOrEqual': return a <= b
225
+ default: return a === b
226
+ }
227
+ }
228
+
229
+ // Actions
230
+ // ■■■■■■■
231
+
232
+ /**
233
+ * Runs actions against the values. SetFrame, SetProgress and SetTheme have
234
+ * nothing to act on here and are skipped, as on Roku.
235
+ *
236
+ * @param {any[]} actions
237
+ * @param {Graph} graph
238
+ * @param {Values} values updated in place
239
+ * @param {Events} events updated in place: Fire adds a pending event
240
+ * @param {Scripts | null} [scripts] runs a RunScript action
241
+ * @param {string} [trigger] the interaction the actions run for, which a script's pointer event is of
242
+ * @returns {Effects}
243
+ */
244
+ export function perform(actions, graph, values, events, scripts, trigger) {
245
+ /** @type {Effects} */
246
+ const effects = { changed: false, events: [], focus: [] }
247
+ const list = actions || []
248
+ for (let n = 0; n < list.length; n++) runOne(list[n], graph, values, events, effects, scripts || null, trigger || '')
249
+ return effects
250
+ }
251
+
252
+ /**
253
+ * @param {any} action
254
+ * @param {Graph} graph
255
+ * @param {Values} values
256
+ * @param {Events} events
257
+ * @param {Effects} effects
258
+ * @param {Scripts | null} scripts
259
+ * @param {string} trigger
260
+ */
261
+ function runOne(action, graph, values, events, effects, scripts, trigger) {
262
+ const name = action.inputName
263
+ const current = values[name]
264
+ switch (action.type) {
265
+ case 'Fire':
266
+ events[name] = true
267
+ break
268
+ case 'FireCustomEvent':
269
+ effects.events.push(action.value)
270
+ break
271
+ case 'FocusTarget':
272
+ case 'FocusMove':
273
+ case 'FocusClear':
274
+ effects.focus.push(action)
275
+ break
276
+ case 'OpenUrl':
277
+ effects.events.push('openUrl:' + action.url)
278
+ break
279
+ case 'RunScript':
280
+ if (scripts && scripts.perform(action, trigger, values, events)) effects.changed = true
281
+ break
282
+ case 'Toggle':
283
+ if (typeof current === 'boolean') setValue(values, name, !current, effects)
284
+ break
285
+ case 'Increment':
286
+ case 'Decrement':
287
+ if (typeof current === 'number') setValue(values, name, current + stepSize(action, values), effects)
288
+ break
289
+ case 'Reset':
290
+ setValue(values, name, defaults(graph)[name], effects)
291
+ break
292
+ case 'SetBoolean':
293
+ case 'SetNumeric':
294
+ case 'SetString':
295
+ setValue(values, name, resolve(action.value, values), effects)
296
+ break
297
+ default:
298
+ }
299
+ }
300
+
301
+ /**
302
+ * The action's value, or 1 when it names no number; negative to decrement.
303
+ *
304
+ * @param {any} action
305
+ * @param {Values} values
306
+ */
307
+ function stepSize(action, values) {
308
+ const size = resolve(action.value, values)
309
+ const step = typeof size === 'number' ? size : 1
310
+ return action.type === 'Decrement' ? -step : step
311
+ }
312
+
313
+ /**
314
+ * A literal, or "$name" naming another input.
315
+ *
316
+ * @param {any} value
317
+ * @param {Values} values
318
+ */
319
+ function resolve(value, values) {
320
+ return typeof value === 'string' && value.charAt(0) === '$' ? values[value.slice(1)] : value
321
+ }
322
+
323
+ /**
324
+ * @param {Values} values
325
+ * @param {string} name
326
+ * @param {any} value
327
+ * @param {Effects} effects
328
+ */
329
+ function setValue(values, name, value, effects) {
330
+ if (value === undefined || value === null) return
331
+ if (values[name] === value) return
332
+ values[name] = value
333
+ effects.changed = true
334
+ }
335
+
336
+ // Interactions
337
+ // ■■■■■■■■■■■■
338
+
339
+ /**
340
+ * Every action the matching interactions run, in order.
341
+ *
342
+ * @param {Graph} graph
343
+ * @param {string} kind "Click", "OnComplete", or a pointer interaction ("PointerEnter", ...)
344
+ * @param {string} stateName for lifecycle interactions; "" matches any
345
+ * @param {string} [target] a pointer interaction's target `rid`; "" for those without a target; omitted matches any
346
+ * @returns {any[]}
347
+ */
348
+ export function interactionActions(graph, kind, stateName, target) {
349
+ const actions = []
350
+ const list = graph.interactions || []
351
+ for (let n = 0; n < list.length; n++) {
352
+ const interaction = list[n]
353
+ const isStateMatch = stateName === '' || typeof interaction.stateName !== 'string' || interaction.stateName === stateName
354
+ const isTargetMatch = target === undefined || (interaction.target ? interaction.target.rid : '') === target
355
+ if (interaction.type === kind && isStateMatch && isTargetMatch) for (let k = 0; k < interaction.actions.length; k++) actions.push(interaction.actions[k])
356
+ }
357
+ return actions
358
+ }
359
+
360
+ /** The interactions a pointer sets off */
361
+ const POINTER_TYPES = [ 'Click', 'PointerDown', 'PointerUp', 'PointerEnter', 'PointerExit', 'PointerMove' ]
362
+
363
+ /**
364
+ * Where the pointer interactions listen: each target's `rid`, or "" for the
365
+ * listeners without one, which watch the whole artboard, and the types on it.
366
+ *
367
+ * @param {Graph} graph
368
+ * @returns {Record<string, string[]>}
369
+ */
370
+ export function pointerTargetsOf(graph) {
371
+ /** @type {Record<string, string[]>} */
372
+ const found = {}
373
+ const list = graph.interactions || []
374
+ for (let n = 0; n < list.length; n++) {
375
+ const interaction = list[n]
376
+ if (POINTER_TYPES.indexOf(interaction.type) < 0) continue
377
+ const rid = interaction.target ? interaction.target.rid : ''
378
+ if (!found[rid]) found[rid] = []
379
+ if (found[rid].indexOf(interaction.type) < 0) found[rid].push(interaction.type)
380
+ }
381
+ return found
382
+ }