curvity 0.0.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/dist/my-lib.js ADDED
@@ -0,0 +1,935 @@
1
+ //#region src/graph.ts
2
+ var e = {
3
+ showRuler: !0,
4
+ showYAxis: !0,
5
+ yAxisWidth: 44,
6
+ rulerHeight: 20,
7
+ showSidebar: !0,
8
+ sidebarWidth: 140
9
+ };
10
+ function t(e, t, n = "spline") {
11
+ return {
12
+ time: e,
13
+ value: t,
14
+ inTangent: {
15
+ type: n,
16
+ slope: 0
17
+ },
18
+ outTangent: {
19
+ type: n,
20
+ slope: 0
21
+ }
22
+ };
23
+ }
24
+ var n = { curves: [
25
+ {
26
+ name: "translateX",
27
+ color: "#e06060",
28
+ keyframes: [
29
+ t(0, 0),
30
+ t(.5, 3),
31
+ t(1, 0),
32
+ t(1.5, -3),
33
+ t(2, 0)
34
+ ]
35
+ },
36
+ {
37
+ name: "translateY",
38
+ color: "#60c060",
39
+ keyframes: [
40
+ t(0, 0, "linear"),
41
+ t(.3, 5, "flat"),
42
+ t(.6, 0, "linear"),
43
+ t(.9, 3, "flat"),
44
+ t(1.2, 0, "linear")
45
+ ]
46
+ },
47
+ {
48
+ name: "translateZ",
49
+ color: "#6090e0",
50
+ keyframes: [
51
+ t(0, -2),
52
+ t(1, 2),
53
+ t(2, -2)
54
+ ]
55
+ }
56
+ ] }, r = 40, i = class {
57
+ svg;
58
+ config;
59
+ state;
60
+ constructor(t, r = {}, i = n) {
61
+ if (typeof t == "string") {
62
+ let e = document.querySelector(`#${t}`);
63
+ if (!e) throw Error(`Container not found: '${t}'`);
64
+ this.svg = e;
65
+ } else this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"), t.appendChild(this.svg), this.svg.style.cssText = "width:100%;height:100%;user-select:none;display:block;outline:none", this.svg.setAttribute("tabindex", "0");
66
+ this.config = {
67
+ ...e,
68
+ ...r
69
+ }, this.state = {
70
+ svgWidth: this.svg.clientWidth,
71
+ svgHeight: this.svg.clientHeight,
72
+ timeScale: 200,
73
+ timeOffset: -.1,
74
+ valueScale: 50,
75
+ valueOffset: -6,
76
+ playHead: 0,
77
+ selection: [],
78
+ hiddenCurves: /* @__PURE__ */ new Set(),
79
+ data: i
80
+ }, this._setupEvents(), new ResizeObserver(() => {
81
+ this.state.svgWidth = this.svg.clientWidth, this.state.svgHeight = this.svg.clientHeight, this.redraw();
82
+ }).observe(this.svg), requestAnimationFrame(() => {
83
+ this.state.svgWidth = this.svg.clientWidth, this.state.svgHeight = this.svg.clientHeight, this.autoFit(), this.redraw();
84
+ });
85
+ }
86
+ timeToX(e) {
87
+ return this._chartLeft() + (e - this.state.timeOffset) * this.state.timeScale;
88
+ }
89
+ xToTime(e) {
90
+ return (e - this._chartLeft()) / this.state.timeScale + this.state.timeOffset;
91
+ }
92
+ valueToY(e) {
93
+ return this._chartBottom() - (e - this.state.valueOffset) * this.state.valueScale;
94
+ }
95
+ yToValue(e) {
96
+ return (this._chartBottom() - e) / this.state.valueScale + this.state.valueOffset;
97
+ }
98
+ _chartBottom() {
99
+ return this.state.svgHeight - this.config.rulerHeight;
100
+ }
101
+ _chartLeft() {
102
+ return (this.config.showSidebar ? this.config.sidebarWidth : 0) + (this.config.showYAxis ? this.config.yAxisWidth : 0);
103
+ }
104
+ _chartArea() {
105
+ return {
106
+ left: this._chartLeft(),
107
+ right: this.state.svgWidth,
108
+ top: 0,
109
+ bottom: this._chartBottom()
110
+ };
111
+ }
112
+ _rulerDims() {
113
+ let { rulerHeight: e } = this.config, { svgWidth: t, svgHeight: n } = this.state;
114
+ return {
115
+ top: n - e,
116
+ bottom: n,
117
+ left: this._chartLeft(),
118
+ right: t,
119
+ height: e
120
+ };
121
+ }
122
+ _computeTangent(e, t, n) {
123
+ let r = this.state.data.curves[e].keyframes, i = r[t], a = n === "in" ? i.inTangent : i.outTangent, o = t > 0 ? r[t - 1] : null, s = t < r.length - 1 ? r[t + 1] : null;
124
+ if (a.type === "fixed") {
125
+ let e = n === "out" ? s : o;
126
+ if (!e) return {
127
+ dx: 0,
128
+ dy: 0
129
+ };
130
+ let t = n === "out" ? (e.time - i.time) / 3 : -(i.time - e.time) / 3;
131
+ return {
132
+ dx: t,
133
+ dy: a.slope * t
134
+ };
135
+ }
136
+ if (a.type === "flat") return {
137
+ dx: n === "out" ? s ? (s.time - i.time) / 3 : .3 : o ? -(i.time - o.time) / 3 : -.3,
138
+ dy: 0
139
+ };
140
+ if (a.type === "linear") return n === "out" && s ? {
141
+ dx: (s.time - i.time) / 3,
142
+ dy: (s.value - i.value) / 3
143
+ } : n === "in" && o ? {
144
+ dx: (o.time - i.time) / 3,
145
+ dy: (o.value - i.value) / 3
146
+ } : {
147
+ dx: 0,
148
+ dy: 0
149
+ };
150
+ let c = 0;
151
+ if (o && s ? c = (s.value - o.value) / (s.time - o.time) : s ? c = (s.value - i.value) / (s.time - i.time) : o && (c = (i.value - o.value) / (i.time - o.time)), n === "out" && s) {
152
+ let e = (s.time - i.time) / 3;
153
+ return {
154
+ dx: e,
155
+ dy: c * e
156
+ };
157
+ }
158
+ if (n === "in" && o) {
159
+ let e = -(i.time - o.time) / 3;
160
+ return {
161
+ dx: e,
162
+ dy: c * e
163
+ };
164
+ }
165
+ return {
166
+ dx: 0,
167
+ dy: 0
168
+ };
169
+ }
170
+ _tangentHandlePos(e, t, n) {
171
+ let i = this.state.data.curves[e].keyframes[t], a = this.timeToX(i.time), o = this.valueToY(i.value), s = this._computeTangent(e, t, n), c = s.dx * this.state.timeScale, l = -s.dy * this.state.valueScale, u = Math.hypot(c, l);
172
+ return u < 1e-6 ? {
173
+ x: a + (n === "out" ? r : -r),
174
+ y: o
175
+ } : {
176
+ x: a + c / u * r,
177
+ y: o + l / u * r
178
+ };
179
+ }
180
+ _isKfSelected(e, t) {
181
+ return this.state.selection.some((n) => n.kind === "keyframe" && n.curveIdx === e && n.kfIdx === t);
182
+ }
183
+ _isTangentSelected(e, t, n) {
184
+ return this.state.selection.some((r) => r.kind === "tangent" && r.curveIdx === e && r.kfIdx === t && r.side === n);
185
+ }
186
+ _hasHandleVisible(e, t) {
187
+ return this._isKfSelected(e, t) || this._isTangentSelected(e, t, "in") || this._isTangentSelected(e, t, "out");
188
+ }
189
+ redraw() {
190
+ this.svg.innerHTML = "", this._addClipPath(), this._drawBackground(), this._drawGrid(), this.config.showYAxis && this._drawYAxis(), this._drawCurves(), this._drawKeyframes(), this._drawTangentHandles(), this.config.showRuler && this._drawRuler(), this.state.drag?.type === "marquee" && this._drawMarquee(), this._drawPlayhead(), this.config.showSidebar && this._drawSidebar();
191
+ }
192
+ _addClipPath() {
193
+ let e = this._chartArea(), t = "http://www.w3.org/2000/svg", n = document.createElementNS(t, "defs"), r = document.createElementNS(t, "clipPath");
194
+ r.setAttribute("id", "chart-clip");
195
+ let i = document.createElementNS(t, "rect");
196
+ i.setAttribute("x", String(e.left)), i.setAttribute("y", String(e.top)), i.setAttribute("width", String(e.right - e.left)), i.setAttribute("height", String(e.bottom - e.top)), r.appendChild(i), n.appendChild(r), this.svg.appendChild(n);
197
+ }
198
+ _drawBackground() {
199
+ let { svgWidth: e, svgHeight: t } = this.state;
200
+ this._el("rect", {
201
+ x: 0,
202
+ y: 0,
203
+ width: e,
204
+ height: t,
205
+ fill: "#1c2230"
206
+ });
207
+ }
208
+ _drawGrid() {
209
+ let e = this._chartArea(), t = this.yToValue(e.top), n = this.yToValue(e.bottom), r = this._niceValueStep((t - n) / 8);
210
+ for (let i = Math.ceil(n / r) * r; i <= t + 1e-9; i += r) {
211
+ let t = this.valueToY(i), n = Math.abs(i) < r * .01;
212
+ this._el("line", {
213
+ x1: e.left,
214
+ y1: t,
215
+ x2: e.right,
216
+ y2: t,
217
+ stroke: n ? "#2e4a6e" : "#1e2d40",
218
+ "stroke-width": n ? 1.5 : 1
219
+ });
220
+ }
221
+ let i = this.xToTime(e.right) - this.xToTime(e.left), a = this._niceTimeStep(i / 8);
222
+ for (let t = Math.ceil(this.xToTime(e.left) / a) * a; t <= this.xToTime(e.right) + 1e-9; t += a) {
223
+ let n = this.timeToX(t);
224
+ this._el("line", {
225
+ x1: n,
226
+ y1: e.top,
227
+ x2: n,
228
+ y2: e.bottom,
229
+ stroke: "#1e2d40",
230
+ "stroke-width": 1
231
+ });
232
+ }
233
+ }
234
+ _drawYAxis() {
235
+ let { yAxisWidth: e } = this.config, t = this.config.showSidebar ? this.config.sidebarWidth : 0, n = this._chartArea();
236
+ this._el("rect", {
237
+ x: t,
238
+ y: n.top,
239
+ width: e,
240
+ height: n.bottom - n.top,
241
+ fill: "#141b26"
242
+ });
243
+ let r = this.yToValue(n.top), i = this.yToValue(n.bottom), a = this._niceValueStep((r - i) / 8);
244
+ for (let o = Math.ceil(i / a) * a; o <= r + 1e-9; o += a) {
245
+ let r = this.valueToY(o);
246
+ if (r < n.top + 5 || r > n.bottom - 3) continue;
247
+ this._el("line", {
248
+ x1: t + e - 3,
249
+ y1: r,
250
+ x2: t + e,
251
+ y2: r,
252
+ stroke: "#3a4a60",
253
+ "stroke-width": 1
254
+ });
255
+ let i = Math.abs(o) < a * .001 ? "0" : Math.abs(o) >= 10 ? o.toFixed(0) : o.toPrecision(2).replace(/\.?0+$/, "");
256
+ this._el("text", {
257
+ x: t + e - 5,
258
+ y: r + 3.5,
259
+ fill: "#4a5a70",
260
+ "font-size": 9,
261
+ "text-anchor": "end",
262
+ "font-family": "system-ui,sans-serif"
263
+ }, i);
264
+ }
265
+ this._el("line", {
266
+ x1: t + e,
267
+ y1: n.top,
268
+ x2: t + e,
269
+ y2: n.bottom,
270
+ stroke: "#263040",
271
+ "stroke-width": 1
272
+ });
273
+ }
274
+ _drawSidebar() {
275
+ let { sidebarWidth: e, data: t } = {
276
+ ...this.config,
277
+ data: this.state.data
278
+ }, { svgHeight: n } = this.state;
279
+ this._el("rect", {
280
+ x: 0,
281
+ y: 0,
282
+ width: e,
283
+ height: n,
284
+ fill: "#111720"
285
+ }), this._el("line", {
286
+ x1: e,
287
+ y1: 0,
288
+ x2: e,
289
+ y2: n,
290
+ stroke: "#1e2d40",
291
+ "stroke-width": 1
292
+ }), this._el("text", {
293
+ x: 10,
294
+ y: 16,
295
+ fill: "#3a4a5a",
296
+ "font-size": 9,
297
+ "font-family": "system-ui,sans-serif",
298
+ "font-weight": "600",
299
+ "letter-spacing": "1"
300
+ }, "CURVES"), this._el("line", {
301
+ x1: 0,
302
+ y1: 22,
303
+ x2: e,
304
+ y2: 22,
305
+ stroke: "#1a2535",
306
+ "stroke-width": 1
307
+ });
308
+ for (let n = 0; n < t.curves.length; n++) {
309
+ let r = t.curves[n], i = !this.state.hiddenCurves.has(n), a = this.state.selection.some((e) => e.curveIdx === n), o = 30 + n * 24;
310
+ a && this._el("rect", {
311
+ x: 0,
312
+ y: o - 1,
313
+ width: e,
314
+ height: 24,
315
+ fill: "#1a2840"
316
+ }), this._el("rect", {
317
+ x: 8,
318
+ y: o + 7,
319
+ width: 10,
320
+ height: 10,
321
+ rx: 2,
322
+ fill: i ? r.color : "#2a3040"
323
+ }), this._el("text", {
324
+ x: 24,
325
+ y: o + 16,
326
+ fill: i ? "#b0bcc8" : "#3a4a5a",
327
+ "font-size": 11,
328
+ "font-family": "system-ui,sans-serif"
329
+ }, r.name);
330
+ let s = e - 14, c = o + 12;
331
+ i ? (this._el("ellipse", {
332
+ cx: s,
333
+ cy: c,
334
+ rx: 5,
335
+ ry: 3.5,
336
+ fill: "none",
337
+ stroke: "#4a5a70",
338
+ "stroke-width": 1.2
339
+ }), this._el("circle", {
340
+ cx: s,
341
+ cy: c,
342
+ r: 1.8,
343
+ fill: "#4a5a70"
344
+ })) : (this._el("ellipse", {
345
+ cx: s,
346
+ cy: c,
347
+ rx: 5,
348
+ ry: 3.5,
349
+ fill: "none",
350
+ stroke: "#2a3a4a",
351
+ "stroke-width": 1.2
352
+ }), this._el("line", {
353
+ x1: s - 6,
354
+ y1: c - 4,
355
+ x2: s + 6,
356
+ y2: c + 4,
357
+ stroke: "#2a3a4a",
358
+ "stroke-width": 1.5
359
+ }));
360
+ }
361
+ }
362
+ _drawCurves() {
363
+ let { data: e } = this.state, t = this._chartArea();
364
+ for (let n = 0; n < e.curves.length; n++) {
365
+ if (this.state.hiddenCurves.has(n)) continue;
366
+ let r = e.curves[n], i = r.keyframes;
367
+ if (i.length === 0) continue;
368
+ let a = this.state.selection.some((e) => e.curveIdx === n);
369
+ if (i.length === 1) {
370
+ let e = this.valueToY(i[0].value);
371
+ this._el("line", {
372
+ x1: t.left,
373
+ y1: e,
374
+ x2: t.right,
375
+ y2: e,
376
+ stroke: r.color,
377
+ "stroke-width": a ? 2 : 1.5,
378
+ opacity: .7,
379
+ "clip-path": "url(#chart-clip)"
380
+ });
381
+ continue;
382
+ }
383
+ let o = "";
384
+ for (let e = 0; e < i.length - 1; e++) {
385
+ let t = i[e], r = i[e + 1], a = this.timeToX(t.time), s = this.valueToY(t.value), c = this.timeToX(r.time), l = this.valueToY(r.value);
386
+ if (e === 0 && (o += `M ${a} ${s} `), t.outTangent.type === "stepped") o += `H ${c} V ${l} `;
387
+ else {
388
+ let i = this._computeTangent(n, e, "out"), a = this._computeTangent(n, e + 1, "in"), s = this.timeToX(t.time + i.dx), u = this.valueToY(t.value + i.dy), d = this.timeToX(r.time + a.dx), f = this.valueToY(r.value + a.dy);
389
+ o += `C ${s} ${u} ${d} ${f} ${c} ${l} `;
390
+ }
391
+ }
392
+ this._el("path", {
393
+ d: o,
394
+ stroke: r.color,
395
+ "stroke-width": a ? 2 : 1.5,
396
+ fill: "none",
397
+ "clip-path": "url(#chart-clip)"
398
+ });
399
+ }
400
+ }
401
+ _drawKeyframes() {
402
+ let { data: e } = this.state, t = this._chartArea(), n = 4.5;
403
+ for (let r = 0; r < e.curves.length; r++) {
404
+ if (this.state.hiddenCurves.has(r)) continue;
405
+ let i = e.curves[r];
406
+ for (let e = 0; e < i.keyframes.length; e++) {
407
+ let a = i.keyframes[e], o = this.timeToX(a.time), s = this.valueToY(a.value);
408
+ if (o < t.left - n || o > t.right + n || s < t.top - n || s > t.bottom + n) continue;
409
+ let c = this._isKfSelected(r, e), l = this.state.hover?.type === "keyframe" && this.state.hover.curveIdx === r && this.state.hover.kfIdx === e, u = `${o},${s - n} ${o + n},${s} ${o},${s + n} ${o - n},${s}`;
410
+ c ? this._el("polygon", {
411
+ points: u,
412
+ fill: "#ffe060",
413
+ stroke: "#fffb",
414
+ "stroke-width": .75
415
+ }) : this._el("polygon", {
416
+ points: u,
417
+ fill: l ? i.color : "#1c222e",
418
+ stroke: i.color,
419
+ "stroke-width": 1.5
420
+ });
421
+ }
422
+ }
423
+ }
424
+ _drawTangentHandles() {
425
+ let { data: e } = this.state;
426
+ for (let t = 0; t < e.curves.length; t++) {
427
+ if (this.state.hiddenCurves.has(t)) continue;
428
+ let n = e.curves[t];
429
+ for (let e = 0; e < n.keyframes.length; e++) {
430
+ if (!this._hasHandleVisible(t, e)) continue;
431
+ let r = n.keyframes[e], i = this.timeToX(r.time), a = this.valueToY(r.value);
432
+ for (let o of ["in", "out"]) {
433
+ if (o === "in" && e === 0 || o === "out" && e === n.keyframes.length - 1 || o === "out" && r.outTangent.type === "stepped") continue;
434
+ let s = this._tangentHandlePos(t, e, o), c = this._isTangentSelected(t, e, o), l = this.state.hover?.type === "tangent" && this.state.hover.curveIdx === t && this.state.hover.kfIdx === e && this.state.hover.side === o;
435
+ this._el("line", {
436
+ x1: i,
437
+ y1: a,
438
+ x2: s.x,
439
+ y2: s.y,
440
+ stroke: n.color,
441
+ "stroke-width": 1,
442
+ opacity: .5
443
+ }), this._el("circle", {
444
+ cx: s.x,
445
+ cy: s.y,
446
+ r: 3.5,
447
+ fill: c ? "#ffe060" : l ? "#fff" : n.color,
448
+ stroke: "#fff5",
449
+ "stroke-width": .5
450
+ });
451
+ }
452
+ }
453
+ }
454
+ }
455
+ _drawRuler() {
456
+ let e = this._rulerDims(), t = this._chartArea(), { top: n, left: r, right: i, height: a } = e;
457
+ this._el("rect", {
458
+ x: r,
459
+ y: n,
460
+ width: i - r,
461
+ height: a,
462
+ fill: "#141b26"
463
+ }), this._el("line", {
464
+ x1: r,
465
+ y1: n,
466
+ x2: i,
467
+ y2: n,
468
+ stroke: "#263040",
469
+ "stroke-width": 1
470
+ });
471
+ let o = this.xToTime(t.right) - this.xToTime(t.left), s = this._niceTimeStep(o / 8);
472
+ for (let e = Math.ceil(this.xToTime(t.left) / s) * s; e <= this.xToTime(i) + 1e-9; e += s) {
473
+ let t = this.timeToX(e);
474
+ t < r || (this._el("line", {
475
+ x1: t,
476
+ y1: n,
477
+ x2: t,
478
+ y2: n + 4,
479
+ stroke: "#3a4a60",
480
+ "stroke-width": 1
481
+ }), this._el("text", {
482
+ x: t + 2,
483
+ y: n + 13,
484
+ fill: "#4a5a70",
485
+ "font-size": 9,
486
+ "font-family": "system-ui,sans-serif"
487
+ }, this._formatTime(e, s)));
488
+ }
489
+ }
490
+ _drawMarquee() {
491
+ let e = this.state.drag;
492
+ if (e?.type !== "marquee") return;
493
+ let t = Math.min(e.x0, e.x1), n = Math.min(e.y0, e.y1), r = Math.abs(e.x1 - e.x0), i = Math.abs(e.y1 - e.y0);
494
+ this._el("rect", {
495
+ x: t,
496
+ y: n,
497
+ width: r,
498
+ height: i,
499
+ fill: "rgba(100,150,255,0.06)",
500
+ stroke: "#6496ff",
501
+ "stroke-width": 1,
502
+ "stroke-dasharray": "3 2"
503
+ });
504
+ }
505
+ _drawPlayhead() {
506
+ let { playHead: e, svgWidth: t } = this.state, n = this._chartArea(), r = this._rulerDims(), i = this.timeToX(e);
507
+ i < this.config.yAxisWidth || i > t || (this._el("line", {
508
+ x1: i,
509
+ y1: n.top,
510
+ x2: i,
511
+ y2: n.bottom,
512
+ stroke: "#50e880",
513
+ "stroke-width": 1,
514
+ opacity: .45
515
+ }), this._el("polygon", {
516
+ points: `${i - 5},${r.top} ${i + 5},${r.top} ${i},${r.top + 7}`,
517
+ fill: "#50e880",
518
+ opacity: .9
519
+ }));
520
+ }
521
+ _setupEvents() {
522
+ this.svg.addEventListener("contextmenu", (e) => e.preventDefault()), this.svg.addEventListener("wheel", (e) => {
523
+ e.preventDefault();
524
+ let { x: t, y: n } = this._svgPoint(e), r = e.deltaY < 0 ? 1.12 : 1 / 1.12;
525
+ if (e.altKey) {
526
+ let e = this.yToValue(n);
527
+ this.state.valueScale = Math.max(2, Math.min(1e4, this.state.valueScale * r)), this.state.valueOffset = e - (this._chartBottom() - n) / this.state.valueScale;
528
+ } else {
529
+ let e = this.xToTime(t);
530
+ this.state.timeScale = Math.max(5, Math.min(2e4, this.state.timeScale * r)), this.state.timeOffset = e - (t - this.config.yAxisWidth) / this.state.timeScale;
531
+ }
532
+ this.redraw();
533
+ }, { passive: !1 }), this.svg.addEventListener("mousedown", (e) => {
534
+ let { x: t, y: n } = this._svgPoint(e), r = this._chartArea(), i = this._rulerDims();
535
+ if (e.altKey && e.button === 2) {
536
+ e.preventDefault(), this.state.drag = {
537
+ type: "zoom",
538
+ startX: t,
539
+ startY: n,
540
+ startTimeScale: this.state.timeScale,
541
+ startValueScale: this.state.valueScale,
542
+ startTimeOffset: this.state.timeOffset,
543
+ startValueOffset: this.state.valueOffset,
544
+ pivotTime: this.xToTime(t),
545
+ pivotValue: this.yToValue(n)
546
+ }, this.svg.style.cursor = "zoom-in";
547
+ return;
548
+ }
549
+ if (e.altKey && (e.button === 0 || e.button === 1) || e.button === 2) {
550
+ e.preventDefault(), this.state.drag = {
551
+ type: "pan",
552
+ startX: t,
553
+ startY: n,
554
+ startTimeOffset: this.state.timeOffset,
555
+ startValueOffset: this.state.valueOffset
556
+ }, this.svg.style.cursor = "grabbing";
557
+ return;
558
+ }
559
+ if (this.config.showSidebar && t >= 0 && t <= this.config.sidebarWidth && e.button === 0) {
560
+ let e = Math.floor((n - 30) / 24);
561
+ e >= 0 && e < this.state.data.curves.length && (this.state.hiddenCurves.has(e) ? this.state.hiddenCurves.delete(e) : this.state.hiddenCurves.add(e), this.redraw());
562
+ return;
563
+ }
564
+ if (this._inside({
565
+ x: t,
566
+ y: n
567
+ }, i)) {
568
+ this.state.drag = { type: "scrubPlayhead" }, this.state.playHead = Math.max(0, this.xToTime(t)), this.redraw();
569
+ return;
570
+ }
571
+ if (e.button === 1 && this._inside({
572
+ x: t,
573
+ y: n
574
+ }, r)) {
575
+ e.preventDefault();
576
+ let r = this.state.selection.filter((e) => e.kind === "keyframe");
577
+ r.length > 0 && (this.state.drag = {
578
+ type: "moveKeyframes",
579
+ startX: t,
580
+ startY: n,
581
+ entries: r.map((e) => ({
582
+ curveIdx: e.curveIdx,
583
+ kfIdx: e.kfIdx,
584
+ kfRef: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],
585
+ origTime: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,
586
+ origValue: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value
587
+ }))
588
+ }, this.redraw());
589
+ return;
590
+ }
591
+ if (!this._inside({
592
+ x: t,
593
+ y: n
594
+ }, r) || e.button !== 0) return;
595
+ let a = this._hitTangent(t, n);
596
+ if (a) {
597
+ let { curveIdx: t, kfIdx: n, side: r } = a, i = this._isKfSelected(t, n);
598
+ if (!e.shiftKey) this.state.selection = [{
599
+ kind: "tangent",
600
+ curveIdx: t,
601
+ kfIdx: n,
602
+ side: r
603
+ }];
604
+ else {
605
+ let e = this._isTangentSelected(t, n, r);
606
+ this.state.selection = e ? this.state.selection.filter((e) => !(e.kind === "tangent" && e.curveIdx === t && e.kfIdx === n && e.side === r)) : [...this.state.selection, {
607
+ kind: "tangent",
608
+ curveIdx: t,
609
+ kfIdx: n,
610
+ side: r
611
+ }];
612
+ }
613
+ this.state.drag = {
614
+ type: "moveTangent",
615
+ curveIdx: t,
616
+ kfIdx: n,
617
+ side: r,
618
+ unified: i
619
+ }, this.redraw();
620
+ return;
621
+ }
622
+ let o = this._hitKeyframe(t, n);
623
+ if (o) {
624
+ let { curveIdx: r, kfIdx: i } = o, a = this._isKfSelected(r, i);
625
+ e.shiftKey ? this.state.selection = a ? this.state.selection.filter((e) => !(e.kind === "keyframe" && e.curveIdx === r && e.kfIdx === i)) : [...this.state.selection, {
626
+ kind: "keyframe",
627
+ curveIdx: r,
628
+ kfIdx: i
629
+ }] : a || (this.state.selection = [{
630
+ kind: "keyframe",
631
+ curveIdx: r,
632
+ kfIdx: i
633
+ }]), this._isKfSelected(r, i) && (this.state.drag = {
634
+ type: "moveKeyframes",
635
+ startX: t,
636
+ startY: n,
637
+ entries: this.state.selection.filter((e) => e.kind === "keyframe").map((e) => ({
638
+ curveIdx: e.curveIdx,
639
+ kfIdx: e.kfIdx,
640
+ kfRef: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],
641
+ origTime: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,
642
+ origValue: this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value
643
+ }))
644
+ }), this.redraw();
645
+ return;
646
+ }
647
+ if (Math.abs(this.timeToX(this.state.playHead) - t) < 6) {
648
+ this.state.drag = { type: "scrubPlayhead" }, this.redraw();
649
+ return;
650
+ }
651
+ e.shiftKey || (this.state.selection = []), this.state.drag = {
652
+ type: "marquee",
653
+ x0: t,
654
+ y0: n,
655
+ x1: t,
656
+ y1: n,
657
+ additive: e.shiftKey
658
+ }, this.redraw();
659
+ }), window.addEventListener("mousemove", (e) => {
660
+ let { x: t, y: n } = this._svgPoint(e), { drag: r } = this.state, i = this._chartArea(), a = this._rulerDims();
661
+ if (!r) {
662
+ let e = this._hitTangent(t, n), r = e ? null : this._hitKeyframe(t, n), i = Math.abs(this.timeToX(this.state.playHead) - t) < 6, o = this._inside({
663
+ x: t,
664
+ y: n
665
+ }, a);
666
+ e ? (this.state.hover = {
667
+ type: "tangent",
668
+ ...e
669
+ }, this.svg.style.cursor = "crosshair") : r ? (this.state.hover = {
670
+ type: "keyframe",
671
+ ...r
672
+ }, this.svg.style.cursor = "pointer") : i || o ? (this.state.hover = { type: "playhead" }, this.svg.style.cursor = "col-resize") : (this.state.hover = void 0, this.svg.style.cursor = "default"), this.redraw();
673
+ return;
674
+ }
675
+ if (r.type === "pan") {
676
+ this.state.timeOffset = r.startTimeOffset - (t - r.startX) / this.state.timeScale, this.state.valueOffset = r.startValueOffset + (n - r.startY) / this.state.valueScale, this.redraw();
677
+ return;
678
+ }
679
+ if (r.type === "zoom") {
680
+ let e = .007, i = Math.exp((t - r.startX) * e), a = Math.exp((n - r.startY) * -e);
681
+ this.state.timeScale = Math.max(5, Math.min(2e4, r.startTimeScale * i)), this.state.valueScale = Math.max(2, Math.min(1e4, r.startValueScale * a)), this.state.timeOffset = r.pivotTime - (r.startX - this.config.yAxisWidth) / this.state.timeScale, this.state.valueOffset = r.pivotValue - (this._chartBottom() - r.startY) / this.state.valueScale, this.redraw();
682
+ return;
683
+ }
684
+ if (r.type === "scrubPlayhead") {
685
+ this.state.playHead = Math.max(0, this.xToTime(t)), this.redraw(), this._firePlayheadChange();
686
+ return;
687
+ }
688
+ if (r.type === "marquee") {
689
+ r.x1 = Math.max(i.left, Math.min(t, i.right)), r.y1 = Math.max(i.top, Math.min(n, i.bottom)), this._applyMarqueeSelection(r), this.redraw();
690
+ return;
691
+ }
692
+ if (r.type === "moveKeyframes") {
693
+ let i = t - r.startX, a = n - r.startY;
694
+ e.shiftKey && !r.axisLock && (Math.abs(i) > 5 || Math.abs(a) > 5) && (r.axisLock = Math.abs(i) >= Math.abs(a) ? "x" : "y"), e.shiftKey || (r.axisLock = void 0);
695
+ let o = r.axisLock === "y" ? 0 : i / this.state.timeScale, s = r.axisLock === "x" ? 0 : -a / this.state.valueScale;
696
+ for (let e of r.entries) e.kfRef.time = e.origTime + o, e.kfRef.value = e.origValue + s;
697
+ let c = new Set(r.entries.map((e) => e.curveIdx));
698
+ for (let e of c) this.state.data.curves[e].keyframes.sort((e, t) => e.time - t.time);
699
+ for (let e of r.entries) {
700
+ let t = this.state.data.curves[e.curveIdx].keyframes.indexOf(e.kfRef);
701
+ t >= 0 && (e.kfIdx = t);
702
+ }
703
+ let l = r.entries.map((e) => ({
704
+ kind: "keyframe",
705
+ curveIdx: e.curveIdx,
706
+ kfIdx: e.kfIdx
707
+ }));
708
+ this.state.selection = [...this.state.selection.filter((e) => e.kind !== "keyframe"), ...l], this.redraw();
709
+ return;
710
+ }
711
+ if (r.type === "moveTangent") {
712
+ let { curveIdx: e, kfIdx: i, side: a, unified: o } = r, s = this.state.data.curves[e].keyframes[i], c = a === "in" ? s.inTangent : s.outTangent, l = this.timeToX(s.time), u = this.valueToY(s.value), d = t - l, f = n - u, p = a === "out" ? Math.max(d, 1) : Math.min(d, -1), m = -(f * this.state.timeScale) / (p * this.state.valueScale);
713
+ if (c.type = "fixed", c.slope = m, o) {
714
+ let e = a === "in" ? s.outTangent : s.inTangent;
715
+ e.type !== "stepped" && (e.type = "fixed", e.slope = m);
716
+ }
717
+ this.redraw();
718
+ return;
719
+ }
720
+ }), window.addEventListener("mouseup", () => {
721
+ this.state.drag = void 0, this.svg.style.cursor = "default", this.redraw();
722
+ }), window.addEventListener("keydown", (e) => {
723
+ let n = e.target;
724
+ if (!(n.tagName === "INPUT" || n.tagName === "TEXTAREA")) switch (e.key) {
725
+ case "a":
726
+ case "A":
727
+ this.autoFit(), this.redraw();
728
+ break;
729
+ case "f":
730
+ case "F":
731
+ this.frameSelection(), this.redraw();
732
+ break;
733
+ case "s":
734
+ case "S": {
735
+ let e = this.state.playHead;
736
+ for (let n = 0; n < this.state.data.curves.length; n++) {
737
+ let r = this.state.data.curves[n];
738
+ if (r.keyframes.some((t) => Math.abs(t.time - e) < 1e-6)) continue;
739
+ let i = this._evalCurveAt(n, e);
740
+ r.keyframes.push(t(e, i)), r.keyframes.sort((e, t) => e.time - t.time);
741
+ }
742
+ this.redraw();
743
+ break;
744
+ }
745
+ case "d":
746
+ case "D": {
747
+ let { data: e, selection: t } = this.state, n = t.filter((e) => e.kind === "keyframe"), r = n.length > 0 ? n.map((t) => e.curves[t.curveIdx].keyframes[t.kfIdx]) : e.curves.flatMap((e) => e.keyframes);
748
+ for (let e of r) e.inTangent = {
749
+ type: "spline",
750
+ slope: 0
751
+ }, e.outTangent = {
752
+ type: "spline",
753
+ slope: 0
754
+ };
755
+ this.redraw();
756
+ break;
757
+ }
758
+ case "Delete":
759
+ case "Backspace":
760
+ this.deleteSelected(), this.redraw();
761
+ break;
762
+ }
763
+ });
764
+ }
765
+ _hitKeyframe(e, t) {
766
+ let { data: n } = this.state, r = null;
767
+ for (let i = 0; i < n.curves.length; i++) for (let a = 0; a < n.curves[i].keyframes.length; a++) {
768
+ let o = n.curves[i].keyframes[a], s = Math.hypot(this.timeToX(o.time) - e, this.valueToY(o.value) - t);
769
+ s < 9 && (!r || s < r.dist) && (r = {
770
+ curveIdx: i,
771
+ kfIdx: a,
772
+ dist: s
773
+ });
774
+ }
775
+ return r ? {
776
+ curveIdx: r.curveIdx,
777
+ kfIdx: r.kfIdx
778
+ } : null;
779
+ }
780
+ _hitTangent(e, t) {
781
+ let { data: n } = this.state;
782
+ for (let r = 0; r < n.curves.length; r++) for (let i = 0; i < n.curves[r].keyframes.length; i++) {
783
+ if (!this._hasHandleVisible(r, i)) continue;
784
+ let a = n.curves[r].keyframes[i];
785
+ for (let o of ["in", "out"]) {
786
+ if (o === "in" && i === 0 || o === "out" && i === n.curves[r].keyframes.length - 1 || o === "out" && a.outTangent.type === "stepped") continue;
787
+ let s = this._tangentHandlePos(r, i, o);
788
+ if (Math.hypot(s.x - e, s.y - t) < 8) return {
789
+ curveIdx: r,
790
+ kfIdx: i,
791
+ side: o
792
+ };
793
+ }
794
+ }
795
+ return null;
796
+ }
797
+ _applyMarqueeSelection(e) {
798
+ let { data: t } = this.state, n = Math.min(e.x0, e.x1), r = Math.max(e.x0, e.x1), i = Math.min(e.y0, e.y1), a = Math.max(e.y0, e.y1), o = [];
799
+ for (let e = 0; e < t.curves.length; e++) for (let s = 0; s < t.curves[e].keyframes.length; s++) {
800
+ let c = t.curves[e].keyframes[s], l = this.timeToX(c.time), u = this.valueToY(c.value);
801
+ l >= n && l <= r && u >= i && u <= a && o.push({
802
+ kind: "keyframe",
803
+ curveIdx: e,
804
+ kfIdx: s
805
+ });
806
+ }
807
+ if (e.additive) {
808
+ let e = this.state.selection.filter((e) => !o.some((t) => t.kind === e.kind && t.curveIdx === e.curveIdx && t.kfIdx === e.kfIdx));
809
+ this.state.selection = [...e, ...o];
810
+ } else this.state.selection = o;
811
+ }
812
+ autoFit() {
813
+ let { data: e } = this.state, t = this._chartArea(), n = Infinity, r = -Infinity, i = Infinity, a = -Infinity;
814
+ for (let t = 0; t < e.curves.length; t++) if (!this.state.hiddenCurves.has(t)) for (let o of e.curves[t].keyframes) n = Math.min(n, o.time), r = Math.max(r, o.time), i = Math.min(i, o.value), a = Math.max(a, o.value);
815
+ if (!isFinite(n)) return;
816
+ let o = (r - n) * .15 || .5, s = (a - i) * .2 || 2;
817
+ this.state.timeScale = (t.right - t.left) / (r - n + 2 * o), this.state.timeOffset = n - o, this.state.valueScale = (t.bottom - t.top) / (a - i + 2 * s), this.state.valueOffset = i - s;
818
+ }
819
+ frameSelection() {
820
+ let { data: e, selection: t } = this.state;
821
+ if (t.length === 0) {
822
+ this.autoFit();
823
+ return;
824
+ }
825
+ let n = this._chartArea(), r = Infinity, i = -Infinity, a = Infinity, o = -Infinity;
826
+ for (let n of t) {
827
+ if (n.kind !== "keyframe") continue;
828
+ let t = e.curves[n.curveIdx].keyframes[n.kfIdx];
829
+ r = Math.min(r, t.time), i = Math.max(i, t.time), a = Math.min(a, t.value), o = Math.max(o, t.value);
830
+ }
831
+ if (!isFinite(r)) return;
832
+ let s = (i - r) * .25 || .5, c = (o - a) * .3 || 2;
833
+ this.state.timeScale = (n.right - n.left) / (i - r + 2 * s), this.state.timeOffset = r - s, this.state.valueScale = (n.bottom - n.top) / (o - a + 2 * c), this.state.valueOffset = a - c;
834
+ }
835
+ deleteSelected() {
836
+ let { data: e } = this.state, t = /* @__PURE__ */ new Map();
837
+ for (let e of this.state.selection) e.kind === "keyframe" && (t.has(e.curveIdx) || t.set(e.curveIdx, []), t.get(e.curveIdx).push(e.kfIdx));
838
+ for (let [n, r] of t) for (let t of r.sort((e, t) => t - e)) e.curves[n].keyframes.splice(t, 1);
839
+ this.state.selection = [];
840
+ }
841
+ _evalCurveAt(e, t) {
842
+ let n = this.state.data.curves[e].keyframes;
843
+ if (n.length === 0) return 0;
844
+ if (t <= n[0].time) return n[0].value;
845
+ if (t >= n[n.length - 1].time) return n[n.length - 1].value;
846
+ let r = 0;
847
+ for (let e = 0; e < n.length - 1; e++) if (t <= n[e + 1].time) {
848
+ r = e;
849
+ break;
850
+ }
851
+ let i = n[r], a = n[r + 1];
852
+ if (i.outTangent.type === "stepped") return i.value;
853
+ let o = this._computeTangent(e, r, "out"), s = this._computeTangent(e, r + 1, "in"), c = i.time, l = i.value, u = a.time, d = a.value, f = c + o.dx, p = l + o.dy, m = u + s.dx, h = d + s.dy, g = 0, _ = 1;
854
+ for (let e = 0; e < 32; e++) {
855
+ let e = (g + _) / 2, n = 1 - e;
856
+ n * n * n * c + 3 * n * n * e * f + 3 * n * e * e * m + e * e * e * u < t ? g = e : _ = e;
857
+ }
858
+ let v = (g + _) / 2, y = 1 - v;
859
+ return y * y * y * l + 3 * y * y * v * p + 3 * y * v * v * h + v * v * v * d;
860
+ }
861
+ totalDuration() {
862
+ let e = 0;
863
+ for (let t of this.state.data.curves) for (let n of t.keyframes) e = Math.max(e, n.time);
864
+ return e || 1;
865
+ }
866
+ getPlayhead() {
867
+ return this.state.playHead;
868
+ }
869
+ setPlayhead(e, t = !0) {
870
+ this.state.playHead = Math.max(0, e), t && this.redraw(), this._firePlayheadChange();
871
+ }
872
+ stepPlayhead(e, t = !0) {
873
+ this.setPlayhead(this.state.playHead + e, t);
874
+ }
875
+ getValuesAt(e) {
876
+ let t = {}, { curves: n } = this.state.data;
877
+ for (let r = 0; r < n.length; r++) t[n[r].name] = this._evalCurveAt(r, e);
878
+ return t;
879
+ }
880
+ getValuesAtPlayhead() {
881
+ return this.getValuesAt(this.state.playHead);
882
+ }
883
+ _onPlayheadChange;
884
+ onPlayheadChange(e) {
885
+ this._onPlayheadChange = e ?? void 0;
886
+ }
887
+ _firePlayheadChange() {
888
+ this._onPlayheadChange?.(this.state.playHead, this.getValuesAtPlayhead());
889
+ }
890
+ _niceTimeStep(e) {
891
+ return [
892
+ .001,
893
+ .002,
894
+ .005,
895
+ .01,
896
+ .02,
897
+ .05,
898
+ .1,
899
+ .2,
900
+ .25,
901
+ .5,
902
+ 1,
903
+ 2,
904
+ 5,
905
+ 10,
906
+ 20,
907
+ 50
908
+ ].find((t) => t >= e) ?? 50;
909
+ }
910
+ _niceValueStep(e) {
911
+ if (e <= 0) return 1;
912
+ let t = 10 ** Math.floor(Math.log10(e)), n = e / t;
913
+ return (n < 1.5 ? 1 : n < 3.5 ? 2 : n < 7.5 ? 5 : 10) * t;
914
+ }
915
+ _formatTime(e, t) {
916
+ return t >= 1 ? `${e.toFixed(0)}s` : t >= .1 ? `${e.toFixed(1)}s` : t >= .01 ? `${e.toFixed(2)}s` : `${e.toFixed(3)}s`;
917
+ }
918
+ _el(e, t, n) {
919
+ let r = document.createElementNS("http://www.w3.org/2000/svg", e);
920
+ for (let [e, n] of Object.entries(t)) r.setAttribute(e, String(n));
921
+ return n != null && (r.textContent = n), this.svg.appendChild(r), r;
922
+ }
923
+ _svgPoint(e) {
924
+ let t = this.svg.getBoundingClientRect();
925
+ return {
926
+ x: e.clientX - t.left,
927
+ y: e.clientY - t.top
928
+ };
929
+ }
930
+ _inside(e, t) {
931
+ return e.x >= t.left && e.x <= t.right && e.y >= t.top && e.y <= t.bottom;
932
+ }
933
+ };
934
+ //#endregion
935
+ export { i as Graph };