@px-petals/roku.motion-web 1.0.0 → 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 (77) hide show
  1. package/bands/bands.js +3 -1
  2. package/bundle/bundle.js +56 -0
  3. package/list/list.js +12 -1
  4. package/lottie/lottie.layers.js +64 -11
  5. package/lottie/lottie.paint.js +30 -0
  6. package/lottie/lottie.paths.js +19 -4
  7. package/lottie/lottie.props.js +18 -8
  8. package/lottie/lottie.shapes.js +2 -1
  9. package/mixer/mixer.js +31 -8
  10. package/mixer/mixer.mix.js +17 -0
  11. package/motion/motion.convert.js +32 -1
  12. package/motion/motion.flow.js +20 -2
  13. package/motion/motion.machine.js +28 -13
  14. package/motion/motion.values.js +13 -9
  15. package/motion.js +196 -20
  16. package/package.json +10 -16
  17. package/player/player.js +25 -5
  18. package/readme.md +78 -3
  19. package/script/luau/lib/base/base.js +202 -0
  20. package/script/luau/lib/base/base.table.js +189 -0
  21. package/script/luau/lib/lib.args.js +152 -0
  22. package/script/luau/lib/lib.js +23 -0
  23. package/script/luau/lib/math/math.bit32.js +144 -0
  24. package/script/luau/lib/math/math.js +288 -0
  25. package/script/luau/lib/math/math.vector.js +125 -0
  26. package/script/luau/lib/string/string.format.js +143 -0
  27. package/script/luau/lib/string/string.js +160 -0
  28. package/script/luau/lib/string/string.pattern.js +505 -0
  29. package/script/luau/load/load.js +362 -0
  30. package/script/luau/load/load.ops.js +79 -0
  31. package/script/luau/luau.convert.js +259 -0
  32. package/script/luau/luau.js +132 -0
  33. package/script/luau/luau.md +111 -0
  34. package/script/luau/vm/vm.js +167 -0
  35. package/script/luau/vm/vm.meta.js +401 -0
  36. package/script/luau/vm/vm.num.js +236 -0
  37. package/script/luau/vm/vm.run.js +842 -0
  38. package/script/luau/vm/vm.value.js +406 -0
  39. package/script/play/play.js +199 -0
  40. package/script/play/play.objects.js +295 -0
  41. package/script/play/play.scope.js +91 -0
  42. package/script/rive/api/api.statics.js +31 -0
  43. package/script/rive/api/api.type.js +188 -0
  44. package/script/rive/api/artboard/artboard.js +89 -0
  45. package/script/rive/api/assets/assets.js +72 -0
  46. package/script/rive/api/data/data.value.js +99 -0
  47. package/script/rive/api/draw/draw.contours.js +163 -0
  48. package/script/rive/api/draw/draw.measure.js +196 -0
  49. package/script/rive/api/draw/draw.paint.js +235 -0
  50. package/script/rive/api/draw/draw.path.js +153 -0
  51. package/script/rive/api/draw/draw.renderer.js +117 -0
  52. package/script/rive/api/math/math.color.js +96 -0
  53. package/script/rive/api/math/math.mat2d.js +180 -0
  54. package/script/rive/api/math/math.vector.js +121 -0
  55. package/script/rive/api/media/media.audio.js +113 -0
  56. package/script/rive/api/media/media.draw.js +73 -0
  57. package/script/rive/api/media/media.image.js +61 -0
  58. package/script/rive/api/media/media.mesh.js +50 -0
  59. package/script/rive/api/pointer/pointer.event.js +64 -0
  60. package/script/rive/api/pointer/pointer.route.js +69 -0
  61. package/script/rive/api/text/text.font.js +129 -0
  62. package/script/rive/api/text/text.js +278 -0
  63. package/script/rive/api/text/text.layout.js +259 -0
  64. package/script/rive/context/context.js +76 -0
  65. package/script/rive/context/context.memory.js +196 -0
  66. package/script/rive/context/context.viewmodel.js +302 -0
  67. package/script/rive/context/lists/lists.js +160 -0
  68. package/script/rive/context/lists/lists.values.js +281 -0
  69. package/script/rive/host/host.globals.js +44 -0
  70. package/script/rive/host/host.js +533 -0
  71. package/script/rive/host/host.node.js +45 -0
  72. package/script/rive/rive.js +23 -0
  73. package/script/rive/rive.md +180 -0
  74. package/script/script.js +11 -0
  75. package/skin/skin.deform.js +514 -0
  76. package/skin/skin.ik.js +260 -0
  77. package/skin/skin.js +297 -0
