@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.cjs CHANGED
@@ -40,11 +40,12 @@ __export(index_exports, {
40
40
  KlintCoreFunctions: () => KlintCoreFunctions,
41
41
  KlintFunctions: () => KlintFunctions,
42
42
  Noise: () => Noise_default,
43
- Performance: () => Performance_default,
44
- SSR: () => SSR_default,
43
+ Pixels: () => Pixels,
44
+ Quadtree: () => Quadtree_default,
45
+ Rectangle: () => Rectangle,
45
46
  Strip: () => Strip_default,
46
47
  Text: () => Text_default,
47
- Thing: () => Thing_default,
48
+ Timeline: () => Timeline,
48
49
  Vector: () => Vector_default,
49
50
  useKlint: () => useKlint,
50
51
  useProps: () => useProps,
@@ -97,33 +98,23 @@ var CONFIG_PROPS = [
97
98
  "__fillRule",
98
99
  "__isPlaying"
99
100
  ];
100
- function useAnimate(contextRef, draw, isVisible, enablePerformanceTracking = false) {
101
+ function useAnimate(contextRef, draw, isVisible) {
101
102
  const animationFrameId = (0, import_react.useRef)(0);
102
- const frameTimeHistoryRef = (0, import_react.useRef)([]);
103
- const frameStartTimeRef = (0, import_react.useRef)(0);
104
- const frameCountRef = (0, import_react.useRef)(0);
105
- const lastFpsUpdateRef = (0, import_react.useRef)(0);
106
- const droppedFramesRef = (0, import_react.useRef)(0);
107
103
  const animate = (0, import_react.useCallback)(
108
104
  (timestamp = 0) => {
109
105
  if (!contextRef.current || !isVisible) return;
110
106
  if (!contextRef.current.__isReadyToDraw) return;
111
- if (!contextRef.current.__isPlaying) {
112
- return;
113
- }
107
+ if (!contextRef.current.__isPlaying) return;
114
108
  const context = contextRef.current;
115
109
  const now = timestamp;
116
110
  const target = 1e3 / context.fps;
117
111
  if (!context.__lastTargetTime) {
118
112
  context.__lastTargetTime = now;
119
113
  context.__lastRealTime = now;
120
- frameStartTimeRef.current = now;
121
- lastFpsUpdateRef.current = now;
122
114
  }
123
115
  const sinceLast = now - context.__lastTargetTime;
124
116
  const epsilon = 5;
125
117
  if (sinceLast >= target - epsilon) {
126
- const frameStart = enablePerformanceTracking && context.__performance ? performance.now() : 0;
127
118
  context.deltaTime = now - context.__lastRealTime;
128
119
  draw(context);
129
120
  if (context.time > 1e7) context.time = 0;
@@ -132,40 +123,10 @@ function useAnimate(contextRef, draw, isVisible, enablePerformanceTracking = fal
132
123
  context.frame++;
133
124
  context.__lastTargetTime = now;
134
125
  context.__lastRealTime = now;
135
- if (enablePerformanceTracking && context.__performance) {
136
- const frameTime = performance.now() - frameStart;
137
- const targetFrameTime = 1e3 / context.fps;
138
- frameTimeHistoryRef.current.push(frameTime);
139
- if (frameTimeHistoryRef.current.length > 60) {
140
- frameTimeHistoryRef.current.shift();
141
- }
142
- const avgFrameTime = frameTimeHistoryRef.current.reduce((a, b) => a + b, 0) / frameTimeHistoryRef.current.length;
143
- context.__performance.frameTime = frameTime;
144
- context.__performance.averageFrameTime = avgFrameTime;
145
- context.__performance.minFrameTime = Math.min(
146
- ...frameTimeHistoryRef.current
147
- );
148
- context.__performance.maxFrameTime = Math.max(
149
- ...frameTimeHistoryRef.current
150
- );
151
- if (frameTime > targetFrameTime * 1.1) {
152
- droppedFramesRef.current++;
153
- }
154
- context.__performance.droppedFrames = droppedFramesRef.current;
155
- frameCountRef.current++;
156
- if (now - lastFpsUpdateRef.current >= 1e3) {
157
- context.__performance.fps = frameCountRef.current;
158
- frameCountRef.current = 0;
159
- lastFpsUpdateRef.current = now;
160
- if (typeof performance !== "undefined" && performance.memory) {
161
- context.__performance.memoryUsage = performance.memory.usedJSHeapSize / 1048576;
162
- }
163
- }
164
- }
165
126
  }
166
127
  animationFrameId.current = requestAnimationFrame(animate);
167
128
  },
168
- [draw, isVisible, contextRef, enablePerformanceTracking]
129
+ [draw, isVisible, contextRef]
169
130
  );
170
131
  return {
171
132
  animate,
@@ -178,8 +139,7 @@ function Klint({
178
139
  draw,
179
140
  options = {},
180
141
  preload,
181
- onVisible,
182
- enablePerformanceTracking = false
142
+ onVisible
183
143
  }) {
184
144
  const canvasRef = (0, import_react.useRef)(null);
185
145
  const containerRef = (0, import_react.useRef)(null);
@@ -190,21 +150,26 @@ function Klint({
190
150
  );
191
151
  const [isVisible, setIsVisible] = (0, import_react.useState)(true);
192
152
  (0, import_react.useEffect)(() => {
153
+ const resetContext = () => {
154
+ if (contextRef.current) {
155
+ const ctx = contextRef.current;
156
+ ctx.__isPlaying = false;
157
+ ctx.frame = 0;
158
+ ctx.time = 0;
159
+ ctx.__offscreens?.clear();
160
+ ctx.__startedShape = false;
161
+ ctx.__currentShape = null;
162
+ ctx.__startedContour = false;
163
+ ctx.__currentContours = null;
164
+ ctx.__currentContour = null;
165
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
166
+ }
167
+ };
193
168
  if (typeof import_meta !== "undefined" && import_meta.hot) {
194
- import_meta.hot.dispose(() => {
195
- console.log("[Klint] Component unmounting due to HMR");
196
- if (contextRef.current) {
197
- contextRef.current.__isPlaying = false;
198
- }
199
- });
169
+ import_meta.hot.dispose(resetContext);
200
170
  }
201
171
  if (typeof module !== "undefined" && module.hot) {
202
- module.hot.dispose(() => {
203
- console.log("[Klint] Component unmounting due to Webpack HMR");
204
- if (contextRef.current) {
205
- contextRef.current.__isPlaying = false;
206
- }
207
- });
172
+ module.hot.dispose(resetContext);
208
173
  }
209
174
  }, []);
210
175
  const __options = {
@@ -213,12 +178,7 @@ function Klint({
213
178
  };
214
179
  const [toStaticImage, setStaticImage] = (0, import_react.useState)(null);
215
180
  const initContext = context?.initCoreContext;
216
- const { animate, animationFrameId } = useAnimate(
217
- contextRef,
218
- draw,
219
- isVisible,
220
- enablePerformanceTracking
221
- );
181
+ const { animate, animationFrameId } = useAnimate(contextRef, draw, isVisible);
222
182
  const updateCanvasSize = (shouldRedraw = false) => {
223
183
  if (!containerRef.current || !contextRef.current || !canvasRef.current)
224
184
  return;
@@ -248,16 +208,6 @@ function Klint({
248
208
  const context2 = contextRef.current;
249
209
  if (!context2) return;
250
210
  context2.__dpr = dpr;
251
- if (enablePerformanceTracking) {
252
- context2.__performance = {
253
- fps: 0,
254
- frameTime: 0,
255
- averageFrameTime: 0,
256
- minFrameTime: Infinity,
257
- maxFrameTime: 0,
258
- droppedFrames: 0
259
- };
260
- }
261
211
  if (__options.fps && __options.fps !== context2.fps) {
262
212
  context2.fps = __options.fps;
263
213
  }
@@ -807,6 +757,58 @@ var Easing = class {
807
757
  p = p < 0 ? 0 : p > 1 ? 1 : p;
808
758
  return p * p * (3 - 2 * p);
809
759
  };
760
+ /**
761
+ * Damped spring easing. Physically expressive oscillation.
762
+ * @param val - Progress value (0 to 1)
763
+ * @param tension - Spring tension (0 to 1, default 0.5). Higher = faster oscillation.
764
+ * @param friction - Spring friction (0 to 1, default 0.5). Higher = less damping (more bouncy).
765
+ */
766
+ this.spring = (val, tension = 0.5, friction = 0.5) => {
767
+ const omega = tension * 40;
768
+ const zeta = 1 - friction;
769
+ if (zeta < 1) {
770
+ const omegaD = omega * Math.sqrt(1 - zeta * zeta);
771
+ return 1 - Math.exp(-zeta * omega * val) * (Math.cos(omegaD * val) + zeta * omega / omegaD * Math.sin(omegaD * val));
772
+ }
773
+ return 1 - (1 + omega * val) * Math.exp(-omega * val);
774
+ };
775
+ /**
776
+ * Staircase easing. Quantizes to N discrete steps.
777
+ * @param val - Progress value (0 to 1)
778
+ * @param n - Number of steps (default 4)
779
+ */
780
+ this.steps = (val, n = 4) => {
781
+ if (n <= 0) return val;
782
+ return Math.floor(val * n) / n;
783
+ };
784
+ /**
785
+ * Frame-rate independent exponential smoothing.
786
+ * Use instead of naive `lerp(a, b, 0.1)` which breaks at different FPS.
787
+ * @param current - Current value
788
+ * @param target - Target value
789
+ * @param smoothing - Smoothing factor (higher = faster convergence)
790
+ * @param deltaTime - Time since last frame in seconds
791
+ */
792
+ this.damp = (current, target, smoothing, deltaTime) => {
793
+ return current + (target - current) * (1 - Math.exp(-smoothing * deltaTime));
794
+ };
795
+ /**
796
+ * Quick rise then decay. Great for hit effects, flashes.
797
+ * @param val - Progress value (0 to 1)
798
+ * @param k - Sharpness (default 6). Higher = sharper peak.
799
+ */
800
+ this.impulse = (val, k = 6) => {
801
+ const h = k * val;
802
+ return h * Math.exp(1 - h);
803
+ };
804
+ /**
805
+ * Symmetric arc. Useful for jumps, throw curves.
806
+ * @param val - Progress value (0 to 1)
807
+ * @param k - Steepness (default 1)
808
+ */
809
+ this.parabola = (val, k = 1) => {
810
+ return Math.pow(4 * val * (1 - val), k);
811
+ };
810
812
  this.log = () => {
811
813
  console.log(this);
812
814
  };
@@ -1260,17 +1262,6 @@ var Text = class {
1260
1262
  };
1261
1263
  var Text_default = Text;
1262
1264
 
1263
- // src/elements/Thing.tsx
1264
- var Thing = class {
1265
- constructor(ctx) {
1266
- this.context = ctx;
1267
- }
1268
- log() {
1269
- console.log(this.context);
1270
- }
1271
- };
1272
- var Thing_default = Thing;
1273
-
1274
1265
  // src/elements/Grid.tsx
1275
1266
  var Grid = class {
1276
1267
  /**
@@ -1704,505 +1695,427 @@ var Strip = class {
1704
1695
  var Strip_default = Strip;
1705
1696
 
1706
1697
  // src/elements/Noise.tsx
1707
- var Noise = class {
1708
- /**
1709
- * Creates a new Noise instance
1710
- * @param ctx - The Klint context
1711
- */
1712
- constructor(ctx) {
1713
- this.perm = [];
1714
- this.permMod12 = [];
1715
- this.grad3 = [
1716
- [1, 1, 0],
1717
- [-1, 1, 0],
1718
- [1, -1, 0],
1719
- [-1, -1, 0],
1720
- [1, 0, 1],
1721
- [-1, 0, 1],
1722
- [1, 0, -1],
1723
- [-1, 0, -1],
1724
- [0, 1, 1],
1725
- [0, -1, 1],
1726
- [0, 1, -1],
1727
- [0, -1, -1]
1728
- ];
1729
- this.grad4 = [
1730
- [0, 1, 1, 1],
1731
- [0, 1, 1, -1],
1732
- [0, 1, -1, 1],
1733
- [0, 1, -1, -1],
1734
- [0, -1, 1, 1],
1735
- [0, -1, 1, -1],
1736
- [0, -1, -1, 1],
1737
- [0, -1, -1, -1],
1738
- [1, 0, 1, 1],
1739
- [1, 0, 1, -1],
1740
- [1, 0, -1, 1],
1741
- [1, 0, -1, -1],
1742
- [-1, 0, 1, 1],
1743
- [-1, 0, 1, -1],
1744
- [-1, 0, -1, 1],
1745
- [-1, 0, -1, -1],
1746
- [1, 1, 0, 1],
1747
- [1, 1, 0, -1],
1748
- [1, -1, 0, 1],
1749
- [1, -1, 0, -1],
1750
- [-1, 1, 0, 1],
1751
- [-1, 1, 0, -1],
1752
- [-1, -1, 0, 1],
1753
- [-1, -1, 0, -1],
1754
- [1, 1, 1, 0],
1755
- [1, 1, -1, 0],
1756
- [1, -1, 1, 0],
1757
- [1, -1, -1, 0],
1758
- [-1, 1, 1, 0],
1759
- [-1, 1, -1, 0],
1760
- [-1, -1, 1, 0],
1761
- [-1, -1, -1, 0]
1762
- ];
1763
- this.currentSeed = Math.random();
1764
- this.F2 = 0.5 * (Math.sqrt(3) - 1);
1765
- this.G2 = (3 - Math.sqrt(3)) / 6;
1766
- this.F3 = 1 / 3;
1767
- this.G3 = 1 / 6;
1768
- this.F4 = (Math.sqrt(5) - 1) / 4;
1769
- this.G4 = (5 - Math.sqrt(5)) / 20;
1770
- this.context = ctx;
1771
- this.buildPermutationTable();
1698
+ var _Noise = class _Noise {
1699
+ constructor(_ctx) {
1700
+ this._seed = Math.random();
1701
+ this.p = new Uint8Array(512);
1702
+ this.buildPerm();
1703
+ }
1704
+ rng() {
1705
+ const x = Math.sin(this._seed++) * 1e4;
1706
+ return x - Math.floor(x);
1772
1707
  }
1773
- /**
1774
- * Build permutation table for noise generation
1775
- */
1776
- buildPermutationTable() {
1777
- const p = [];
1778
- for (let i = 0; i < 256; i++) {
1779
- p[i] = i;
1708
+ buildPerm() {
1709
+ const t = new Uint8Array(256);
1710
+ for (let i = 0; i < 256; i++) t[i] = i;
1711
+ for (let i = 255; i > 0; i--) {
1712
+ const j = this.rng() * (i + 1) | 0;
1713
+ const tmp = t[i];
1714
+ t[i] = t[j];
1715
+ t[j] = tmp;
1780
1716
  }
1781
- let n = 256;
1782
- while (n > 0) {
1783
- const index = Math.floor(this.random() * n--);
1784
- const temp = p[n];
1785
- p[n] = p[index];
1786
- p[index] = temp;
1787
- }
1788
- this.perm = [];
1789
- this.permMod12 = [];
1790
- for (let i = 0; i < 512; i++) {
1791
- this.perm[i] = p[i & 255];
1792
- this.permMod12[i] = this.perm[i] % 12;
1793
- }
1794
- }
1795
- /**
1796
- * Seeded random number generator
1797
- */
1798
- random() {
1799
- const x = Math.sin(this.currentSeed++) * 1e4;
1800
- return x - Math.floor(x);
1717
+ for (let i = 0; i < 512; i++) this.p[i] = t[i & 255];
1801
1718
  }
1802
- /**
1803
- * Set seed for noise generation
1804
- * @param seed - Seed value for reproducible noise
1805
- */
1806
- seed(seed) {
1807
- this.currentSeed = seed !== void 0 ? seed : Math.random() * 1e4;
1808
- this.buildPermutationTable();
1719
+ /** Set seed for reproducible noise */
1720
+ seed(s) {
1721
+ this._seed = s ?? Math.random() * 1e4;
1722
+ this.buildPerm();
1809
1723
  }
1810
- /**
1811
- * Fade function for Perlin noise
1812
- */
1813
- fade(t) {
1724
+ // ---- Utilities ----
1725
+ static fade(t) {
1814
1726
  return t * t * t * (t * (t * 6 - 15) + 10);
1815
1727
  }
1816
- /**
1817
- * Linear interpolation
1818
- */
1819
- lerp(t, a, b) {
1728
+ static lerp(t, a, b) {
1820
1729
  return a + t * (b - a);
1821
1730
  }
1731
+ /** Improved Perlin gradient — maps 4-bit hash to one of 12 gradient directions */
1732
+ static grad3(h, x, y, z) {
1733
+ h &= 15;
1734
+ const u = h < 8 ? x : y;
1735
+ const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
1736
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
1737
+ }
1822
1738
  perlin(x, y, z, w) {
1823
- if (y === void 0) {
1824
- const xi = Math.floor(x) & 255;
1825
- const xf = x - Math.floor(x);
1826
- const u = this.fade(xf);
1827
- const a = this.perm[xi];
1828
- const b = this.perm[xi + 1];
1829
- const grad1 = (hash, x2) => (hash & 1) === 0 ? x2 : -x2;
1830
- return this.lerp(u, grad1(a, xf), grad1(b, xf - 1));
1831
- } else if (z === void 0) {
1832
- const xi = Math.floor(x) & 255;
1833
- const yi = Math.floor(y) & 255;
1834
- const xf = x - Math.floor(x);
1835
- const yf = y - Math.floor(y);
1836
- const u = this.fade(xf);
1837
- const v = this.fade(yf);
1838
- const aa = this.perm[this.perm[xi] + yi];
1839
- const ab = this.perm[this.perm[xi] + yi + 1];
1840
- const ba = this.perm[this.perm[xi + 1] + yi];
1841
- const bb = this.perm[this.perm[xi + 1] + yi + 1];
1842
- const grad2 = (hash, x3, y2) => {
1843
- const h = hash & 3;
1844
- const u2 = h < 2 ? x3 : y2;
1845
- const v2 = h < 2 ? y2 : x3;
1846
- return ((h & 1) === 0 ? u2 : -u2) + ((h & 2) === 0 ? v2 : -v2);
1847
- };
1848
- const x1 = this.lerp(u, grad2(aa, xf, yf), grad2(ba, xf - 1, yf));
1849
- const x2 = this.lerp(u, grad2(ab, xf, yf - 1), grad2(bb, xf - 1, yf - 1));
1850
- return this.lerp(v, x1, x2);
1851
- } else if (w === void 0) {
1852
- const xi = Math.floor(x) & 255;
1853
- const yi = Math.floor(y) & 255;
1854
- const zi = Math.floor(z) & 255;
1855
- const xf = x - Math.floor(x);
1856
- const yf = y - Math.floor(y);
1857
- const zf = z - Math.floor(z);
1858
- const u = this.fade(xf);
1859
- const v = this.fade(yf);
1860
- const w2 = this.fade(zf);
1861
- const aaa = this.perm[this.perm[this.perm[xi] + yi] + zi];
1862
- const aba = this.perm[this.perm[this.perm[xi] + yi + 1] + zi];
1863
- const aab = this.perm[this.perm[this.perm[xi] + yi] + zi + 1];
1864
- const abb = this.perm[this.perm[this.perm[xi] + yi + 1] + zi + 1];
1865
- const baa = this.perm[this.perm[this.perm[xi + 1] + yi] + zi];
1866
- const bba = this.perm[this.perm[this.perm[xi + 1] + yi + 1] + zi];
1867
- const bab = this.perm[this.perm[this.perm[xi + 1] + yi] + zi + 1];
1868
- const bbb = this.perm[this.perm[this.perm[xi + 1] + yi + 1] + zi + 1];
1869
- const grad3 = (hash, x2, y2, z2) => {
1870
- const h = hash & 15;
1871
- const u2 = h < 8 ? x2 : y2;
1872
- const v2 = h < 4 ? y2 : h === 12 || h === 14 ? x2 : z2;
1873
- return ((h & 1) === 0 ? u2 : -u2) + ((h & 2) === 0 ? v2 : -v2);
1874
- };
1875
- const x1 = this.lerp(
1876
- w2,
1877
- this.lerp(
1878
- v,
1879
- this.lerp(u, grad3(aaa, xf, yf, zf), grad3(baa, xf - 1, yf, zf)),
1880
- this.lerp(u, grad3(aba, xf, yf - 1, zf), grad3(bba, xf - 1, yf - 1, zf))
1739
+ if (y === void 0) return this.perlin1(x);
1740
+ if (z === void 0) return this.perlin2(x, y);
1741
+ if (w === void 0) return this.perlin3(x, y, z);
1742
+ return this.perlin4(x, y, z, w);
1743
+ }
1744
+ perlin1(x) {
1745
+ const p = this.p;
1746
+ const fx = Math.floor(x);
1747
+ const X = fx & 255;
1748
+ const xf = x - fx;
1749
+ const u = _Noise.fade(xf);
1750
+ const g = (h, d) => (h & 1) === 0 ? d : -d;
1751
+ return _Noise.lerp(u, g(p[X], xf), g(p[X + 1], xf - 1));
1752
+ }
1753
+ perlin2(x, y) {
1754
+ const { fade, lerp } = _Noise;
1755
+ const p = this.p;
1756
+ const fx = Math.floor(x), fy = Math.floor(y);
1757
+ const X = fx & 255, Y = fy & 255;
1758
+ const xf = x - fx, yf = y - fy;
1759
+ const u = fade(xf), v = fade(yf);
1760
+ const g = (h, x2, y2) => {
1761
+ const b = h & 3;
1762
+ return ((b & 1) === 0 ? x2 : -x2) + ((b & 2) === 0 ? y2 : -y2);
1763
+ };
1764
+ const aa = p[p[X] + Y], ab = p[p[X] + Y + 1];
1765
+ const ba = p[p[X + 1] + Y], bb = p[p[X + 1] + Y + 1];
1766
+ return lerp(
1767
+ v,
1768
+ lerp(u, g(aa, xf, yf), g(ba, xf - 1, yf)),
1769
+ lerp(u, g(ab, xf, yf - 1), g(bb, xf - 1, yf - 1))
1770
+ );
1771
+ }
1772
+ perlin3(x, y, z) {
1773
+ const { fade, lerp, grad3 } = _Noise;
1774
+ const p = this.p;
1775
+ const fx = Math.floor(x), fy = Math.floor(y), fz = Math.floor(z);
1776
+ const X = fx & 255, Y = fy & 255, Z = fz & 255;
1777
+ const xf = x - fx, yf = y - fy, zf = z - fz;
1778
+ const u = fade(xf), v = fade(yf), w = fade(zf);
1779
+ const A = p[X] + Y, B = p[X + 1] + Y;
1780
+ const AA = p[A] + Z, AB = p[A + 1] + Z, BA = p[B] + Z, BB = p[B + 1] + Z;
1781
+ return lerp(
1782
+ w,
1783
+ lerp(
1784
+ v,
1785
+ lerp(u, grad3(p[AA], xf, yf, zf), grad3(p[BA], xf - 1, yf, zf)),
1786
+ lerp(u, grad3(p[AB], xf, yf - 1, zf), grad3(p[BB], xf - 1, yf - 1, zf))
1787
+ ),
1788
+ lerp(
1789
+ v,
1790
+ lerp(
1791
+ u,
1792
+ grad3(p[AA + 1], xf, yf, zf - 1),
1793
+ grad3(p[BA + 1], xf - 1, yf, zf - 1)
1881
1794
  ),
1882
- this.lerp(
1883
- v,
1884
- this.lerp(u, grad3(aab, xf, yf, zf - 1), grad3(bab, xf - 1, yf, zf - 1)),
1885
- this.lerp(u, grad3(abb, xf, yf - 1, zf - 1), grad3(bbb, xf - 1, yf - 1, zf - 1))
1795
+ lerp(
1796
+ u,
1797
+ grad3(p[AB + 1], xf, yf - 1, zf - 1),
1798
+ grad3(p[BB + 1], xf - 1, yf - 1, zf - 1)
1886
1799
  )
1887
- );
1888
- return x1;
1889
- } else {
1890
- return this.perlin(x, y, z) * 0.5 + this.perlin(x + w, y + w, z + w) * 0.5;
1891
- }
1800
+ )
1801
+ );
1802
+ }
1803
+ perlin4(x, y, z, w) {
1804
+ const { fade, lerp } = _Noise;
1805
+ const p = this.p;
1806
+ const fx = Math.floor(x), fy = Math.floor(y), fz = Math.floor(z), fw = Math.floor(w);
1807
+ const X = fx & 255, Y = fy & 255, Z = fz & 255, W = fw & 255;
1808
+ const xf = x - fx, yf = y - fy, zf = z - fz, wf = w - fw;
1809
+ const fu = fade(xf), fv = fade(yf), fW = fade(zf), ft = fade(wf);
1810
+ const g = (h2, x2, y2, z2, w2) => {
1811
+ const b = h2 & 31;
1812
+ const u = b < 24 ? x2 : y2;
1813
+ const v = b < 16 ? y2 : z2;
1814
+ const t = b < 8 ? z2 : w2;
1815
+ return ((b & 1) === 0 ? u : -u) + ((b & 2) === 0 ? v : -v) + ((b & 4) === 0 ? t : -t);
1816
+ };
1817
+ const h = (dx, dy, dz, dw) => p[p[p[p[X + dx] + Y + dy] + Z + dz] + W + dw];
1818
+ return lerp(
1819
+ ft,
1820
+ lerp(
1821
+ fW,
1822
+ lerp(
1823
+ fv,
1824
+ lerp(
1825
+ fu,
1826
+ g(h(0, 0, 0, 0), xf, yf, zf, wf),
1827
+ g(h(1, 0, 0, 0), xf - 1, yf, zf, wf)
1828
+ ),
1829
+ lerp(
1830
+ fu,
1831
+ g(h(0, 1, 0, 0), xf, yf - 1, zf, wf),
1832
+ g(h(1, 1, 0, 0), xf - 1, yf - 1, zf, wf)
1833
+ )
1834
+ ),
1835
+ lerp(
1836
+ fv,
1837
+ lerp(
1838
+ fu,
1839
+ g(h(0, 0, 1, 0), xf, yf, zf - 1, wf),
1840
+ g(h(1, 0, 1, 0), xf - 1, yf, zf - 1, wf)
1841
+ ),
1842
+ lerp(
1843
+ fu,
1844
+ g(h(0, 1, 1, 0), xf, yf - 1, zf - 1, wf),
1845
+ g(h(1, 1, 1, 0), xf - 1, yf - 1, zf - 1, wf)
1846
+ )
1847
+ )
1848
+ ),
1849
+ lerp(
1850
+ fW,
1851
+ lerp(
1852
+ fv,
1853
+ lerp(
1854
+ fu,
1855
+ g(h(0, 0, 0, 1), xf, yf, zf, wf - 1),
1856
+ g(h(1, 0, 0, 1), xf - 1, yf, zf, wf - 1)
1857
+ ),
1858
+ lerp(
1859
+ fu,
1860
+ g(h(0, 1, 0, 1), xf, yf - 1, zf, wf - 1),
1861
+ g(h(1, 1, 0, 1), xf - 1, yf - 1, zf, wf - 1)
1862
+ )
1863
+ ),
1864
+ lerp(
1865
+ fv,
1866
+ lerp(
1867
+ fu,
1868
+ g(h(0, 0, 1, 1), xf, yf, zf - 1, wf - 1),
1869
+ g(h(1, 0, 1, 1), xf - 1, yf, zf - 1, wf - 1)
1870
+ ),
1871
+ lerp(
1872
+ fu,
1873
+ g(h(0, 1, 1, 1), xf, yf - 1, zf - 1, wf - 1),
1874
+ g(h(1, 1, 1, 1), xf - 1, yf - 1, zf - 1, wf - 1)
1875
+ )
1876
+ )
1877
+ )
1878
+ );
1892
1879
  }
1893
1880
  simplex(x, y, z, w) {
1894
- if (y === void 0) {
1895
- return this.perlin(x);
1896
- } else if (z === void 0) {
1897
- let n0 = 0, n1 = 0, n2 = 0;
1898
- const s = (x + y) * this.F2;
1899
- const i = Math.floor(x + s);
1900
- const j = Math.floor(y + s);
1901
- const t = (i + j) * this.G2;
1902
- const X0 = i - t;
1903
- const Y0 = j - t;
1904
- const x0 = x - X0;
1905
- const y0 = y - Y0;
1906
- let i1, j1;
1907
- if (x0 > y0) {
1881
+ if (y === void 0) return this.perlin1(x);
1882
+ if (z === void 0) return this.simplex2(x, y);
1883
+ if (w === void 0) return this.simplex3(x, y, z);
1884
+ return this.simplex4(x, y, z, w);
1885
+ }
1886
+ simplex2(x, y) {
1887
+ const { F2, G2, grad3 } = _Noise;
1888
+ const p = this.p;
1889
+ const s = (x + y) * F2;
1890
+ const i = Math.floor(x + s), j = Math.floor(y + s);
1891
+ const t = (i + j) * G2;
1892
+ const x0 = x - (i - t), y0 = y - (j - t);
1893
+ const i1 = x0 > y0 ? 1 : 0, j1 = x0 > y0 ? 0 : 1;
1894
+ const x1 = x0 - i1 + G2, y1 = y0 - j1 + G2;
1895
+ const x2 = x0 - 1 + 2 * G2, y2 = y0 - 1 + 2 * G2;
1896
+ const ii = i & 255, jj = j & 255;
1897
+ const gi0 = p[ii + p[jj]] % 12;
1898
+ const gi1 = p[ii + i1 + p[jj + j1]] % 12;
1899
+ const gi2 = p[ii + 1 + p[jj + 1]] % 12;
1900
+ let n0 = 0, n1 = 0, n2 = 0;
1901
+ let t0 = 0.5 - x0 * x0 - y0 * y0;
1902
+ if (t0 >= 0) {
1903
+ t0 *= t0;
1904
+ n0 = t0 * t0 * grad3(gi0, x0, y0, 0);
1905
+ }
1906
+ let t1 = 0.5 - x1 * x1 - y1 * y1;
1907
+ if (t1 >= 0) {
1908
+ t1 *= t1;
1909
+ n1 = t1 * t1 * grad3(gi1, x1, y1, 0);
1910
+ }
1911
+ let t2 = 0.5 - x2 * x2 - y2 * y2;
1912
+ if (t2 >= 0) {
1913
+ t2 *= t2;
1914
+ n2 = t2 * t2 * grad3(gi2, x2, y2, 0);
1915
+ }
1916
+ return 70 * (n0 + n1 + n2);
1917
+ }
1918
+ simplex3(x, y, z) {
1919
+ const { F3, G3, grad3 } = _Noise;
1920
+ const p = this.p;
1921
+ const s = (x + y + z) * F3;
1922
+ const i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s);
1923
+ const t = (i + j + k) * G3;
1924
+ const x0 = x - (i - t), y0 = y - (j - t), z0 = z - (k - t);
1925
+ let i1, j1, k1;
1926
+ let i2, j2, k2;
1927
+ if (x0 >= y0) {
1928
+ if (y0 >= z0) {
1929
+ i1 = 1;
1930
+ j1 = 0;
1931
+ k1 = 0;
1932
+ i2 = 1;
1933
+ j2 = 1;
1934
+ k2 = 0;
1935
+ } else if (x0 >= z0) {
1908
1936
  i1 = 1;
1909
1937
  j1 = 0;
1938
+ k1 = 0;
1939
+ i2 = 1;
1940
+ j2 = 0;
1941
+ k2 = 1;
1910
1942
  } else {
1911
1943
  i1 = 0;
1912
- j1 = 1;
1913
- }
1914
- const x1 = x0 - i1 + this.G2;
1915
- const y1 = y0 - j1 + this.G2;
1916
- const x2 = x0 - 1 + 2 * this.G2;
1917
- const y2 = y0 - 1 + 2 * this.G2;
1918
- const ii = i & 255;
1919
- const jj = j & 255;
1920
- const gi0 = this.permMod12[ii + this.perm[jj]];
1921
- const gi1 = this.permMod12[ii + i1 + this.perm[jj + j1]];
1922
- const gi2 = this.permMod12[ii + 1 + this.perm[jj + 1]];
1923
- let t0 = 0.5 - x0 * x0 - y0 * y0;
1924
- if (t0 < 0) {
1925
- n0 = 0;
1926
- } else {
1927
- t0 *= t0;
1928
- n0 = t0 * t0 * this.dot2(this.grad3[gi0], x0, y0);
1929
- }
1930
- let t1 = 0.5 - x1 * x1 - y1 * y1;
1931
- if (t1 < 0) {
1932
- n1 = 0;
1933
- } else {
1934
- t1 *= t1;
1935
- n1 = t1 * t1 * this.dot2(this.grad3[gi1], x1, y1);
1936
- }
1937
- let t2 = 0.5 - x2 * x2 - y2 * y2;
1938
- if (t2 < 0) {
1939
- n2 = 0;
1940
- } else {
1941
- t2 *= t2;
1942
- n2 = t2 * t2 * this.dot2(this.grad3[gi2], x2, y2);
1943
- }
1944
- return 70 * (n0 + n1 + n2);
1945
- } else if (w === void 0) {
1946
- let n0 = 0, n1 = 0, n2 = 0, n3 = 0;
1947
- const s = (x + y + z) * this.F3;
1948
- const i = Math.floor(x + s);
1949
- const j = Math.floor(y + s);
1950
- const k = Math.floor(z + s);
1951
- const t = (i + j + k) * this.G3;
1952
- const X0 = i - t;
1953
- const Y0 = j - t;
1954
- const Z0 = k - t;
1955
- const x0 = x - X0;
1956
- const y0 = y - Y0;
1957
- const z0 = z - Z0;
1958
- let i1, j1, k1;
1959
- let i2, j2, k2;
1960
- if (x0 >= y0) {
1961
- if (y0 >= z0) {
1962
- i1 = 1;
1963
- j1 = 0;
1964
- k1 = 0;
1965
- i2 = 1;
1966
- j2 = 1;
1967
- k2 = 0;
1968
- } else if (x0 >= z0) {
1969
- i1 = 1;
1970
- j1 = 0;
1971
- k1 = 0;
1972
- i2 = 1;
1973
- j2 = 0;
1974
- k2 = 1;
1975
- } else {
1976
- i1 = 0;
1977
- j1 = 0;
1978
- k1 = 1;
1979
- i2 = 1;
1980
- j2 = 0;
1981
- k2 = 1;
1982
- }
1983
- } else {
1984
- if (y0 < z0) {
1985
- i1 = 0;
1986
- j1 = 0;
1987
- k1 = 1;
1988
- i2 = 0;
1989
- j2 = 1;
1990
- k2 = 1;
1991
- } else if (x0 < z0) {
1992
- i1 = 0;
1993
- j1 = 1;
1994
- k1 = 0;
1995
- i2 = 0;
1996
- j2 = 1;
1997
- k2 = 1;
1998
- } else {
1999
- i1 = 0;
2000
- j1 = 1;
2001
- k1 = 0;
2002
- i2 = 1;
2003
- j2 = 1;
2004
- k2 = 0;
2005
- }
2006
- }
2007
- const x1 = x0 - i1 + this.G3;
2008
- const y1 = y0 - j1 + this.G3;
2009
- const z1 = z0 - k1 + this.G3;
2010
- const x2 = x0 - i2 + 2 * this.G3;
2011
- const y2 = y0 - j2 + 2 * this.G3;
2012
- const z2 = z0 - k2 + 2 * this.G3;
2013
- const x3 = x0 - 1 + 3 * this.G3;
2014
- const y3 = y0 - 1 + 3 * this.G3;
2015
- const z3 = z0 - 1 + 3 * this.G3;
2016
- const ii = i & 255;
2017
- const jj = j & 255;
2018
- const kk = k & 255;
2019
- const gi0 = this.permMod12[ii + this.perm[jj + this.perm[kk]]];
2020
- const gi1 = this.permMod12[ii + i1 + this.perm[jj + j1 + this.perm[kk + k1]]];
2021
- const gi2 = this.permMod12[ii + i2 + this.perm[jj + j2 + this.perm[kk + k2]]];
2022
- const gi3 = this.permMod12[ii + 1 + this.perm[jj + 1 + this.perm[kk + 1]]];
2023
- let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
2024
- if (t0 < 0) {
2025
- n0 = 0;
2026
- } else {
2027
- t0 *= t0;
2028
- n0 = t0 * t0 * this.dot3(this.grad3[gi0], x0, y0, z0);
2029
- }
2030
- let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
2031
- if (t1 < 0) {
2032
- n1 = 0;
2033
- } else {
2034
- t1 *= t1;
2035
- n1 = t1 * t1 * this.dot3(this.grad3[gi1], x1, y1, z1);
1944
+ j1 = 0;
1945
+ k1 = 1;
1946
+ i2 = 1;
1947
+ j2 = 0;
1948
+ k2 = 1;
2036
1949
  }
2037
- let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
2038
- if (t2 < 0) {
2039
- n2 = 0;
1950
+ } else {
1951
+ if (y0 < z0) {
1952
+ i1 = 0;
1953
+ j1 = 0;
1954
+ k1 = 1;
1955
+ i2 = 0;
1956
+ j2 = 1;
1957
+ k2 = 1;
1958
+ } else if (x0 < z0) {
1959
+ i1 = 0;
1960
+ j1 = 1;
1961
+ k1 = 0;
1962
+ i2 = 0;
1963
+ j2 = 1;
1964
+ k2 = 1;
2040
1965
  } else {
2041
- t2 *= t2;
2042
- n2 = t2 * t2 * this.dot3(this.grad3[gi2], x2, y2, z2);
1966
+ i1 = 0;
1967
+ j1 = 1;
1968
+ k1 = 0;
1969
+ i2 = 1;
1970
+ j2 = 1;
1971
+ k2 = 0;
2043
1972
  }
2044
- let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
2045
- if (t3 < 0) {
2046
- n3 = 0;
1973
+ }
1974
+ const x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;
1975
+ const x2 = x0 - i2 + 2 * G3, y2 = y0 - j2 + 2 * G3, z2 = z0 - k2 + 2 * G3;
1976
+ const x3 = x0 - 1 + 3 * G3, y3 = y0 - 1 + 3 * G3, z3 = z0 - 1 + 3 * G3;
1977
+ const ii = i & 255, jj = j & 255, kk = k & 255;
1978
+ const gi0 = p[ii + p[jj + p[kk]]] % 12;
1979
+ const gi1 = p[ii + i1 + p[jj + j1 + p[kk + k1]]] % 12;
1980
+ const gi2 = p[ii + i2 + p[jj + j2 + p[kk + k2]]] % 12;
1981
+ const gi3 = p[ii + 1 + p[jj + 1 + p[kk + 1]]] % 12;
1982
+ let n = 0;
1983
+ let tc = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
1984
+ if (tc >= 0) {
1985
+ tc *= tc;
1986
+ n += tc * tc * grad3(gi0, x0, y0, z0);
1987
+ }
1988
+ tc = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
1989
+ if (tc >= 0) {
1990
+ tc *= tc;
1991
+ n += tc * tc * grad3(gi1, x1, y1, z1);
1992
+ }
1993
+ tc = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
1994
+ if (tc >= 0) {
1995
+ tc *= tc;
1996
+ n += tc * tc * grad3(gi2, x2, y2, z2);
1997
+ }
1998
+ tc = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
1999
+ if (tc >= 0) {
2000
+ tc *= tc;
2001
+ n += tc * tc * grad3(gi3, x3, y3, z3);
2002
+ }
2003
+ return 32 * n;
2004
+ }
2005
+ simplex4(x, y, z, w) {
2006
+ const { F4, G4 } = _Noise;
2007
+ const p = this.p;
2008
+ const s = (x + y + z + w) * F4;
2009
+ const i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s), l = Math.floor(w + s);
2010
+ const t = (i + j + k + l) * G4;
2011
+ const x0 = x - (i - t), y0 = y - (j - t), z0 = z - (k - t), w0 = w - (l - t);
2012
+ let rx = 0, ry = 0, rz = 0, rw = 0;
2013
+ if (x0 > y0) rx++;
2014
+ else ry++;
2015
+ if (x0 > z0) rx++;
2016
+ else rz++;
2017
+ if (x0 > w0) rx++;
2018
+ else rw++;
2019
+ if (y0 > z0) ry++;
2020
+ else rz++;
2021
+ if (y0 > w0) ry++;
2022
+ else rw++;
2023
+ if (z0 > w0) rz++;
2024
+ else rw++;
2025
+ const i1 = +(rx >= 3), j1 = +(ry >= 3), k1 = +(rz >= 3), l1 = +(rw >= 3);
2026
+ const i2 = +(rx >= 2), j2 = +(ry >= 2), k2 = +(rz >= 2), l2 = +(rw >= 2);
2027
+ const i3 = +(rx >= 1), j3 = +(ry >= 1), k3 = +(rz >= 1), l3 = +(rw >= 1);
2028
+ const x1 = x0 - i1 + G4, y1 = y0 - j1 + G4, z1 = z0 - k1 + G4, w1 = w0 - l1 + G4;
2029
+ const x2 = x0 - i2 + 2 * G4, y2 = y0 - j2 + 2 * G4, z2 = z0 - k2 + 2 * G4, w2 = w0 - l2 + 2 * G4;
2030
+ const x3 = x0 - i3 + 3 * G4, y3 = y0 - j3 + 3 * G4, z3 = z0 - k3 + 3 * G4, w3 = w0 - l3 + 3 * G4;
2031
+ const x4 = x0 - 1 + 4 * G4, y4 = y0 - 1 + 4 * G4, z4 = z0 - 1 + 4 * G4, w4 = w0 - 1 + 4 * G4;
2032
+ const ii = i & 255, jj = j & 255, kk = k & 255, ll = l & 255;
2033
+ const g4 = (gi, x5, y5, z5, w5) => {
2034
+ const g = gi >> 3;
2035
+ const b = gi & 7;
2036
+ let a, c, d;
2037
+ if (g === 0) {
2038
+ a = y5;
2039
+ c = z5;
2040
+ d = w5;
2041
+ } else if (g === 1) {
2042
+ a = x5;
2043
+ c = z5;
2044
+ d = w5;
2045
+ } else if (g === 2) {
2046
+ a = x5;
2047
+ c = y5;
2048
+ d = w5;
2047
2049
  } else {
2048
- t3 *= t3;
2049
- n3 = t3 * t3 * this.dot3(this.grad3[gi3], x3, y3, z3);
2050
+ a = x5;
2051
+ c = y5;
2052
+ d = z5;
2050
2053
  }
2051
- return 32 * (n0 + n1 + n2 + n3);
2052
- } else {
2053
- return this.simplex(x, y, z) * 0.5 + this.simplex(x + w, y + w, z + w) * 0.5;
2054
+ return (b & 4 ? -a : a) + (b & 2 ? -c : c) + (b & 1 ? -d : d);
2055
+ };
2056
+ const gi0 = p[ii + p[jj + p[kk + p[ll]]]] & 31;
2057
+ const gi1 = p[ii + i1 + p[jj + j1 + p[kk + k1 + p[ll + l1]]]] & 31;
2058
+ const gi2 = p[ii + i2 + p[jj + j2 + p[kk + k2 + p[ll + l2]]]] & 31;
2059
+ const gi3 = p[ii + i3 + p[jj + j3 + p[kk + k3 + p[ll + l3]]]] & 31;
2060
+ const gi4 = p[ii + 1 + p[jj + 1 + p[kk + 1 + p[ll + 1]]]] & 31;
2061
+ let n = 0;
2062
+ let tc = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
2063
+ if (tc >= 0) {
2064
+ tc *= tc;
2065
+ n += tc * tc * g4(gi0, x0, y0, z0, w0);
2054
2066
  }
2055
- }
2056
- /**
2057
- * Dot product for 2D
2058
- */
2059
- dot2(g, x, y) {
2060
- return g[0] * x + g[1] * y;
2061
- }
2062
- /**
2063
- * Dot product for 3D
2064
- */
2065
- dot3(g, x, y, z) {
2066
- return g[0] * x + g[1] * y + g[2] * z;
2067
+ tc = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
2068
+ if (tc >= 0) {
2069
+ tc *= tc;
2070
+ n += tc * tc * g4(gi1, x1, y1, z1, w1);
2071
+ }
2072
+ tc = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
2073
+ if (tc >= 0) {
2074
+ tc *= tc;
2075
+ n += tc * tc * g4(gi2, x2, y2, z2, w2);
2076
+ }
2077
+ tc = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
2078
+ if (tc >= 0) {
2079
+ tc *= tc;
2080
+ n += tc * tc * g4(gi3, x3, y3, z3, w3);
2081
+ }
2082
+ tc = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
2083
+ if (tc >= 0) {
2084
+ tc *= tc;
2085
+ n += tc * tc * g4(gi4, x4, y4, z4, w4);
2086
+ }
2087
+ return 27 * n;
2067
2088
  }
2068
2089
  hash(x, y, z, w) {
2069
- let n = 0;
2090
+ let n;
2070
2091
  if (y === void 0) {
2071
- n = Math.sin(x * 12.9898 + this.currentSeed) * 43758.5453;
2092
+ n = Math.sin(x * 12.9898 + this._seed) * 43758.5453;
2072
2093
  } else if (z === void 0) {
2073
- n = Math.sin(x * 12.9898 + y * 78.233 + this.currentSeed) * 43758.5453;
2094
+ n = Math.sin(x * 12.9898 + y * 78.233 + this._seed) * 43758.5453;
2074
2095
  } else if (w === void 0) {
2075
- n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + this.currentSeed) * 43758.5453;
2096
+ n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + this._seed) * 43758.5453;
2076
2097
  } else {
2077
- n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + w * 59.1337 + this.currentSeed) * 43758.5453;
2098
+ n = Math.sin(
2099
+ x * 12.9898 + y * 78.233 + z * 37.719 + w * 53.137 + this._seed
2100
+ ) * 43758.5453;
2078
2101
  }
2079
2102
  return n - Math.floor(n);
2080
2103
  }
2081
- /**
2082
- * Fractal Brownian Motion (fBm) noise
2083
- * Supports options object for amplitude/frequency/lacunarity/gain/octaves.
2084
- */
2085
- fbm(x, y, z, options) {
2086
- let yVal = void 0;
2087
- let zVal = void 0;
2088
- let opts = typeof y === "object" ? y : typeof z === "object" ? z : {};
2089
- if (typeof y === "number") yVal = y;
2090
- if (typeof z === "number") zVal = z;
2091
- if (options) opts = { ...opts, ...options };
2092
- const octaves = opts.octaves ?? 4;
2093
- if (octaves <= 0) return 0;
2094
- let amplitude = opts.amplitude ?? 1;
2095
- let frequency = opts.frequency ?? 1;
2096
- const lacunarity = opts.lacunarity ?? 2;
2097
- const gain = opts.gain ?? 0.5;
2098
- let value = 0;
2099
- let maxValue = 0;
2100
- for (let i = 0; i < octaves; i++) {
2101
- if (yVal === void 0) {
2102
- value += amplitude * this.perlin(x * frequency);
2103
- } else if (zVal === void 0) {
2104
- value += amplitude * this.perlin(x * frequency, yVal * frequency);
2105
- } else {
2106
- value += amplitude * this.perlin(x * frequency, yVal * frequency, zVal * frequency);
2107
- }
2108
- maxValue += amplitude;
2109
- amplitude *= gain;
2110
- frequency *= lacunarity;
2111
- }
2112
- return maxValue === 0 ? 0 : value / maxValue;
2113
- }
2114
- /**
2115
- * Turbulence noise (absolute value of noise)
2116
- * Supports options object for octaves.
2117
- */
2118
- turbulence(x, y, z, options) {
2119
- let yVal = void 0;
2120
- let zVal = void 0;
2121
- let opts = typeof y === "object" ? y : typeof z === "object" ? z : {};
2122
- if (typeof y === "number") yVal = y;
2123
- if (typeof z === "number") zVal = z;
2124
- if (options) opts = { ...opts, ...options };
2125
- const octaves = opts.octaves ?? 4;
2126
- if (octaves <= 0) return 0;
2127
- let value = 0;
2128
- let amplitude = 1;
2129
- let frequency = 1;
2130
- let maxValue = 0;
2131
- for (let i = 0; i < octaves; i++) {
2132
- let noise = 0;
2133
- if (yVal === void 0) {
2134
- noise = this.perlin(x * frequency);
2135
- } else if (zVal === void 0) {
2136
- noise = this.perlin(x * frequency, yVal * frequency);
2137
- } else {
2138
- noise = this.perlin(x * frequency, yVal * frequency, zVal * frequency);
2139
- }
2140
- value += amplitude * Math.abs(noise);
2141
- maxValue += amplitude;
2142
- amplitude *= 0.5;
2143
- frequency *= 2;
2144
- }
2145
- return maxValue === 0 ? 0 : value / maxValue;
2146
- }
2147
- /**
2148
- * Ridged multifractal noise (simple implementation)
2149
- */
2150
- ridge(x, y, options) {
2151
- let yVal = void 0;
2152
- let opts = typeof y === "object" ? y : {};
2153
- if (typeof y === "number") yVal = y;
2154
- if (options) opts = { ...opts, ...options };
2155
- const octaves = opts.octaves ?? 4;
2156
- if (octaves <= 0) return 0;
2157
- let amplitude = opts.amplitude ?? 1;
2158
- let frequency = opts.frequency ?? 1;
2159
- const lacunarity = opts.lacunarity ?? 2;
2160
- const gain = opts.gain ?? 0.5;
2161
- let value = 0;
2162
- let weight = 1;
2163
- let maxValue = 0;
2164
- for (let i = 0; i < octaves; i++) {
2165
- const n = yVal === void 0 ? this.perlin(x * frequency) : this.perlin(x * frequency, yVal * frequency);
2166
- let signal = 1 - Math.abs(n);
2167
- signal *= signal;
2168
- signal *= weight;
2169
- weight = signal * 2;
2170
- weight = Math.min(Math.max(weight, 0), 1);
2171
- value += signal * amplitude;
2172
- maxValue += amplitude;
2173
- amplitude *= gain;
2174
- frequency *= lacunarity;
2175
- }
2176
- return maxValue === 0 ? 0 : value / maxValue;
2177
- }
2178
- /**
2179
- * Cellular / Worley noise (2D simple implementation)
2180
- */
2181
- cellular(x, y, options) {
2182
- let yVal = void 0;
2183
- let opts = typeof y === "object" ? y : {};
2184
- if (typeof y === "number") yVal = y;
2185
- if (options) opts = { ...opts, ...options };
2186
- if (yVal === void 0) yVal = 0;
2187
- const xi = Math.floor(x);
2188
- const yi = Math.floor(yVal);
2189
- const distance = opts.distance ?? "euclidean";
2190
- let minDist = Infinity;
2191
- for (let j = -1; j <= 1; j++) {
2192
- for (let i = -1; i <= 1; i++) {
2193
- const fx = i + this.hash(xi + i, yi + j);
2194
- const fy = j + this.hash(yi + j, xi + i);
2195
- const dx = fx + xi - x;
2196
- const dy = fy + yi - yVal;
2197
- const dist = distance === "manhattan" ? Math.abs(dx) + Math.abs(dy) : Math.sqrt(dx * dx + dy * dy);
2198
- if (dist < minDist) minDist = dist;
2199
- }
2200
- }
2201
- const maxDist = Math.SQRT2;
2202
- const normalized = 1 - Math.min(minDist / maxDist, 1);
2203
- return normalized;
2104
+ // ---- GAUSSIAN RANDOM ----
2105
+ /** Box-Muller transform returns normally distributed random number (seeded) */
2106
+ gaussianRandom(mean = 0, stddev = 1) {
2107
+ const u1 = 1 - this.rng();
2108
+ const u2 = this.rng();
2109
+ return mean + stddev * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
2204
2110
  }
2205
2111
  };
2112
+ _Noise.F2 = (Math.sqrt(3) - 1) / 2;
2113
+ _Noise.G2 = (3 - Math.sqrt(3)) / 6;
2114
+ _Noise.F3 = 1 / 3;
2115
+ _Noise.G3 = 1 / 6;
2116
+ _Noise.F4 = (Math.sqrt(5) - 1) / 4;
2117
+ _Noise.G4 = (5 - Math.sqrt(5)) / 20;
2118
+ var Noise = _Noise;
2206
2119
  var Noise_default = Noise;
2207
2120
 
2208
2121
  // src/elements/Hotspot.tsx
@@ -2299,349 +2212,385 @@ var Hotspot = class {
2299
2212
  };
2300
2213
  var Hotspot_default = Hotspot;
2301
2214
 
2302
- // src/elements/Performance.tsx
2303
- var Performance = class {
2304
- constructor(ctx) {
2305
- this.textMetricsCache = /* @__PURE__ */ new Map();
2306
- this.frameTimeHistory = [];
2307
- this.MAX_HISTORY = 60;
2308
- this.context = ctx;
2215
+ // src/elements/Quadtree.tsx
2216
+ var Rectangle = class {
2217
+ constructor(x, y, w, h) {
2218
+ this.x = x;
2219
+ this.y = y;
2220
+ this.w = w;
2221
+ this.h = h;
2309
2222
  }
2310
- /**
2311
- * Batch multiple canvas operations together for better performance
2312
- */
2313
- batchDraw(drawFn) {
2314
- this.context.save();
2315
- this.context.beginPath();
2316
- try {
2317
- drawFn();
2318
- } finally {
2319
- this.context.restore();
2320
- }
2223
+ contains(point) {
2224
+ 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;
2321
2225
  }
2322
- /**
2323
- * Optimize drawing by using offscreen canvas for static elements
2324
- */
2325
- useOffscreenCache(id, width, height, renderFn) {
2326
- let offscreen = this.context.__offscreens.get(id);
2327
- if (!offscreen || offscreen instanceof HTMLImageElement) {
2328
- offscreen = this.context.createOffscreen(id, width, height);
2329
- if (offscreen && !(offscreen instanceof HTMLImageElement)) {
2330
- renderFn(offscreen);
2331
- }
2332
- }
2333
- if (offscreen) {
2334
- if (offscreen instanceof HTMLImageElement) {
2335
- this.context.image(offscreen, 0, 0);
2336
- } else {
2337
- this.context.image(offscreen.canvas, 0, 0);
2338
- }
2339
- }
2226
+ intersects(range) {
2227
+ 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);
2340
2228
  }
2341
- /**
2342
- * Throttle expensive operations to run less frequently
2343
- */
2344
- throttleFrame(interval, fn) {
2345
- const cacheKey = `__throttle_${fn.toString().slice(0, 50)}`;
2346
- const cached = this.context[cacheKey];
2347
- if (!cached || this.context.frame % interval === 0) {
2348
- const result = fn();
2349
- this.context[cacheKey] = { value: result, frame: this.context.frame };
2350
- return result;
2351
- }
2352
- return cached.value;
2229
+ intersectsCircle(cx, cy, r) {
2230
+ const dx = Math.abs(cx - this.x);
2231
+ const dy = Math.abs(cy - this.y);
2232
+ if (dx > this.w + r || dy > this.h + r) return false;
2233
+ if (dx <= this.w || dy <= this.h) return true;
2234
+ return (dx - this.w) ** 2 + (dy - this.h) ** 2 <= r * r;
2353
2235
  }
2354
- /**
2355
- * Detect potential memory leaks by tracking object creation
2356
- */
2357
- useLeakDetection() {
2358
- const objectCounts = /* @__PURE__ */ new Map();
2359
- let lastCheckFrame = 0;
2360
- return {
2361
- track: (type) => {
2362
- const count = objectCounts.get(type) || 0;
2363
- objectCounts.set(type, count + 1);
2364
- },
2365
- check: () => {
2366
- if (this.context.frame - lastCheckFrame < 60) return;
2367
- lastCheckFrame = this.context.frame;
2368
- const warnings = [];
2369
- objectCounts.forEach((count, type) => {
2370
- if (count > 1e4) {
2371
- warnings.push(
2372
- `Potential memory leak: ${type} has ${count} instances`
2373
- );
2374
- }
2375
- });
2376
- if (warnings.length > 0) {
2377
- console.warn("[Klint] Memory leak warnings:", warnings);
2378
- }
2379
- objectCounts.clear();
2380
- }
2381
- };
2236
+ };
2237
+ var Quadtree = class _Quadtree {
2238
+ constructor(boundary, capacity = 4, maxDepth = 8, depth = 0) {
2239
+ this.ne = null;
2240
+ this.nw = null;
2241
+ this.se = null;
2242
+ this.sw = null;
2243
+ this.boundary = boundary;
2244
+ this.capacity = capacity;
2245
+ this.maxDepth = maxDepth;
2246
+ this.depth = depth;
2247
+ this.points = [];
2248
+ this.divided = false;
2249
+ }
2250
+ /**
2251
+ * Create a Quadtree from corner-based bounds (top-left + size).
2252
+ * Internally converts to center-based representation.
2253
+ */
2254
+ static create(x, y, width, height, options) {
2255
+ const hw = width / 2;
2256
+ const hh = height / 2;
2257
+ return new _Quadtree(
2258
+ new Rectangle(x + hw, y + hh, hw, hh),
2259
+ options?.capacity ?? 4,
2260
+ options?.maxDepth ?? 8
2261
+ );
2382
2262
  }
2383
- /**
2384
- * Optimize text rendering by caching text measurements
2385
- */
2386
- getCachedTextMetrics(text, font) {
2387
- const cacheKey = `${font}:${text}`;
2388
- if (!this.textMetricsCache.has(cacheKey)) {
2389
- this.context.save();
2390
- this.context.font = font;
2391
- const metrics = this.context.measureText(text);
2392
- this.context.restore();
2393
- this.textMetricsCache.set(cacheKey, metrics);
2394
- if (this.textMetricsCache.size > 1e3) {
2395
- const firstKey = this.textMetricsCache.keys().next().value;
2396
- if (firstKey) this.textMetricsCache.delete(firstKey);
2263
+ insert(point) {
2264
+ if (!this.boundary.contains(point)) return false;
2265
+ if (!this.divided) {
2266
+ if (this.points.length < this.capacity || this.depth >= this.maxDepth) {
2267
+ this.points.push(point);
2268
+ return true;
2397
2269
  }
2270
+ this.subdivide();
2271
+ }
2272
+ return this.ne.insert(point) || this.nw.insert(point) || this.se.insert(point) || this.sw.insert(point);
2273
+ }
2274
+ subdivide() {
2275
+ const { x, y, w, h } = this.boundary;
2276
+ const hw = w / 2;
2277
+ const hh = h / 2;
2278
+ const d = this.depth + 1;
2279
+ this.ne = new _Quadtree(
2280
+ new Rectangle(x + hw, y - hh, hw, hh),
2281
+ this.capacity,
2282
+ this.maxDepth,
2283
+ d
2284
+ );
2285
+ this.nw = new _Quadtree(
2286
+ new Rectangle(x - hw, y - hh, hw, hh),
2287
+ this.capacity,
2288
+ this.maxDepth,
2289
+ d
2290
+ );
2291
+ this.se = new _Quadtree(
2292
+ new Rectangle(x + hw, y + hh, hw, hh),
2293
+ this.capacity,
2294
+ this.maxDepth,
2295
+ d
2296
+ );
2297
+ this.sw = new _Quadtree(
2298
+ new Rectangle(x - hw, y + hh, hw, hh),
2299
+ this.capacity,
2300
+ this.maxDepth,
2301
+ d
2302
+ );
2303
+ this.divided = true;
2304
+ for (const p of this.points) {
2305
+ this.ne.insert(p) || this.nw.insert(p) || this.se.insert(p) || this.sw.insert(p);
2398
2306
  }
2399
- return this.textMetricsCache.get(cacheKey);
2400
- }
2401
- /**
2402
- * Clear all performance caches
2403
- */
2404
- clearCaches() {
2405
- this.textMetricsCache.clear();
2406
- }
2407
- /**
2408
- * Get current performance metrics
2409
- */
2410
- getMetrics() {
2411
- return this.context.__performance || null;
2307
+ this.points = [];
2412
2308
  }
2413
- /**
2414
- * Render performance widget on canvas
2415
- */
2416
- render(options = {}) {
2417
- const metrics = this.getMetrics();
2418
- if (!metrics) return;
2419
- const {
2420
- x = 10,
2421
- y = 10,
2422
- width = 200,
2423
- height = 120,
2424
- backgroundColor = "rgba(0, 0, 0, 0.8)",
2425
- textColor = "#ffffff",
2426
- accentColor = "#4ecdc4",
2427
- showGraph = true,
2428
- graphHeight = 40,
2429
- fontSize = 12,
2430
- showMemory = true
2431
- } = options;
2432
- if (showGraph) {
2433
- this.frameTimeHistory.push(metrics.frameTime);
2434
- if (this.frameTimeHistory.length > this.MAX_HISTORY) {
2435
- this.frameTimeHistory.shift();
2436
- }
2309
+ query(range, found = []) {
2310
+ if (!this.boundary.intersects(range)) return found;
2311
+ for (const p of this.points) {
2312
+ if (range.contains(p)) found.push(p);
2437
2313
  }
2438
- this.context.save();
2439
- this.context.fillStyle = backgroundColor;
2440
- this.context.fillRect(x, y, width, height);
2441
- this.context.strokeStyle = accentColor;
2442
- this.context.lineWidth = 1;
2443
- this.context.strokeRect(x, y, width, height);
2444
- this.context.fillStyle = textColor;
2445
- this.context.font = `${fontSize}px monospace`;
2446
- this.context.textAlign = "left";
2447
- this.context.textBaseline = "top";
2448
- let currentY = y + 8;
2449
- const fpsColor = metrics.fps >= 55 ? "#4ecdc4" : metrics.fps >= 30 ? "#ffd93d" : "#ff6b6b";
2450
- this.context.fillStyle = fpsColor;
2451
- this.context.fillText(`FPS: ${metrics.fps}`, x + 8, currentY);
2452
- currentY += fontSize + 4;
2453
- this.context.fillStyle = textColor;
2454
- this.context.fillText(`Frame: ${metrics.frameTime.toFixed(2)}ms`, x + 8, currentY);
2455
- currentY += fontSize + 4;
2456
- this.context.fillText(`Avg: ${metrics.averageFrameTime.toFixed(2)}ms`, x + 8, currentY);
2457
- currentY += fontSize + 4;
2458
- this.context.fillText(
2459
- `Min: ${metrics.minFrameTime.toFixed(2)}ms / Max: ${metrics.maxFrameTime.toFixed(2)}ms`,
2460
- x + 8,
2461
- currentY
2462
- );
2463
- currentY += fontSize + 4;
2464
- if (metrics.droppedFrames > 0) {
2465
- this.context.fillStyle = "#ff6b6b";
2466
- this.context.fillText(`Dropped: ${metrics.droppedFrames}`, x + 8, currentY);
2467
- currentY += fontSize + 4;
2314
+ if (this.divided) {
2315
+ this.ne.query(range, found);
2316
+ this.nw.query(range, found);
2317
+ this.se.query(range, found);
2318
+ this.sw.query(range, found);
2468
2319
  }
2469
- if (showMemory && metrics.memoryUsage !== void 0) {
2470
- this.context.fillStyle = textColor;
2471
- this.context.fillText(`Memory: ${metrics.memoryUsage.toFixed(2)}MB`, x + 8, currentY);
2472
- currentY += fontSize + 4;
2320
+ return found;
2321
+ }
2322
+ queryRadius(cx, cy, radius, found = []) {
2323
+ if (!this.boundary.intersectsCircle(cx, cy, radius)) return found;
2324
+ const rSq = radius * radius;
2325
+ for (const p of this.points) {
2326
+ if ((p.x - cx) ** 2 + (p.y - cy) ** 2 <= rSq) found.push(p);
2473
2327
  }
2474
- if (showGraph && this.frameTimeHistory.length > 1) {
2475
- const graphX = x + 8;
2476
- const graphY = currentY + 4;
2477
- const graphWidth = width - 16;
2478
- const targetFrameTime = 1e3 / this.context.fps;
2479
- this.context.fillStyle = "rgba(255, 255, 255, 0.1)";
2480
- this.context.fillRect(graphX, graphY, graphWidth, graphHeight);
2481
- const targetY = graphY + graphHeight - targetFrameTime / (targetFrameTime * 2) * graphHeight;
2482
- this.context.strokeStyle = "rgba(255, 255, 255, 0.3)";
2483
- this.context.lineWidth = 1;
2484
- this.context.beginPath();
2485
- this.context.moveTo(graphX, targetY);
2486
- this.context.lineTo(graphX + graphWidth, targetY);
2487
- this.context.stroke();
2488
- this.context.strokeStyle = accentColor;
2489
- this.context.lineWidth = 2;
2490
- this.context.beginPath();
2491
- const maxFrameTime = Math.max(...this.frameTimeHistory, targetFrameTime * 2);
2492
- const stepX = graphWidth / (this.frameTimeHistory.length - 1);
2493
- this.frameTimeHistory.forEach((frameTime, index) => {
2494
- const normalizedTime = Math.min(frameTime / maxFrameTime, 1);
2495
- const pointY = graphY + graphHeight - normalizedTime * graphHeight;
2496
- const pointX = graphX + index * stepX;
2497
- if (index === 0) {
2498
- this.context.moveTo(pointX, pointY);
2499
- } else {
2500
- this.context.lineTo(pointX, pointY);
2501
- }
2502
- });
2503
- this.context.stroke();
2328
+ if (this.divided) {
2329
+ this.ne.queryRadius(cx, cy, radius, found);
2330
+ this.nw.queryRadius(cx, cy, radius, found);
2331
+ this.se.queryRadius(cx, cy, radius, found);
2332
+ this.sw.queryRadius(cx, cy, radius, found);
2504
2333
  }
2505
- this.context.restore();
2334
+ return found;
2506
2335
  }
2507
- /**
2508
- * Alias for render() - shows performance widget
2509
- */
2510
- show(options) {
2511
- this.render(options);
2336
+ clear() {
2337
+ this.points = [];
2338
+ this.divided = false;
2339
+ this.ne = this.nw = this.se = this.sw = null;
2340
+ }
2341
+ get size() {
2342
+ let n = this.points.length;
2343
+ if (this.divided) {
2344
+ n += this.ne.size + this.nw.size + this.se.size + this.sw.size;
2345
+ }
2346
+ return n;
2512
2347
  }
2513
2348
  };
2514
- var Performance_default = Performance;
2349
+ var Quadtree_default = Quadtree;
2515
2350
 
2516
- // src/elements/SSR.tsx
2517
- var SSR = class {
2351
+ // src/elements/Pixels.tsx
2352
+ var Pixels = class {
2518
2353
  constructor(ctx) {
2519
- this.context = ctx || null;
2354
+ this.ctx = ctx;
2520
2355
  }
2521
2356
  /**
2522
- * Render a Klint sketch to a static image (base64 data URL)
2357
+ * Read all pixels from the canvas as ImageData.
2523
2358
  */
2524
- async renderToImage(draw, options) {
2525
- if (typeof window !== "undefined" && typeof document !== "undefined") {
2526
- return this.renderInBrowser(draw, options);
2527
- }
2528
- throw new Error(
2529
- "Server-side rendering requires 'canvas' package. Install it with: npm install canvas\nAlternatively, use SSR.generateImageUrl() to generate images via an API endpoint."
2530
- );
2359
+ load() {
2360
+ return this.ctx.getImageData(0, 0, this.ctx.width, this.ctx.height);
2531
2361
  }
2532
2362
  /**
2533
- * Generate a static image URL for a Klint sketch
2363
+ * Write pixel data back to the canvas.
2364
+ * Accepts a Uint8ClampedArray or a plain number array of RGBA values.
2534
2365
  */
2535
- async generateImageUrl(draw, options) {
2536
- if (typeof window === "undefined") {
2537
- throw new Error(
2538
- "generateImageUrl() requires a browser environment. For server-side rendering, use renderToImage() with the 'canvas' package."
2539
- );
2540
- }
2541
- return this.renderInBrowser(draw, options);
2366
+ update(pixels) {
2367
+ const pixelArray = pixels instanceof Uint8ClampedArray ? pixels : new Uint8ClampedArray(pixels);
2368
+ const imageData = new ImageData(
2369
+ new Uint8ClampedArray(pixelArray.buffer),
2370
+ this.ctx.width,
2371
+ this.ctx.height
2372
+ );
2373
+ this.ctx.putImageData(imageData, 0, 0);
2542
2374
  }
2543
2375
  /**
2544
- * Check if Klint can run in the current environment
2376
+ * Read pixel values at a position.
2377
+ * Returns an array of [r, g, b, a] values (0-255).
2378
+ *
2379
+ * @param x - X coordinate
2380
+ * @param y - Y coordinate
2381
+ * @param w - Width of region (default: 1)
2382
+ * @param h - Height of region (default: 1)
2545
2383
  */
2546
- canRender() {
2547
- if (typeof window !== "undefined" && typeof document !== "undefined") {
2548
- return true;
2549
- }
2550
- try {
2551
- require("canvas");
2552
- return true;
2553
- } catch {
2554
- return false;
2555
- }
2384
+ read(x, y, w = 1, h = 1) {
2385
+ const imageData = this.ctx.getImageData(x, y, w, h);
2386
+ return Array.from(imageData.data);
2556
2387
  }
2557
- /**
2558
- * Render in browser environment (client-side)
2559
- */
2560
- async renderInBrowser(draw, options) {
2561
- return new Promise((resolve, reject) => {
2388
+ };
2389
+
2390
+ // src/elements/Timeline.tsx
2391
+ var Timeline = class {
2392
+ constructor() {
2393
+ this.callbacks = { start: [], end: [], loop: [] };
2394
+ }
2395
+ onStart(fn) {
2396
+ this.callbacks.start.push(fn);
2397
+ }
2398
+ onEnd(fn) {
2399
+ this.callbacks.end.push(fn);
2400
+ }
2401
+ onLoop(fn) {
2402
+ this.callbacks.loop.push(fn);
2403
+ }
2404
+ create(setup, options = {}) {
2405
+ const tracks = /* @__PURE__ */ new Map();
2406
+ let currentProgress = 0;
2407
+ let hasStarted = false;
2408
+ let hasEnded = false;
2409
+ const callbacks = this.callbacks;
2410
+ const defaultEasing = options.defaultEasing || ((t) => t);
2411
+ const defaultLoop = options.defaultLoop || 0;
2412
+ const executeCallback = (callback, errorMsg = "Callback error") => {
2562
2413
  try {
2563
- const canvas = document.createElement("canvas");
2564
- const dpr = options.dpr || (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
2565
- canvas.width = options.width * dpr;
2566
- canvas.height = options.height * dpr;
2567
- const ctx = canvas.getContext("2d");
2568
- if (!ctx) {
2569
- reject(new Error("Failed to get canvas context"));
2570
- return;
2414
+ callback();
2415
+ } catch (e) {
2416
+ console.warn(`${errorMsg}:`, e);
2417
+ }
2418
+ };
2419
+ function createKeyframes() {
2420
+ const segments = [];
2421
+ let currentPos = 0;
2422
+ let loopCount = defaultLoop;
2423
+ const parentTrack = void 0;
2424
+ const parseEasingCallback = (easing, callback) => {
2425
+ if (typeof easing === "function" && typeof callback === "undefined") {
2426
+ if (easing.length === 0) {
2427
+ return { easing: defaultEasing, callback: easing };
2428
+ }
2429
+ return {
2430
+ easing,
2431
+ callback: void 0
2432
+ };
2433
+ }
2434
+ return {
2435
+ easing: easing || defaultEasing,
2436
+ callback
2437
+ };
2438
+ };
2439
+ const builder = {
2440
+ start(value, delay = 0, callback) {
2441
+ segments.push({ pos: delay, value, callback, type: "start" });
2442
+ currentPos = delay;
2443
+ return builder;
2444
+ },
2445
+ at(progress, value, easing, callback) {
2446
+ const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
2447
+ segments.push({
2448
+ pos: progress,
2449
+ value,
2450
+ easing: finalEasing,
2451
+ callback: finalCallback,
2452
+ type: "tween"
2453
+ });
2454
+ currentPos = progress;
2455
+ return builder;
2456
+ },
2457
+ then(value, duration, easing, callback) {
2458
+ const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
2459
+ const nextPos = currentPos + duration;
2460
+ segments.push({
2461
+ pos: nextPos,
2462
+ value,
2463
+ easing: finalEasing,
2464
+ callback: finalCallback,
2465
+ type: "tween"
2466
+ });
2467
+ currentPos = nextPos;
2468
+ return builder;
2469
+ },
2470
+ loop(count = Infinity) {
2471
+ loopCount = count;
2472
+ return builder;
2473
+ },
2474
+ _compile: () => {
2475
+ segments.sort((a, b) => a.pos - b.pos);
2476
+ return { segments, loopCount, parentTrack };
2477
+ }
2478
+ };
2479
+ return builder;
2480
+ }
2481
+ function interpolateTrack(compiled, progress) {
2482
+ const { segments } = compiled;
2483
+ if (!segments.length) return 0;
2484
+ progress = Math.max(0, Math.min(1, progress));
2485
+ const valueSegments = segments.filter(
2486
+ (seg) => seg.value !== void 0
2487
+ );
2488
+ if (!valueSegments.length) return 0;
2489
+ let prevSeg = valueSegments[0];
2490
+ for (let i = 1; i < valueSegments.length; i++) {
2491
+ const seg = valueSegments[i];
2492
+ if (progress <= seg.pos) {
2493
+ if (seg.type === "tween" && prevSeg.value !== void 0 && seg.value !== void 0) {
2494
+ const t = (progress - prevSeg.pos) / (seg.pos - prevSeg.pos);
2495
+ const easedT = seg.easing ? seg.easing(t) : t;
2496
+ return prevSeg.value + (seg.value - prevSeg.value) * easedT;
2497
+ }
2498
+ return seg.value || 0;
2499
+ }
2500
+ if (seg.value !== void 0) {
2501
+ prevSeg = seg;
2571
2502
  }
2572
- const klintContext = this.createMinimalContext(
2573
- ctx,
2574
- canvas,
2575
- options.width,
2576
- options.height,
2577
- dpr
2578
- );
2579
- draw(klintContext);
2580
- const mimeType = options.format === "jpeg" ? "image/jpeg" : options.format === "webp" ? "image/webp" : "image/png";
2581
- const quality = options.quality !== void 0 ? options.quality : 0.85;
2582
- const dataUrl = canvas.toDataURL(mimeType, quality);
2583
- resolve(dataUrl);
2584
- } catch (error) {
2585
- reject(error);
2586
2503
  }
2587
- });
2588
- }
2589
- /**
2590
- * Create a minimal KlintContext for server rendering
2591
- * This provides basic functionality without full Klint features
2592
- */
2593
- createMinimalContext(ctx, canvas, width, height, dpr) {
2594
- return {
2595
- canvas,
2596
- width: width * dpr,
2597
- height: height * dpr,
2598
- // Basic canvas methods
2599
- fillRect: ctx.fillRect.bind(ctx),
2600
- strokeRect: ctx.strokeRect.bind(ctx),
2601
- clearRect: ctx.clearRect.bind(ctx),
2602
- beginPath: ctx.beginPath.bind(ctx),
2603
- moveTo: ctx.moveTo.bind(ctx),
2604
- lineTo: ctx.lineTo.bind(ctx),
2605
- arc: ctx.arc.bind(ctx),
2606
- fill: ctx.fill.bind(ctx),
2607
- stroke: ctx.stroke.bind(ctx),
2608
- save: ctx.save.bind(ctx),
2609
- restore: ctx.restore.bind(ctx),
2610
- translate: ctx.translate.bind(ctx),
2611
- rotate: ctx.rotate.bind(ctx),
2612
- scale: ctx.scale.bind(ctx),
2613
- // Basic properties
2614
- fillStyle: ctx.fillStyle,
2615
- strokeStyle: ctx.strokeStyle,
2616
- lineWidth: ctx.lineWidth,
2617
- // Minimal Klint-like API
2618
- background: (color) => {
2619
- ctx.fillStyle = color || "#000";
2620
- ctx.fillRect(0, 0, width * dpr, height * dpr);
2621
- },
2622
- fillColor: (color) => {
2623
- ctx.fillStyle = color;
2504
+ return prevSeg.value || 0;
2505
+ }
2506
+ const timeline = {
2507
+ track: (keyframesOrFn) => {
2508
+ const compiled = typeof keyframesOrFn === "function" ? timeline.keyframes(keyframesOrFn) : keyframesOrFn;
2509
+ const track = {
2510
+ ...compiled,
2511
+ currentValue: 0,
2512
+ getValue: (progress) => interpolateTrack(compiled, progress),
2513
+ get current() {
2514
+ return this.currentValue;
2515
+ },
2516
+ value() {
2517
+ return this.currentValue;
2518
+ }
2519
+ };
2520
+ tracks.set(track, compiled);
2521
+ return track;
2624
2522
  },
2625
- strokeColor: (color) => {
2626
- ctx.strokeStyle = color;
2523
+ keyframes: (fn) => {
2524
+ const kf = createKeyframes();
2525
+ fn(kf);
2526
+ return kf._compile();
2627
2527
  },
2628
- circle: (x, y, radius) => {
2629
- ctx.beginPath();
2630
- ctx.arc(x, y, radius, 0, Math.PI * 2);
2631
- ctx.fill();
2528
+ stagger: (count, offset, keyframesFn) => {
2529
+ const compiled = timeline.keyframes(keyframesFn);
2530
+ return Array.from({ length: count }, (_, i) => {
2531
+ const track = {
2532
+ ...compiled,
2533
+ currentValue: 0,
2534
+ staggerDelay: i * offset,
2535
+ getValue: (progress) => {
2536
+ const staggeredProgress = Math.max(0, progress - i * offset);
2537
+ const normalizedProgress = Math.min(
2538
+ 1,
2539
+ staggeredProgress / (1 - i * offset)
2540
+ );
2541
+ return normalizedProgress > 0 ? interpolateTrack(compiled, normalizedProgress) : 0;
2542
+ },
2543
+ get current() {
2544
+ return this.currentValue;
2545
+ },
2546
+ value() {
2547
+ return this.currentValue;
2548
+ }
2549
+ };
2550
+ tracks.set(track, compiled);
2551
+ return track;
2552
+ });
2632
2553
  },
2633
- rectangle: (x, y, w, h) => {
2634
- ctx.fillRect(x, y, w, h || w);
2554
+ update: (progress) => {
2555
+ const prevProgress = currentProgress;
2556
+ currentProgress = progress;
2557
+ if (!hasStarted && progress > 0) {
2558
+ hasStarted = true;
2559
+ callbacks.start.forEach(
2560
+ (cb) => executeCallback(cb, "Start callback error")
2561
+ );
2562
+ }
2563
+ if (!hasEnded && progress >= 1) {
2564
+ hasEnded = true;
2565
+ callbacks.end.forEach(
2566
+ (cb) => executeCallback(cb, "End callback error")
2567
+ );
2568
+ }
2569
+ if (progress < prevProgress) {
2570
+ if (progress === 0) {
2571
+ hasStarted = false;
2572
+ hasEnded = false;
2573
+ } else if (progress < 1) {
2574
+ hasEnded = false;
2575
+ }
2576
+ }
2577
+ for (const [track, compiled] of tracks) {
2578
+ track.currentValue = track.getValue(progress);
2579
+ if (compiled.segments) {
2580
+ compiled.segments.forEach((seg) => {
2581
+ if (seg.callback && seg.pos <= progress && seg.pos > prevProgress) {
2582
+ executeCallback(seg.callback);
2583
+ }
2584
+ });
2585
+ }
2586
+ }
2635
2587
  },
2636
- // Time properties (static for server rendering)
2637
- frame: 0,
2638
- time: 0,
2639
- deltaTime: 0,
2640
- fps: 60
2588
+ progress: () => currentProgress
2641
2589
  };
2590
+ const result = setup(timeline);
2591
+ return { ...result, update: timeline.update };
2642
2592
  }
2643
2593
  };
2644
- var SSR_default = SSR;
2645
2594
 
2646
2595
  // src/KlintFunctions.tsx
2647
2596
  var KlintCoreFunctions = {
@@ -2663,37 +2612,6 @@ var KlintCoreFunctions = {
2663
2612
  // to do
2664
2613
  redraw: () => () => {
2665
2614
  },
2666
- extend: (ctx) => (name, data, enforceReplace = false) => {
2667
- if (name in ctx && !enforceReplace) return;
2668
- ctx[name] = data;
2669
- },
2670
- passImage: () => (element) => {
2671
- if (!element.complete) {
2672
- console.warn("Image passed to passImage() is not fully loaded");
2673
- return null;
2674
- }
2675
- return element;
2676
- },
2677
- passImages: () => (elements) => {
2678
- return elements.map((element) => {
2679
- if (!element.complete) {
2680
- console.warn("Image passed to passImages() is not fully loaded");
2681
- return null;
2682
- }
2683
- return element;
2684
- });
2685
- },
2686
- saveConfig: (ctx) => (from) => {
2687
- return Object.fromEntries(
2688
- CONFIG_PROPS.map((key) => [
2689
- key,
2690
- from?.[key] ?? ctx[key]
2691
- ])
2692
- );
2693
- },
2694
- restoreConfig: (ctx) => (config) => {
2695
- Object.assign(ctx, config);
2696
- },
2697
2615
  describe: (ctx) => (description) => {
2698
2616
  ctx.__description = description;
2699
2617
  },
@@ -2798,6 +2716,7 @@ var KlintFunctions = {
2798
2716
  strokeWidth: (ctx) => (width) => {
2799
2717
  if (width <= 0) {
2800
2718
  ctx.lineWidth = EPSILON;
2719
+ return;
2801
2720
  }
2802
2721
  ctx.lineWidth = width;
2803
2722
  },
@@ -2974,7 +2893,7 @@ var KlintFunctions = {
2974
2893
  ctx.__currentContours = null;
2975
2894
  ctx.__startedShape = false;
2976
2895
  },
2977
- gradient: (ctx) => (x1 = 0, y1 = 0, x2 = ctx.width, y2 = ctx.width) => {
2896
+ gradient: (ctx) => (x1 = 0, y1 = 0, x2 = ctx.width, y2 = ctx.height) => {
2978
2897
  return ctx.createLinearGradient(x1, y1, x2, y2);
2979
2898
  },
2980
2899
  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)) => {
@@ -3237,19 +3156,6 @@ var KlintFunctions = {
3237
3156
  const adjustedY = ctx.__imageOrigin === "center" ? dy - height / 2 : dy;
3238
3157
  ctx.drawImage(sourceImage, adjustedX, adjustedY);
3239
3158
  },
3240
- // unsure about keeping those next two, maybe a shader plugin would be better
3241
- loadPixels: (ctx) => () => {
3242
- return ctx.getImageData(0, 0, ctx.width, ctx.height);
3243
- },
3244
- updatePixels: (ctx) => (pixels) => {
3245
- const pixelArray = pixels instanceof Uint8ClampedArray ? new Uint8ClampedArray(pixels) : new Uint8ClampedArray(pixels);
3246
- const imageData = new ImageData(pixelArray, ctx.width, ctx.height);
3247
- ctx.putImageData(imageData, 0, 0);
3248
- },
3249
- readPixels: (ctx) => (x, y, w = 1, h = 1) => {
3250
- const imageData = ctx.getImageData(x, y, w, h);
3251
- return Array.from(imageData.data);
3252
- },
3253
3159
  scaleTo: () => (originWidth, originHeight, destinationWidth, destinationHeight, cover = false) => {
3254
3160
  const widthRatio = destinationWidth / originWidth;
3255
3161
  const heightRatio = destinationHeight / originHeight;
@@ -3261,6 +3167,12 @@ var KlintFunctions = {
3261
3167
  blend: (ctx) => (blend) => {
3262
3168
  ctx.globalCompositeOperation = blend === "default" ? "source-over" : blend;
3263
3169
  },
3170
+ smooth: (ctx) => () => {
3171
+ ctx.imageSmoothingEnabled = true;
3172
+ },
3173
+ noSmooth: (ctx) => () => {
3174
+ ctx.imageSmoothingEnabled = false;
3175
+ },
3264
3176
  setCanvasOrigin: (ctx) => (type) => {
3265
3177
  ctx.__canvasOrigin = type;
3266
3178
  },
@@ -3324,6 +3236,41 @@ var KlintFunctions = {
3324
3236
  ctx.fill = originalFill;
3325
3237
  ctx.stroke = originalStroke;
3326
3238
  },
3239
+ screenToWorld: (ctx) => (screenX, screenY) => {
3240
+ const isCenter = ctx.__canvasOrigin === "center";
3241
+ const deviceX = isCenter ? screenX + ctx.width * 0.5 : screenX;
3242
+ const deviceY = isCenter ? screenY + ctx.height * 0.5 : screenY;
3243
+ const inverse = ctx.getTransform().inverse();
3244
+ const pt = inverse.transformPoint(new DOMPoint(deviceX, deviceY));
3245
+ return { x: pt.x, y: pt.y };
3246
+ },
3247
+ worldToScreen: (ctx) => (worldX, worldY) => {
3248
+ const isCenter = ctx.__canvasOrigin === "center";
3249
+ const matrix = ctx.getTransform();
3250
+ const pt = matrix.transformPoint(new DOMPoint(worldX, worldY));
3251
+ const screenX = isCenter ? pt.x - ctx.width * 0.5 : pt.x;
3252
+ const screenY = isCenter ? pt.y - ctx.height * 0.5 : pt.y;
3253
+ return { x: screenX, y: screenY };
3254
+ },
3255
+ getVisibleBounds: (ctx) => () => {
3256
+ const inverse = ctx.getTransform().inverse();
3257
+ const c0 = inverse.transformPoint(new DOMPoint(0, 0));
3258
+ const c1 = inverse.transformPoint(new DOMPoint(ctx.width, 0));
3259
+ const c2 = inverse.transformPoint(new DOMPoint(ctx.width, ctx.height));
3260
+ const c3 = inverse.transformPoint(new DOMPoint(0, ctx.height));
3261
+ const left = Math.min(c0.x, c1.x, c2.x, c3.x);
3262
+ const right = Math.max(c0.x, c1.x, c2.x, c3.x);
3263
+ const top = Math.min(c0.y, c1.y, c2.y, c3.y);
3264
+ const bottom = Math.max(c0.y, c1.y, c2.y, c3.y);
3265
+ return {
3266
+ left,
3267
+ top,
3268
+ right,
3269
+ bottom,
3270
+ width: right - left,
3271
+ height: bottom - top
3272
+ };
3273
+ },
3327
3274
  canIuseFilter: (ctx) => () => {
3328
3275
  return ctx.filter !== void 0 && ctx.filter !== null;
3329
3276
  },
@@ -3363,7 +3310,6 @@ var KlintFunctions = {
3363
3310
  };
3364
3311
 
3365
3312
  // src/useKlint.tsx
3366
- var import_meta2 = {};
3367
3313
  var DEFAULT_MOUSE_STATE = {
3368
3314
  x: 0,
3369
3315
  y: 0,
@@ -3415,29 +3361,8 @@ function useKlint() {
3415
3361
  const gestureRef = (0, import_react2.useRef)(null);
3416
3362
  const keyboardRef = (0, import_react2.useRef)(null);
3417
3363
  const useDev = () => {
3418
- (0, import_react2.useEffect)(() => {
3419
- if (process.env.NODE_ENV === "development") {
3420
- if (typeof import_meta2 !== "undefined" && import_meta2.hot) {
3421
- console.log("[Klint] hot updated - clearing non-context state");
3422
- if (contextRef.current) {
3423
- const ctx = contextRef.current;
3424
- ctx.frame = 0;
3425
- ctx.time = 0;
3426
- ctx.__offscreens?.clear();
3427
- ctx.__startedShape = false;
3428
- ctx.__currentShape = null;
3429
- ctx.__startedContour = false;
3430
- ctx.__currentContours = null;
3431
- ctx.__currentContour = null;
3432
- ctx.__isReadyToDraw = true;
3433
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3434
- }
3435
- }
3436
- }
3437
- }, []);
3438
3364
  (0, import_react2.useEffect)(() => {
3439
3365
  if (process.env.NODE_ENV === "development" && contextRef.current) {
3440
- console.log("[Klint] hot updated - clearing context state");
3441
3366
  contextRef.current.__isReadyToDraw = true;
3442
3367
  }
3443
3368
  });
@@ -3561,7 +3486,7 @@ function useKlint() {
3561
3486
  canvas.addEventListener("mouseleave", handleMouseLeave, { signal });
3562
3487
  canvas.addEventListener("click", handleClick, { signal });
3563
3488
  return () => controller.abort();
3564
- });
3489
+ }, []);
3565
3490
  return {
3566
3491
  mouse: mouseRef.current,
3567
3492
  onClick: (callback) => clickCallbackRef.current = callback,
@@ -3596,7 +3521,7 @@ function useKlint() {
3596
3521
  };
3597
3522
  canvas.addEventListener("wheel", handleScroll, { signal });
3598
3523
  return () => controller.abort();
3599
- });
3524
+ }, []);
3600
3525
  return {
3601
3526
  scroll: scrollRef.current,
3602
3527
  onScroll: (callback) => scrollCallbackRef.current = callback
@@ -3773,6 +3698,17 @@ function useKlint() {
3773
3698
  const keyPressedCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3774
3699
  const keyReleasedCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3775
3700
  const keyComboCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3701
+ const normalizeKey = (key) => {
3702
+ const keyMap = {
3703
+ " ": "Space",
3704
+ Control: "Ctrl",
3705
+ Escape: "Esc"
3706
+ };
3707
+ return keyMap[key] || key;
3708
+ };
3709
+ const createComboKey = (keys) => {
3710
+ return keys.map(normalizeKey).sort().join("+");
3711
+ };
3776
3712
  (0, import_react2.useEffect)(() => {
3777
3713
  if (!contextRef.current) return;
3778
3714
  const ctx = contextRef.current;
@@ -3785,17 +3721,9 @@ function useKlint() {
3785
3721
  keyboardRef.current.modifiers.ctrl = e.ctrlKey;
3786
3722
  keyboardRef.current.modifiers.meta = e.metaKey;
3787
3723
  };
3788
- const normalizeKey2 = (key) => {
3789
- const keyMap = {
3790
- " ": "Space",
3791
- Control: "Ctrl",
3792
- Escape: "Esc"
3793
- };
3794
- return keyMap[key] || key;
3795
- };
3796
3724
  const handleKeyDown = (e) => {
3797
3725
  if (!keyboardRef.current) return;
3798
- const normalizedKey = normalizeKey2(e.key);
3726
+ const normalizedKey = normalizeKey(e.key);
3799
3727
  keyboardRef.current.pressedKeys.add(normalizedKey);
3800
3728
  keyboardRef.current.lastKey = normalizedKey;
3801
3729
  keyboardRef.current.lastKeyTime = performance.now();
@@ -3815,7 +3743,7 @@ function useKlint() {
3815
3743
  };
3816
3744
  const handleKeyUp = (e) => {
3817
3745
  if (!keyboardRef.current) return;
3818
- const normalizedKey = normalizeKey2(e.key);
3746
+ const normalizedKey = normalizeKey(e.key);
3819
3747
  keyboardRef.current.pressedKeys.delete(normalizedKey);
3820
3748
  updateModifiers(e);
3821
3749
  const keyCallback = keyReleasedCallbackRef.current.get(normalizedKey);
@@ -3827,17 +3755,6 @@ function useKlint() {
3827
3755
  window.addEventListener("keyup", handleKeyUp, { signal });
3828
3756
  return () => controller.abort();
3829
3757
  }, []);
3830
- const createComboKey = (keys) => {
3831
- return keys.map(normalizeKey).sort().join("+");
3832
- };
3833
- const normalizeKey = (key) => {
3834
- const keyMap = {
3835
- " ": "Space",
3836
- Control: "Ctrl",
3837
- Escape: "Esc"
3838
- };
3839
- return keyMap[key] || key;
3840
- };
3841
3758
  return {
3842
3759
  keyboard: keyboardRef.current,
3843
3760
  // Register callback for single key press
@@ -3871,210 +3788,6 @@ function useKlint() {
3871
3788
  }
3872
3789
  };
3873
3790
  };
3874
- const KlintTimeline = () => {
3875
- const timelinesRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3876
- const callbacksRef = (0, import_react2.useRef)({ start: [], end: [], loop: [] });
3877
- const Timeline = {
3878
- create: (setup, options = {}) => {
3879
- const tracks = /* @__PURE__ */ new Map();
3880
- const parents = /* @__PURE__ */ new Set();
3881
- let currentProgress = 0;
3882
- let hasStarted = false;
3883
- let hasEnded = false;
3884
- const defaultEasing = options.defaultEasing || ((t) => t);
3885
- const defaultLoop = options.defaultLoop || 0;
3886
- const executeCallback = (callback, errorMsg = "Callback error") => {
3887
- try {
3888
- callback();
3889
- } catch (e) {
3890
- console.warn(`${errorMsg}:`, e);
3891
- }
3892
- };
3893
- function createKeyframes() {
3894
- const segments = [];
3895
- let currentPos = 0;
3896
- let loopCount = defaultLoop;
3897
- let parentTrack = void 0;
3898
- const parseEasingCallback = (easing, callback) => {
3899
- if (typeof easing === "function" && typeof callback === "undefined") {
3900
- if (easing.length === 0) {
3901
- return {
3902
- easing: defaultEasing,
3903
- callback: easing
3904
- };
3905
- }
3906
- return {
3907
- easing,
3908
- callback: void 0
3909
- };
3910
- }
3911
- return {
3912
- easing: easing || defaultEasing,
3913
- callback
3914
- };
3915
- };
3916
- const builder = {
3917
- start(value, delay = 0, callback) {
3918
- segments.push({ pos: delay, value, callback, type: "start" });
3919
- currentPos = delay;
3920
- return builder;
3921
- },
3922
- at(progress, value, easing, callback) {
3923
- const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
3924
- segments.push({
3925
- pos: progress,
3926
- value,
3927
- easing: finalEasing,
3928
- callback: finalCallback,
3929
- type: "tween"
3930
- });
3931
- currentPos = progress;
3932
- return builder;
3933
- },
3934
- then(value, duration, easing, callback) {
3935
- const { easing: finalEasing, callback: finalCallback } = parseEasingCallback(easing, callback);
3936
- const nextPos = currentPos + duration;
3937
- segments.push({
3938
- pos: nextPos,
3939
- value,
3940
- easing: finalEasing,
3941
- callback: finalCallback,
3942
- type: "tween"
3943
- });
3944
- currentPos = nextPos;
3945
- return builder;
3946
- },
3947
- loop(count = Infinity) {
3948
- loopCount = count;
3949
- return builder;
3950
- },
3951
- _compile: () => {
3952
- segments.sort((a, b) => a.pos - b.pos);
3953
- return { segments, loopCount, parentTrack };
3954
- }
3955
- };
3956
- return builder;
3957
- }
3958
- function interpolateTrack(compiled, progress) {
3959
- const { segments } = compiled;
3960
- if (!segments.length) return 0;
3961
- progress = Math.max(0, Math.min(1, progress));
3962
- const valueSegments = segments.filter(
3963
- (seg) => seg.type !== "callback"
3964
- );
3965
- if (!valueSegments.length) return 0;
3966
- let prevSeg = valueSegments[0];
3967
- for (let i = 1; i < valueSegments.length; i++) {
3968
- const seg = valueSegments[i];
3969
- if (progress <= seg.pos) {
3970
- if (seg.type === "tween" && prevSeg.value !== void 0 && seg.value !== void 0) {
3971
- const t = (progress - prevSeg.pos) / (seg.pos - prevSeg.pos);
3972
- const easedT = seg.easing ? seg.easing(t) : t;
3973
- return prevSeg.value + (seg.value - prevSeg.value) * easedT;
3974
- }
3975
- return seg.value || 0;
3976
- }
3977
- if (seg.value !== void 0) {
3978
- prevSeg = seg;
3979
- }
3980
- }
3981
- return prevSeg.value || 0;
3982
- }
3983
- const timeline = {
3984
- track: (keyframesOrFn) => {
3985
- const compiled = typeof keyframesOrFn === "function" ? timeline.keyframes(keyframesOrFn) : keyframesOrFn;
3986
- const track = {
3987
- ...compiled,
3988
- currentValue: 0,
3989
- getValue: (progress) => interpolateTrack(compiled, progress),
3990
- get current() {
3991
- return this.currentValue;
3992
- },
3993
- value: function() {
3994
- return this.currentValue;
3995
- }
3996
- };
3997
- tracks.set(track, compiled);
3998
- return track;
3999
- },
4000
- keyframes: (fn) => {
4001
- const kf = createKeyframes();
4002
- fn(kf);
4003
- return kf._compile();
4004
- },
4005
- stagger: (count, offset, keyframesFn) => {
4006
- const compiled = timeline.keyframes(keyframesFn);
4007
- return Array.from({ length: count }, (_, i) => {
4008
- const track = {
4009
- ...compiled,
4010
- currentValue: 0,
4011
- staggerDelay: i * offset,
4012
- getValue: (progress) => {
4013
- const staggeredProgress = Math.max(0, progress - i * offset);
4014
- const normalizedProgress = Math.min(
4015
- 1,
4016
- staggeredProgress / (1 - i * offset)
4017
- );
4018
- return normalizedProgress > 0 ? interpolateTrack(compiled, normalizedProgress) : 0;
4019
- },
4020
- get current() {
4021
- return this.currentValue;
4022
- },
4023
- value: function() {
4024
- return this.currentValue;
4025
- }
4026
- };
4027
- tracks.set(track, compiled);
4028
- return track;
4029
- });
4030
- },
4031
- update: (progress) => {
4032
- const prevProgress = currentProgress;
4033
- currentProgress = progress;
4034
- if (!hasStarted && progress > 0) {
4035
- hasStarted = true;
4036
- callbacksRef.current.start.forEach(
4037
- (cb) => executeCallback(cb, "Start callback error")
4038
- );
4039
- }
4040
- if (!hasEnded && progress >= 1) {
4041
- hasEnded = true;
4042
- callbacksRef.current.end.forEach(
4043
- (cb) => executeCallback(cb, "End callback error")
4044
- );
4045
- }
4046
- if (progress < prevProgress) {
4047
- if (progress === 0) {
4048
- hasStarted = false;
4049
- hasEnded = false;
4050
- } else if (progress < 1) {
4051
- hasEnded = false;
4052
- }
4053
- }
4054
- for (const [track, compiled] of tracks) {
4055
- track.currentValue = track.getValue(progress);
4056
- if (compiled.segments) {
4057
- compiled.segments.forEach((seg) => {
4058
- if (seg.callback && seg.pos <= progress && seg.pos > prevProgress) {
4059
- executeCallback(seg.callback);
4060
- }
4061
- });
4062
- }
4063
- }
4064
- },
4065
- progress: () => currentProgress
4066
- };
4067
- const result = setup(timeline);
4068
- return { ...result, update: timeline.update };
4069
- }
4070
- };
4071
- return {
4072
- Timeline,
4073
- onStart: (fn) => callbacksRef.current.start.push(fn),
4074
- onEnd: (fn) => callbacksRef.current.end.push(fn),
4075
- onLoop: (fn) => callbacksRef.current.loop.push(fn)
4076
- };
4077
- };
4078
3791
  const KlintWindow = () => {
4079
3792
  const resizeCallbackRef = (0, import_react2.useRef)(
4080
3793
  null
@@ -4145,13 +3858,13 @@ function useKlint() {
4145
3858
  context.Vector = new Vector_default();
4146
3859
  context.Easing = new Easing_default();
4147
3860
  context.Text = new Text_default(context);
4148
- context.Thing = new Thing_default(context);
4149
3861
  context.Grid = new Grid_default(context);
4150
3862
  context.Strip = new Strip_default(context);
4151
3863
  context.Noise = new Noise_default(context);
4152
3864
  context.Hotspot = new Hotspot_default(context);
4153
- context.Performance = new Performance_default(context);
4154
- context.SSR = new SSR_default(context);
3865
+ context.Quadtree = Quadtree_default;
3866
+ context.Pixels = new Pixels(context);
3867
+ context.Timeline = new Timeline();
4155
3868
  Object.entries(KlintCoreFunctions).forEach(([name, fn]) => {
4156
3869
  context[name] = fn(context);
4157
3870
  });
@@ -4182,30 +3895,6 @@ function useKlint() {
4182
3895
  contextRef.current.__isPlaying = !contextRef.current.__isPlaying;
4183
3896
  }
4184
3897
  }, []);
4185
- const KlintPerformance = () => {
4186
- const metricsRef = (0, import_react2.useRef)(null);
4187
- (0, import_react2.useEffect)(() => {
4188
- if (!contextRef.current?.__performance) return;
4189
- const updateMetrics = () => {
4190
- if (contextRef.current?.__performance) {
4191
- metricsRef.current = { ...contextRef.current.__performance };
4192
- }
4193
- };
4194
- const interval = setInterval(updateMetrics, 100);
4195
- return () => clearInterval(interval);
4196
- }, []);
4197
- return {
4198
- metrics: metricsRef.current,
4199
- getMetrics: () => contextRef.current?.__performance || null,
4200
- reset: () => {
4201
- if (contextRef.current?.__performance) {
4202
- contextRef.current.__performance.droppedFrames = 0;
4203
- contextRef.current.__performance.minFrameTime = Infinity;
4204
- contextRef.current.__performance.maxFrameTime = 0;
4205
- }
4206
- }
4207
- };
4208
- };
4209
3898
  return {
4210
3899
  context: {
4211
3900
  context: contextRef.current,
@@ -4217,8 +3906,6 @@ function useKlint() {
4217
3906
  KlintKeyboard,
4218
3907
  KlintWindow,
4219
3908
  KlintImage,
4220
- KlintTimeline,
4221
- KlintPerformance,
4222
3909
  togglePlay,
4223
3910
  useDev
4224
3911
  };
@@ -4274,11 +3961,12 @@ var useStorage = (initialProps = {}) => {
4274
3961
  KlintCoreFunctions,
4275
3962
  KlintFunctions,
4276
3963
  Noise,
4277
- Performance,
4278
- SSR,
3964
+ Pixels,
3965
+ Quadtree,
3966
+ Rectangle,
4279
3967
  Strip,
4280
3968
  Text,
4281
- Thing,
3969
+ Timeline,
4282
3970
  Vector,
4283
3971
  useKlint,
4284
3972
  useProps,