@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.
- package/bands/bands.js +3 -1
- package/bundle/bundle.js +56 -0
- package/list/list.js +12 -1
- package/lottie/lottie.layers.js +64 -11
- package/lottie/lottie.paint.js +30 -0
- package/lottie/lottie.paths.js +19 -4
- package/lottie/lottie.props.js +18 -8
- package/lottie/lottie.shapes.js +2 -1
- package/mixer/mixer.js +31 -8
- package/mixer/mixer.mix.js +17 -0
- package/motion/motion.convert.js +32 -1
- package/motion/motion.flow.js +20 -2
- package/motion/motion.machine.js +28 -13
- package/motion/motion.values.js +13 -9
- package/motion.js +196 -20
- package/package.json +10 -16
- package/player/player.js +25 -5
- package/readme.md +78 -3
- package/script/luau/lib/base/base.js +202 -0
- package/script/luau/lib/base/base.table.js +189 -0
- package/script/luau/lib/lib.args.js +152 -0
- package/script/luau/lib/lib.js +23 -0
- package/script/luau/lib/math/math.bit32.js +144 -0
- package/script/luau/lib/math/math.js +288 -0
- package/script/luau/lib/math/math.vector.js +125 -0
- package/script/luau/lib/string/string.format.js +143 -0
- package/script/luau/lib/string/string.js +160 -0
- package/script/luau/lib/string/string.pattern.js +505 -0
- package/script/luau/load/load.js +362 -0
- package/script/luau/load/load.ops.js +79 -0
- package/script/luau/luau.convert.js +259 -0
- package/script/luau/luau.js +132 -0
- package/script/luau/luau.md +111 -0
- package/script/luau/vm/vm.js +167 -0
- package/script/luau/vm/vm.meta.js +401 -0
- package/script/luau/vm/vm.num.js +236 -0
- package/script/luau/vm/vm.run.js +842 -0
- package/script/luau/vm/vm.value.js +406 -0
- package/script/play/play.js +199 -0
- package/script/play/play.objects.js +295 -0
- package/script/play/play.scope.js +91 -0
- package/script/rive/api/api.statics.js +31 -0
- package/script/rive/api/api.type.js +188 -0
- package/script/rive/api/artboard/artboard.js +89 -0
- package/script/rive/api/assets/assets.js +72 -0
- package/script/rive/api/data/data.value.js +99 -0
- package/script/rive/api/draw/draw.contours.js +163 -0
- package/script/rive/api/draw/draw.measure.js +196 -0
- package/script/rive/api/draw/draw.paint.js +235 -0
- package/script/rive/api/draw/draw.path.js +153 -0
- package/script/rive/api/draw/draw.renderer.js +117 -0
- package/script/rive/api/math/math.color.js +96 -0
- package/script/rive/api/math/math.mat2d.js +180 -0
- package/script/rive/api/math/math.vector.js +121 -0
- package/script/rive/api/media/media.audio.js +113 -0
- package/script/rive/api/media/media.draw.js +73 -0
- package/script/rive/api/media/media.image.js +61 -0
- package/script/rive/api/media/media.mesh.js +50 -0
- package/script/rive/api/pointer/pointer.event.js +64 -0
- package/script/rive/api/pointer/pointer.route.js +69 -0
- package/script/rive/api/text/text.font.js +129 -0
- package/script/rive/api/text/text.js +278 -0
- package/script/rive/api/text/text.layout.js +259 -0
- package/script/rive/context/context.js +76 -0
- package/script/rive/context/context.memory.js +196 -0
- package/script/rive/context/context.viewmodel.js +302 -0
- package/script/rive/context/lists/lists.js +160 -0
- package/script/rive/context/lists/lists.values.js +281 -0
- package/script/rive/host/host.globals.js +44 -0
- package/script/rive/host/host.js +533 -0
- package/script/rive/host/host.node.js +45 -0
- package/script/rive/rive.js +23 -0
- package/script/rive/rive.md +180 -0
- package/script/script.js +11 -0
- package/skin/skin.deform.js +514 -0
- package/skin/skin.ik.js +260 -0
- 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
|
+
}
|