@@ -0,0 +1,514 @@
1
+ /**
2
+ * Rive's skin deform: bone world transforms, tendon matrices and the weighted
3
+ * sum per vertex (rive-runtime `bones/skin.cpp`, `weight.cpp`, `bone.cpp`,
4
+ * `transform_component.cpp`), then the corner rounding of `shapes/path.cpp`.
5
+ *
6
+ * A rig is made once per skin: it holds the skin's setup values, the scratch
7
+ * the deform works in and the Lottie path it writes, all allocated up front,
8
+ * so a deform allocates nothing. The values a deform reads are `rig.now`,
9
+ * laid out as each bone's `x, y, rotation, scaleX, scaleY, length`, then
10
+ * each node's `x, y, rotation, scaleX, scaleY`, then each vertex's `x, y,
11
+ * ix, iy, ox, oy, r` (its handles are offsets from it); the caller sets what
12
+ * differs from `rig.base`, the setup. After the vertices come each IK
13
+ * constraint's `strength, x, y`.
14
+ *
15
+ * The bones an IK constraint bends are solved against its target once every
16
+ * bone is posed (`skin.ik.js`), and the bones below the chain follow it.
17
+ */
18
+
19
+ import { LOCALS, MAX_CHAIN, PARENT, solve, WORLDS } from './skin.ik.js'
20
+
21
+ /**
22
+ * @typedef {number[]} Matrix `[a, b, c, d, tx, ty]`, Rive's `Mat2D` order (the x axis is `a, b`)
23
+ * @typedef {{ rid?: number, parent: number, x?: number, y?: number, rotation?: number, scaleX?: number, scaleY?: number, length?: number, parentWorld?: Matrix, node?: number, binds?: Record<string, any> }} Bone
24
+ * @typedef {{ parent: number, x?: number, y?: number, rotation?: number, scaleX?: number, scaleY?: number, parentWorld?: Matrix, binds?: Record<string, any> }} Node
25
+ * @typedef {{ tip: number, count: number, invert?: boolean, strength?: number, target: number[], space?: Matrix, binds?: Record<string, any> }} Ik an IK constraint over `count` bones ending at `tip`, aimed at `target` (x, y in `space`)
26
+ * @typedef {{ v: number[], i?: number[], o?: number[], r?: number, w?: number[][], iw?: number[][], ow?: number[][] }} Vertex
27
+ * @typedef {{ o: string, i: number, c: string, k: number[], s?: number[] }} Track
28
+ * @typedef {{ sid: string, closed?: boolean, space?: Matrix, spaceNode?: number, bones: Bone[], nodes?: Node[], parentWorld?: Matrix, skin?: Matrix, tendons: { bone: number, bind: Matrix }[], vertices: Vertex[], ik?: Ik[], keys?: Record<string, Track[]> }} Skin
29
+ * @typedef {{ v: number[][], i: number[][], o: number[][], c: boolean }} LottiePath
30
+ *
31
+ * @typedef {object} Rig
32
+ * @property {Skin} skin
33
+ * @property {Float64Array} base the setup values
34
+ * @property {Float64Array} now the values to deform with
35
+ * @property {number} nodeAt where the nodes' values start in `now`
36
+ * @property {number} vertexAt where the vertices' values start
37
+ * @property {number} ikAt where the IK constraints' values start
38
+ * @property {Float64Array} locals scratch: each bone's local matrix
39
+ * @property {Uint8Array} moved scratch: whether a bone's world was solved, or follows one that was
40
+ * @property {LottiePath} path what `deformRig` writes
41
+ * @property {Float64Array} worlds scratch: the matrices of the nodes, then the bones
42
+ * @property {Float64Array} tendons scratch: the tendons' matrices
43
+ * @property {Float64Array} local scratch: a local transform
44
+ * @property {Float64Array} space scratch: the space
45
+ * @property {Float64Array} world scratch: each vertex and its handles, deformed
46
+ * @property {Float64Array} point scratch: a point
47
+ */
48
+
49
+ /** The channels of a bone, a node, a vertex, in the order `rig.now` holds them. */
50
+ export const BONE_CHANNELS = { x: 0, y: 1, rotation: 2, scaleX: 3, scaleY: 4, length: 5 }
51
+ export const NODE_CHANNELS = { x: 0, y: 1, rotation: 2, scaleX: 3, scaleY: 4 }
52
+ export const VERTEX_CHANNELS = { x: 0, y: 1, ix: 2, iy: 3, ox: 4, oy: 5, r: 6 }
53
+ export const IK_CHANNELS = { strength: 0, x: 1, y: 2 }
54
+
55
+ const IDENTITY = [ 1, 0, 0, 1, 0, 0 ]
56
+
57
+ /** @param {number | undefined} value @param {number} fallback */
58
+ const number = (value, fallback) => (typeof value === 'number' ? value : fallback)
59
+
60
+ /**
61
+ * An empty path with a skin's number of points: one per vertex, two for a
62
+ * vertex of a closed path that has a radius.
63
+ *
64
+ * @param {Skin} skin
65
+ * @returns {LottiePath}
66
+ */
67
+ export function createPath(skin) {
68
+ /** @type {LottiePath} */
69
+ const path = { v: [], i: [], o: [], c: !!skin.closed }
70
+ for (let n = 0; n < skin.vertices.length; n++) {
71
+ const count = skin.closed && skin.vertices[n].r !== undefined ? 2 : 1
72
+ for (let k = 0; k < count; k++) {
73
+ path.v.push([ 0, 0 ])
74
+ path.i.push([ 0, 0 ])
75
+ path.o.push([ 0, 0 ])
76
+ }
77
+ }
78
+ return path
79
+ }
80
+
81
+ /**
82
+ * Makes a skin's rig: its setup values and everything a deform works in.
83
+ *
84
+ * @param {Skin} skin
85
+ * @returns {Rig}
86
+ */
87
+ export function createRig(skin) {
88
+ const nodes = skin.nodes || []
89
+ const nodeAt = skin.bones.length * 6
90
+ const vertexAt = nodeAt + nodes.length * 5
91
+ const links = skin.ik || []
92
+ const ikAt = vertexAt + skin.vertices.length * 7
93
+ const base = new Float64Array(ikAt + links.length * 3)
94
+ for (let n = 0; n < skin.bones.length; n++) {
95
+ const bone = skin.bones[n]
96
+ base.set([ number(bone.x, 0), number(bone.y, 0), number(bone.rotation, 0), number(bone.scaleX, 1), number(bone.scaleY, 1), number(bone.length, 0) ], n * 6)
97
+ }
98
+ for (let n = 0; n < nodes.length; n++) {
99
+ const node = nodes[n]
100
+ base.set([ number(node.x, 0), number(node.y, 0), number(node.rotation, 0), number(node.scaleX, 1), number(node.scaleY, 1) ], nodeAt + n * 5)
101
+ }
102
+ for (let n = 0; n < skin.vertices.length; n++) {
103
+ const vertex = skin.vertices[n]
104
+ const i = vertex.i || [ 0, 0 ]
105
+ const o = vertex.o || [ 0, 0 ]
106
+ base.set([ vertex.v[0], vertex.v[1], i[0], i[1], o[0], o[1], number(vertex.r, 0) ], vertexAt + n * 7)
107
+ }
108
+
109
+ for (let n = 0; n < links.length; n++) base.set([ number(links[n].strength, 1), links[n].target[0], links[n].target[1] ], ikAt + n * 3)
110
+ return {
111
+ skin,
112
+ base,
113
+ now: new Float64Array(base),
114
+ nodeAt,
115
+ vertexAt,
116
+ ikAt,
117
+ locals: new Float64Array(skin.bones.length * 6),
118
+ moved: new Uint8Array(skin.bones.length),
119
+ path: createPath(skin),
120
+ worlds: new Float64Array((nodes.length + skin.bones.length) * 6),
121
+ tendons: new Float64Array(skin.tendons.length * 6),
122
+ local: new Float64Array(6),
123
+ space: new Float64Array(6),
124
+ world: new Float64Array(skin.vertices.length * 6),
125
+ point: new Float64Array(2),
126
+ }
127
+ }
128
+
129
+ /**
130
+ * `out = p × q`, writing the six numbers at `o`; `out` may be `p` or `q`.
131
+ *
132
+ * @param {Float64Array} out
133
+ * @param {number} o
134
+ * @param {ArrayLike<number>} p
135
+ * @param {number} po
136
+ * @param {ArrayLike<number>} q
137
+ * @param {number} qo
138
+ */
139
+ function multiply(out, o, p, po, q, qo) {
140
+ const p0 = p[po], p1 = p[po + 1], p2 = p[po + 2], p3 = p[po + 3], p4 = p[po + 4], p5 = p[po + 5]
141
+ const q0 = q[qo], q1 = q[qo + 1], q2 = q[qo + 2], q3 = q[qo + 3], q4 = q[qo + 4], q5 = q[qo + 5]
142
+ out[o] = p0 * q0 + p2 * q1
143
+ out[o + 1] = p1 * q0 + p3 * q1
144
+ out[o + 2] = p0 * q2 + p2 * q3
145
+ out[o + 3] = p1 * q2 + p3 * q3
146
+ out[o + 4] = p0 * q4 + p2 * q5 + p4
147
+ out[o + 5] = p1 * q4 + p3 * q5 + p5
148
+ }
149
+
150
+ /**
151
+ * A local transform: translate, rotate, then scale, as `TransformComponent`
152
+ * composes it.
153
+ *
154
+ * @param {Float64Array} local
155
+ * @param {number} x
156
+ * @param {number} y
157
+ * @param {number} rotation radians
158
+ * @param {number} scaleX
159
+ * @param {number} scaleY
160
+ */
161
+ function compose(local, x, y, rotation, scaleX, scaleY) {
162
+ const cos = Math.cos(rotation)
163
+ const sin = Math.sin(rotation)
164
+ local[0] = cos * scaleX
165
+ local[1] = sin * scaleX
166
+ local[2] = -sin * scaleY
167
+ local[3] = cos * scaleY
168
+ local[4] = x
169
+ local[5] = y
170
+ }
171
+
172
+ /**
173
+ * Every node's and bone's world matrix, into `rig.worlds`: the nodes first,
174
+ * then the bones. A root bone sits at its own x and y in its parent's world
175
+ * (a node's, else its own `parentWorld`, else the skin's); a child bone sits
176
+ * at its parent's tip, `length` along the parent's x axis, whatever x and y it
177
+ * holds (`Bone::x`, `Bone::y`).
178
+ *
179
+ * @param {Rig} rig
180
+ */
181
+ function computeWorlds(rig) {
182
+ const { skin, now, worlds, local } = rig
183
+ const nodes = skin.nodes || []
184
+ const parentWorld = skin.parentWorld || IDENTITY
185
+ for (let n = 0; n < nodes.length; n++) {
186
+ const at = rig.nodeAt + n * 5
187
+ compose(local, now[at], now[at + 1], now[at + 2], now[at + 3], now[at + 4])
188
+ const node = nodes[n]
189
+ if (node.parent < 0) multiply(worlds, n * 6, node.parentWorld || parentWorld, 0, local, 0)
190
+ else multiply(worlds, n * 6, worlds, node.parent * 6, local, 0)
191
+ }
192
+ const first = nodes.length * 6
193
+ for (let n = 0; n < skin.bones.length; n++) {
194
+ const bone = skin.bones[n]
195
+ const at = n * 6
196
+ if (bone.parent < 0) {
197
+ compose(local, now[at], now[at + 1], now[at + 2], now[at + 3], now[at + 4])
198
+ rig.locals.set(local, at)
199
+ if (bone.node !== undefined && bone.node >= 0) multiply(worlds, first + at, worlds, bone.node * 6, local, 0)
200
+ else multiply(worlds, first + at, bone.parentWorld || parentWorld, 0, local, 0)
201
+ } else {
202
+ compose(local, now[bone.parent * 6 + 5], 0, now[at + 2], now[at + 3], now[at + 4])
203
+ rig.locals.set(local, at)
204
+ multiply(worlds, first + at, worlds, first + bone.parent * 6, local, 0)
205
+ }
206
+ }
207
+ if (skin.ik && skin.ik.length) bend(rig, first)
208
+ }
209
+
210
+ /** @type {number[]} The bones of the chain being solved, the top one first */
211
+ const CHAIN = []
212
+
213
+ /**
214
+ * The world a root bone sits in, into `PARENT`.
215
+ *
216
+ * @param {Rig} rig
217
+ * @param {number} index the root bone
218
+ */
219
+ function rootWorld(rig, index) {
220
+ const bone = rig.skin.bones[index]
221
+ if (bone.node !== undefined && bone.node >= 0) {
222
+ for (let m = 0; m < 6; m++) PARENT[m] = rig.worlds[bone.node * 6 + m]
223
+ return
224
+ }
225
+ const world = bone.parentWorld || rig.skin.parentWorld || IDENTITY
226
+ for (let m = 0; m < 6; m++) PARENT[m] = world[m]
227
+ }
228
+
229
+ /**
230
+ * Solves the IK constraints over bones already posed, and moves the bones
231
+ * below each chain with it. A chain longer than the solver holds keeps the
232
+ * pose the bones' own values give it.
233
+ *
234
+ * @param {Rig} rig
235
+ * @param {number} first where the bones' worlds start in `rig.worlds`
236
+ */
237
+ function bend(rig, first) {
238
+ const { skin, now, worlds, locals, moved } = rig
239
+ const bones = skin.bones
240
+ const links = /** @type {Ik[]} */ (skin.ik)
241
+ for (let n = 0; n < bones.length; n++) moved[n] = 0
242
+ for (let k = 0; k < links.length; k++) {
243
+ const link = links[k]
244
+ let length = 1
245
+ let top = link.tip
246
+ while (length < link.count && bones[top].parent >= 0) {
247
+ top = bones[top].parent
248
+ length++
249
+ }
250
+ if (length > MAX_CHAIN) continue
251
+ for (let at = length - 1, bone = link.tip; at >= 0; at--, bone = bones[bone].parent) {
252
+ CHAIN[at] = bone
253
+ for (let m = 0; m < 6; m++) LOCALS[at * 6 + m] = locals[bone * 6 + m]
254
+ }
255
+ if (bones[top].parent >= 0) for (let m = 0; m < 6; m++) PARENT[m] = worlds[first + bones[top].parent * 6 + m]
256
+ else rootWorld(rig, top)
257
+
258
+ const own = rig.ikAt + k * 3
259
+ const space = link.space || IDENTITY
260
+ const x = now[own + 1]
261
+ const y = now[own + 2]
262
+ solve(length, now[link.tip * 6 + 5], space[0] * x + space[2] * y + space[4], space[1] * x + space[3] * y + space[5], !!link.invert, now[own])
263
+ for (let at = 0; at < length; at++) {
264
+ for (let m = 0; m < 6; m++) worlds[first + CHAIN[at] * 6 + m] = WORLDS[(at + 1) * 6 + m]
265
+ moved[CHAIN[at]] = 1
266
+ }
267
+ }
268
+ for (let n = 0; n < bones.length; n++) {
269
+ const parent = bones[n].parent
270
+ if (moved[n] || parent < 0 || !moved[parent]) continue
271
+ multiply(worlds, first + n * 6, worlds, first + parent * 6, locals, n * 6)
272
+ moved[n] = 1
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Where a point goes: the skin transform first, then the weighted sum of its
278
+ * tendons' matrices (`Weight::deform`). The result is `rig.point`.
279
+ *
280
+ * @param {Rig} rig
281
+ * @param {number[][]} weights `[tendonIndex, weight]` pairs
282
+ * @param {number} x
283
+ * @param {number} y
284
+ */
285
+ function place(rig, weights, x, y) {
286
+ const skin = rig.skin.skin || IDENTITY
287
+ const px = skin[0] * x + skin[2] * y + skin[4]
288
+ const py = skin[1] * x + skin[3] * y + skin[5]
289
+ const tendons = rig.tendons
290
+ const count = rig.skin.tendons.length
291
+ let rx = 0
292
+ let ry = 0
293
+ for (let n = 0; n < weights.length; n++) {
294
+ const tendon = weights[n][0]
295
+ const weight = weights[n][1]
296
+ // -1, or an index past the tendons, is Rive's tendon 0: the identity
297
+ if (tendon < 0 || tendon >= count) {
298
+ rx += px * weight
299
+ ry += py * weight
300
+ } else {
301
+ const at = tendon * 6
302
+ rx += (tendons[at] * px + tendons[at + 2] * py + tendons[at + 4]) * weight
303
+ ry += (tendons[at + 1] * px + tendons[at + 3] * py + tendons[at + 5]) * weight
304
+ }
305
+ }
306
+ rig.point[0] = rx
307
+ rig.point[1] = ry
308
+ }
309
+
310
+ /**
311
+ * Natural rounding's control point distance for a corner (`Path::computeIdealControlPointDistance`).
312
+ *
313
+ * @param {number} cross
314
+ * @param {number} dot
315
+ * @param {number} radius
316
+ */
317
+ function idealDistance(cross, dot, radius) {
318
+ const angle = Math.abs(Math.atan2(cross, dot))
319
+ const factor = angle < Math.PI / 2 ? 1 + Math.cos(angle) : 2 - Math.sin(angle)
320
+ return Math.min(radius, (4 / 3) * Math.tan(angle / 4) * radius * factor)
321
+ }
322
+
323
+ /**
324
+ * Deforms the skin with the values in `rig.now`, into a path (`rig.path`
325
+ * unless one made by `createPath` is given): each vertex
326
+ * is moved in world space, the corners of vertices with a radius are rounded
327
+ * there (Rive rounds the vertices' render positions, which the skin has
328
+ * moved), and the space takes the result to the Lottie group.
329
+ *
330
+ * @param {Rig} rig
331
+ * @param {LottiePath} [path]
332
+ * @returns {LottiePath}
333
+ */
334
+ export function deformRig(rig, path = rig.path) {
335
+ const { skin, now, world, space } = rig
336
+ const count = skin.vertices.length
337
+ computeWorlds(rig)
338
+ const first = (skin.nodes || []).length * 6
339
+ for (let n = 0; n < skin.tendons.length; n++) {
340
+ const tendon = skin.tendons[n]
341
+ if (tendon.bone >= 0 && tendon.bone < skin.bones.length) multiply(rig.tendons, n * 6, rig.worlds, first + tendon.bone * 6, tendon.bind || IDENTITY, 0)
342
+ else rig.tendons.set(IDENTITY, n * 6)
343
+ }
344
+
345
+ // the space: the skin's, or the inverse of the live world of the node the group sits in
346
+ if (skin.spaceNode !== undefined && skin.spaceNode >= 0) {
347
+ const at = skin.spaceNode * 6
348
+ const w = rig.worlds
349
+ const det = w[at] * w[at + 3] - w[at + 2] * w[at + 1]
350
+ const inverse = det === 0 ? 0 : 1 / det
351
+ space[0] = w[at + 3] * inverse
352
+ space[1] = -w[at + 1] * inverse
353
+ space[2] = -w[at + 2] * inverse
354
+ space[3] = w[at] * inverse
355
+ space[4] = (w[at + 2] * w[at + 5] - w[at + 3] * w[at + 4]) * inverse
356
+ space[5] = (w[at + 1] * w[at + 4] - w[at] * w[at + 5]) * inverse
357
+ } else {
358
+ space.set(skin.space || IDENTITY)
359
+ }
360
+
361
+ // each vertex, and its handles as offsets from it, in world space
362
+ for (let n = 0; n < count; n++) {
363
+ const vertex = skin.vertices[n]
364
+ const at = rig.vertexAt + n * 7
365
+ const to = n * 6
366
+ const w = vertex.w
367
+ if (!w || !w.length) {
368
+ // a vertex with no weight is drawn where it is, in world space
369
+ for (let k = 0; k < 6; k++) world[to + k] = now[at + k]
370
+ continue
371
+ }
372
+ place(rig, w, now[at], now[at + 1])
373
+ const vx = rig.point[0]
374
+ const vy = rig.point[1]
375
+ world[to] = vx
376
+ world[to + 1] = vy
377
+ // a handle is deformed as the point vertex + offset, with its own weights, and offset again from the deformed vertex
378
+ place(rig, vertex.iw || w, now[at] + now[at + 2], now[at + 1] + now[at + 3])
379
+ world[to + 2] = rig.point[0] - vx
380
+ world[to + 3] = rig.point[1] - vy
381
+ place(rig, vertex.ow || w, now[at] + now[at + 4], now[at + 1] + now[at + 5])
382
+ world[to + 4] = rig.point[0] - vx
383
+ world[to + 5] = rig.point[1] - vy
384
+ }
385
+
386
+ let k = 0
387
+ for (let n = 0; n < count; n++) {
388
+ const to = n * 6
389
+ if (skin.closed && skin.vertices[n].r !== undefined) {
390
+ k = round(rig, path, n, now[rig.vertexAt + n * 7 + 6], k)
391
+ continue
392
+ }
393
+ emit(path, k++, space, world[to], world[to + 1], world[to + 2], world[to + 3], world[to + 4], world[to + 5])
394
+ }
395
+ return path
396
+ }
397
+
398
+ /**
399
+ * Writes a point with its handles (offsets) through the space.
400
+ *
401
+ * @param {LottiePath} path
402
+ * @param {number} k
403
+ * @param {Float64Array} space
404
+ * @param {number} x
405
+ * @param {number} y
406
+ * @param {number} ix
407
+ * @param {number} iy
408
+ * @param {number} ox
409
+ * @param {number} oy
410
+ */
411
+ function emit(path, k, space, x, y, ix, iy, ox, oy) {
412
+ const v = path.v[k]
413
+ const i = path.i[k]
414
+ const o = path.o[k]
415
+ v[0] = space[0] * x + space[2] * y + space[4]
416
+ v[1] = space[1] * x + space[3] * y + space[5]
417
+ i[0] = space[0] * ix + space[2] * iy
418
+ i[1] = space[1] * ix + space[3] * iy
419
+ o[0] = space[0] * ox + space[2] * oy
420
+ o[1] = space[1] * ox + space[3] * oy
421
+ }
422
+
423
+ /**
424
+ * Rounds the corner at vertex `n` into two points, as `Path::buildPath`
425
+ * does: the corner is rounded against the points the neighbouring curves
426
+ * leave from (the previous vertex's out handle, the next vertex's in handle),
427
+ * by at most half of each side. A negative radius turns the fillet inside out.
428
+ * Both points are always written, so a path keeps its number of points
429
+ * whatever the radius is.
430
+ *
431
+ * @param {Rig} rig
432
+ * @param {LottiePath} path
433
+ * @param {number} n
434
+ * @param {number} radius
435
+ * @param {number} k the first point of the output to write
436
+ * @returns {number} the next point
437
+ */
438
+ function round(rig, path, n, radius, k) {
439
+ const { world, space } = rig
440
+ const count = rig.skin.vertices.length
441
+ const pos = n * 6
442
+ const prev = ((n + count - 1) % count) * 6
443
+ const next = ((n + 1) % count) * 6
444
+ const x = world[pos]
445
+ const y = world[pos + 1]
446
+ const prevX = world[prev] + world[prev + 4] - x
447
+ const prevY = world[prev + 1] + world[prev + 5] - y
448
+ const nextX = world[next] + world[next + 2] - x
449
+ const nextY = world[next + 1] + world[next + 3] - y
450
+ const prevLength = Math.sqrt(prevX * prevX + prevY * prevY)
451
+ const nextLength = Math.sqrt(nextX * nextX + nextY * nextY)
452
+ // a side of no length rounds by nothing, as Rive's zero-length normalize leaves it
453
+ const ux = prevLength === 0 ? 0 : prevX / prevLength
454
+ const uy = prevLength === 0 ? 0 : prevY / prevLength
455
+ const vx = nextLength === 0 ? 0 : nextX / nextLength
456
+ const vy = nextLength === 0 ? 0 : nextY / nextLength
457
+ const rendered = Math.min(prevLength / 2, nextLength / 2, Math.abs(radius))
458
+ const ideal = idealDistance(ux * vy - uy * vx, ux * vx + uy * vy, rendered)
459
+ const reach = rendered - ideal
460
+
461
+ const inX = x + ux * rendered
462
+ const inY = y + uy * rendered
463
+ const outX = x + vx * rendered
464
+ const outY = y + vy * rendered
465
+ let ox = x + ux * reach
466
+ let oy = y + uy * reach
467
+ let ix = x + vx * reach
468
+ let iy = y + vy * reach
469
+ if (radius < 0) {
470
+ // rotatePoints: the handles turn twice the corner's angle about the points the fillet starts and ends at
471
+ const angle = Math.atan2((inX - outX) * (y - outY) - (inY - outY) * (x - outX), (inX - outX) * (x - outX) + (inY - outY) * (y - outY))
472
+ const s = Math.sin(angle * 2)
473
+ const c = Math.cos(angle * 2)
474
+ const dx = ox - inX
475
+ const dy = oy - inY
476
+ ox = dx * c - dy * s + inX
477
+ oy = dx * s + dy * c + inY
478
+ const ex = ix - outX
479
+ const ey = iy - outY
480
+ ix = ex * c + ey * s + outX
481
+ iy = -ex * s + ey * c + outY
482
+ }
483
+ emit(path, k, space, inX, inY, 0, 0, ox - inX, oy - inY)
484
+ emit(path, k + 1, space, outX, outY, ix - outX, iy - outY, 0, 0)
485
+ return k + 2
486
+ }
487
+
488
+ /**
489
+ * The skin's path for a set of bone values, as a fresh rig makes it: a
490
+ * convenience for a one-off deform. Each value of `poses` is a bone's `x`,
491
+ * `y`, `rotation`, `scaleX`, `scaleY` or `length`; the setup stays where it
492
+ * is not given. A caller deforming often keeps a rig instead.
493
+ *
494
+ * @param {Skin} skin
495
+ * @param {Array<Record<string, number>>} poses per bone
496
+ * @param {Array<Record<string, number>> | { nodes?: Array<Record<string, number>>, vertices?: Array<Record<string, number>>, ik?: Array<Record<string, number>> }} [more] IK constraint values (`strength`, `x`, `y`), or node, vertex and IK values likewise
497
+ * @returns {LottiePath}
498
+ */
499
+ export function deform(skin, poses, more) {
500
+ const rig = createRig(skin)
501
+ /** @param {Array<Record<string, number>> | undefined} list @param {Record<string, number>} channels @param {number} at @param {number} width */
502
+ const apply = (list, channels, at, width) => {
503
+ if (!list) return
504
+ for (let n = 0; n < list.length; n++) for (const name in list[n]) rig.now[at + n * width + channels[name]] = list[n][name]
505
+ }
506
+ apply(poses, BONE_CHANNELS, 0, 6)
507
+ if (Array.isArray(more)) apply(more, IK_CHANNELS, rig.ikAt, 3)
508
+ else if (more) {
509
+ apply(more.ik, IK_CHANNELS, rig.ikAt, 3)
510
+ apply(more.nodes, NODE_CHANNELS, rig.nodeAt, 5)
511
+ apply(more.vertices, VERTEX_CHANNELS, rig.vertexAt, 7)
512
+ }
513
+ return deformRig(rig)
514
+ }