@shopify/klint 0.3.0 → 0.4.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/index.js CHANGED
@@ -1,7 +1,3 @@
1
- import {
2
- __require
3
- } from "./chunk-3RG5ZIWI.js";
4
-
5
1
  // src/Klint.tsx
6
2
  import React, { useRef, useEffect, useState, useCallback } from "react";
7
3
  var DEFAULT_FPS = 60;
@@ -46,33 +42,23 @@ var CONFIG_PROPS = [
46
42
  "__fillRule",
47
43
  "__isPlaying"
48
44
  ];
49
- function useAnimate(contextRef, draw, isVisible, enablePerformanceTracking = false) {
45
+ function useAnimate(contextRef, draw, isVisible) {
50
46
  const animationFrameId = useRef(0);
51
- const frameTimeHistoryRef = useRef([]);
52
- const frameStartTimeRef = useRef(0);
53
- const frameCountRef = useRef(0);
54
- const lastFpsUpdateRef = useRef(0);
55
- const droppedFramesRef = useRef(0);
56
47
  const animate = useCallback(
57
48
  (timestamp = 0) => {
58
49
  if (!contextRef.current || !isVisible) return;
59
50
  if (!contextRef.current.__isReadyToDraw) return;
60
- if (!contextRef.current.__isPlaying) {
61
- return;
62
- }
51
+ if (!contextRef.current.__isPlaying) return;
63
52
  const context = contextRef.current;
64
53
  const now = timestamp;
65
54
  const target = 1e3 / context.fps;
66
55
  if (!context.__lastTargetTime) {
67
56
  context.__lastTargetTime = now;
68
57
  context.__lastRealTime = now;
69
- frameStartTimeRef.current = now;
70
- lastFpsUpdateRef.current = now;
71
58
  }
72
59
  const sinceLast = now - context.__lastTargetTime;
73
60
  const epsilon = 5;
74
61
  if (sinceLast >= target - epsilon) {
75
- const frameStart = enablePerformanceTracking && context.__performance ? performance.now() : 0;
76
62
  context.deltaTime = now - context.__lastRealTime;
77
63
  draw(context);
78
64
  if (context.time > 1e7) context.time = 0;
@@ -81,40 +67,10 @@ function useAnimate(contextRef, draw, isVisible, enablePerformanceTracking = fal
81
67
  context.frame++;
82
68
  context.__lastTargetTime = now;
83
69
  context.__lastRealTime = now;
84
- if (enablePerformanceTracking && context.__performance) {
85
- const frameTime = performance.now() - frameStart;
86
- const targetFrameTime = 1e3 / context.fps;
87
- frameTimeHistoryRef.current.push(frameTime);
88
- if (frameTimeHistoryRef.current.length > 60) {
89
- frameTimeHistoryRef.current.shift();
90
- }
91
- const avgFrameTime = frameTimeHistoryRef.current.reduce((a, b) => a + b, 0) / frameTimeHistoryRef.current.length;
92
- context.__performance.frameTime = frameTime;
93
- context.__performance.averageFrameTime = avgFrameTime;
94
- context.__performance.minFrameTime = Math.min(
95
- ...frameTimeHistoryRef.current
96
- );
97
- context.__performance.maxFrameTime = Math.max(
98
- ...frameTimeHistoryRef.current
99
- );
100
- if (frameTime > targetFrameTime * 1.1) {
101
- droppedFramesRef.current++;
102
- }
103
- context.__performance.droppedFrames = droppedFramesRef.current;
104
- frameCountRef.current++;
105
- if (now - lastFpsUpdateRef.current >= 1e3) {
106
- context.__performance.fps = frameCountRef.current;
107
- frameCountRef.current = 0;
108
- lastFpsUpdateRef.current = now;
109
- if (typeof performance !== "undefined" && performance.memory) {
110
- context.__performance.memoryUsage = performance.memory.usedJSHeapSize / 1048576;
111
- }
112
- }
113
- }
114
70
  }
115
71
  animationFrameId.current = requestAnimationFrame(animate);
116
72
  },
117
- [draw, isVisible, contextRef, enablePerformanceTracking]
73
+ [draw, isVisible, contextRef]
118
74
  );
119
75
  return {
120
76
  animate,
@@ -127,8 +83,7 @@ function Klint({
127
83
  draw,
128
84
  options = {},
129
85
  preload,
130
- onVisible,
131
- enablePerformanceTracking = false
86
+ onVisible
132
87
  }) {
133
88
  const canvasRef = useRef(null);
134
89
  const containerRef = useRef(null);
@@ -139,21 +94,26 @@ function Klint({
139
94
  );
140
95
  const [isVisible, setIsVisible] = useState(true);
141
96
  useEffect(() => {
97
+ const resetContext = () => {
98
+ if (contextRef.current) {
99
+ const ctx = contextRef.current;
100
+ ctx.__isPlaying = false;
101
+ ctx.frame = 0;
102
+ ctx.time = 0;
103
+ ctx.__offscreens?.clear();
104
+ ctx.__startedShape = false;
105
+ ctx.__currentShape = null;
106
+ ctx.__startedContour = false;
107
+ ctx.__currentContours = null;
108
+ ctx.__currentContour = null;
109
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
110
+ }
111
+ };
142
112
  if (typeof import.meta !== "undefined" && import.meta.hot) {
143
- import.meta.hot.dispose(() => {
144
- console.log("[Klint] Component unmounting due to HMR");
145
- if (contextRef.current) {
146
- contextRef.current.__isPlaying = false;
147
- }
148
- });
113
+ import.meta.hot.dispose(resetContext);
149
114
  }
150
115
  if (typeof module !== "undefined" && module.hot) {
151
- module.hot.dispose(() => {
152
- console.log("[Klint] Component unmounting due to Webpack HMR");
153
- if (contextRef.current) {
154
- contextRef.current.__isPlaying = false;
155
- }
156
- });
116
+ module.hot.dispose(resetContext);
157
117
  }
158
118
  }, []);
159
119
  const __options = {
@@ -162,12 +122,7 @@ function Klint({
162
122
  };
163
123
  const [toStaticImage, setStaticImage] = useState(null);
164
124
  const initContext = context?.initCoreContext;
165
- const { animate, animationFrameId } = useAnimate(
166
- contextRef,
167
- draw,
168
- isVisible,
169
- enablePerformanceTracking
170
- );
125
+ const { animate, animationFrameId } = useAnimate(contextRef, draw, isVisible);
171
126
  const updateCanvasSize = (shouldRedraw = false) => {
172
127
  if (!containerRef.current || !contextRef.current || !canvasRef.current)
173
128
  return;
@@ -197,16 +152,6 @@ function Klint({
197
152
  const context2 = contextRef.current;
198
153
  if (!context2) return;
199
154
  context2.__dpr = dpr;
200
- if (enablePerformanceTracking) {
201
- context2.__performance = {
202
- fps: 0,
203
- frameTime: 0,
204
- averageFrameTime: 0,
205
- minFrameTime: Infinity,
206
- maxFrameTime: 0,
207
- droppedFrames: 0
208
- };
209
- }
210
155
  if (__options.fps && __options.fps !== context2.fps) {
211
156
  context2.fps = __options.fps;
212
157
  }
@@ -756,6 +701,58 @@ var Easing = class {
756
701
  p = p < 0 ? 0 : p > 1 ? 1 : p;
757
702
  return p * p * (3 - 2 * p);
758
703
  };
704
+ /**
705
+ * Damped spring easing. Physically expressive oscillation.
706
+ * @param val - Progress value (0 to 1)
707
+ * @param tension - Spring tension (0 to 1, default 0.5). Higher = faster oscillation.
708
+ * @param friction - Spring friction (0 to 1, default 0.5). Higher = less damping (more bouncy).
709
+ */
710
+ this.spring = (val, tension = 0.5, friction = 0.5) => {
711
+ const omega = tension * 40;
712
+ const zeta = 1 - friction;
713
+ if (zeta < 1) {
714
+ const omegaD = omega * Math.sqrt(1 - zeta * zeta);
715
+ return 1 - Math.exp(-zeta * omega * val) * (Math.cos(omegaD * val) + zeta * omega / omegaD * Math.sin(omegaD * val));
716
+ }
717
+ return 1 - (1 + omega * val) * Math.exp(-omega * val);
718
+ };
719
+ /**
720
+ * Staircase easing. Quantizes to N discrete steps.
721
+ * @param val - Progress value (0 to 1)
722
+ * @param n - Number of steps (default 4)
723
+ */
724
+ this.steps = (val, n = 4) => {
725
+ if (n <= 0) return val;
726
+ return Math.floor(val * n) / n;
727
+ };
728
+ /**
729
+ * Frame-rate independent exponential smoothing.
730
+ * Use instead of naive `lerp(a, b, 0.1)` which breaks at different FPS.
731
+ * @param current - Current value
732
+ * @param target - Target value
733
+ * @param smoothing - Smoothing factor (higher = faster convergence)
734
+ * @param deltaTime - Time since last frame in seconds
735
+ */
736
+ this.damp = (current, target, smoothing, deltaTime) => {
737
+ return current + (target - current) * (1 - Math.exp(-smoothing * deltaTime));
738
+ };
739
+ /**
740
+ * Quick rise then decay. Great for hit effects, flashes.
741
+ * @param val - Progress value (0 to 1)
742
+ * @param k - Sharpness (default 6). Higher = sharper peak.
743
+ */
744
+ this.impulse = (val, k = 6) => {
745
+ const h = k * val;
746
+ return h * Math.exp(1 - h);
747
+ };
748
+ /**
749
+ * Symmetric arc. Useful for jumps, throw curves.
750
+ * @param val - Progress value (0 to 1)
751
+ * @param k - Steepness (default 1)
752
+ */
753
+ this.parabola = (val, k = 1) => {
754
+ return Math.pow(4 * val * (1 - val), k);
755
+ };
759
756
  this.log = () => {
760
757
  console.log(this);
761
758
  };
@@ -1209,17 +1206,6 @@ var Text = class {
1209
1206
  };
1210
1207
  var Text_default = Text;
1211
1208
 
1212
- // src/elements/Thing.tsx
1213
- var Thing = class {
1214
- constructor(ctx) {
1215
- this.context = ctx;
1216
- }
1217
- log() {
1218
- console.log(this.context);
1219
- }
1220
- };
1221
- var Thing_default = Thing;
1222
-
1223
1209
  // src/elements/Grid.tsx
1224
1210
  var Grid = class {
1225
1211
  /**
@@ -1653,505 +1639,427 @@ var Strip = class {
1653
1639
  var Strip_default = Strip;
1654
1640
 
1655
1641
  // src/elements/Noise.tsx
1656
- var Noise = class {
1657
- /**
1658
- * Creates a new Noise instance
1659
- * @param ctx - The Klint context
1660
- */
1661
- constructor(ctx) {
1662
- this.perm = [];
1663
- this.permMod12 = [];
1664
- this.grad3 = [
1665
- [1, 1, 0],
1666
- [-1, 1, 0],
1667
- [1, -1, 0],
1668
- [-1, -1, 0],
1669
- [1, 0, 1],
1670
- [-1, 0, 1],
1671
- [1, 0, -1],
1672
- [-1, 0, -1],
1673
- [0, 1, 1],
1674
- [0, -1, 1],
1675
- [0, 1, -1],
1676
- [0, -1, -1]
1677
- ];
1678
- this.grad4 = [
1679
- [0, 1, 1, 1],
1680
- [0, 1, 1, -1],
1681
- [0, 1, -1, 1],
1682
- [0, 1, -1, -1],
1683
- [0, -1, 1, 1],
1684
- [0, -1, 1, -1],
1685
- [0, -1, -1, 1],
1686
- [0, -1, -1, -1],
1687
- [1, 0, 1, 1],
1688
- [1, 0, 1, -1],
1689
- [1, 0, -1, 1],
1690
- [1, 0, -1, -1],
1691
- [-1, 0, 1, 1],
1692
- [-1, 0, 1, -1],
1693
- [-1, 0, -1, 1],
1694
- [-1, 0, -1, -1],
1695
- [1, 1, 0, 1],
1696
- [1, 1, 0, -1],
1697
- [1, -1, 0, 1],
1698
- [1, -1, 0, -1],
1699
- [-1, 1, 0, 1],
1700
- [-1, 1, 0, -1],
1701
- [-1, -1, 0, 1],
1702
- [-1, -1, 0, -1],
1703
- [1, 1, 1, 0],
1704
- [1, 1, -1, 0],
1705
- [1, -1, 1, 0],
1706
- [1, -1, -1, 0],
1707
- [-1, 1, 1, 0],
1708
- [-1, 1, -1, 0],
1709
- [-1, -1, 1, 0],
1710
- [-1, -1, -1, 0]
1711
- ];
1712
- this.currentSeed = Math.random();
1713
- this.F2 = 0.5 * (Math.sqrt(3) - 1);
1714
- this.G2 = (3 - Math.sqrt(3)) / 6;
1715
- this.F3 = 1 / 3;
1716
- this.G3 = 1 / 6;
1717
- this.F4 = (Math.sqrt(5) - 1) / 4;
1718
- this.G4 = (5 - Math.sqrt(5)) / 20;
1719
- this.context = ctx;
1720
- this.buildPermutationTable();
1642
+ var _Noise = class _Noise {
1643
+ constructor(_ctx) {
1644
+ this._seed = Math.random();
1645
+ this.p = new Uint8Array(512);
1646
+ this.buildPerm();
1647
+ }
1648
+ rng() {
1649
+ const x = Math.sin(this._seed++) * 1e4;
1650
+ return x - Math.floor(x);
1721
1651
  }
1722
- /**
1723
- * Build permutation table for noise generation
1724
- */
1725
- buildPermutationTable() {
1726
- const p = [];
1727
- for (let i = 0; i < 256; i++) {
1728
- p[i] = i;
1729
- }
1730
- let n = 256;
1731
- while (n > 0) {
1732
- const index = Math.floor(this.random() * n--);
1733
- const temp = p[n];
1734
- p[n] = p[index];
1735
- p[index] = temp;
1736
- }
1737
- this.perm = [];
1738
- this.permMod12 = [];
1739
- for (let i = 0; i < 512; i++) {
1740
- this.perm[i] = p[i & 255];
1741
- this.permMod12[i] = this.perm[i] % 12;
1652
+ buildPerm() {
1653
+ const t = new Uint8Array(256);
1654
+ for (let i = 0; i < 256; i++) t[i] = i;
1655
+ for (let i = 255; i > 0; i--) {
1656
+ const j = this.rng() * (i + 1) | 0;
1657
+ const tmp = t[i];
1658
+ t[i] = t[j];
1659
+ t[j] = tmp;
1742
1660
  }
1661
+ for (let i = 0; i < 512; i++) this.p[i] = t[i & 255];
1743
1662
  }
1744
- /**
1745
- * Seeded random number generator
1746
- */
1747
- random() {
1748
- const x = Math.sin(this.currentSeed++) * 1e4;
1749
- return x - Math.floor(x);
1663
+ /** Set seed for reproducible noise */
1664
+ seed(s) {
1665
+ this._seed = s ?? Math.random() * 1e4;
1666
+ this.buildPerm();
1750
1667
  }
1751
- /**
1752
- * Set seed for noise generation
1753
- * @param seed - Seed value for reproducible noise
1754
- */
1755
- seed(seed) {
1756
- this.currentSeed = seed !== void 0 ? seed : Math.random() * 1e4;
1757
- this.buildPermutationTable();
1758
- }
1759
- /**
1760
- * Fade function for Perlin noise
1761
- */
1762
- fade(t) {
1668
+ // ---- Utilities ----
1669
+ static fade(t) {
1763
1670
  return t * t * t * (t * (t * 6 - 15) + 10);
1764
1671
  }
1765
- /**
1766
- * Linear interpolation
1767
- */
1768
- lerp(t, a, b) {
1672
+ static lerp(t, a, b) {
1769
1673
  return a + t * (b - a);
1770
1674
  }
1675
+ /** Improved Perlin gradient — maps 4-bit hash to one of 12 gradient directions */
1676
+ static grad3(h, x, y, z) {
1677
+ h &= 15;
1678
+ const u = h < 8 ? x : y;
1679
+ const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
1680
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
1681
+ }
1771
1682
  perlin(x, y, z, w) {
1772
- if (y === void 0) {
1773
- const xi = Math.floor(x) & 255;
1774
- const xf = x - Math.floor(x);
1775
- const u = this.fade(xf);
1776
- const a = this.perm[xi];
1777
- const b = this.perm[xi + 1];
1778
- const grad1 = (hash, x2) => (hash & 1) === 0 ? x2 : -x2;
1779
- return this.lerp(u, grad1(a, xf), grad1(b, xf - 1));
1780
- } else if (z === void 0) {
1781
- const xi = Math.floor(x) & 255;
1782
- const yi = Math.floor(y) & 255;
1783
- const xf = x - Math.floor(x);
1784
- const yf = y - Math.floor(y);
1785
- const u = this.fade(xf);
1786
- const v = this.fade(yf);
1787
- const aa = this.perm[this.perm[xi] + yi];
1788
- const ab = this.perm[this.perm[xi] + yi + 1];
1789
- const ba = this.perm[this.perm[xi + 1] + yi];
1790
- const bb = this.perm[this.perm[xi + 1] + yi + 1];
1791
- const grad2 = (hash, x3, y2) => {
1792
- const h = hash & 3;
1793
- const u2 = h < 2 ? x3 : y2;
1794
- const v2 = h < 2 ? y2 : x3;
1795
- return ((h & 1) === 0 ? u2 : -u2) + ((h & 2) === 0 ? v2 : -v2);
1796
- };
1797
- const x1 = this.lerp(u, grad2(aa, xf, yf), grad2(ba, xf - 1, yf));
1798
- const x2 = this.lerp(u, grad2(ab, xf, yf - 1), grad2(bb, xf - 1, yf - 1));
1799
- return this.lerp(v, x1, x2);
1800
- } else if (w === void 0) {
1801
- const xi = Math.floor(x) & 255;
1802
- const yi = Math.floor(y) & 255;
1803
- const zi = Math.floor(z) & 255;
1804
- const xf = x - Math.floor(x);
1805
- const yf = y - Math.floor(y);
1806
- const zf = z - Math.floor(z);
1807
- const u = this.fade(xf);
1808
- const v = this.fade(yf);
1809
- const w2 = this.fade(zf);
1810
- const aaa = this.perm[this.perm[this.perm[xi] + yi] + zi];
1811
- const aba = this.perm[this.perm[this.perm[xi] + yi + 1] + zi];
1812
- const aab = this.perm[this.perm[this.perm[xi] + yi] + zi + 1];
1813
- const abb = this.perm[this.perm[this.perm[xi] + yi + 1] + zi + 1];
1814
- const baa = this.perm[this.perm[this.perm[xi + 1] + yi] + zi];
1815
- const bba = this.perm[this.perm[this.perm[xi + 1] + yi + 1] + zi];
1816
- const bab = this.perm[this.perm[this.perm[xi + 1] + yi] + zi + 1];
1817
- const bbb = this.perm[this.perm[this.perm[xi + 1] + yi + 1] + zi + 1];
1818
- const grad3 = (hash, x2, y2, z2) => {
1819
- const h = hash & 15;
1820
- const u2 = h < 8 ? x2 : y2;
1821
- const v2 = h < 4 ? y2 : h === 12 || h === 14 ? x2 : z2;
1822
- return ((h & 1) === 0 ? u2 : -u2) + ((h & 2) === 0 ? v2 : -v2);
1823
- };
1824
- const x1 = this.lerp(
1825
- w2,
1826
- this.lerp(
1827
- v,
1828
- this.lerp(u, grad3(aaa, xf, yf, zf), grad3(baa, xf - 1, yf, zf)),
1829
- this.lerp(u, grad3(aba, xf, yf - 1, zf), grad3(bba, xf - 1, yf - 1, zf))
1683
+ if (y === void 0) return this.perlin1(x);
1684
+ if (z === void 0) return this.perlin2(x, y);
1685
+ if (w === void 0) return this.perlin3(x, y, z);
1686
+ return this.perlin4(x, y, z, w);
1687
+ }
1688
+ perlin1(x) {
1689
+ const p = this.p;
1690
+ const fx = Math.floor(x);
1691
+ const X = fx & 255;
1692
+ const xf = x - fx;
1693
+ const u = _Noise.fade(xf);
1694
+ const g = (h, d) => (h & 1) === 0 ? d : -d;
1695
+ return _Noise.lerp(u, g(p[X], xf), g(p[X + 1], xf - 1));
1696
+ }
1697
+ perlin2(x, y) {
1698
+ const { fade, lerp } = _Noise;
1699
+ const p = this.p;
1700
+ const fx = Math.floor(x), fy = Math.floor(y);
1701
+ const X = fx & 255, Y = fy & 255;
1702
+ const xf = x - fx, yf = y - fy;
1703
+ const u = fade(xf), v = fade(yf);
1704
+ const g = (h, x2, y2) => {
1705
+ const b = h & 3;
1706
+ return ((b & 1) === 0 ? x2 : -x2) + ((b & 2) === 0 ? y2 : -y2);
1707
+ };
1708
+ const aa = p[p[X] + Y], ab = p[p[X] + Y + 1];
1709
+ const ba = p[p[X + 1] + Y], bb = p[p[X + 1] + Y + 1];
1710
+ return lerp(
1711
+ v,
1712
+ lerp(u, g(aa, xf, yf), g(ba, xf - 1, yf)),
1713
+ lerp(u, g(ab, xf, yf - 1), g(bb, xf - 1, yf - 1))
1714
+ );
1715
+ }
1716
+ perlin3(x, y, z) {
1717
+ const { fade, lerp, grad3 } = _Noise;
1718
+ const p = this.p;
1719
+ const fx = Math.floor(x), fy = Math.floor(y), fz = Math.floor(z);
1720
+ const X = fx & 255, Y = fy & 255, Z = fz & 255;
1721
+ const xf = x - fx, yf = y - fy, zf = z - fz;
1722
+ const u = fade(xf), v = fade(yf), w = fade(zf);
1723
+ const A = p[X] + Y, B = p[X + 1] + Y;
1724
+ const AA = p[A] + Z, AB = p[A + 1] + Z, BA = p[B] + Z, BB = p[B + 1] + Z;
1725
+ return lerp(
1726
+ w,
1727
+ lerp(
1728
+ v,
1729
+ lerp(u, grad3(p[AA], xf, yf, zf), grad3(p[BA], xf - 1, yf, zf)),
1730
+ lerp(u, grad3(p[AB], xf, yf - 1, zf), grad3(p[BB], xf - 1, yf - 1, zf))
1731
+ ),
1732
+ lerp(
1733
+ v,
1734
+ lerp(
1735
+ u,
1736
+ grad3(p[AA + 1], xf, yf, zf - 1),
1737
+ grad3(p[BA + 1], xf - 1, yf, zf - 1)
1830
1738
  ),
1831
- this.lerp(
1832
- v,
1833
- this.lerp(u, grad3(aab, xf, yf, zf - 1), grad3(bab, xf - 1, yf, zf - 1)),
1834
- this.lerp(u, grad3(abb, xf, yf - 1, zf - 1), grad3(bbb, xf - 1, yf - 1, zf - 1))
1739
+ lerp(
1740
+ u,
1741
+ grad3(p[AB + 1], xf, yf - 1, zf - 1),
1742
+ grad3(p[BB + 1], xf - 1, yf - 1, zf - 1)
1835
1743
  )
1836
- );
1837
- return x1;
1838
- } else {
1839
- return this.perlin(x, y, z) * 0.5 + this.perlin(x + w, y + w, z + w) * 0.5;
1840
- }
1744
+ )
1745
+ );
1746
+ }
1747
+ perlin4(x, y, z, w) {
1748
+ const { fade, lerp } = _Noise;
1749
+ const p = this.p;
1750
+ const fx = Math.floor(x), fy = Math.floor(y), fz = Math.floor(z), fw = Math.floor(w);
1751
+ const X = fx & 255, Y = fy & 255, Z = fz & 255, W = fw & 255;
1752
+ const xf = x - fx, yf = y - fy, zf = z - fz, wf = w - fw;
1753
+ const fu = fade(xf), fv = fade(yf), fW = fade(zf), ft = fade(wf);
1754
+ const g = (h2, x2, y2, z2, w2) => {
1755
+ const b = h2 & 31;
1756
+ const u = b < 24 ? x2 : y2;
1757
+ const v = b < 16 ? y2 : z2;
1758
+ const t = b < 8 ? z2 : w2;
1759
+ return ((b & 1) === 0 ? u : -u) + ((b & 2) === 0 ? v : -v) + ((b & 4) === 0 ? t : -t);
1760
+ };
1761
+ const h = (dx, dy, dz, dw) => p[p[p[p[X + dx] + Y + dy] + Z + dz] + W + dw];
1762
+ return lerp(
1763
+ ft,
1764
+ lerp(
1765
+ fW,
1766
+ lerp(
1767
+ fv,
1768
+ lerp(
1769
+ fu,
1770
+ g(h(0, 0, 0, 0), xf, yf, zf, wf),
1771
+ g(h(1, 0, 0, 0), xf - 1, yf, zf, wf)
1772
+ ),
1773
+ lerp(
1774
+ fu,
1775
+ g(h(0, 1, 0, 0), xf, yf - 1, zf, wf),
1776
+ g(h(1, 1, 0, 0), xf - 1, yf - 1, zf, wf)
1777
+ )
1778
+ ),
1779
+ lerp(
1780
+ fv,
1781
+ lerp(
1782
+ fu,
1783
+ g(h(0, 0, 1, 0), xf, yf, zf - 1, wf),
1784
+ g(h(1, 0, 1, 0), xf - 1, yf, zf - 1, wf)
1785
+ ),
1786
+ lerp(
1787
+ fu,
1788
+ g(h(0, 1, 1, 0), xf, yf - 1, zf - 1, wf),
1789
+ g(h(1, 1, 1, 0), xf - 1, yf - 1, zf - 1, wf)
1790
+ )
1791
+ )
1792
+ ),
1793
+ lerp(
1794
+ fW,
1795
+ lerp(
1796
+ fv,
1797
+ lerp(
1798
+ fu,
1799
+ g(h(0, 0, 0, 1), xf, yf, zf, wf - 1),
1800
+ g(h(1, 0, 0, 1), xf - 1, yf, zf, wf - 1)
1801
+ ),
1802
+ lerp(
1803
+ fu,
1804
+ g(h(0, 1, 0, 1), xf, yf - 1, zf, wf - 1),
1805
+ g(h(1, 1, 0, 1), xf - 1, yf - 1, zf, wf - 1)
1806
+ )
1807
+ ),
1808
+ lerp(
1809
+ fv,
1810
+ lerp(
1811
+ fu,
1812
+ g(h(0, 0, 1, 1), xf, yf, zf - 1, wf - 1),
1813
+ g(h(1, 0, 1, 1), xf - 1, yf, zf - 1, wf - 1)
1814
+ ),
1815
+ lerp(
1816
+ fu,
1817
+ g(h(0, 1, 1, 1), xf, yf - 1, zf - 1, wf - 1),
1818
+ g(h(1, 1, 1, 1), xf - 1, yf - 1, zf - 1, wf - 1)
1819
+ )
1820
+ )
1821
+ )
1822
+ );
1841
1823
  }
1842
1824
  simplex(x, y, z, w) {
1843
- if (y === void 0) {
1844
- return this.perlin(x);
1845
- } else if (z === void 0) {
1846
- let n0 = 0, n1 = 0, n2 = 0;
1847
- const s = (x + y) * this.F2;
1848
- const i = Math.floor(x + s);
1849
- const j = Math.floor(y + s);
1850
- const t = (i + j) * this.G2;
1851
- const X0 = i - t;
1852
- const Y0 = j - t;
1853
- const x0 = x - X0;
1854
- const y0 = y - Y0;
1855
- let i1, j1;
1856
- if (x0 > y0) {
1825
+ if (y === void 0) return this.perlin1(x);
1826
+ if (z === void 0) return this.simplex2(x, y);
1827
+ if (w === void 0) return this.simplex3(x, y, z);
1828
+ return this.simplex4(x, y, z, w);
1829
+ }
1830
+ simplex2(x, y) {
1831
+ const { F2, G2, grad3 } = _Noise;
1832
+ const p = this.p;
1833
+ const s = (x + y) * F2;
1834
+ const i = Math.floor(x + s), j = Math.floor(y + s);
1835
+ const t = (i + j) * G2;
1836
+ const x0 = x - (i - t), y0 = y - (j - t);
1837
+ const i1 = x0 > y0 ? 1 : 0, j1 = x0 > y0 ? 0 : 1;
1838
+ const x1 = x0 - i1 + G2, y1 = y0 - j1 + G2;
1839
+ const x2 = x0 - 1 + 2 * G2, y2 = y0 - 1 + 2 * G2;
1840
+ const ii = i & 255, jj = j & 255;
1841
+ const gi0 = p[ii + p[jj]] % 12;
1842
+ const gi1 = p[ii + i1 + p[jj + j1]] % 12;
1843
+ const gi2 = p[ii + 1 + p[jj + 1]] % 12;
1844
+ let n0 = 0, n1 = 0, n2 = 0;
1845
+ let t0 = 0.5 - x0 * x0 - y0 * y0;
1846
+ if (t0 >= 0) {
1847
+ t0 *= t0;
1848
+ n0 = t0 * t0 * grad3(gi0, x0, y0, 0);
1849
+ }
1850
+ let t1 = 0.5 - x1 * x1 - y1 * y1;
1851
+ if (t1 >= 0) {
1852
+ t1 *= t1;
1853
+ n1 = t1 * t1 * grad3(gi1, x1, y1, 0);
1854
+ }
1855
+ let t2 = 0.5 - x2 * x2 - y2 * y2;
1856
+ if (t2 >= 0) {
1857
+ t2 *= t2;
1858
+ n2 = t2 * t2 * grad3(gi2, x2, y2, 0);
1859
+ }
1860
+ return 70 * (n0 + n1 + n2);
1861
+ }
1862
+ simplex3(x, y, z) {
1863
+ const { F3, G3, grad3 } = _Noise;
1864
+ const p = this.p;
1865
+ const s = (x + y + z) * F3;
1866
+ const i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s);
1867
+ const t = (i + j + k) * G3;
1868
+ const x0 = x - (i - t), y0 = y - (j - t), z0 = z - (k - t);
1869
+ let i1, j1, k1;
1870
+ let i2, j2, k2;
1871
+ if (x0 >= y0) {
1872
+ if (y0 >= z0) {
1873
+ i1 = 1;
1874
+ j1 = 0;
1875
+ k1 = 0;
1876
+ i2 = 1;
1877
+ j2 = 1;
1878
+ k2 = 0;
1879
+ } else if (x0 >= z0) {
1857
1880
  i1 = 1;
1858
1881
  j1 = 0;
1882
+ k1 = 0;
1883
+ i2 = 1;
1884
+ j2 = 0;
1885
+ k2 = 1;
1859
1886
  } else {
1860
1887
  i1 = 0;
1861
- j1 = 1;
1862
- }
1863
- const x1 = x0 - i1 + this.G2;
1864
- const y1 = y0 - j1 + this.G2;
1865
- const x2 = x0 - 1 + 2 * this.G2;
1866
- const y2 = y0 - 1 + 2 * this.G2;
1867
- const ii = i & 255;
1868
- const jj = j & 255;
1869
- const gi0 = this.permMod12[ii + this.perm[jj]];
1870
- const gi1 = this.permMod12[ii + i1 + this.perm[jj + j1]];
1871
- const gi2 = this.permMod12[ii + 1 + this.perm[jj + 1]];
1872
- let t0 = 0.5 - x0 * x0 - y0 * y0;
1873
- if (t0 < 0) {
1874
- n0 = 0;
1875
- } else {
1876
- t0 *= t0;
1877
- n0 = t0 * t0 * this.dot2(this.grad3[gi0], x0, y0);
1878
- }
1879
- let t1 = 0.5 - x1 * x1 - y1 * y1;
1880
- if (t1 < 0) {
1881
- n1 = 0;
1882
- } else {
1883
- t1 *= t1;
1884
- n1 = t1 * t1 * this.dot2(this.grad3[gi1], x1, y1);
1885
- }
1886
- let t2 = 0.5 - x2 * x2 - y2 * y2;
1887
- if (t2 < 0) {
1888
- n2 = 0;
1889
- } else {
1890
- t2 *= t2;
1891
- n2 = t2 * t2 * this.dot2(this.grad3[gi2], x2, y2);
1892
- }
1893
- return 70 * (n0 + n1 + n2);
1894
- } else if (w === void 0) {
1895
- let n0 = 0, n1 = 0, n2 = 0, n3 = 0;
1896
- const s = (x + y + z) * this.F3;
1897
- const i = Math.floor(x + s);
1898
- const j = Math.floor(y + s);
1899
- const k = Math.floor(z + s);
1900
- const t = (i + j + k) * this.G3;
1901
- const X0 = i - t;
1902
- const Y0 = j - t;
1903
- const Z0 = k - t;
1904
- const x0 = x - X0;
1905
- const y0 = y - Y0;
1906
- const z0 = z - Z0;
1907
- let i1, j1, k1;
1908
- let i2, j2, k2;
1909
- if (x0 >= y0) {
1910
- if (y0 >= z0) {
1911
- i1 = 1;
1912
- j1 = 0;
1913
- k1 = 0;
1914
- i2 = 1;
1915
- j2 = 1;
1916
- k2 = 0;
1917
- } else if (x0 >= z0) {
1918
- i1 = 1;
1919
- j1 = 0;
1920
- k1 = 0;
1921
- i2 = 1;
1922
- j2 = 0;
1923
- k2 = 1;
1924
- } else {
1925
- i1 = 0;
1926
- j1 = 0;
1927
- k1 = 1;
1928
- i2 = 1;
1929
- j2 = 0;
1930
- k2 = 1;
1931
- }
1932
- } else {
1933
- if (y0 < z0) {
1934
- i1 = 0;
1935
- j1 = 0;
1936
- k1 = 1;
1937
- i2 = 0;
1938
- j2 = 1;
1939
- k2 = 1;
1940
- } else if (x0 < z0) {
1941
- i1 = 0;
1942
- j1 = 1;
1943
- k1 = 0;
1944
- i2 = 0;
1945
- j2 = 1;
1946
- k2 = 1;
1947
- } else {
1948
- i1 = 0;
1949
- j1 = 1;
1950
- k1 = 0;
1951
- i2 = 1;
1952
- j2 = 1;
1953
- k2 = 0;
1954
- }
1955
- }
1956
- const x1 = x0 - i1 + this.G3;
1957
- const y1 = y0 - j1 + this.G3;
1958
- const z1 = z0 - k1 + this.G3;
1959
- const x2 = x0 - i2 + 2 * this.G3;
1960
- const y2 = y0 - j2 + 2 * this.G3;
1961
- const z2 = z0 - k2 + 2 * this.G3;
1962
- const x3 = x0 - 1 + 3 * this.G3;
1963
- const y3 = y0 - 1 + 3 * this.G3;
1964
- const z3 = z0 - 1 + 3 * this.G3;
1965
- const ii = i & 255;
1966
- const jj = j & 255;
1967
- const kk = k & 255;
1968
- const gi0 = this.permMod12[ii + this.perm[jj + this.perm[kk]]];
1969
- const gi1 = this.permMod12[ii + i1 + this.perm[jj + j1 + this.perm[kk + k1]]];
1970
- const gi2 = this.permMod12[ii + i2 + this.perm[jj + j2 + this.perm[kk + k2]]];
1971
- const gi3 = this.permMod12[ii + 1 + this.perm[jj + 1 + this.perm[kk + 1]]];
1972
- let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
1973
- if (t0 < 0) {
1974
- n0 = 0;
1975
- } else {
1976
- t0 *= t0;
1977
- n0 = t0 * t0 * this.dot3(this.grad3[gi0], x0, y0, z0);
1978
- }
1979
- let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
1980
- if (t1 < 0) {
1981
- n1 = 0;
1982
- } else {
1983
- t1 *= t1;
1984
- n1 = t1 * t1 * this.dot3(this.grad3[gi1], x1, y1, z1);
1888
+ j1 = 0;
1889
+ k1 = 1;
1890
+ i2 = 1;
1891
+ j2 = 0;
1892
+ k2 = 1;
1985
1893
  }
1986
- let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
1987
- if (t2 < 0) {
1988
- n2 = 0;
1894
+ } else {
1895
+ if (y0 < z0) {
1896
+ i1 = 0;
1897
+ j1 = 0;
1898
+ k1 = 1;
1899
+ i2 = 0;
1900
+ j2 = 1;
1901
+ k2 = 1;
1902
+ } else if (x0 < z0) {
1903
+ i1 = 0;
1904
+ j1 = 1;
1905
+ k1 = 0;
1906
+ i2 = 0;
1907
+ j2 = 1;
1908
+ k2 = 1;
1989
1909
  } else {
1990
- t2 *= t2;
1991
- n2 = t2 * t2 * this.dot3(this.grad3[gi2], x2, y2, z2);
1910
+ i1 = 0;
1911
+ j1 = 1;
1912
+ k1 = 0;
1913
+ i2 = 1;
1914
+ j2 = 1;
1915
+ k2 = 0;
1992
1916
  }
1993
- let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
1994
- if (t3 < 0) {
1995
- n3 = 0;
1917
+ }
1918
+ const x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;
1919
+ const x2 = x0 - i2 + 2 * G3, y2 = y0 - j2 + 2 * G3, z2 = z0 - k2 + 2 * G3;
1920
+ const x3 = x0 - 1 + 3 * G3, y3 = y0 - 1 + 3 * G3, z3 = z0 - 1 + 3 * G3;
1921
+ const ii = i & 255, jj = j & 255, kk = k & 255;
1922
+ const gi0 = p[ii + p[jj + p[kk]]] % 12;
1923
+ const gi1 = p[ii + i1 + p[jj + j1 + p[kk + k1]]] % 12;
1924
+ const gi2 = p[ii + i2 + p[jj + j2 + p[kk + k2]]] % 12;
1925
+ const gi3 = p[ii + 1 + p[jj + 1 + p[kk + 1]]] % 12;
1926
+ let n = 0;
1927
+ let tc = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
1928
+ if (tc >= 0) {
1929
+ tc *= tc;
1930
+ n += tc * tc * grad3(gi0, x0, y0, z0);
1931
+ }
1932
+ tc = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
1933
+ if (tc >= 0) {
1934
+ tc *= tc;
1935
+ n += tc * tc * grad3(gi1, x1, y1, z1);
1936
+ }
1937
+ tc = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
1938
+ if (tc >= 0) {
1939
+ tc *= tc;
1940
+ n += tc * tc * grad3(gi2, x2, y2, z2);
1941
+ }
1942
+ tc = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
1943
+ if (tc >= 0) {
1944
+ tc *= tc;
1945
+ n += tc * tc * grad3(gi3, x3, y3, z3);
1946
+ }
1947
+ return 32 * n;
1948
+ }
1949
+ simplex4(x, y, z, w) {
1950
+ const { F4, G4 } = _Noise;
1951
+ const p = this.p;
1952
+ const s = (x + y + z + w) * F4;
1953
+ const i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s), l = Math.floor(w + s);
1954
+ const t = (i + j + k + l) * G4;
1955
+ const x0 = x - (i - t), y0 = y - (j - t), z0 = z - (k - t), w0 = w - (l - t);
1956
+ let rx = 0, ry = 0, rz = 0, rw = 0;
1957
+ if (x0 > y0) rx++;
1958
+ else ry++;
1959
+ if (x0 > z0) rx++;
1960
+ else rz++;
1961
+ if (x0 > w0) rx++;
1962
+ else rw++;
1963
+ if (y0 > z0) ry++;
1964
+ else rz++;
1965
+ if (y0 > w0) ry++;
1966
+ else rw++;
1967
+ if (z0 > w0) rz++;
1968
+ else rw++;
1969
+ const i1 = +(rx >= 3), j1 = +(ry >= 3), k1 = +(rz >= 3), l1 = +(rw >= 3);
1970
+ const i2 = +(rx >= 2), j2 = +(ry >= 2), k2 = +(rz >= 2), l2 = +(rw >= 2);
1971
+ const i3 = +(rx >= 1), j3 = +(ry >= 1), k3 = +(rz >= 1), l3 = +(rw >= 1);
1972
+ const x1 = x0 - i1 + G4, y1 = y0 - j1 + G4, z1 = z0 - k1 + G4, w1 = w0 - l1 + G4;
1973
+ const x2 = x0 - i2 + 2 * G4, y2 = y0 - j2 + 2 * G4, z2 = z0 - k2 + 2 * G4, w2 = w0 - l2 + 2 * G4;
1974
+ const x3 = x0 - i3 + 3 * G4, y3 = y0 - j3 + 3 * G4, z3 = z0 - k3 + 3 * G4, w3 = w0 - l3 + 3 * G4;
1975
+ const x4 = x0 - 1 + 4 * G4, y4 = y0 - 1 + 4 * G4, z4 = z0 - 1 + 4 * G4, w4 = w0 - 1 + 4 * G4;
1976
+ const ii = i & 255, jj = j & 255, kk = k & 255, ll = l & 255;
1977
+ const g4 = (gi, x5, y5, z5, w5) => {
1978
+ const g = gi >> 3;
1979
+ const b = gi & 7;
1980
+ let a, c, d;
1981
+ if (g === 0) {
1982
+ a = y5;
1983
+ c = z5;
1984
+ d = w5;
1985
+ } else if (g === 1) {
1986
+ a = x5;
1987
+ c = z5;
1988
+ d = w5;
1989
+ } else if (g === 2) {
1990
+ a = x5;
1991
+ c = y5;
1992
+ d = w5;
1996
1993
  } else {
1997
- t3 *= t3;
1998
- n3 = t3 * t3 * this.dot3(this.grad3[gi3], x3, y3, z3);
1994
+ a = x5;
1995
+ c = y5;
1996
+ d = z5;
1999
1997
  }
2000
- return 32 * (n0 + n1 + n2 + n3);
2001
- } else {
2002
- return this.simplex(x, y, z) * 0.5 + this.simplex(x + w, y + w, z + w) * 0.5;
1998
+ return (b & 4 ? -a : a) + (b & 2 ? -c : c) + (b & 1 ? -d : d);
1999
+ };
2000
+ const gi0 = p[ii + p[jj + p[kk + p[ll]]]] & 31;
2001
+ const gi1 = p[ii + i1 + p[jj + j1 + p[kk + k1 + p[ll + l1]]]] & 31;
2002
+ const gi2 = p[ii + i2 + p[jj + j2 + p[kk + k2 + p[ll + l2]]]] & 31;
2003
+ const gi3 = p[ii + i3 + p[jj + j3 + p[kk + k3 + p[ll + l3]]]] & 31;
2004
+ const gi4 = p[ii + 1 + p[jj + 1 + p[kk + 1 + p[ll + 1]]]] & 31;
2005
+ let n = 0;
2006
+ let tc = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
2007
+ if (tc >= 0) {
2008
+ tc *= tc;
2009
+ n += tc * tc * g4(gi0, x0, y0, z0, w0);
2003
2010
  }
2004
- }
2005
- /**
2006
- * Dot product for 2D
2007
- */
2008
- dot2(g, x, y) {
2009
- return g[0] * x + g[1] * y;
2010
- }
2011
- /**
2012
- * Dot product for 3D
2013
- */
2014
- dot3(g, x, y, z) {
2015
- return g[0] * x + g[1] * y + g[2] * z;
2011
+ tc = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
2012
+ if (tc >= 0) {
2013
+ tc *= tc;
2014
+ n += tc * tc * g4(gi1, x1, y1, z1, w1);
2015
+ }
2016
+ tc = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
2017
+ if (tc >= 0) {
2018
+ tc *= tc;
2019
+ n += tc * tc * g4(gi2, x2, y2, z2, w2);
2020
+ }
2021
+ tc = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
2022
+ if (tc >= 0) {
2023
+ tc *= tc;
2024
+ n += tc * tc * g4(gi3, x3, y3, z3, w3);
2025
+ }
2026
+ tc = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
2027
+ if (tc >= 0) {
2028
+ tc *= tc;
2029
+ n += tc * tc * g4(gi4, x4, y4, z4, w4);
2030
+ }
2031
+ return 27 * n;
2016
2032
  }
2017
2033
  hash(x, y, z, w) {
2018
- let n = 0;
2034
+ let n;
2019
2035
  if (y === void 0) {
2020
- n = Math.sin(x * 12.9898 + this.currentSeed) * 43758.5453;
2036
+ n = Math.sin(x * 12.9898 + this._seed) * 43758.5453;
2021
2037
  } else if (z === void 0) {
2022
- n = Math.sin(x * 12.9898 + y * 78.233 + this.currentSeed) * 43758.5453;
2038
+ n = Math.sin(x * 12.9898 + y * 78.233 + this._seed) * 43758.5453;
2023
2039
  } else if (w === void 0) {
2024
- n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + this.currentSeed) * 43758.5453;
2040
+ n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + this._seed) * 43758.5453;
2025
2041
  } else {
2026
- n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + w * 59.1337 + this.currentSeed) * 43758.5453;
2042
+ n = Math.sin(
2043
+ x * 12.9898 + y * 78.233 + z * 37.719 + w * 53.137 + this._seed
2044
+ ) * 43758.5453;
2027
2045
  }
2028
2046
  return n - Math.floor(n);
2029
2047
  }
2030
- /**
2031
- * Fractal Brownian Motion (fBm) noise
2032
- * Supports options object for amplitude/frequency/lacunarity/gain/octaves.
2033
- */
2034
- fbm(x, y, z, options) {
2035
- let yVal = void 0;
2036
- let zVal = void 0;
2037
- let opts = typeof y === "object" ? y : typeof z === "object" ? z : {};
2038
- if (typeof y === "number") yVal = y;
2039
- if (typeof z === "number") zVal = z;
2040
- if (options) opts = { ...opts, ...options };
2041
- const octaves = opts.octaves ?? 4;
2042
- if (octaves <= 0) return 0;
2043
- let amplitude = opts.amplitude ?? 1;
2044
- let frequency = opts.frequency ?? 1;
2045
- const lacunarity = opts.lacunarity ?? 2;
2046
- const gain = opts.gain ?? 0.5;
2047
- let value = 0;
2048
- let maxValue = 0;
2049
- for (let i = 0; i < octaves; i++) {
2050
- if (yVal === void 0) {
2051
- value += amplitude * this.perlin(x * frequency);
2052
- } else if (zVal === void 0) {
2053
- value += amplitude * this.perlin(x * frequency, yVal * frequency);
2054
- } else {
2055
- value += amplitude * this.perlin(x * frequency, yVal * frequency, zVal * frequency);
2056
- }
2057
- maxValue += amplitude;
2058
- amplitude *= gain;
2059
- frequency *= lacunarity;
2060
- }
2061
- return maxValue === 0 ? 0 : value / maxValue;
2062
- }
2063
- /**
2064
- * Turbulence noise (absolute value of noise)
2065
- * Supports options object for octaves.
2066
- */
2067
- turbulence(x, y, z, options) {
2068
- let yVal = void 0;
2069
- let zVal = void 0;
2070
- let opts = typeof y === "object" ? y : typeof z === "object" ? z : {};
2071
- if (typeof y === "number") yVal = y;
2072
- if (typeof z === "number") zVal = z;
2073
- if (options) opts = { ...opts, ...options };
2074
- const octaves = opts.octaves ?? 4;
2075
- if (octaves <= 0) return 0;
2076
- let value = 0;
2077
- let amplitude = 1;
2078
- let frequency = 1;
2079
- let maxValue = 0;
2080
- for (let i = 0; i < octaves; i++) {
2081
- let noise = 0;
2082
- if (yVal === void 0) {
2083
- noise = this.perlin(x * frequency);
2084
- } else if (zVal === void 0) {
2085
- noise = this.perlin(x * frequency, yVal * frequency);
2086
- } else {
2087
- noise = this.perlin(x * frequency, yVal * frequency, zVal * frequency);
2088
- }
2089
- value += amplitude * Math.abs(noise);
2090
- maxValue += amplitude;
2091
- amplitude *= 0.5;
2092
- frequency *= 2;
2093
- }
2094
- return maxValue === 0 ? 0 : value / maxValue;
2095
- }
2096
- /**
2097
- * Ridged multifractal noise (simple implementation)
2098
- */
2099
- ridge(x, y, options) {
2100
- let yVal = void 0;
2101
- let opts = typeof y === "object" ? y : {};
2102
- if (typeof y === "number") yVal = y;
2103
- if (options) opts = { ...opts, ...options };
2104
- const octaves = opts.octaves ?? 4;
2105
- if (octaves <= 0) return 0;
2106
- let amplitude = opts.amplitude ?? 1;
2107
- let frequency = opts.frequency ?? 1;
2108
- const lacunarity = opts.lacunarity ?? 2;
2109
- const gain = opts.gain ?? 0.5;
2110
- let value = 0;
2111
- let weight = 1;
2112
- let maxValue = 0;
2113
- for (let i = 0; i < octaves; i++) {
2114
- const n = yVal === void 0 ? this.perlin(x * frequency) : this.perlin(x * frequency, yVal * frequency);
2115
- let signal = 1 - Math.abs(n);
2116
- signal *= signal;
2117
- signal *= weight;
2118
- weight = signal * 2;
2119
- weight = Math.min(Math.max(weight, 0), 1);
2120
- value += signal * amplitude;
2121
- maxValue += amplitude;
2122
- amplitude *= gain;
2123
- frequency *= lacunarity;
2124
- }
2125
- return maxValue === 0 ? 0 : value / maxValue;
2126
- }
2127
- /**
2128
- * Cellular / Worley noise (2D simple implementation)
2129
- */
2130
- cellular(x, y, options) {
2131
- let yVal = void 0;
2132
- let opts = typeof y === "object" ? y : {};
2133
- if (typeof y === "number") yVal = y;
2134
- if (options) opts = { ...opts, ...options };
2135
- if (yVal === void 0) yVal = 0;
2136
- const xi = Math.floor(x);
2137
- const yi = Math.floor(yVal);
2138
- const distance = opts.distance ?? "euclidean";
2139
- let minDist = Infinity;
2140
- for (let j = -1; j <= 1; j++) {
2141
- for (let i = -1; i <= 1; i++) {
2142
- const fx = i + this.hash(xi + i, yi + j);
2143
- const fy = j + this.hash(yi + j, xi + i);
2144
- const dx = fx + xi - x;
2145
- const dy = fy + yi - yVal;
2146
- const dist = distance === "manhattan" ? Math.abs(dx) + Math.abs(dy) : Math.sqrt(dx * dx + dy * dy);
2147
- if (dist < minDist) minDist = dist;
2148
- }
2149
- }
2150
- const maxDist = Math.SQRT2;
2151
- const normalized = 1 - Math.min(minDist / maxDist, 1);
2152
- return normalized;
2048
+ // ---- GAUSSIAN RANDOM ----
2049
+ /** Box-Muller transform returns normally distributed random number (seeded) */
2050
+ gaussianRandom(mean = 0, stddev = 1) {
2051
+ const u1 = 1 - this.rng();
2052
+ const u2 = this.rng();
2053
+ return mean + stddev * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
2153
2054
  }
2154
2055
  };
2056
+ _Noise.F2 = (Math.sqrt(3) - 1) / 2;
2057
+ _Noise.G2 = (3 - Math.sqrt(3)) / 6;
2058
+ _Noise.F3 = 1 / 3;
2059
+ _Noise.G3 = 1 / 6;
2060
+ _Noise.F4 = (Math.sqrt(5) - 1) / 4;
2061
+ _Noise.G4 = (5 - Math.sqrt(5)) / 20;
2062
+ var Noise = _Noise;
2155
2063
  var Noise_default = Noise;
2156
2064
 
2157
2065
  // src/elements/Hotspot.tsx
@@ -2248,349 +2156,385 @@ var Hotspot = class {
2248
2156
  };
2249
2157
  var Hotspot_default = Hotspot;
2250
2158
 
2251
- // src/elements/Performance.tsx
2252
- var Performance = class {
2253
- constructor(ctx) {
2254
- this.textMetricsCache = /* @__PURE__ */ new Map();
2255
- this.frameTimeHistory = [];
2256
- this.MAX_HISTORY = 60;
2257
- this.context = ctx;
2159
+ // src/elements/Quadtree.tsx
2160
+ var Rectangle = class {
2161
+ constructor(x, y, w, h) {
2162
+ this.x = x;
2163
+ this.y = y;
2164
+ this.w = w;
2165
+ this.h = h;
2258
2166
  }
2259
- /**
2260
- * Batch multiple canvas operations together for better performance
2261
- */
2262
- batchDraw(drawFn) {
2263
- this.context.save();
2264
- this.context.beginPath();
2265
- try {
2266
- drawFn();
2267
- } finally {
2268
- this.context.restore();
2269
- }
2167
+ contains(point) {
2168
+ return point.x >= this.x - this.w && point.x < this.x + this.w && point.y >= this.y - this.h && point.y < this.y + this.h;
2270
2169
  }
2271
- /**
2272
- * Optimize drawing by using offscreen canvas for static elements
2273
- */
2274
- useOffscreenCache(id, width, height, renderFn) {
2275
- let offscreen = this.context.__offscreens.get(id);
2276
- if (!offscreen || offscreen instanceof HTMLImageElement) {
2277
- offscreen = this.context.createOffscreen(id, width, height);
2278
- if (offscreen && !(offscreen instanceof HTMLImageElement)) {
2279
- renderFn(offscreen);
2280
- }
2281
- }
2282
- if (offscreen) {
2283
- if (offscreen instanceof HTMLImageElement) {
2284
- this.context.image(offscreen, 0, 0);
2285
- } else {
2286
- this.context.image(offscreen.canvas, 0, 0);
2287
- }
2288
- }
2170
+ intersects(range) {
2171
+ return !(range.x - range.w > this.x + this.w || range.x + range.w < this.x - this.w || range.y - range.h > this.y + this.h || range.y + range.h < this.y - this.h);
2289
2172
  }
2290
- /**
2291
- * Throttle expensive operations to run less frequently
2292
- */
2293
- throttleFrame(interval, fn) {
2294
- const cacheKey = `__throttle_${fn.toString().slice(0, 50)}`;
2295
- const cached = this.context[cacheKey];
2296
- if (!cached || this.context.frame % interval === 0) {
2297
- const result = fn();
2298
- this.context[cacheKey] = { value: result, frame: this.context.frame };
2299
- return result;
2300
- }
2301
- return cached.value;
2173
+ intersectsCircle(cx, cy, r) {
2174
+ const dx = Math.abs(cx - this.x);
2175
+ const dy = Math.abs(cy - this.y);
2176
+ if (dx > this.w + r || dy > this.h + r) return false;
2177
+ if (dx <= this.w || dy <= this.h) return true;
2178
+ return (dx - this.w) ** 2 + (dy - this.h) ** 2 <= r * r;
2302
2179
  }
2303
- /**
2304
- * Detect potential memory leaks by tracking object creation
2305
- */
2306
- useLeakDetection() {
2307
- const objectCounts = /* @__PURE__ */ new Map();
2308
- let lastCheckFrame = 0;
2309
- return {
2310
- track: (type) => {
2311
- const count = objectCounts.get(type) || 0;
2312
- objectCounts.set(type, count + 1);
2313
- },
2314
- check: () => {
2315
- if (this.context.frame - lastCheckFrame < 60) return;
2316
- lastCheckFrame = this.context.frame;
2317
- const warnings = [];
2318
- objectCounts.forEach((count, type) => {
2319
- if (count > 1e4) {
2320
- warnings.push(
2321
- `Potential memory leak: ${type} has ${count} instances`
2322
- );
2323
- }
2324
- });
2325
- if (warnings.length > 0) {
2326
- console.warn("[Klint] Memory leak warnings:", warnings);
2327
- }
2328
- objectCounts.clear();
2329
- }
2330
- };
2180
+ };
2181
+ var Quadtree = class _Quadtree {
2182
+ constructor(boundary, capacity = 4, maxDepth = 8, depth = 0) {
2183
+ this.ne = null;
2184
+ this.nw = null;
2185
+ this.se = null;
2186
+ this.sw = null;
2187
+ this.boundary = boundary;
2188
+ this.capacity = capacity;
2189
+ this.maxDepth = maxDepth;
2190
+ this.depth = depth;
2191
+ this.points = [];
2192
+ this.divided = false;
2193
+ }
2194
+ /**
2195
+ * Create a Quadtree from corner-based bounds (top-left + size).
2196
+ * Internally converts to center-based representation.
2197
+ */
2198
+ static create(x, y, width, height, options) {
2199
+ const hw = width / 2;
2200
+ const hh = height / 2;
2201
+ return new _Quadtree(
2202
+ new Rectangle(x + hw, y + hh, hw, hh),
2203
+ options?.capacity ?? 4,
2204
+ options?.maxDepth ?? 8
2205
+ );
2331
2206
  }
2332
- /**
2333
- * Optimize text rendering by caching text measurements
2334
- */
2335
- getCachedTextMetrics(text, font) {
2336
- const cacheKey = `${font}:${text}`;
2337
- if (!this.textMetricsCache.has(cacheKey)) {
2338
- this.context.save();
2339
- this.context.font = font;
2340
- const metrics = this.context.measureText(text);
2341
- this.context.restore();
2342
- this.textMetricsCache.set(cacheKey, metrics);
2343
- if (this.textMetricsCache.size > 1e3) {
2344
- const firstKey = this.textMetricsCache.keys().next().value;
2345
- if (firstKey) this.textMetricsCache.delete(firstKey);
2207
+ insert(point) {
2208
+ if (!this.boundary.contains(point)) return false;
2209
+ if (!this.divided) {
2210
+ if (this.points.length < this.capacity || this.depth >= this.maxDepth) {
2211
+ this.points.push(point);
2212
+ return true;
2346
2213
  }
2214
+ this.subdivide();
2215
+ }
2216
+ return this.ne.insert(point) || this.nw.insert(point) || this.se.insert(point) || this.sw.insert(point);
2217
+ }
2218
+ subdivide() {
2219
+ const { x, y, w, h } = this.boundary;
2220
+ const hw = w / 2;
2221
+ const hh = h / 2;
2222
+ const d = this.depth + 1;
2223
+ this.ne = new _Quadtree(
2224
+ new Rectangle(x + hw, y - hh, hw, hh),
2225
+ this.capacity,
2226
+ this.maxDepth,
2227
+ d
2228
+ );
2229
+ this.nw = new _Quadtree(
2230
+ new Rectangle(x - hw, y - hh, hw, hh),
2231
+ this.capacity,
2232
+ this.maxDepth,
2233
+ d
2234
+ );
2235
+ this.se = new _Quadtree(
2236
+ new Rectangle(x + hw, y + hh, hw, hh),
2237
+ this.capacity,
2238
+ this.maxDepth,
2239
+ d
2240
+ );
2241
+ this.sw = new _Quadtree(
2242
+ new Rectangle(x - hw, y + hh, hw, hh),
2243
+ this.capacity,
2244
+ this.maxDepth,
2245
+ d
2246
+ );
2247
+ this.divided = true;
2248
+ for (const p of this.points) {
2249
+ this.ne.insert(p) || this.nw.insert(p) || this.se.insert(p) || this.sw.insert(p);
2347
2250
  }
2348
- return this.textMetricsCache.get(cacheKey);
2349
- }
2350
- /**
2351
- * Clear all performance caches
2352
- */
2353
- clearCaches() {
2354
- this.textMetricsCache.clear();
2355
- }
2356
- /**
2357
- * Get current performance metrics
2358
- */
2359
- getMetrics() {
2360
- return this.context.__performance || null;
2251
+ this.points = [];
2361
2252
  }
2362
- /**
2363
- * Render performance widget on canvas
2364
- */
2365
- render(options = {}) {
2366
- const metrics = this.getMetrics();
2367
- if (!metrics) return;
2368
- const {
2369
- x = 10,
2370
- y = 10,
2371
- width = 200,
2372
- height = 120,
2373
- backgroundColor = "rgba(0, 0, 0, 0.8)",
2374
- textColor = "#ffffff",
2375
- accentColor = "#4ecdc4",
2376
- showGraph = true,
2377
- graphHeight = 40,
2378
- fontSize = 12,
2379
- showMemory = true
2380
- } = options;
2381
- if (showGraph) {
2382
- this.frameTimeHistory.push(metrics.frameTime);
2383
- if (this.frameTimeHistory.length > this.MAX_HISTORY) {
2384
- this.frameTimeHistory.shift();
2385
- }
2253
+ query(range, found = []) {
2254
+ if (!this.boundary.intersects(range)) return found;
2255
+ for (const p of this.points) {
2256
+ if (range.contains(p)) found.push(p);
2386
2257
  }
2387
- this.context.save();
2388
- this.context.fillStyle = backgroundColor;
2389
- this.context.fillRect(x, y, width, height);
2390
- this.context.strokeStyle = accentColor;
2391
- this.context.lineWidth = 1;
2392
- this.context.strokeRect(x, y, width, height);
2393
- this.context.fillStyle = textColor;
2394
- this.context.font = `${fontSize}px monospace`;
2395
- this.context.textAlign = "left";
2396
- this.context.textBaseline = "top";
2397
- let currentY = y + 8;
2398
- const fpsColor = metrics.fps >= 55 ? "#4ecdc4" : metrics.fps >= 30 ? "#ffd93d" : "#ff6b6b";
2399
- this.context.fillStyle = fpsColor;
2400
- this.context.fillText(`FPS: ${metrics.fps}`, x + 8, currentY);
2401
- currentY += fontSize + 4;
2402
- this.context.fillStyle = textColor;
2403
- this.context.fillText(`Frame: ${metrics.frameTime.toFixed(2)}ms`, x + 8, currentY);
2404
- currentY += fontSize + 4;
2405
- this.context.fillText(`Avg: ${metrics.averageFrameTime.toFixed(2)}ms`, x + 8, currentY);
2406
- currentY += fontSize + 4;
2407
- this.context.fillText(
2408
- `Min: ${metrics.minFrameTime.toFixed(2)}ms / Max: ${metrics.maxFrameTime.toFixed(2)}ms`,
2409
- x + 8,
2410
- currentY
2411
- );
2412
- currentY += fontSize + 4;
2413
- if (metrics.droppedFrames > 0) {
2414
- this.context.fillStyle = "#ff6b6b";
2415
- this.context.fillText(`Dropped: ${metrics.droppedFrames}`, x + 8, currentY);
2416
- currentY += fontSize + 4;
2258
+ if (this.divided) {
2259
+ this.ne.query(range, found);
2260
+ this.nw.query(range, found);
2261
+ this.se.query(range, found);
2262
+ this.sw.query(range, found);
2417
2263
  }
2418
- if (showMemory && metrics.memoryUsage !== void 0) {
2419
- this.context.fillStyle = textColor;
2420
- this.context.fillText(`Memory: ${metrics.memoryUsage.toFixed(2)}MB`, x + 8, currentY);
2421
- currentY += fontSize + 4;
2264
+ return found;
2265
+ }
2266
+ queryRadius(cx, cy, radius, found = []) {
2267
+ if (!this.boundary.intersectsCircle(cx, cy, radius)) return found;
2268
+ const rSq = radius * radius;
2269
+ for (const p of this.points) {
2270
+ if ((p.x - cx) ** 2 + (p.y - cy) ** 2 <= rSq) found.push(p);
2422
2271
  }
2423
- if (showGraph && this.frameTimeHistory.length > 1) {
2424
- const graphX = x + 8;
2425
- const graphY = currentY + 4;
2426
- const graphWidth = width - 16;
2427
- const targetFrameTime = 1e3 / this.context.fps;
2428
- this.context.fillStyle = "rgba(255, 255, 255, 0.1)";
2429
- this.context.fillRect(graphX, graphY, graphWidth, graphHeight);
2430
- const targetY = graphY + graphHeight - targetFrameTime / (targetFrameTime * 2) * graphHeight;
2431
- this.context.strokeStyle = "rgba(255, 255, 255, 0.3)";
2432
- this.context.lineWidth = 1;
2433
- this.context.beginPath();
2434
- this.context.moveTo(graphX, targetY);
2435
- this.context.lineTo(graphX + graphWidth, targetY);
2436
- this.context.stroke();
2437
- this.context.strokeStyle = accentColor;
2438
- this.context.lineWidth = 2;
2439
- this.context.beginPath();
2440
- const maxFrameTime = Math.max(...this.frameTimeHistory, targetFrameTime * 2);
2441
- const stepX = graphWidth / (this.frameTimeHistory.length - 1);
2442
- this.frameTimeHistory.forEach((frameTime, index) => {
2443
- const normalizedTime = Math.min(frameTime / maxFrameTime, 1);
2444
- const pointY = graphY + graphHeight - normalizedTime * graphHeight;
2445
- const pointX = graphX + index * stepX;
2446
- if (index === 0) {
2447
- this.context.moveTo(pointX, pointY);
2448
- } else {
2449
- this.context.lineTo(pointX, pointY);
2450
- }
2451
- });
2452
- this.context.stroke();
2272
+ if (this.divided) {
2273
+ this.ne.queryRadius(cx, cy, radius, found);
2274
+ this.nw.queryRadius(cx, cy, radius, found);
2275
+ this.se.queryRadius(cx, cy, radius, found);
2276
+ this.sw.queryRadius(cx, cy, radius, found);
2453
2277
  }
2454
- this.context.restore();
2278
+ return found;
2455
2279
  }
2456
- /**
2457
- * Alias for render() - shows performance widget
2458
- */
2459
- show(options) {
2460
- this.render(options);
2280
+ clear() {
2281
+ this.points = [];
2282
+ this.divided = false;
2283
+ this.ne = this.nw = this.se = this.sw = null;
2284
+ }
2285
+ get size() {
2286
+ let n = this.points.length;
2287
+ if (this.divided) {
2288
+ n += this.ne.size + this.nw.size + this.se.size + this.sw.size;
2289
+ }
2290
+ return n;
2461
2291
  }
2462
2292
  };
2463
- var Performance_default = Performance;
2293
+ var Quadtree_default = Quadtree;
2464
2294
 
2465
- // src/elements/SSR.tsx
2466
- var SSR = class {
2295
+ // src/elements/Pixels.tsx
2296
+ var Pixels = class {
2467
2297
  constructor(ctx) {
2468
- this.context = ctx || null;
2298
+ this.ctx = ctx;
2469
2299
  }
2470
2300
  /**
2471
- * Render a Klint sketch to a static image (base64 data URL)
2301
+ * Read all pixels from the canvas as ImageData.
2472
2302
  */
2473
- async renderToImage(draw, options) {
2474
- if (typeof window !== "undefined" && typeof document !== "undefined") {
2475
- return this.renderInBrowser(draw, options);
2476
- }
2477
- throw new Error(
2478
- "Server-side rendering requires 'canvas' package. Install it with: npm install canvas\nAlternatively, use SSR.generateImageUrl() to generate images via an API endpoint."
2479
- );
2303
+ load() {
2304
+ return this.ctx.getImageData(0, 0, this.ctx.width, this.ctx.height);
2480
2305
  }
2481
2306
  /**
2482
- * Generate a static image URL for a Klint sketch
2307
+ * Write pixel data back to the canvas.
2308
+ * Accepts a Uint8ClampedArray or a plain number array of RGBA values.
2483
2309
  */
2484
- async generateImageUrl(draw, options) {
2485
- if (typeof window === "undefined") {
2486
- throw new Error(
2487
- "generateImageUrl() requires a browser environment. For server-side rendering, use renderToImage() with the 'canvas' package."
2488
- );
2489
- }
2490
- return this.renderInBrowser(draw, options);
2310
+ update(pixels) {
2311
+ const pixelArray = pixels instanceof Uint8ClampedArray ? pixels : new Uint8ClampedArray(pixels);
2312
+ const imageData = new ImageData(
2313
+ new Uint8ClampedArray(pixelArray.buffer),
2314
+ this.ctx.width,
2315
+ this.ctx.height
2316
+ );
2317
+ this.ctx.putImageData(imageData, 0, 0);
2491
2318
  }
2492
2319
  /**
2493
- * Check if Klint can run in the current environment
2320
+ * Read pixel values at a position.
2321
+ * Returns an array of [r, g, b, a] values (0-255).
2322
+ *
2323
+ * @param x - X coordinate
2324
+ * @param y - Y coordinate
2325
+ * @param w - Width of region (default: 1)
2326
+ * @param h - Height of region (default: 1)
2494
2327
  */
2495
- canRender() {
2496
- if (typeof window !== "undefined" && typeof document !== "undefined") {
2497
- return true;
2498
- }
2499
- try {
2500
- __require("canvas");
2501
- return true;
2502
- } catch {
2503
- return false;
2504
- }
2328
+ read(x, y, w = 1, h = 1) {
2329
+ const imageData = this.ctx.getImageData(x, y, w, h);
2330
+ return Array.from(imageData.data);
2505
2331
  }
2506
- /**
2507
- * Render in browser environment (client-side)
2508
- */
2509
- async renderInBrowser(draw, options) {
2510
- return new Promise((resolve, reject) => {
2332
+ };
2333
+
2334
+ // src/elements/Timeline.tsx
2335
+ var Timeline = class {
2336
+ constructor() {
2337
+ this.callbacks = { start: [], end: [], loop: [] };
2338
+ }
2339
+ onStart(fn) {
2340
+ this.callbacks.start.push(fn);
2341
+ }
2342
+ onEnd(fn) {
2343
+ this.callbacks.end.push(fn);
2344
+ }
2345
+ onLoop(fn) {
2346
+ this.callbacks.loop.push(fn);
2347
+ }
2348
+ create(setup, options = {}) {
2349
+ const tracks = /* @__PURE__ */ new Map();
2350
+ let currentProgress = 0;
2351
+ let hasStarted = false;
2352
+ let hasEnded = false;
2353
+ const callbacks = this.callbacks;
2354
+ const defaultEasing = options.defaultEasing || ((t) => t);
2355
+ const defaultLoop = options.defaultLoop || 0;
2356
+ const executeCallback = (callback, errorMsg = "Callback error") => {
2511
2357
  try {
2512
- const canvas = document.createElement("canvas");
2513
- const dpr = options.dpr || (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
2514
- canvas.width = options.width * dpr;
2515
- canvas.height = options.height * dpr;
2516
- const ctx = canvas.getContext("2d");
2517
- if (!ctx) {
2518
- reject(new Error("Failed to get canvas context"));
2519
- return;
2358
+ callback();
2359
+ } catch (e) {
2360
+ console.warn(`${errorMsg}:`, e);
2361
+ }
2362
+ };
2363
+ function createKeyframes() {
2364
+ const segments = [];
2365
+ let currentPos = 0;
2366
+ let loopCount = defaultLoop;
2367
+ const parentTrack = void 0;
2368
+ const parseEasingCallback = (easing, callback) => {
2369
+ if (typeof easing === "function" && typeof callback === "undefined") {
2370
+ if (easing.length === 0) {
2371
+ return { easing: defaultEasing, callback: easing };
2372
+ }
2373
+ return {
2374
+ easing,
2375
+ callback: void 0
2376
+ };
2377
+ }
2378
+ return {
2379
+ easing: easing || defaultEasing,
2380
+ callback
2381
+ };
2382
+ };
2383
+ const builder = {
2384
+ start(value, delay = 0, callback) {
2385
+ segments.push({ pos: delay, value, callback, type: "start" });
2386
+ currentPos = delay;
2387
+ return builder;
2388
+ },
2389
+ at(progress, value, easing, callback) {
2390
+ const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
2391
+ segments.push({
2392
+ pos: progress,
2393
+ value,
2394
+ easing: finalEasing,
2395
+ callback: finalCallback,
2396
+ type: "tween"
2397
+ });
2398
+ currentPos = progress;
2399
+ return builder;
2400
+ },
2401
+ then(value, duration, easing, callback) {
2402
+ const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
2403
+ const nextPos = currentPos + duration;
2404
+ segments.push({
2405
+ pos: nextPos,
2406
+ value,
2407
+ easing: finalEasing,
2408
+ callback: finalCallback,
2409
+ type: "tween"
2410
+ });
2411
+ currentPos = nextPos;
2412
+ return builder;
2413
+ },
2414
+ loop(count = Infinity) {
2415
+ loopCount = count;
2416
+ return builder;
2417
+ },
2418
+ _compile: () => {
2419
+ segments.sort((a, b) => a.pos - b.pos);
2420
+ return { segments, loopCount, parentTrack };
2421
+ }
2422
+ };
2423
+ return builder;
2424
+ }
2425
+ function interpolateTrack(compiled, progress) {
2426
+ const { segments } = compiled;
2427
+ if (!segments.length) return 0;
2428
+ progress = Math.max(0, Math.min(1, progress));
2429
+ const valueSegments = segments.filter(
2430
+ (seg) => seg.value !== void 0
2431
+ );
2432
+ if (!valueSegments.length) return 0;
2433
+ let prevSeg = valueSegments[0];
2434
+ for (let i = 1; i < valueSegments.length; i++) {
2435
+ const seg = valueSegments[i];
2436
+ if (progress <= seg.pos) {
2437
+ if (seg.type === "tween" && prevSeg.value !== void 0 && seg.value !== void 0) {
2438
+ const t = (progress - prevSeg.pos) / (seg.pos - prevSeg.pos);
2439
+ const easedT = seg.easing ? seg.easing(t) : t;
2440
+ return prevSeg.value + (seg.value - prevSeg.value) * easedT;
2441
+ }
2442
+ return seg.value || 0;
2443
+ }
2444
+ if (seg.value !== void 0) {
2445
+ prevSeg = seg;
2520
2446
  }
2521
- const klintContext = this.createMinimalContext(
2522
- ctx,
2523
- canvas,
2524
- options.width,
2525
- options.height,
2526
- dpr
2527
- );
2528
- draw(klintContext);
2529
- const mimeType = options.format === "jpeg" ? "image/jpeg" : options.format === "webp" ? "image/webp" : "image/png";
2530
- const quality = options.quality !== void 0 ? options.quality : 0.85;
2531
- const dataUrl = canvas.toDataURL(mimeType, quality);
2532
- resolve(dataUrl);
2533
- } catch (error) {
2534
- reject(error);
2535
2447
  }
2536
- });
2537
- }
2538
- /**
2539
- * Create a minimal KlintContext for server rendering
2540
- * This provides basic functionality without full Klint features
2541
- */
2542
- createMinimalContext(ctx, canvas, width, height, dpr) {
2543
- return {
2544
- canvas,
2545
- width: width * dpr,
2546
- height: height * dpr,
2547
- // Basic canvas methods
2548
- fillRect: ctx.fillRect.bind(ctx),
2549
- strokeRect: ctx.strokeRect.bind(ctx),
2550
- clearRect: ctx.clearRect.bind(ctx),
2551
- beginPath: ctx.beginPath.bind(ctx),
2552
- moveTo: ctx.moveTo.bind(ctx),
2553
- lineTo: ctx.lineTo.bind(ctx),
2554
- arc: ctx.arc.bind(ctx),
2555
- fill: ctx.fill.bind(ctx),
2556
- stroke: ctx.stroke.bind(ctx),
2557
- save: ctx.save.bind(ctx),
2558
- restore: ctx.restore.bind(ctx),
2559
- translate: ctx.translate.bind(ctx),
2560
- rotate: ctx.rotate.bind(ctx),
2561
- scale: ctx.scale.bind(ctx),
2562
- // Basic properties
2563
- fillStyle: ctx.fillStyle,
2564
- strokeStyle: ctx.strokeStyle,
2565
- lineWidth: ctx.lineWidth,
2566
- // Minimal Klint-like API
2567
- background: (color) => {
2568
- ctx.fillStyle = color || "#000";
2569
- ctx.fillRect(0, 0, width * dpr, height * dpr);
2570
- },
2571
- fillColor: (color) => {
2572
- ctx.fillStyle = color;
2448
+ return prevSeg.value || 0;
2449
+ }
2450
+ const timeline = {
2451
+ track: (keyframesOrFn) => {
2452
+ const compiled = typeof keyframesOrFn === "function" ? timeline.keyframes(keyframesOrFn) : keyframesOrFn;
2453
+ const track = {
2454
+ ...compiled,
2455
+ currentValue: 0,
2456
+ getValue: (progress) => interpolateTrack(compiled, progress),
2457
+ get current() {
2458
+ return this.currentValue;
2459
+ },
2460
+ value() {
2461
+ return this.currentValue;
2462
+ }
2463
+ };
2464
+ tracks.set(track, compiled);
2465
+ return track;
2573
2466
  },
2574
- strokeColor: (color) => {
2575
- ctx.strokeStyle = color;
2467
+ keyframes: (fn) => {
2468
+ const kf = createKeyframes();
2469
+ fn(kf);
2470
+ return kf._compile();
2576
2471
  },
2577
- circle: (x, y, radius) => {
2578
- ctx.beginPath();
2579
- ctx.arc(x, y, radius, 0, Math.PI * 2);
2580
- ctx.fill();
2472
+ stagger: (count, offset, keyframesFn) => {
2473
+ const compiled = timeline.keyframes(keyframesFn);
2474
+ return Array.from({ length: count }, (_, i) => {
2475
+ const track = {
2476
+ ...compiled,
2477
+ currentValue: 0,
2478
+ staggerDelay: i * offset,
2479
+ getValue: (progress) => {
2480
+ const staggeredProgress = Math.max(0, progress - i * offset);
2481
+ const normalizedProgress = Math.min(
2482
+ 1,
2483
+ staggeredProgress / (1 - i * offset)
2484
+ );
2485
+ return normalizedProgress > 0 ? interpolateTrack(compiled, normalizedProgress) : 0;
2486
+ },
2487
+ get current() {
2488
+ return this.currentValue;
2489
+ },
2490
+ value() {
2491
+ return this.currentValue;
2492
+ }
2493
+ };
2494
+ tracks.set(track, compiled);
2495
+ return track;
2496
+ });
2581
2497
  },
2582
- rectangle: (x, y, w, h) => {
2583
- ctx.fillRect(x, y, w, h || w);
2498
+ update: (progress) => {
2499
+ const prevProgress = currentProgress;
2500
+ currentProgress = progress;
2501
+ if (!hasStarted && progress > 0) {
2502
+ hasStarted = true;
2503
+ callbacks.start.forEach(
2504
+ (cb) => executeCallback(cb, "Start callback error")
2505
+ );
2506
+ }
2507
+ if (!hasEnded && progress >= 1) {
2508
+ hasEnded = true;
2509
+ callbacks.end.forEach(
2510
+ (cb) => executeCallback(cb, "End callback error")
2511
+ );
2512
+ }
2513
+ if (progress < prevProgress) {
2514
+ if (progress === 0) {
2515
+ hasStarted = false;
2516
+ hasEnded = false;
2517
+ } else if (progress < 1) {
2518
+ hasEnded = false;
2519
+ }
2520
+ }
2521
+ for (const [track, compiled] of tracks) {
2522
+ track.currentValue = track.getValue(progress);
2523
+ if (compiled.segments) {
2524
+ compiled.segments.forEach((seg) => {
2525
+ if (seg.callback && seg.pos <= progress && seg.pos > prevProgress) {
2526
+ executeCallback(seg.callback);
2527
+ }
2528
+ });
2529
+ }
2530
+ }
2584
2531
  },
2585
- // Time properties (static for server rendering)
2586
- frame: 0,
2587
- time: 0,
2588
- deltaTime: 0,
2589
- fps: 60
2532
+ progress: () => currentProgress
2590
2533
  };
2534
+ const result = setup(timeline);
2535
+ return { ...result, update: timeline.update };
2591
2536
  }
2592
2537
  };
2593
- var SSR_default = SSR;
2594
2538
 
2595
2539
  // src/KlintFunctions.tsx
2596
2540
  var KlintCoreFunctions = {
@@ -2612,37 +2556,6 @@ var KlintCoreFunctions = {
2612
2556
  // to do
2613
2557
  redraw: () => () => {
2614
2558
  },
2615
- extend: (ctx) => (name, data, enforceReplace = false) => {
2616
- if (name in ctx && !enforceReplace) return;
2617
- ctx[name] = data;
2618
- },
2619
- passImage: () => (element) => {
2620
- if (!element.complete) {
2621
- console.warn("Image passed to passImage() is not fully loaded");
2622
- return null;
2623
- }
2624
- return element;
2625
- },
2626
- passImages: () => (elements) => {
2627
- return elements.map((element) => {
2628
- if (!element.complete) {
2629
- console.warn("Image passed to passImages() is not fully loaded");
2630
- return null;
2631
- }
2632
- return element;
2633
- });
2634
- },
2635
- saveConfig: (ctx) => (from) => {
2636
- return Object.fromEntries(
2637
- CONFIG_PROPS.map((key) => [
2638
- key,
2639
- from?.[key] ?? ctx[key]
2640
- ])
2641
- );
2642
- },
2643
- restoreConfig: (ctx) => (config) => {
2644
- Object.assign(ctx, config);
2645
- },
2646
2559
  describe: (ctx) => (description) => {
2647
2560
  ctx.__description = description;
2648
2561
  },
@@ -2747,6 +2660,7 @@ var KlintFunctions = {
2747
2660
  strokeWidth: (ctx) => (width) => {
2748
2661
  if (width <= 0) {
2749
2662
  ctx.lineWidth = EPSILON;
2663
+ return;
2750
2664
  }
2751
2665
  ctx.lineWidth = width;
2752
2666
  },
@@ -2923,7 +2837,7 @@ var KlintFunctions = {
2923
2837
  ctx.__currentContours = null;
2924
2838
  ctx.__startedShape = false;
2925
2839
  },
2926
- gradient: (ctx) => (x1 = 0, y1 = 0, x2 = ctx.width, y2 = ctx.width) => {
2840
+ gradient: (ctx) => (x1 = 0, y1 = 0, x2 = ctx.width, y2 = ctx.height) => {
2927
2841
  return ctx.createLinearGradient(x1, y1, x2, y2);
2928
2842
  },
2929
2843
  radialGradient: (ctx) => (x1 = ctx.width / 2, y1 = ctx.height / 2, r1 = 0, x2 = ctx.width / 2, y2 = ctx.height / 2, r2 = Math.min(ctx.width, ctx.height)) => {
@@ -3186,19 +3100,6 @@ var KlintFunctions = {
3186
3100
  const adjustedY = ctx.__imageOrigin === "center" ? dy - height / 2 : dy;
3187
3101
  ctx.drawImage(sourceImage, adjustedX, adjustedY);
3188
3102
  },
3189
- // unsure about keeping those next two, maybe a shader plugin would be better
3190
- loadPixels: (ctx) => () => {
3191
- return ctx.getImageData(0, 0, ctx.width, ctx.height);
3192
- },
3193
- updatePixels: (ctx) => (pixels) => {
3194
- const pixelArray = pixels instanceof Uint8ClampedArray ? new Uint8ClampedArray(pixels) : new Uint8ClampedArray(pixels);
3195
- const imageData = new ImageData(pixelArray, ctx.width, ctx.height);
3196
- ctx.putImageData(imageData, 0, 0);
3197
- },
3198
- readPixels: (ctx) => (x, y, w = 1, h = 1) => {
3199
- const imageData = ctx.getImageData(x, y, w, h);
3200
- return Array.from(imageData.data);
3201
- },
3202
3103
  scaleTo: () => (originWidth, originHeight, destinationWidth, destinationHeight, cover = false) => {
3203
3104
  const widthRatio = destinationWidth / originWidth;
3204
3105
  const heightRatio = destinationHeight / originHeight;
@@ -3210,6 +3111,12 @@ var KlintFunctions = {
3210
3111
  blend: (ctx) => (blend) => {
3211
3112
  ctx.globalCompositeOperation = blend === "default" ? "source-over" : blend;
3212
3113
  },
3114
+ smooth: (ctx) => () => {
3115
+ ctx.imageSmoothingEnabled = true;
3116
+ },
3117
+ noSmooth: (ctx) => () => {
3118
+ ctx.imageSmoothingEnabled = false;
3119
+ },
3213
3120
  setCanvasOrigin: (ctx) => (type) => {
3214
3121
  ctx.__canvasOrigin = type;
3215
3122
  },
@@ -3273,6 +3180,41 @@ var KlintFunctions = {
3273
3180
  ctx.fill = originalFill;
3274
3181
  ctx.stroke = originalStroke;
3275
3182
  },
3183
+ screenToWorld: (ctx) => (screenX, screenY) => {
3184
+ const isCenter = ctx.__canvasOrigin === "center";
3185
+ const deviceX = isCenter ? screenX + ctx.width * 0.5 : screenX;
3186
+ const deviceY = isCenter ? screenY + ctx.height * 0.5 : screenY;
3187
+ const inverse = ctx.getTransform().inverse();
3188
+ const pt = inverse.transformPoint(new DOMPoint(deviceX, deviceY));
3189
+ return { x: pt.x, y: pt.y };
3190
+ },
3191
+ worldToScreen: (ctx) => (worldX, worldY) => {
3192
+ const isCenter = ctx.__canvasOrigin === "center";
3193
+ const matrix = ctx.getTransform();
3194
+ const pt = matrix.transformPoint(new DOMPoint(worldX, worldY));
3195
+ const screenX = isCenter ? pt.x - ctx.width * 0.5 : pt.x;
3196
+ const screenY = isCenter ? pt.y - ctx.height * 0.5 : pt.y;
3197
+ return { x: screenX, y: screenY };
3198
+ },
3199
+ getVisibleBounds: (ctx) => () => {
3200
+ const inverse = ctx.getTransform().inverse();
3201
+ const c0 = inverse.transformPoint(new DOMPoint(0, 0));
3202
+ const c1 = inverse.transformPoint(new DOMPoint(ctx.width, 0));
3203
+ const c2 = inverse.transformPoint(new DOMPoint(ctx.width, ctx.height));
3204
+ const c3 = inverse.transformPoint(new DOMPoint(0, ctx.height));
3205
+ const left = Math.min(c0.x, c1.x, c2.x, c3.x);
3206
+ const right = Math.max(c0.x, c1.x, c2.x, c3.x);
3207
+ const top = Math.min(c0.y, c1.y, c2.y, c3.y);
3208
+ const bottom = Math.max(c0.y, c1.y, c2.y, c3.y);
3209
+ return {
3210
+ left,
3211
+ top,
3212
+ right,
3213
+ bottom,
3214
+ width: right - left,
3215
+ height: bottom - top
3216
+ };
3217
+ },
3276
3218
  canIuseFilter: (ctx) => () => {
3277
3219
  return ctx.filter !== void 0 && ctx.filter !== null;
3278
3220
  },
@@ -3363,29 +3305,8 @@ function useKlint() {
3363
3305
  const gestureRef = useRef2(null);
3364
3306
  const keyboardRef = useRef2(null);
3365
3307
  const useDev = () => {
3366
- useEffect2(() => {
3367
- if (process.env.NODE_ENV === "development") {
3368
- if (typeof import.meta !== "undefined" && import.meta.hot) {
3369
- console.log("[Klint] hot updated - clearing non-context state");
3370
- if (contextRef.current) {
3371
- const ctx = contextRef.current;
3372
- ctx.frame = 0;
3373
- ctx.time = 0;
3374
- ctx.__offscreens?.clear();
3375
- ctx.__startedShape = false;
3376
- ctx.__currentShape = null;
3377
- ctx.__startedContour = false;
3378
- ctx.__currentContours = null;
3379
- ctx.__currentContour = null;
3380
- ctx.__isReadyToDraw = true;
3381
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3382
- }
3383
- }
3384
- }
3385
- }, []);
3386
3308
  useEffect2(() => {
3387
3309
  if (process.env.NODE_ENV === "development" && contextRef.current) {
3388
- console.log("[Klint] hot updated - clearing context state");
3389
3310
  contextRef.current.__isReadyToDraw = true;
3390
3311
  }
3391
3312
  });
@@ -3509,7 +3430,7 @@ function useKlint() {
3509
3430
  canvas.addEventListener("mouseleave", handleMouseLeave, { signal });
3510
3431
  canvas.addEventListener("click", handleClick, { signal });
3511
3432
  return () => controller.abort();
3512
- });
3433
+ }, []);
3513
3434
  return {
3514
3435
  mouse: mouseRef.current,
3515
3436
  onClick: (callback) => clickCallbackRef.current = callback,
@@ -3544,7 +3465,7 @@ function useKlint() {
3544
3465
  };
3545
3466
  canvas.addEventListener("wheel", handleScroll, { signal });
3546
3467
  return () => controller.abort();
3547
- });
3468
+ }, []);
3548
3469
  return {
3549
3470
  scroll: scrollRef.current,
3550
3471
  onScroll: (callback) => scrollCallbackRef.current = callback
@@ -3721,6 +3642,17 @@ function useKlint() {
3721
3642
  const keyPressedCallbackRef = useRef2(/* @__PURE__ */ new Map());
3722
3643
  const keyReleasedCallbackRef = useRef2(/* @__PURE__ */ new Map());
3723
3644
  const keyComboCallbackRef = useRef2(/* @__PURE__ */ new Map());
3645
+ const normalizeKey = (key) => {
3646
+ const keyMap = {
3647
+ " ": "Space",
3648
+ Control: "Ctrl",
3649
+ Escape: "Esc"
3650
+ };
3651
+ return keyMap[key] || key;
3652
+ };
3653
+ const createComboKey = (keys) => {
3654
+ return keys.map(normalizeKey).sort().join("+");
3655
+ };
3724
3656
  useEffect2(() => {
3725
3657
  if (!contextRef.current) return;
3726
3658
  const ctx = contextRef.current;
@@ -3733,17 +3665,9 @@ function useKlint() {
3733
3665
  keyboardRef.current.modifiers.ctrl = e.ctrlKey;
3734
3666
  keyboardRef.current.modifiers.meta = e.metaKey;
3735
3667
  };
3736
- const normalizeKey2 = (key) => {
3737
- const keyMap = {
3738
- " ": "Space",
3739
- Control: "Ctrl",
3740
- Escape: "Esc"
3741
- };
3742
- return keyMap[key] || key;
3743
- };
3744
3668
  const handleKeyDown = (e) => {
3745
3669
  if (!keyboardRef.current) return;
3746
- const normalizedKey = normalizeKey2(e.key);
3670
+ const normalizedKey = normalizeKey(e.key);
3747
3671
  keyboardRef.current.pressedKeys.add(normalizedKey);
3748
3672
  keyboardRef.current.lastKey = normalizedKey;
3749
3673
  keyboardRef.current.lastKeyTime = performance.now();
@@ -3763,7 +3687,7 @@ function useKlint() {
3763
3687
  };
3764
3688
  const handleKeyUp = (e) => {
3765
3689
  if (!keyboardRef.current) return;
3766
- const normalizedKey = normalizeKey2(e.key);
3690
+ const normalizedKey = normalizeKey(e.key);
3767
3691
  keyboardRef.current.pressedKeys.delete(normalizedKey);
3768
3692
  updateModifiers(e);
3769
3693
  const keyCallback = keyReleasedCallbackRef.current.get(normalizedKey);
@@ -3775,17 +3699,6 @@ function useKlint() {
3775
3699
  window.addEventListener("keyup", handleKeyUp, { signal });
3776
3700
  return () => controller.abort();
3777
3701
  }, []);
3778
- const createComboKey = (keys) => {
3779
- return keys.map(normalizeKey).sort().join("+");
3780
- };
3781
- const normalizeKey = (key) => {
3782
- const keyMap = {
3783
- " ": "Space",
3784
- Control: "Ctrl",
3785
- Escape: "Esc"
3786
- };
3787
- return keyMap[key] || key;
3788
- };
3789
3702
  return {
3790
3703
  keyboard: keyboardRef.current,
3791
3704
  // Register callback for single key press
@@ -3819,210 +3732,6 @@ function useKlint() {
3819
3732
  }
3820
3733
  };
3821
3734
  };
3822
- const KlintTimeline = () => {
3823
- const timelinesRef = useRef2(/* @__PURE__ */ new Map());
3824
- const callbacksRef = useRef2({ start: [], end: [], loop: [] });
3825
- const Timeline = {
3826
- create: (setup, options = {}) => {
3827
- const tracks = /* @__PURE__ */ new Map();
3828
- const parents = /* @__PURE__ */ new Set();
3829
- let currentProgress = 0;
3830
- let hasStarted = false;
3831
- let hasEnded = false;
3832
- const defaultEasing = options.defaultEasing || ((t) => t);
3833
- const defaultLoop = options.defaultLoop || 0;
3834
- const executeCallback = (callback, errorMsg = "Callback error") => {
3835
- try {
3836
- callback();
3837
- } catch (e) {
3838
- console.warn(`${errorMsg}:`, e);
3839
- }
3840
- };
3841
- function createKeyframes() {
3842
- const segments = [];
3843
- let currentPos = 0;
3844
- let loopCount = defaultLoop;
3845
- let parentTrack = void 0;
3846
- const parseEasingCallback = (easing, callback) => {
3847
- if (typeof easing === "function" && typeof callback === "undefined") {
3848
- if (easing.length === 0) {
3849
- return {
3850
- easing: defaultEasing,
3851
- callback: easing
3852
- };
3853
- }
3854
- return {
3855
- easing,
3856
- callback: void 0
3857
- };
3858
- }
3859
- return {
3860
- easing: easing || defaultEasing,
3861
- callback
3862
- };
3863
- };
3864
- const builder = {
3865
- start(value, delay = 0, callback) {
3866
- segments.push({ pos: delay, value, callback, type: "start" });
3867
- currentPos = delay;
3868
- return builder;
3869
- },
3870
- at(progress, value, easing, callback) {
3871
- const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
3872
- segments.push({
3873
- pos: progress,
3874
- value,
3875
- easing: finalEasing,
3876
- callback: finalCallback,
3877
- type: "tween"
3878
- });
3879
- currentPos = progress;
3880
- return builder;
3881
- },
3882
- then(value, duration, easing, callback) {
3883
- const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
3884
- const nextPos = currentPos + duration;
3885
- segments.push({
3886
- pos: nextPos,
3887
- value,
3888
- easing: finalEasing,
3889
- callback: finalCallback,
3890
- type: "tween"
3891
- });
3892
- currentPos = nextPos;
3893
- return builder;
3894
- },
3895
- loop(count = Infinity) {
3896
- loopCount = count;
3897
- return builder;
3898
- },
3899
- _compile: () => {
3900
- segments.sort((a, b) => a.pos - b.pos);
3901
- return { segments, loopCount, parentTrack };
3902
- }
3903
- };
3904
- return builder;
3905
- }
3906
- function interpolateTrack(compiled, progress) {
3907
- const { segments } = compiled;
3908
- if (!segments.length) return 0;
3909
- progress = Math.max(0, Math.min(1, progress));
3910
- const valueSegments = segments.filter(
3911
- (seg) => seg.type !== "callback"
3912
- );
3913
- if (!valueSegments.length) return 0;
3914
- let prevSeg = valueSegments[0];
3915
- for (let i = 1; i < valueSegments.length; i++) {
3916
- const seg = valueSegments[i];
3917
- if (progress <= seg.pos) {
3918
- if (seg.type === "tween" && prevSeg.value !== void 0 && seg.value !== void 0) {
3919
- const t = (progress - prevSeg.pos) / (seg.pos - prevSeg.pos);
3920
- const easedT = seg.easing ? seg.easing(t) : t;
3921
- return prevSeg.value + (seg.value - prevSeg.value) * easedT;
3922
- }
3923
- return seg.value || 0;
3924
- }
3925
- if (seg.value !== void 0) {
3926
- prevSeg = seg;
3927
- }
3928
- }
3929
- return prevSeg.value || 0;
3930
- }
3931
- const timeline = {
3932
- track: (keyframesOrFn) => {
3933
- const compiled = typeof keyframesOrFn === "function" ? timeline.keyframes(keyframesOrFn) : keyframesOrFn;
3934
- const track = {
3935
- ...compiled,
3936
- currentValue: 0,
3937
- getValue: (progress) => interpolateTrack(compiled, progress),
3938
- get current() {
3939
- return this.currentValue;
3940
- },
3941
- value: function() {
3942
- return this.currentValue;
3943
- }
3944
- };
3945
- tracks.set(track, compiled);
3946
- return track;
3947
- },
3948
- keyframes: (fn) => {
3949
- const kf = createKeyframes();
3950
- fn(kf);
3951
- return kf._compile();
3952
- },
3953
- stagger: (count, offset, keyframesFn) => {
3954
- const compiled = timeline.keyframes(keyframesFn);
3955
- return Array.from({ length: count }, (_, i) => {
3956
- const track = {
3957
- ...compiled,
3958
- currentValue: 0,
3959
- staggerDelay: i * offset,
3960
- getValue: (progress) => {
3961
- const staggeredProgress = Math.max(0, progress - i * offset);
3962
- const normalizedProgress = Math.min(
3963
- 1,
3964
- staggeredProgress / (1 - i * offset)
3965
- );
3966
- return normalizedProgress > 0 ? interpolateTrack(compiled, normalizedProgress) : 0;
3967
- },
3968
- get current() {
3969
- return this.currentValue;
3970
- },
3971
- value: function() {
3972
- return this.currentValue;
3973
- }
3974
- };
3975
- tracks.set(track, compiled);
3976
- return track;
3977
- });
3978
- },
3979
- update: (progress) => {
3980
- const prevProgress = currentProgress;
3981
- currentProgress = progress;
3982
- if (!hasStarted && progress > 0) {
3983
- hasStarted = true;
3984
- callbacksRef.current.start.forEach(
3985
- (cb) => executeCallback(cb, "Start callback error")
3986
- );
3987
- }
3988
- if (!hasEnded && progress >= 1) {
3989
- hasEnded = true;
3990
- callbacksRef.current.end.forEach(
3991
- (cb) => executeCallback(cb, "End callback error")
3992
- );
3993
- }
3994
- if (progress < prevProgress) {
3995
- if (progress === 0) {
3996
- hasStarted = false;
3997
- hasEnded = false;
3998
- } else if (progress < 1) {
3999
- hasEnded = false;
4000
- }
4001
- }
4002
- for (const [track, compiled] of tracks) {
4003
- track.currentValue = track.getValue(progress);
4004
- if (compiled.segments) {
4005
- compiled.segments.forEach((seg) => {
4006
- if (seg.callback && seg.pos <= progress && seg.pos > prevProgress) {
4007
- executeCallback(seg.callback);
4008
- }
4009
- });
4010
- }
4011
- }
4012
- },
4013
- progress: () => currentProgress
4014
- };
4015
- const result = setup(timeline);
4016
- return { ...result, update: timeline.update };
4017
- }
4018
- };
4019
- return {
4020
- Timeline,
4021
- onStart: (fn) => callbacksRef.current.start.push(fn),
4022
- onEnd: (fn) => callbacksRef.current.end.push(fn),
4023
- onLoop: (fn) => callbacksRef.current.loop.push(fn)
4024
- };
4025
- };
4026
3735
  const KlintWindow = () => {
4027
3736
  const resizeCallbackRef = useRef2(
4028
3737
  null
@@ -4093,13 +3802,13 @@ function useKlint() {
4093
3802
  context.Vector = new Vector_default();
4094
3803
  context.Easing = new Easing_default();
4095
3804
  context.Text = new Text_default(context);
4096
- context.Thing = new Thing_default(context);
4097
3805
  context.Grid = new Grid_default(context);
4098
3806
  context.Strip = new Strip_default(context);
4099
3807
  context.Noise = new Noise_default(context);
4100
3808
  context.Hotspot = new Hotspot_default(context);
4101
- context.Performance = new Performance_default(context);
4102
- context.SSR = new SSR_default(context);
3809
+ context.Quadtree = Quadtree_default;
3810
+ context.Pixels = new Pixels(context);
3811
+ context.Timeline = new Timeline();
4103
3812
  Object.entries(KlintCoreFunctions).forEach(([name, fn]) => {
4104
3813
  context[name] = fn(context);
4105
3814
  });
@@ -4130,30 +3839,6 @@ function useKlint() {
4130
3839
  contextRef.current.__isPlaying = !contextRef.current.__isPlaying;
4131
3840
  }
4132
3841
  }, []);
4133
- const KlintPerformance = () => {
4134
- const metricsRef = useRef2(null);
4135
- useEffect2(() => {
4136
- if (!contextRef.current?.__performance) return;
4137
- const updateMetrics = () => {
4138
- if (contextRef.current?.__performance) {
4139
- metricsRef.current = { ...contextRef.current.__performance };
4140
- }
4141
- };
4142
- const interval = setInterval(updateMetrics, 100);
4143
- return () => clearInterval(interval);
4144
- }, []);
4145
- return {
4146
- metrics: metricsRef.current,
4147
- getMetrics: () => contextRef.current?.__performance || null,
4148
- reset: () => {
4149
- if (contextRef.current?.__performance) {
4150
- contextRef.current.__performance.droppedFrames = 0;
4151
- contextRef.current.__performance.minFrameTime = Infinity;
4152
- contextRef.current.__performance.maxFrameTime = 0;
4153
- }
4154
- }
4155
- };
4156
- };
4157
3842
  return {
4158
3843
  context: {
4159
3844
  context: contextRef.current,
@@ -4165,8 +3850,6 @@ function useKlint() {
4165
3850
  KlintKeyboard,
4166
3851
  KlintWindow,
4167
3852
  KlintImage,
4168
- KlintTimeline,
4169
- KlintPerformance,
4170
3853
  togglePlay,
4171
3854
  useDev
4172
3855
  };
@@ -4221,11 +3904,12 @@ export {
4221
3904
  KlintCoreFunctions,
4222
3905
  KlintFunctions,
4223
3906
  Noise_default as Noise,
4224
- Performance_default as Performance,
4225
- SSR_default as SSR,
3907
+ Pixels,
3908
+ Quadtree_default as Quadtree,
3909
+ Rectangle,
4226
3910
  Strip_default as Strip,
4227
3911
  Text_default as Text,
4228
- Thing_default as Thing,
3912
+ Timeline,
4229
3913
  Vector_default as Vector,
4230
3914
  useKlint,
4231
3915
  useProps,