@shopify/klint 0.0.97 → 0.2.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/bin/create-editor +423 -0
- package/bin/create-sandbox +232 -0
- package/bin/klint +146 -0
- package/dist/index.cjs +2369 -317
- package/dist/index.d.cts +847 -66
- package/dist/index.d.ts +847 -66
- package/dist/index.js +2362 -316
- package/package.json +11 -17
- package/dist/elements/index.cjs +0 -946
- package/dist/elements/index.d.cts +0 -658
- package/dist/elements/index.d.ts +0 -658
- package/dist/elements/index.js +0 -913
- package/dist/plugins/index.cjs +0 -1174
- package/dist/plugins/index.d.ts +0 -731
- package/dist/plugins/index.js +0 -1140
package/dist/index.cjs
CHANGED
|
@@ -31,10 +31,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CONFIG_PROPS: () => CONFIG_PROPS,
|
|
34
|
+
Color: () => Color_default,
|
|
34
35
|
EPSILON: () => EPSILON,
|
|
36
|
+
Easing: () => Easing_default,
|
|
37
|
+
Grid: () => Grid_default,
|
|
38
|
+
Hotspot: () => Hotspot_default,
|
|
35
39
|
Klint: () => Klint,
|
|
36
40
|
KlintCoreFunctions: () => KlintCoreFunctions,
|
|
37
41
|
KlintFunctions: () => KlintFunctions,
|
|
42
|
+
Noise: () => Noise_default,
|
|
43
|
+
Performance: () => Performance_default,
|
|
44
|
+
SSR: () => SSR_default,
|
|
45
|
+
Strip: () => Strip_default,
|
|
46
|
+
Text: () => Text_default,
|
|
47
|
+
Thing: () => Thing_default,
|
|
48
|
+
Vector: () => Vector_default,
|
|
38
49
|
useKlint: () => useKlint,
|
|
39
50
|
useProps: () => useProps,
|
|
40
51
|
useStorage: () => useStorage
|
|
@@ -43,6 +54,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
43
54
|
|
|
44
55
|
// src/Klint.tsx
|
|
45
56
|
var import_react = __toESM(require("react"), 1);
|
|
57
|
+
var import_meta = {};
|
|
46
58
|
var DEFAULT_FPS = 60;
|
|
47
59
|
var DEFAULT_ALT = "A beautiful artwork made with Klint Canvas";
|
|
48
60
|
var EPSILON = 1e-4;
|
|
@@ -80,11 +92,18 @@ var CONFIG_PROPS = [
|
|
|
80
92
|
"__textWeight",
|
|
81
93
|
"__textStyle",
|
|
82
94
|
"__textSize",
|
|
95
|
+
"__textLeading",
|
|
83
96
|
"__textAlignment",
|
|
97
|
+
"__fillRule",
|
|
84
98
|
"__isPlaying"
|
|
85
99
|
];
|
|
86
|
-
function useAnimate(contextRef, draw, isVisible) {
|
|
100
|
+
function useAnimate(contextRef, draw, isVisible, enablePerformanceTracking = false) {
|
|
87
101
|
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);
|
|
88
107
|
const animate = (0, import_react.useCallback)(
|
|
89
108
|
(timestamp = 0) => {
|
|
90
109
|
if (!contextRef.current || !isVisible) return;
|
|
@@ -98,22 +117,55 @@ function useAnimate(contextRef, draw, isVisible) {
|
|
|
98
117
|
if (!context.__lastTargetTime) {
|
|
99
118
|
context.__lastTargetTime = now;
|
|
100
119
|
context.__lastRealTime = now;
|
|
120
|
+
frameStartTimeRef.current = now;
|
|
121
|
+
lastFpsUpdateRef.current = now;
|
|
101
122
|
}
|
|
102
123
|
const sinceLast = now - context.__lastTargetTime;
|
|
103
124
|
const epsilon = 5;
|
|
104
125
|
if (sinceLast >= target - epsilon) {
|
|
126
|
+
const frameStart = enablePerformanceTracking && context.__performance ? performance.now() : 0;
|
|
105
127
|
context.deltaTime = now - context.__lastRealTime;
|
|
106
128
|
draw(context);
|
|
107
129
|
if (context.time > 1e7) context.time = 0;
|
|
108
130
|
if (context.frame > 1e7) context.frame = 0;
|
|
109
|
-
context.time += context.deltaTime /
|
|
131
|
+
context.time += context.deltaTime / 1e3;
|
|
110
132
|
context.frame++;
|
|
111
133
|
context.__lastTargetTime = now;
|
|
112
134
|
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
|
+
}
|
|
113
165
|
}
|
|
114
166
|
animationFrameId.current = requestAnimationFrame(animate);
|
|
115
167
|
},
|
|
116
|
-
[draw, isVisible, contextRef]
|
|
168
|
+
[draw, isVisible, contextRef, enablePerformanceTracking]
|
|
117
169
|
);
|
|
118
170
|
return {
|
|
119
171
|
animate,
|
|
@@ -126,7 +178,8 @@ function Klint({
|
|
|
126
178
|
draw,
|
|
127
179
|
options = {},
|
|
128
180
|
preload,
|
|
129
|
-
onVisible
|
|
181
|
+
onVisible,
|
|
182
|
+
enablePerformanceTracking = false
|
|
130
183
|
}) {
|
|
131
184
|
const canvasRef = (0, import_react.useRef)(null);
|
|
132
185
|
const containerRef = (0, import_react.useRef)(null);
|
|
@@ -136,13 +189,36 @@ function Klint({
|
|
|
136
189
|
null
|
|
137
190
|
);
|
|
138
191
|
const [isVisible, setIsVisible] = (0, import_react.useState)(true);
|
|
192
|
+
(0, import_react.useEffect)(() => {
|
|
193
|
+
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
|
+
});
|
|
200
|
+
}
|
|
201
|
+
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
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}, []);
|
|
139
210
|
const __options = {
|
|
140
211
|
...DEFAULT_OPTIONS,
|
|
141
212
|
...options
|
|
142
213
|
};
|
|
143
214
|
const [toStaticImage, setStaticImage] = (0, import_react.useState)(null);
|
|
144
215
|
const initContext = context?.initCoreContext;
|
|
145
|
-
const { animate, animationFrameId } = useAnimate(
|
|
216
|
+
const { animate, animationFrameId } = useAnimate(
|
|
217
|
+
contextRef,
|
|
218
|
+
draw,
|
|
219
|
+
isVisible,
|
|
220
|
+
enablePerformanceTracking
|
|
221
|
+
);
|
|
146
222
|
const updateCanvasSize = (shouldRedraw = false) => {
|
|
147
223
|
if (!containerRef.current || !contextRef.current || !canvasRef.current)
|
|
148
224
|
return;
|
|
@@ -172,6 +248,16 @@ function Klint({
|
|
|
172
248
|
const context2 = contextRef.current;
|
|
173
249
|
if (!context2) return;
|
|
174
250
|
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
|
+
}
|
|
175
261
|
if (__options.fps && __options.fps !== context2.fps) {
|
|
176
262
|
context2.fps = __options.fps;
|
|
177
263
|
}
|
|
@@ -634,7 +720,8 @@ var Color_default = Color;
|
|
|
634
720
|
|
|
635
721
|
// src/elements/Easing.tsx
|
|
636
722
|
var Easing = class {
|
|
637
|
-
constructor(
|
|
723
|
+
constructor() {
|
|
724
|
+
this.zeroOut = (val) => Object.is(val, -0) || Math.abs(val) < 1e-12 ? 0 : val;
|
|
638
725
|
this.normalize = (val) => {
|
|
639
726
|
return val * 0.5 + 0.5;
|
|
640
727
|
};
|
|
@@ -658,19 +745,19 @@ var Easing = class {
|
|
|
658
745
|
};
|
|
659
746
|
this.overshootIn = (val) => {
|
|
660
747
|
const k = 1.70158;
|
|
661
|
-
return val * val * (val * (k + 1) - k);
|
|
748
|
+
return this.zeroOut(val * val * (val * (k + 1) - k));
|
|
662
749
|
};
|
|
663
750
|
this.overshootOut = (val) => {
|
|
664
751
|
const m = val - 1;
|
|
665
752
|
const k = 1.70158;
|
|
666
|
-
return 1 + m * m * (m * (k + 1) + k);
|
|
753
|
+
return this.zeroOut(1 + m * m * (m * (k + 1) + k));
|
|
667
754
|
};
|
|
668
755
|
this.overshootInOut = (val) => {
|
|
669
756
|
const m = val - 1;
|
|
670
757
|
const t = val * 2;
|
|
671
758
|
const k = 1.70158 * 1.525;
|
|
672
|
-
if (val < 0.5) return val * t * (t * (k + 1) - k);
|
|
673
|
-
return 1 + 2 * m * m * (2 * m * (k + 1) + k);
|
|
759
|
+
if (val < 0.5) return this.zeroOut(val * t * (t * (k + 1) - k));
|
|
760
|
+
return this.zeroOut(1 + 2 * m * m * (2 * m * (k + 1) + k));
|
|
674
761
|
};
|
|
675
762
|
this.bounceOut = (val) => {
|
|
676
763
|
const r = 1 / 2.75;
|
|
@@ -723,48 +810,22 @@ var Easing = class {
|
|
|
723
810
|
this.log = () => {
|
|
724
811
|
console.log(this);
|
|
725
812
|
};
|
|
726
|
-
this.context = ctx;
|
|
727
813
|
}
|
|
728
814
|
};
|
|
729
815
|
var Easing_default = Easing;
|
|
730
816
|
|
|
731
|
-
// src/elements/State.tsx
|
|
732
|
-
var State = class {
|
|
733
|
-
constructor() {
|
|
734
|
-
this.store = /* @__PURE__ */ new Map();
|
|
735
|
-
}
|
|
736
|
-
set(key, value, callback) {
|
|
737
|
-
this.store.set(key, value);
|
|
738
|
-
callback?.(key, value);
|
|
739
|
-
}
|
|
740
|
-
get(key, callback) {
|
|
741
|
-
const value = this.store.get(key);
|
|
742
|
-
callback?.(key, value);
|
|
743
|
-
return value;
|
|
744
|
-
}
|
|
745
|
-
has(key) {
|
|
746
|
-
return this.store.has(key);
|
|
747
|
-
}
|
|
748
|
-
delete(key, callback) {
|
|
749
|
-
this.store.delete(key);
|
|
750
|
-
callback?.(key);
|
|
751
|
-
}
|
|
752
|
-
log() {
|
|
753
|
-
return this.store;
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
var State_default = State;
|
|
757
|
-
|
|
758
817
|
// src/elements/Vector.tsx
|
|
759
818
|
var Vector = class _Vector {
|
|
760
819
|
/**
|
|
761
820
|
* Creates a new Vector
|
|
762
821
|
* @param x - X-coordinate (default: 0)
|
|
763
822
|
* @param y - Y-coordinate (default: 0)
|
|
823
|
+
* @param z - Z-coordinate (default: 0)
|
|
764
824
|
*/
|
|
765
|
-
constructor(x = 0, y = 0) {
|
|
825
|
+
constructor(x = 0, y = 0, z = 0) {
|
|
766
826
|
this.x = x;
|
|
767
827
|
this.y = y;
|
|
828
|
+
this.z = z;
|
|
768
829
|
}
|
|
769
830
|
/**
|
|
770
831
|
* Adds another vector to this vector
|
|
@@ -774,6 +835,7 @@ var Vector = class _Vector {
|
|
|
774
835
|
add(v) {
|
|
775
836
|
this.x += v.x;
|
|
776
837
|
this.y += v.y;
|
|
838
|
+
this.z += v.z;
|
|
777
839
|
return this;
|
|
778
840
|
}
|
|
779
841
|
/**
|
|
@@ -784,6 +846,7 @@ var Vector = class _Vector {
|
|
|
784
846
|
sub(v) {
|
|
785
847
|
this.x -= v.x;
|
|
786
848
|
this.y -= v.y;
|
|
849
|
+
this.z -= v.z;
|
|
787
850
|
return this;
|
|
788
851
|
}
|
|
789
852
|
/**
|
|
@@ -794,6 +857,7 @@ var Vector = class _Vector {
|
|
|
794
857
|
mult(n) {
|
|
795
858
|
this.x *= n;
|
|
796
859
|
this.y *= n;
|
|
860
|
+
this.z *= n;
|
|
797
861
|
return this;
|
|
798
862
|
}
|
|
799
863
|
/**
|
|
@@ -804,11 +868,11 @@ var Vector = class _Vector {
|
|
|
804
868
|
div(n) {
|
|
805
869
|
this.x /= n;
|
|
806
870
|
this.y /= n;
|
|
871
|
+
this.z /= n;
|
|
807
872
|
return this;
|
|
808
873
|
}
|
|
809
|
-
// to do : project, perp, slerp
|
|
810
874
|
/**
|
|
811
|
-
* Rotates this vector by an angle
|
|
875
|
+
* Rotates this vector by an angle (around z-axis for 2D rotation)
|
|
812
876
|
* @param angle - Angle in radians
|
|
813
877
|
* @returns This vector after rotation
|
|
814
878
|
*/
|
|
@@ -826,7 +890,7 @@ var Vector = class _Vector {
|
|
|
826
890
|
* @returns The magnitude of the vector
|
|
827
891
|
*/
|
|
828
892
|
mag() {
|
|
829
|
-
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
893
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
830
894
|
}
|
|
831
895
|
/**
|
|
832
896
|
* Alias for mag() - calculates the length of this vector
|
|
@@ -841,7 +905,7 @@ var Vector = class _Vector {
|
|
|
841
905
|
* @returns The dot product
|
|
842
906
|
*/
|
|
843
907
|
dot(v) {
|
|
844
|
-
return this.x * v.x + this.y * v.y;
|
|
908
|
+
return this.x * v.x + this.y * v.y + this.z * v.z;
|
|
845
909
|
}
|
|
846
910
|
/**
|
|
847
911
|
* Calculates the distance between this vector and another vector
|
|
@@ -849,21 +913,21 @@ var Vector = class _Vector {
|
|
|
849
913
|
* @returns The distance between the vectors
|
|
850
914
|
*/
|
|
851
915
|
dist(v) {
|
|
852
|
-
return Math.hypot(this.x - v.x, this.y - v.y);
|
|
916
|
+
return Math.hypot(this.x - v.x, this.y - v.y, this.z - v.z);
|
|
853
917
|
}
|
|
854
918
|
/**
|
|
855
|
-
* Calculates the angle of this vector
|
|
919
|
+
* Calculates the angle of this vector (in 2D, ignoring z)
|
|
856
920
|
* @returns The angle in radians
|
|
857
921
|
*/
|
|
858
922
|
angle() {
|
|
859
|
-
return Math.atan2(
|
|
923
|
+
return Math.atan2(this.y, this.x);
|
|
860
924
|
}
|
|
861
925
|
/**
|
|
862
926
|
* Creates a copy of this vector
|
|
863
927
|
* @returns A new Vector with the same coordinates
|
|
864
928
|
*/
|
|
865
929
|
copy() {
|
|
866
|
-
return new _Vector(this.x, this.y);
|
|
930
|
+
return new _Vector(this.x, this.y, this.z);
|
|
867
931
|
}
|
|
868
932
|
/**
|
|
869
933
|
* Normalizes this vector (sets its magnitude to 1)
|
|
@@ -877,107 +941,128 @@ var Vector = class _Vector {
|
|
|
877
941
|
* Sets the coordinates of this vector
|
|
878
942
|
* @param x - New X-coordinate
|
|
879
943
|
* @param y - New Y-coordinate
|
|
944
|
+
* @param z - New Z-coordinate (default: 0)
|
|
880
945
|
* @returns This vector after setting coordinates
|
|
881
946
|
*/
|
|
882
|
-
set(x, y) {
|
|
947
|
+
set(x, y, z = 0) {
|
|
883
948
|
this.x = x;
|
|
884
949
|
this.y = y;
|
|
950
|
+
this.z = z;
|
|
885
951
|
return this;
|
|
886
952
|
}
|
|
887
953
|
/**
|
|
888
|
-
*
|
|
889
|
-
* @param
|
|
890
|
-
* @
|
|
891
|
-
* @param r - The radius (distance from center)
|
|
892
|
-
* @returns A new Vector at the calculated position
|
|
954
|
+
* Calculates the cross product of this vector with another vector
|
|
955
|
+
* @param v - The other vector
|
|
956
|
+
* @returns A new Vector representing the cross product
|
|
893
957
|
*/
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
var Vector_default = Vector;
|
|
901
|
-
|
|
902
|
-
// src/elements/Time.tsx
|
|
903
|
-
var Time = class {
|
|
904
|
-
constructor(ctx) {
|
|
905
|
-
this.timelines = /* @__PURE__ */ new Map();
|
|
906
|
-
this.currentTimeline = "default";
|
|
907
|
-
this.DEFAULT_DURATION = 8 * 60;
|
|
908
|
-
this.staggers = [];
|
|
909
|
-
this.context = ctx;
|
|
910
|
-
this.timelines.set("default", {
|
|
911
|
-
progress: 0,
|
|
912
|
-
duration: this.DEFAULT_DURATION
|
|
913
|
-
});
|
|
958
|
+
cross(v) {
|
|
959
|
+
return new _Vector(
|
|
960
|
+
this.y * v.z - this.z * v.y,
|
|
961
|
+
this.z * v.x - this.x * v.z,
|
|
962
|
+
this.x * v.y - this.y * v.x
|
|
963
|
+
);
|
|
914
964
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
965
|
+
/**
|
|
966
|
+
* Calculates this vector's position relative to another vector
|
|
967
|
+
* @param v - The reference vector
|
|
968
|
+
* @returns This vector after making it relative to v
|
|
969
|
+
*/
|
|
970
|
+
relativeTo(v) {
|
|
971
|
+
this.x -= v.x;
|
|
972
|
+
this.y -= v.y;
|
|
973
|
+
this.z -= v.z;
|
|
920
974
|
return this;
|
|
921
975
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
976
|
+
/**
|
|
977
|
+
* Makes this vector point towards a target vector
|
|
978
|
+
* @param target - The target vector to look at
|
|
979
|
+
* @returns This vector after pointing towards target
|
|
980
|
+
*/
|
|
981
|
+
lookAt(target) {
|
|
982
|
+
const direction = new _Vector(
|
|
983
|
+
target.x - this.x,
|
|
984
|
+
target.y - this.y,
|
|
985
|
+
target.z - this.z
|
|
986
|
+
);
|
|
987
|
+
direction.normalize();
|
|
988
|
+
this.x = direction.x;
|
|
989
|
+
this.y = direction.y;
|
|
990
|
+
this.z = direction.z;
|
|
929
991
|
return this;
|
|
930
992
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
993
|
+
/**
|
|
994
|
+
* Converts this vector from world space to screen space
|
|
995
|
+
* @param width - Screen width
|
|
996
|
+
* @param height - Screen height
|
|
997
|
+
* @returns This vector after screen space conversion
|
|
998
|
+
*/
|
|
999
|
+
toScreen(width, height) {
|
|
1000
|
+
this.x = (this.x + 1) * width / 2;
|
|
1001
|
+
this.y = (1 - this.y) * height / 2;
|
|
934
1002
|
return this;
|
|
935
1003
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1004
|
+
/**
|
|
1005
|
+
* Spherical linear interpolation between this vector and another vector
|
|
1006
|
+
* @param v - The target vector
|
|
1007
|
+
* @param amt - Interpolation amount (0-1)
|
|
1008
|
+
* @returns This vector after interpolation
|
|
1009
|
+
*/
|
|
1010
|
+
slerp(v, amt) {
|
|
1011
|
+
if (amt === 0) {
|
|
1012
|
+
return this;
|
|
1013
|
+
}
|
|
1014
|
+
if (amt === 1) {
|
|
1015
|
+
return this.set(v.x, v.y, v.z);
|
|
1016
|
+
}
|
|
1017
|
+
const selfMag = this.mag();
|
|
1018
|
+
const vMag = v.mag();
|
|
1019
|
+
const magmag = selfMag * vMag;
|
|
1020
|
+
if (magmag === 0) {
|
|
1021
|
+
this.mult(1 - amt).add(new _Vector(v.x * amt, v.y * amt, v.z * amt));
|
|
1022
|
+
return this;
|
|
1023
|
+
}
|
|
1024
|
+
const axis = this.cross(v);
|
|
1025
|
+
const axisMag = axis.mag();
|
|
1026
|
+
const theta = Math.atan2(axisMag, this.dot(v));
|
|
1027
|
+
if (axisMag > 0) {
|
|
1028
|
+
axis.x /= axisMag;
|
|
1029
|
+
axis.y /= axisMag;
|
|
1030
|
+
axis.z /= axisMag;
|
|
1031
|
+
} else if (theta < Math.PI * 0.5) {
|
|
1032
|
+
this.mult(1 - amt).add(new _Vector(v.x * amt, v.y * amt, v.z * amt));
|
|
1033
|
+
return this;
|
|
1034
|
+
} else {
|
|
1035
|
+
if (this.z === 0 && v.z === 0) {
|
|
1036
|
+
axis.set(0, 0, 1);
|
|
1037
|
+
} else if (this.x !== 0) {
|
|
1038
|
+
axis.set(this.y, -this.x, 0).normalize();
|
|
958
1039
|
} else {
|
|
959
|
-
|
|
1040
|
+
axis.set(1, 0, 0);
|
|
960
1041
|
}
|
|
961
1042
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
Math.min(1, to),
|
|
970
|
-
0,
|
|
971
|
-
1
|
|
972
|
-
);
|
|
973
|
-
callback(localProgress);
|
|
1043
|
+
const ey = axis.cross(this);
|
|
1044
|
+
const lerpedMagFactor = 1 - amt + amt * vMag / selfMag;
|
|
1045
|
+
const cosMultiplier = lerpedMagFactor * Math.cos(amt * theta);
|
|
1046
|
+
const sinMultiplier = lerpedMagFactor * Math.sin(amt * theta);
|
|
1047
|
+
this.x = this.x * cosMultiplier + ey.x * sinMultiplier;
|
|
1048
|
+
this.y = this.y * cosMultiplier + ey.y * sinMultiplier;
|
|
1049
|
+
this.z = this.z * cosMultiplier + ey.z * sinMultiplier;
|
|
974
1050
|
return this;
|
|
975
1051
|
}
|
|
976
|
-
|
|
977
|
-
|
|
1052
|
+
/**
|
|
1053
|
+
* Creates a new vector at a specified angle and distance from a center point
|
|
1054
|
+
* @param center - The center point vector
|
|
1055
|
+
* @param a - The angle in radians
|
|
1056
|
+
* @param r - The radius (distance from center)
|
|
1057
|
+
* @returns A new Vector at the calculated position
|
|
1058
|
+
*/
|
|
1059
|
+
static fromAngle(center, a, r) {
|
|
1060
|
+
const x = Math.cos(a) * r + center.x;
|
|
1061
|
+
const y = Math.sin(a) * r + center.y;
|
|
1062
|
+
return new _Vector(x, y, center.z);
|
|
978
1063
|
}
|
|
979
1064
|
};
|
|
980
|
-
var
|
|
1065
|
+
var Vector_default = Vector;
|
|
981
1066
|
|
|
982
1067
|
// src/elements/Text.tsx
|
|
983
1068
|
var Text = class {
|
|
@@ -1186,139 +1271,1513 @@ var Thing = class {
|
|
|
1186
1271
|
};
|
|
1187
1272
|
var Thing_default = Thing;
|
|
1188
1273
|
|
|
1189
|
-
// src/
|
|
1190
|
-
var
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
if (
|
|
1215
|
-
|
|
1216
|
-
|
|
1274
|
+
// src/elements/Grid.tsx
|
|
1275
|
+
var Grid = class {
|
|
1276
|
+
/**
|
|
1277
|
+
* Creates a new Grid instance
|
|
1278
|
+
* @param ctx - The Klint context
|
|
1279
|
+
*/
|
|
1280
|
+
constructor(ctx) {
|
|
1281
|
+
this.context = ctx;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Create a rectangular grid of points
|
|
1285
|
+
* @param x - X position of the grid
|
|
1286
|
+
* @param y - Y position of the grid
|
|
1287
|
+
* @param width - Width of the grid
|
|
1288
|
+
* @param height - Height of the grid
|
|
1289
|
+
* @param countX - Number of points horizontally
|
|
1290
|
+
* @param countY - Number of points vertically
|
|
1291
|
+
* @param options - Grid options
|
|
1292
|
+
* @returns Array of grid points
|
|
1293
|
+
*/
|
|
1294
|
+
rect(x, y, width, height, countX, countY, options) {
|
|
1295
|
+
const origin = options?.origin || "corner";
|
|
1296
|
+
const points = [];
|
|
1297
|
+
let startX = x;
|
|
1298
|
+
let startY = y;
|
|
1299
|
+
if (origin === "center") {
|
|
1300
|
+
startX = x - width / 2;
|
|
1301
|
+
startY = y - height / 2;
|
|
1217
1302
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1303
|
+
const spacingX = countX > 1 ? width / (countX - 1) : 0;
|
|
1304
|
+
const spacingY = countY > 1 ? height / (countY - 1) : 0;
|
|
1305
|
+
for (let j = 0; j < countY; j++) {
|
|
1306
|
+
for (let i = 0; i < countX; i++) {
|
|
1307
|
+
const pointX = startX + i * spacingX;
|
|
1308
|
+
const pointY = startY + j * spacingY;
|
|
1309
|
+
const id = j * countX + i;
|
|
1310
|
+
points.push({
|
|
1311
|
+
x: pointX,
|
|
1312
|
+
y: pointY,
|
|
1313
|
+
i,
|
|
1314
|
+
j,
|
|
1315
|
+
id
|
|
1316
|
+
});
|
|
1225
1317
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
context.Color = ctx.Color;
|
|
1269
|
-
context.createVector = (x = 0, y = 0) => new Vector_default(x, y);
|
|
1270
|
-
context.Easing = ctx.Easing;
|
|
1271
|
-
context.Text = ctx.Text;
|
|
1272
|
-
Object.entries(KlintFunctions).forEach(([name, fn]) => {
|
|
1273
|
-
context[name] = fn(context);
|
|
1318
|
+
}
|
|
1319
|
+
return points;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Create a radial grid of points
|
|
1323
|
+
* @param x - Center X position
|
|
1324
|
+
* @param y - Center Y position
|
|
1325
|
+
* @param radius - Maximum radius
|
|
1326
|
+
* @param count - Number of points per ring
|
|
1327
|
+
* @param ringCount - Number of rings
|
|
1328
|
+
* @param ringSpace - Space between rings
|
|
1329
|
+
* @param options - Grid options
|
|
1330
|
+
* @returns Array of grid points
|
|
1331
|
+
*/
|
|
1332
|
+
radial(x, y, radius, count, ringCount, ringSpace, options) {
|
|
1333
|
+
const perStepCount = options?.perStepCount || 0;
|
|
1334
|
+
const points = [];
|
|
1335
|
+
let id = 0;
|
|
1336
|
+
for (let ring = 0; ring < ringCount; ring++) {
|
|
1337
|
+
const ringRadius = ring * ringSpace;
|
|
1338
|
+
if (ringRadius > radius) break;
|
|
1339
|
+
const ringPointCount = count + perStepCount * ring;
|
|
1340
|
+
for (let i = 0; i < ringPointCount; i++) {
|
|
1341
|
+
const angle = Math.PI * 2 * i / ringPointCount;
|
|
1342
|
+
const pointX = x + Math.cos(angle) * ringRadius;
|
|
1343
|
+
const pointY = y + Math.sin(angle) * ringRadius;
|
|
1344
|
+
points.push({
|
|
1345
|
+
x: pointX,
|
|
1346
|
+
y: pointY,
|
|
1347
|
+
i,
|
|
1348
|
+
j: ring,
|
|
1349
|
+
id: id++
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (ringCount > 0 && points.length === 0) {
|
|
1354
|
+
points.push({
|
|
1355
|
+
x,
|
|
1356
|
+
y,
|
|
1357
|
+
i: 0,
|
|
1358
|
+
j: 0,
|
|
1359
|
+
id: 0
|
|
1274
1360
|
});
|
|
1275
1361
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1362
|
+
return points;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Create a hexagonal grid of points
|
|
1366
|
+
* @param x - X position of the grid
|
|
1367
|
+
* @param y - Y position of the grid
|
|
1368
|
+
* @param width - Width of the grid area
|
|
1369
|
+
* @param height - Height of the grid area
|
|
1370
|
+
* @param size - Size of each hexagon
|
|
1371
|
+
* @param options - Grid options
|
|
1372
|
+
* @returns Array of grid points
|
|
1373
|
+
*/
|
|
1374
|
+
hex(x, y, width, height, size, options) {
|
|
1375
|
+
const origin = options?.origin || "corner";
|
|
1376
|
+
const pointy = options?.pointy !== false;
|
|
1377
|
+
const points = [];
|
|
1378
|
+
let startX = x;
|
|
1379
|
+
let startY = y;
|
|
1380
|
+
if (origin === "center") {
|
|
1381
|
+
startX = x - width / 2;
|
|
1382
|
+
startY = y - height / 2;
|
|
1383
|
+
}
|
|
1384
|
+
const hexWidth = pointy ? Math.sqrt(3) * size : 2 * size;
|
|
1385
|
+
const hexHeight = pointy ? 2 * size : Math.sqrt(3) * size;
|
|
1386
|
+
const cols = Math.ceil(width / hexWidth) + 1;
|
|
1387
|
+
const rows = Math.ceil(height / (hexHeight * 0.75)) + 1;
|
|
1388
|
+
let id = 0;
|
|
1389
|
+
for (let j = 0; j < rows; j++) {
|
|
1390
|
+
for (let i = 0; i < cols; i++) {
|
|
1391
|
+
let pointX;
|
|
1392
|
+
let pointY;
|
|
1393
|
+
if (pointy) {
|
|
1394
|
+
pointX = startX + i * hexWidth;
|
|
1395
|
+
pointY = startY + j * hexHeight * 0.75;
|
|
1396
|
+
if (j % 2 === 1) {
|
|
1397
|
+
pointX += hexWidth / 2;
|
|
1398
|
+
}
|
|
1399
|
+
} else {
|
|
1400
|
+
pointX = startX + i * hexWidth * 0.75;
|
|
1401
|
+
pointY = startY + j * hexHeight;
|
|
1402
|
+
if (i % 2 === 1) {
|
|
1403
|
+
pointY += hexHeight / 2;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (pointX <= startX + width && pointY <= startY + height) {
|
|
1407
|
+
points.push({
|
|
1408
|
+
x: pointX,
|
|
1409
|
+
y: pointY,
|
|
1410
|
+
i,
|
|
1411
|
+
j,
|
|
1412
|
+
id: id++
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1280
1415
|
}
|
|
1281
1416
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1417
|
+
return points;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Create a triangular grid of points
|
|
1421
|
+
* @param x - X position of the grid
|
|
1422
|
+
* @param y - Y position of the grid
|
|
1423
|
+
* @param width - Width of the grid
|
|
1424
|
+
* @param height - Height of the grid
|
|
1425
|
+
* @param size - Size of each triangle
|
|
1426
|
+
* @param options - Grid options
|
|
1427
|
+
* @returns Array of grid points
|
|
1428
|
+
*/
|
|
1429
|
+
triangle(x, y, width, height, size, options) {
|
|
1430
|
+
const origin = options?.origin || "corner";
|
|
1431
|
+
const points = [];
|
|
1432
|
+
let startX = x;
|
|
1433
|
+
let startY = y;
|
|
1434
|
+
if (origin === "center") {
|
|
1435
|
+
startX = x - width / 2;
|
|
1436
|
+
startY = y - height / 2;
|
|
1284
1437
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1438
|
+
const triHeight = Math.sqrt(3) / 2 * size;
|
|
1439
|
+
const cols = Math.ceil(width / (size / 2)) + 1;
|
|
1440
|
+
const rows = Math.ceil(height / triHeight) + 1;
|
|
1441
|
+
let id = 0;
|
|
1442
|
+
for (let j = 0; j < rows; j++) {
|
|
1443
|
+
for (let i = 0; i < cols; i++) {
|
|
1444
|
+
const pointX = startX + i * (size / 2);
|
|
1445
|
+
const pointY = startY + j * triHeight;
|
|
1446
|
+
if (pointX <= startX + width && pointY <= startY + height) {
|
|
1447
|
+
points.push({
|
|
1448
|
+
x: pointX,
|
|
1449
|
+
y: pointY,
|
|
1450
|
+
i,
|
|
1451
|
+
j,
|
|
1452
|
+
id: id++
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1291
1456
|
}
|
|
1292
|
-
|
|
1293
|
-
return context;
|
|
1294
|
-
},
|
|
1295
|
-
getOffscreen: (ctx) => (id) => {
|
|
1296
|
-
const offscreen = ctx.__offscreens.get(id);
|
|
1297
|
-
if (!offscreen)
|
|
1298
|
-
throw new Error(`No offscreen context found with id: ${id}`);
|
|
1299
|
-
return offscreen;
|
|
1457
|
+
return points;
|
|
1300
1458
|
}
|
|
1301
1459
|
};
|
|
1302
|
-
var
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1460
|
+
var Grid_default = Grid;
|
|
1461
|
+
|
|
1462
|
+
// src/elements/Strip.tsx
|
|
1463
|
+
var Strip = class {
|
|
1464
|
+
/**
|
|
1465
|
+
* Creates a new Strip instance
|
|
1466
|
+
* @param ctx - The Klint context
|
|
1467
|
+
*/
|
|
1468
|
+
constructor(ctx) {
|
|
1469
|
+
this.context = ctx;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Create a strip of triangles from points
|
|
1473
|
+
* Points are connected in a zigzag pattern:
|
|
1474
|
+
* 0 - 2 - 4 ...
|
|
1475
|
+
* | / | / |
|
|
1476
|
+
* 1 - 3 - 5 ...
|
|
1477
|
+
*
|
|
1478
|
+
* @param points - Array of points (must be even number for complete triangles)
|
|
1479
|
+
* @param draw - Optional callback to customize each triangle's appearance
|
|
1480
|
+
*/
|
|
1481
|
+
triangles(points, draw) {
|
|
1482
|
+
if (points.length < 3) return;
|
|
1483
|
+
const numTriangles = Math.floor((points.length - 2) * 2);
|
|
1484
|
+
for (let i = 0; i < numTriangles; i++) {
|
|
1485
|
+
const baseIndex = Math.floor(i / 2) * 2;
|
|
1486
|
+
const isEven = i % 2 === 0;
|
|
1487
|
+
let p1;
|
|
1488
|
+
let p2;
|
|
1489
|
+
let p3;
|
|
1490
|
+
if (isEven) {
|
|
1491
|
+
p1 = points[baseIndex];
|
|
1492
|
+
p2 = points[baseIndex + 1];
|
|
1493
|
+
p3 = points[baseIndex + 2];
|
|
1494
|
+
} else {
|
|
1495
|
+
p1 = points[baseIndex + 1];
|
|
1496
|
+
p2 = points[baseIndex + 3];
|
|
1497
|
+
p3 = points[baseIndex + 2];
|
|
1498
|
+
}
|
|
1499
|
+
if (!p1 || !p2 || !p3) continue;
|
|
1500
|
+
const center = {
|
|
1501
|
+
x: (p1.x + p2.x + p3.x) / 3,
|
|
1502
|
+
y: (p1.y + p2.y + p3.y) / 3
|
|
1503
|
+
};
|
|
1504
|
+
const triangle = {
|
|
1505
|
+
id: i,
|
|
1506
|
+
center,
|
|
1507
|
+
points: [p1, p2, p3]
|
|
1508
|
+
};
|
|
1509
|
+
let fillColor;
|
|
1510
|
+
if (draw) {
|
|
1511
|
+
const result = draw(triangle);
|
|
1512
|
+
if (typeof result === "string") {
|
|
1513
|
+
fillColor = result;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
this.context.beginPath();
|
|
1517
|
+
this.context.moveTo(p1.x, p1.y);
|
|
1518
|
+
this.context.lineTo(p2.x, p2.y);
|
|
1519
|
+
this.context.lineTo(p3.x, p3.y);
|
|
1520
|
+
this.context.closePath();
|
|
1521
|
+
if (fillColor) {
|
|
1522
|
+
const prevFill = this.context.fillStyle;
|
|
1523
|
+
this.context.fillStyle = fillColor;
|
|
1524
|
+
this.context.fill();
|
|
1525
|
+
this.context.fillStyle = prevFill;
|
|
1526
|
+
} else if (this.context.checkTransparency("fill")) {
|
|
1527
|
+
this.context.fill();
|
|
1528
|
+
}
|
|
1529
|
+
if (this.context.checkTransparency("stroke")) {
|
|
1530
|
+
this.context.stroke();
|
|
1531
|
+
}
|
|
1315
1532
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Create a strip of quads from points
|
|
1536
|
+
* Points are connected in a grid pattern:
|
|
1537
|
+
* 0 - 2 - 4 ...
|
|
1538
|
+
* | | |
|
|
1539
|
+
* 1 - 3 - 5 ...
|
|
1540
|
+
*
|
|
1541
|
+
* @param points - Array of points (must be even number for complete quads)
|
|
1542
|
+
* @param draw - Optional callback to customize each quad's appearance
|
|
1543
|
+
*/
|
|
1544
|
+
quads(points, draw) {
|
|
1545
|
+
if (points.length < 4) return;
|
|
1546
|
+
const numQuads = Math.floor((points.length - 2) / 2);
|
|
1547
|
+
for (let i = 0; i < numQuads; i++) {
|
|
1548
|
+
const baseIndex = i * 2;
|
|
1549
|
+
const p1 = points[baseIndex];
|
|
1550
|
+
const p2 = points[baseIndex + 1];
|
|
1551
|
+
const p3 = points[baseIndex + 3];
|
|
1552
|
+
const p4 = points[baseIndex + 2];
|
|
1553
|
+
if (!p1 || !p2 || !p3 || !p4) continue;
|
|
1554
|
+
const center = {
|
|
1555
|
+
x: (p1.x + p2.x + p3.x + p4.x) / 4,
|
|
1556
|
+
y: (p1.y + p2.y + p3.y + p4.y) / 4
|
|
1557
|
+
};
|
|
1558
|
+
const quad = {
|
|
1559
|
+
id: i,
|
|
1560
|
+
center,
|
|
1561
|
+
points: [p1, p2, p3, p4]
|
|
1562
|
+
};
|
|
1563
|
+
let fillColor;
|
|
1564
|
+
if (draw) {
|
|
1565
|
+
const result = draw(quad);
|
|
1566
|
+
if (typeof result === "string") {
|
|
1567
|
+
fillColor = result;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
this.context.beginPath();
|
|
1571
|
+
this.context.moveTo(p1.x, p1.y);
|
|
1572
|
+
this.context.lineTo(p2.x, p2.y);
|
|
1573
|
+
this.context.lineTo(p3.x, p3.y);
|
|
1574
|
+
this.context.lineTo(p4.x, p4.y);
|
|
1575
|
+
this.context.closePath();
|
|
1576
|
+
if (fillColor) {
|
|
1577
|
+
const prevFill = this.context.fillStyle;
|
|
1578
|
+
this.context.fillStyle = fillColor;
|
|
1579
|
+
this.context.fill();
|
|
1580
|
+
this.context.fillStyle = prevFill;
|
|
1581
|
+
} else if (this.context.checkTransparency("fill")) {
|
|
1582
|
+
this.context.fill();
|
|
1583
|
+
}
|
|
1584
|
+
if (this.context.checkTransparency("stroke")) {
|
|
1585
|
+
this.context.stroke();
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Create a single hull shape from points
|
|
1591
|
+
* Points are connected following the winding order:
|
|
1592
|
+
* 0 - 2 - 4 - ... n-1
|
|
1593
|
+
* | |
|
|
1594
|
+
* 1 - 3 - 5 - ... n
|
|
1595
|
+
*
|
|
1596
|
+
* Final order: 0 - 2 - 4 ... n-1, n, ... 5 - 3 - 1
|
|
1597
|
+
*
|
|
1598
|
+
* @param points - Array of points
|
|
1599
|
+
* @param draw - Optional callback to add elements along the hull
|
|
1600
|
+
*/
|
|
1601
|
+
hull(points, draw) {
|
|
1602
|
+
if (points.length < 2) return;
|
|
1603
|
+
const hullPath = [];
|
|
1604
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
1605
|
+
hullPath.push(points[i]);
|
|
1606
|
+
}
|
|
1607
|
+
for (let i = points.length - 1 - points.length % 2; i >= 1; i -= 2) {
|
|
1608
|
+
hullPath.push(points[i]);
|
|
1609
|
+
}
|
|
1610
|
+
this.context.beginPath();
|
|
1611
|
+
this.context.moveTo(hullPath[0].x, hullPath[0].y);
|
|
1612
|
+
for (let i = 1; i < hullPath.length; i++) {
|
|
1613
|
+
this.context.lineTo(hullPath[i].x, hullPath[i].y);
|
|
1614
|
+
}
|
|
1615
|
+
this.context.closePath();
|
|
1616
|
+
if (this.context.checkTransparency("fill")) {
|
|
1617
|
+
this.context.fill();
|
|
1618
|
+
}
|
|
1619
|
+
if (this.context.checkTransparency("stroke")) {
|
|
1620
|
+
this.context.stroke();
|
|
1621
|
+
}
|
|
1622
|
+
if (draw) {
|
|
1623
|
+
const numPairs = Math.floor(points.length / 2);
|
|
1624
|
+
for (let i = 0; i < numPairs; i++) {
|
|
1625
|
+
const topPoint = points[i * 2];
|
|
1626
|
+
const bottomPoint = points[i * 2 + 1];
|
|
1627
|
+
if (topPoint && bottomPoint) {
|
|
1628
|
+
const center = {
|
|
1629
|
+
x: (topPoint.x + bottomPoint.x) / 2,
|
|
1630
|
+
y: (topPoint.y + bottomPoint.y) / 2
|
|
1631
|
+
};
|
|
1632
|
+
const hull = {
|
|
1633
|
+
id: i,
|
|
1634
|
+
center
|
|
1635
|
+
};
|
|
1636
|
+
draw(hull);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Create a ribbon/tape effect from points
|
|
1643
|
+
* Similar to hull but with configurable width
|
|
1644
|
+
*
|
|
1645
|
+
* @param points - Array of center points
|
|
1646
|
+
* @param width - Width of the ribbon
|
|
1647
|
+
* @param draw - Optional callback
|
|
1648
|
+
*/
|
|
1649
|
+
ribbon(points, width, draw) {
|
|
1650
|
+
if (points.length < 2) return;
|
|
1651
|
+
const offsetPoints = [];
|
|
1652
|
+
for (let i = 0; i < points.length; i++) {
|
|
1653
|
+
const curr = points[i];
|
|
1654
|
+
const prev = points[i - 1] || curr;
|
|
1655
|
+
const next = points[i + 1] || curr;
|
|
1656
|
+
const dx = next.x - prev.x;
|
|
1657
|
+
const dy = next.y - prev.y;
|
|
1658
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
1659
|
+
const perpX = -dy / len * (width / 2);
|
|
1660
|
+
const perpY = dx / len * (width / 2);
|
|
1661
|
+
offsetPoints.push({
|
|
1662
|
+
top: { x: curr.x + perpX, y: curr.y + perpY },
|
|
1663
|
+
bottom: { x: curr.x - perpX, y: curr.y - perpY }
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
this.context.beginPath();
|
|
1667
|
+
this.context.moveTo(offsetPoints[0].top.x, offsetPoints[0].top.y);
|
|
1668
|
+
for (let i = 1; i < offsetPoints.length; i++) {
|
|
1669
|
+
this.context.lineTo(offsetPoints[i].top.x, offsetPoints[i].top.y);
|
|
1670
|
+
}
|
|
1671
|
+
for (let i = offsetPoints.length - 1; i >= 0; i--) {
|
|
1672
|
+
this.context.lineTo(offsetPoints[i].bottom.x, offsetPoints[i].bottom.y);
|
|
1673
|
+
}
|
|
1674
|
+
this.context.closePath();
|
|
1675
|
+
if (this.context.checkTransparency("fill")) {
|
|
1676
|
+
this.context.fill();
|
|
1677
|
+
}
|
|
1678
|
+
if (this.context.checkTransparency("stroke")) {
|
|
1679
|
+
this.context.stroke();
|
|
1680
|
+
}
|
|
1681
|
+
if (draw) {
|
|
1682
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
1683
|
+
const center = {
|
|
1684
|
+
x: (points[i].x + points[i + 1].x) / 2,
|
|
1685
|
+
y: (points[i].y + points[i + 1].y) / 2
|
|
1686
|
+
};
|
|
1687
|
+
const result = draw({ id: i, center });
|
|
1688
|
+
if (typeof result === "string") {
|
|
1689
|
+
this.context.beginPath();
|
|
1690
|
+
this.context.moveTo(offsetPoints[i].top.x, offsetPoints[i].top.y);
|
|
1691
|
+
this.context.lineTo(offsetPoints[i + 1].top.x, offsetPoints[i + 1].top.y);
|
|
1692
|
+
this.context.lineTo(offsetPoints[i + 1].bottom.x, offsetPoints[i + 1].bottom.y);
|
|
1693
|
+
this.context.lineTo(offsetPoints[i].bottom.x, offsetPoints[i].bottom.y);
|
|
1694
|
+
this.context.closePath();
|
|
1695
|
+
const prevFill = this.context.fillStyle;
|
|
1696
|
+
this.context.fillStyle = result;
|
|
1697
|
+
this.context.fill();
|
|
1698
|
+
this.context.fillStyle = prevFill;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
var Strip_default = Strip;
|
|
1705
|
+
|
|
1706
|
+
// 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();
|
|
1772
|
+
}
|
|
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;
|
|
1780
|
+
}
|
|
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);
|
|
1801
|
+
}
|
|
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();
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Fade function for Perlin noise
|
|
1812
|
+
*/
|
|
1813
|
+
fade(t) {
|
|
1814
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Linear interpolation
|
|
1818
|
+
*/
|
|
1819
|
+
lerp(t, a, b) {
|
|
1820
|
+
return a + t * (b - a);
|
|
1821
|
+
}
|
|
1822
|
+
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))
|
|
1881
|
+
),
|
|
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))
|
|
1886
|
+
)
|
|
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
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
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) {
|
|
1908
|
+
i1 = 1;
|
|
1909
|
+
j1 = 0;
|
|
1910
|
+
} else {
|
|
1911
|
+
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);
|
|
2036
|
+
}
|
|
2037
|
+
let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
|
|
2038
|
+
if (t2 < 0) {
|
|
2039
|
+
n2 = 0;
|
|
2040
|
+
} else {
|
|
2041
|
+
t2 *= t2;
|
|
2042
|
+
n2 = t2 * t2 * this.dot3(this.grad3[gi2], x2, y2, z2);
|
|
2043
|
+
}
|
|
2044
|
+
let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
|
|
2045
|
+
if (t3 < 0) {
|
|
2046
|
+
n3 = 0;
|
|
2047
|
+
} else {
|
|
2048
|
+
t3 *= t3;
|
|
2049
|
+
n3 = t3 * t3 * this.dot3(this.grad3[gi3], x3, y3, z3);
|
|
2050
|
+
}
|
|
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
|
+
}
|
|
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
|
+
}
|
|
2068
|
+
hash(x, y, z, w) {
|
|
2069
|
+
let n = 0;
|
|
2070
|
+
if (y === void 0) {
|
|
2071
|
+
n = Math.sin(x * 12.9898 + this.currentSeed) * 43758.5453;
|
|
2072
|
+
} else if (z === void 0) {
|
|
2073
|
+
n = Math.sin(x * 12.9898 + y * 78.233 + this.currentSeed) * 43758.5453;
|
|
2074
|
+
} else if (w === void 0) {
|
|
2075
|
+
n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + this.currentSeed) * 43758.5453;
|
|
2076
|
+
} else {
|
|
2077
|
+
n = Math.sin(x * 12.9898 + y * 78.233 + z * 37.719 + w * 59.1337 + this.currentSeed) * 43758.5453;
|
|
2078
|
+
}
|
|
2079
|
+
return n - Math.floor(n);
|
|
2080
|
+
}
|
|
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;
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
var Noise_default = Noise;
|
|
2207
|
+
|
|
2208
|
+
// src/elements/Hotspot.tsx
|
|
2209
|
+
var Hotspot = class {
|
|
2210
|
+
/**
|
|
2211
|
+
* Creates a new Hotspot instance
|
|
2212
|
+
* @param ctx - The Klint context
|
|
2213
|
+
*/
|
|
2214
|
+
constructor(ctx) {
|
|
2215
|
+
this.context = ctx;
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Check if point is inside a circle
|
|
2219
|
+
* @param point - Point to check (usually mouse position)
|
|
2220
|
+
* @param x - Circle center X
|
|
2221
|
+
* @param y - Circle center Y
|
|
2222
|
+
* @param radius - Circle radius
|
|
2223
|
+
* @returns True if point is inside the circle
|
|
2224
|
+
*/
|
|
2225
|
+
circle(point, x, y, radius) {
|
|
2226
|
+
const dx = point.x - x;
|
|
2227
|
+
const dy = point.y - y;
|
|
2228
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
2229
|
+
return distance <= radius;
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Check if point is inside a rectangle
|
|
2233
|
+
* @param point - Point to check (usually mouse position)
|
|
2234
|
+
* @param x - Rectangle X position
|
|
2235
|
+
* @param y - Rectangle Y position
|
|
2236
|
+
* @param width - Rectangle width
|
|
2237
|
+
* @param height - Rectangle height
|
|
2238
|
+
* @returns True if point is inside the rectangle
|
|
2239
|
+
*/
|
|
2240
|
+
rect(point, x, y, width, height) {
|
|
2241
|
+
const origin = this.context.__rectangleOrigin || "corner";
|
|
2242
|
+
let left, top;
|
|
2243
|
+
if (origin === "center") {
|
|
2244
|
+
left = x - width / 2;
|
|
2245
|
+
top = y - height / 2;
|
|
2246
|
+
} else {
|
|
2247
|
+
left = x;
|
|
2248
|
+
top = y;
|
|
2249
|
+
}
|
|
2250
|
+
const right = left + width;
|
|
2251
|
+
const bottom = top + height;
|
|
2252
|
+
return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom;
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Check if point is inside an ellipse
|
|
2256
|
+
* @param point - Point to check
|
|
2257
|
+
* @param x - Ellipse center X
|
|
2258
|
+
* @param y - Ellipse center Y
|
|
2259
|
+
* @param radiusX - Horizontal radius
|
|
2260
|
+
* @param radiusY - Vertical radius
|
|
2261
|
+
* @param rotation - Rotation angle in radians
|
|
2262
|
+
* @returns True if point is inside the ellipse
|
|
2263
|
+
*/
|
|
2264
|
+
ellipse(point, x, y, radiusX, radiusY, rotation = 0) {
|
|
2265
|
+
const cos = Math.cos(-rotation);
|
|
2266
|
+
const sin = Math.sin(-rotation);
|
|
2267
|
+
const dx = point.x - x;
|
|
2268
|
+
const dy = point.y - y;
|
|
2269
|
+
const localX = dx * cos - dy * sin;
|
|
2270
|
+
const localY = dx * sin + dy * cos;
|
|
2271
|
+
return localX * localX / (radiusX * radiusX) + localY * localY / (radiusY * radiusY) <= 1;
|
|
2272
|
+
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Check if point is inside a polygon
|
|
2275
|
+
* @param point - Point to check
|
|
2276
|
+
* @param vertices - Array of polygon vertices
|
|
2277
|
+
* @returns True if point is inside the polygon
|
|
2278
|
+
*/
|
|
2279
|
+
polygon(point, vertices) {
|
|
2280
|
+
let inside = false;
|
|
2281
|
+
const n = vertices.length;
|
|
2282
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
2283
|
+
const xi = vertices[i].x, yi = vertices[i].y;
|
|
2284
|
+
const xj = vertices[j].x, yj = vertices[j].y;
|
|
2285
|
+
const intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
|
|
2286
|
+
if (intersect) inside = !inside;
|
|
2287
|
+
}
|
|
2288
|
+
return inside;
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Check if point is inside a Path2D
|
|
2292
|
+
* @param point - Point to check
|
|
2293
|
+
* @param path - Path2D object
|
|
2294
|
+
* @returns True if point is inside the path
|
|
2295
|
+
*/
|
|
2296
|
+
path(point, path) {
|
|
2297
|
+
return this.context.isPointInPath(path, point.x, point.y);
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
var Hotspot_default = Hotspot;
|
|
2301
|
+
|
|
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;
|
|
2309
|
+
}
|
|
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
|
+
}
|
|
2321
|
+
}
|
|
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
|
+
}
|
|
2340
|
+
}
|
|
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;
|
|
2353
|
+
}
|
|
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
|
+
};
|
|
2382
|
+
}
|
|
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);
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
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;
|
|
2412
|
+
}
|
|
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
|
+
}
|
|
2437
|
+
}
|
|
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;
|
|
2468
|
+
}
|
|
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;
|
|
2473
|
+
}
|
|
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();
|
|
2504
|
+
}
|
|
2505
|
+
this.context.restore();
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Alias for render() - shows performance widget
|
|
2509
|
+
*/
|
|
2510
|
+
show(options) {
|
|
2511
|
+
this.render(options);
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
var Performance_default = Performance;
|
|
2515
|
+
|
|
2516
|
+
// src/elements/SSR.tsx
|
|
2517
|
+
var SSR = class {
|
|
2518
|
+
constructor(ctx) {
|
|
2519
|
+
this.context = ctx || null;
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Render a Klint sketch to a static image (base64 data URL)
|
|
2523
|
+
*/
|
|
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
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Generate a static image URL for a Klint sketch
|
|
2534
|
+
*/
|
|
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);
|
|
2542
|
+
}
|
|
2543
|
+
/**
|
|
2544
|
+
* Check if Klint can run in the current environment
|
|
2545
|
+
*/
|
|
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
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* Render in browser environment (client-side)
|
|
2559
|
+
*/
|
|
2560
|
+
async renderInBrowser(draw, options) {
|
|
2561
|
+
return new Promise((resolve, reject) => {
|
|
2562
|
+
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;
|
|
2571
|
+
}
|
|
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
|
+
}
|
|
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;
|
|
2624
|
+
},
|
|
2625
|
+
strokeColor: (color) => {
|
|
2626
|
+
ctx.strokeStyle = color;
|
|
2627
|
+
},
|
|
2628
|
+
circle: (x, y, radius) => {
|
|
2629
|
+
ctx.beginPath();
|
|
2630
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
2631
|
+
ctx.fill();
|
|
2632
|
+
},
|
|
2633
|
+
rectangle: (x, y, w, h) => {
|
|
2634
|
+
ctx.fillRect(x, y, w, h || w);
|
|
2635
|
+
},
|
|
2636
|
+
// Time properties (static for server rendering)
|
|
2637
|
+
frame: 0,
|
|
2638
|
+
time: 0,
|
|
2639
|
+
deltaTime: 0,
|
|
2640
|
+
fps: 60
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
var SSR_default = SSR;
|
|
2645
|
+
|
|
2646
|
+
// src/KlintFunctions.tsx
|
|
2647
|
+
var KlintCoreFunctions = {
|
|
2648
|
+
saveCanvas: (ctx) => () => {
|
|
2649
|
+
const link = document.createElement("a");
|
|
2650
|
+
link.download = "canvas.png";
|
|
2651
|
+
link.href = ctx.canvas.toDataURL();
|
|
2652
|
+
link.click();
|
|
2653
|
+
},
|
|
2654
|
+
fullscreen: (ctx) => () => {
|
|
2655
|
+
ctx.canvas.requestFullscreen?.();
|
|
2656
|
+
},
|
|
2657
|
+
play: (ctx) => () => {
|
|
2658
|
+
if (!ctx.__isPlaying) ctx.__isPlaying = true;
|
|
2659
|
+
},
|
|
2660
|
+
pause: (ctx) => () => {
|
|
2661
|
+
if (ctx.__isPlaying) ctx.__isPlaying = false;
|
|
2662
|
+
},
|
|
2663
|
+
// to do
|
|
2664
|
+
redraw: () => () => {
|
|
2665
|
+
},
|
|
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
|
+
describe: (ctx) => (description) => {
|
|
2698
|
+
ctx.__description = description;
|
|
2699
|
+
},
|
|
2700
|
+
createOffscreen: (ctx) => (id, width, height, options, callback) => {
|
|
2701
|
+
const offscreen = document.createElement("canvas");
|
|
2702
|
+
offscreen.width = width * ctx.__dpr;
|
|
2703
|
+
offscreen.height = height * ctx.__dpr;
|
|
2704
|
+
const context = offscreen.getContext("2d", {
|
|
2705
|
+
alpha: options?.alpha ?? true,
|
|
2706
|
+
willReadFrequently: options?.willreadfrequently ?? false
|
|
2707
|
+
});
|
|
2708
|
+
if (!context) throw new Error("Failed to create offscreen context");
|
|
2709
|
+
context.__dpr = ctx.__dpr;
|
|
2710
|
+
context.width = width * ctx.__dpr;
|
|
2711
|
+
context.height = height * ctx.__dpr;
|
|
2712
|
+
context.__isMainContext = false;
|
|
2713
|
+
context.__imageOrigin = "corner";
|
|
2714
|
+
context.__rectangleOrigin = "corner";
|
|
2715
|
+
context.__canvasOrigin = "corner";
|
|
2716
|
+
context.__textFont = "sans-serif";
|
|
2717
|
+
context.__textWeight = "normal";
|
|
2718
|
+
context.__textStyle = "normal";
|
|
2719
|
+
context.__textSize = 120;
|
|
2720
|
+
context.__textLeading = void 0;
|
|
2721
|
+
context.__textAlignment = {
|
|
2722
|
+
horizontal: "left",
|
|
2723
|
+
vertical: "top"
|
|
2724
|
+
};
|
|
2725
|
+
context.__fillRule = "nonzero";
|
|
2726
|
+
if (!options?.ignoreFunctions) {
|
|
2727
|
+
context.Color = ctx.Color;
|
|
2728
|
+
context.createVector = (x = 0, y = 0) => new Vector_default(x, y);
|
|
2729
|
+
context.Easing = ctx.Easing;
|
|
2730
|
+
context.Text = ctx.Text;
|
|
2731
|
+
Object.entries(KlintFunctions).forEach(([name, fn]) => {
|
|
2732
|
+
context[name] = fn(context);
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
if (options?.origin) {
|
|
2736
|
+
context.__canvasOrigin = options.origin;
|
|
2737
|
+
if (options.origin === "center") {
|
|
2738
|
+
context.translate(context.width * 0.5, context.height * 0.5);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
if (callback) {
|
|
2742
|
+
callback(context);
|
|
2743
|
+
}
|
|
2744
|
+
if (options?.static === "true") {
|
|
2745
|
+
const base64 = offscreen.toDataURL();
|
|
2746
|
+
const img = new Image();
|
|
2747
|
+
img.src = base64;
|
|
2748
|
+
ctx.__offscreens.set(id, img);
|
|
2749
|
+
return img;
|
|
2750
|
+
}
|
|
2751
|
+
ctx.__offscreens.set(id, context);
|
|
2752
|
+
return context;
|
|
2753
|
+
},
|
|
2754
|
+
getOffscreen: (ctx) => (id) => {
|
|
2755
|
+
const offscreen = ctx.__offscreens.get(id);
|
|
2756
|
+
if (!offscreen)
|
|
2757
|
+
throw new Error(`No offscreen context found with id: ${id}`);
|
|
2758
|
+
return offscreen;
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
var KlintFunctions = {
|
|
2762
|
+
extend: (ctx) => (name, data, enforceReplace = false) => {
|
|
2763
|
+
if (name in ctx && !enforceReplace) return;
|
|
2764
|
+
ctx[name] = data;
|
|
2765
|
+
},
|
|
2766
|
+
background: (ctx) => (color) => {
|
|
2767
|
+
ctx.resetTransform();
|
|
2768
|
+
ctx.push();
|
|
2769
|
+
if (color && color !== "transparent") {
|
|
2770
|
+
ctx.fillStyle = color;
|
|
2771
|
+
ctx.fillRect(0, 0, ctx.width, ctx.height);
|
|
2772
|
+
} else {
|
|
2773
|
+
ctx.clearRect(0, 0, ctx.width, ctx.height);
|
|
2774
|
+
}
|
|
2775
|
+
ctx.pop();
|
|
2776
|
+
if (ctx.__canvasOrigin === "center")
|
|
2777
|
+
ctx.translate(ctx.width * 0.5, ctx.height * 0.5);
|
|
2778
|
+
},
|
|
2779
|
+
reset: (ctx) => () => {
|
|
2780
|
+
ctx.clearRect(0, 0, ctx.width, ctx.height);
|
|
1322
2781
|
ctx.resetTransform();
|
|
1323
2782
|
},
|
|
1324
2783
|
clear: (ctx) => () => {
|
|
@@ -1365,9 +2824,12 @@ var KlintFunctions = {
|
|
|
1365
2824
|
return true;
|
|
1366
2825
|
},
|
|
1367
2826
|
drawIfVisible: (ctx) => () => {
|
|
1368
|
-
if (ctx.checkTransparency("fill")) ctx.fill();
|
|
2827
|
+
if (ctx.checkTransparency("fill")) ctx.fill(ctx.__fillRule || "nonzero");
|
|
1369
2828
|
if (ctx.checkTransparency("stroke")) ctx.stroke();
|
|
1370
2829
|
},
|
|
2830
|
+
fillRule: (ctx) => (rule) => {
|
|
2831
|
+
ctx.__fillRule = rule;
|
|
2832
|
+
},
|
|
1371
2833
|
line: (ctx) => (x1, y1, x2, y2) => {
|
|
1372
2834
|
if (!ctx.checkTransparency("stroke")) return;
|
|
1373
2835
|
ctx.beginPath();
|
|
@@ -1429,17 +2891,29 @@ var KlintFunctions = {
|
|
|
1429
2891
|
ctx.__currentContours = [];
|
|
1430
2892
|
},
|
|
1431
2893
|
beginContour: (ctx) => () => {
|
|
1432
|
-
if (!ctx.__startedShape) return;
|
|
1433
|
-
if (ctx.__startedContour && ctx.__currentContour?.length) {
|
|
1434
|
-
ctx.__currentContours?.push([...ctx.__currentContour]);
|
|
1435
|
-
}
|
|
2894
|
+
if (!ctx.__startedShape || ctx.__startedContour) return;
|
|
1436
2895
|
ctx.__startedContour = true;
|
|
1437
2896
|
ctx.__currentContour = [];
|
|
1438
2897
|
},
|
|
1439
2898
|
vertex: (ctx) => (x, y) => {
|
|
1440
2899
|
if (!ctx.__startedShape) return;
|
|
1441
2900
|
const points = ctx.__startedContour ? ctx.__currentContour : ctx.__currentShape;
|
|
1442
|
-
points?.push(
|
|
2901
|
+
points?.push({ type: "line", x, y });
|
|
2902
|
+
},
|
|
2903
|
+
bezierVertex: (ctx) => (cp1x, cp1y, cp2x, cp2y, x, y) => {
|
|
2904
|
+
if (!ctx.__startedShape) return;
|
|
2905
|
+
const points = ctx.__startedContour ? ctx.__currentContour : ctx.__currentShape;
|
|
2906
|
+
points?.push({ type: "bezier", cp1x, cp1y, cp2x, cp2y, x, y });
|
|
2907
|
+
},
|
|
2908
|
+
quadraticVertex: (ctx) => (cpx, cpy, x, y) => {
|
|
2909
|
+
if (!ctx.__startedShape) return;
|
|
2910
|
+
const points = ctx.__startedContour ? ctx.__currentContour : ctx.__currentShape;
|
|
2911
|
+
points?.push({ type: "quadratic", cpx, cpy, x, y });
|
|
2912
|
+
},
|
|
2913
|
+
arcVertex: (ctx) => (x1, y1, x2, y2, radius) => {
|
|
2914
|
+
if (!ctx.__startedShape) return;
|
|
2915
|
+
const points = ctx.__startedContour ? ctx.__currentContour : ctx.__currentShape;
|
|
2916
|
+
points?.push({ type: "arc", x1, y1, x2, y2, radius });
|
|
1443
2917
|
},
|
|
1444
2918
|
endContour: (ctx) => (forceRevert = true) => {
|
|
1445
2919
|
if (!ctx.__startedContour || !ctx.__currentContour?.length) return;
|
|
@@ -1457,17 +2931,38 @@ var KlintFunctions = {
|
|
|
1457
2931
|
const points = ctx.__currentShape;
|
|
1458
2932
|
if (!points?.length) return;
|
|
1459
2933
|
const drawPath = (points2, close2 = false) => {
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
2934
|
+
if (points2.length === 0) return;
|
|
2935
|
+
const firstPoint = points2[0];
|
|
2936
|
+
const startX = firstPoint.type === "line" ? firstPoint.x : firstPoint.type === "bezier" ? firstPoint.x : firstPoint.type === "quadratic" ? firstPoint.x : firstPoint.x2;
|
|
2937
|
+
const startY = firstPoint.type === "line" ? firstPoint.y : firstPoint.type === "bezier" ? firstPoint.y : firstPoint.type === "quadratic" ? firstPoint.y : firstPoint.y2;
|
|
2938
|
+
ctx.moveTo(startX, startY);
|
|
2939
|
+
for (let i = 0; i < points2.length; i++) {
|
|
2940
|
+
const point = points2[i];
|
|
2941
|
+
switch (point.type) {
|
|
2942
|
+
case "line":
|
|
2943
|
+
if (i > 0) ctx.lineTo(point.x, point.y);
|
|
2944
|
+
break;
|
|
2945
|
+
case "bezier":
|
|
2946
|
+
ctx.bezierCurveTo(
|
|
2947
|
+
point.cp1x,
|
|
2948
|
+
point.cp1y,
|
|
2949
|
+
point.cp2x,
|
|
2950
|
+
point.cp2y,
|
|
2951
|
+
point.x,
|
|
2952
|
+
point.y
|
|
2953
|
+
);
|
|
2954
|
+
break;
|
|
2955
|
+
case "quadratic":
|
|
2956
|
+
ctx.quadraticCurveTo(point.cpx, point.cpy, point.x, point.y);
|
|
2957
|
+
break;
|
|
2958
|
+
case "arc":
|
|
2959
|
+
ctx.arcTo(point.x1, point.y1, point.x2, point.y2, point.radius);
|
|
2960
|
+
break;
|
|
1469
2961
|
}
|
|
1470
2962
|
}
|
|
2963
|
+
if (close2 && points2.length > 1) {
|
|
2964
|
+
ctx.closePath();
|
|
2965
|
+
}
|
|
1471
2966
|
};
|
|
1472
2967
|
ctx.beginPath();
|
|
1473
2968
|
drawPath(points, close);
|
|
@@ -1491,6 +2986,9 @@ var KlintFunctions = {
|
|
|
1491
2986
|
addColorStop: () => (gradient, offset = 0, color = "#000") => {
|
|
1492
2987
|
return gradient.addColorStop(offset, color);
|
|
1493
2988
|
},
|
|
2989
|
+
PI: () => Math.PI,
|
|
2990
|
+
TWO_PI: () => Math.PI * 2,
|
|
2991
|
+
TAU: () => Math.PI * 2,
|
|
1494
2992
|
constrain: () => (val, floor, ceil) => {
|
|
1495
2993
|
return Math.max(floor, Math.min(val, ceil));
|
|
1496
2994
|
},
|
|
@@ -1528,6 +3026,14 @@ var KlintFunctions = {
|
|
|
1528
3026
|
const t = (n - A) / (B - A);
|
|
1529
3027
|
return ctx.lerp(C, D, t, bounded);
|
|
1530
3028
|
},
|
|
3029
|
+
bezierLerp: () => (a, b, c, d, t) => {
|
|
3030
|
+
const u = 1 - t;
|
|
3031
|
+
return u * u * u * a + 3 * u * u * t * b + 3 * u * t * t * c + t * t * t * d;
|
|
3032
|
+
},
|
|
3033
|
+
bezierTangent: () => (a, b, c, d, t) => {
|
|
3034
|
+
const u = 1 - t;
|
|
3035
|
+
return 3 * d * t * t - 3 * c * t * t + 6 * c * u * t - 6 * b * u * t + 3 * b * u * u - 3 * a * u * u;
|
|
3036
|
+
},
|
|
1531
3037
|
textFont: (ctx) => (font) => {
|
|
1532
3038
|
ctx.__textFont = font;
|
|
1533
3039
|
},
|
|
@@ -1563,7 +3069,8 @@ var KlintFunctions = {
|
|
|
1563
3069
|
ctx.__textAlignment.vertical = vertical ?? ctx.__textAlignment.vertical;
|
|
1564
3070
|
},
|
|
1565
3071
|
textLeading: (ctx) => (spacing) => {
|
|
1566
|
-
ctx.
|
|
3072
|
+
ctx.__textLeading = spacing;
|
|
3073
|
+
return ctx.__textLeading;
|
|
1567
3074
|
},
|
|
1568
3075
|
computeFont: (ctx) => () => {
|
|
1569
3076
|
ctx.computeTextStyle();
|
|
@@ -1582,10 +3089,110 @@ var KlintFunctions = {
|
|
|
1582
3089
|
if (ctx.textBaseline !== ctx.__textAlignment.vertical) {
|
|
1583
3090
|
ctx.textBaseline = ctx.__textAlignment.vertical;
|
|
1584
3091
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
ctx.
|
|
3092
|
+
const textString = String(text);
|
|
3093
|
+
if (textString.includes("\n")) {
|
|
3094
|
+
const lines = textString.split("\n");
|
|
3095
|
+
const firstLineMetrics = ctx.measureText(lines[0] || "M");
|
|
3096
|
+
const defaultLineHeight = firstLineMetrics.actualBoundingBoxAscent + firstLineMetrics.actualBoundingBoxDescent;
|
|
3097
|
+
const lineHeight = ctx.__textLeading ?? ctx.__textSize * 1.2;
|
|
3098
|
+
const totalHeight = lines.length * lineHeight - (lineHeight - defaultLineHeight);
|
|
3099
|
+
let startY = y;
|
|
3100
|
+
if (ctx.__textAlignment.vertical === "middle") {
|
|
3101
|
+
startY = y - totalHeight / 2 + defaultLineHeight / 2;
|
|
3102
|
+
} else if (ctx.__textAlignment.vertical === "bottom") {
|
|
3103
|
+
startY = y - totalHeight + defaultLineHeight;
|
|
3104
|
+
} else if (ctx.__textAlignment.vertical === "top") {
|
|
3105
|
+
startY = y + defaultLineHeight / 2;
|
|
3106
|
+
}
|
|
3107
|
+
lines.forEach((line, index) => {
|
|
3108
|
+
const lineY = startY + index * lineHeight;
|
|
3109
|
+
if (ctx.checkTransparency("fill"))
|
|
3110
|
+
ctx.fillText(line, x, lineY, maxWidth);
|
|
3111
|
+
if (ctx.checkTransparency("stroke"))
|
|
3112
|
+
ctx.strokeText(line, x, lineY, maxWidth);
|
|
3113
|
+
});
|
|
3114
|
+
} else {
|
|
3115
|
+
if (ctx.checkTransparency("fill"))
|
|
3116
|
+
ctx.fillText(textString, x, y, maxWidth);
|
|
3117
|
+
if (ctx.checkTransparency("stroke"))
|
|
3118
|
+
ctx.strokeText(textString, x, y, maxWidth);
|
|
3119
|
+
}
|
|
3120
|
+
},
|
|
3121
|
+
paragraph: (ctx) => (text, x, y, width, options) => {
|
|
3122
|
+
if (text === void 0) return;
|
|
3123
|
+
ctx.computeFont();
|
|
3124
|
+
const textString = String(text);
|
|
3125
|
+
const justification = options?.justification || "left";
|
|
3126
|
+
const overflow = options?.overflow || 0;
|
|
3127
|
+
const breakMode = options?.break || "words";
|
|
3128
|
+
const originalAlign = ctx.textAlign;
|
|
3129
|
+
const originalBaseline = ctx.textBaseline;
|
|
3130
|
+
if (justification === "center") {
|
|
3131
|
+
ctx.textAlign = "center";
|
|
3132
|
+
} else if (justification === "right") {
|
|
3133
|
+
ctx.textAlign = "right";
|
|
3134
|
+
} else {
|
|
3135
|
+
ctx.textAlign = "left";
|
|
3136
|
+
}
|
|
3137
|
+
ctx.textBaseline = "top";
|
|
3138
|
+
const tokens = breakMode === "letters" ? textString.split("") : textString.split(/\s+/);
|
|
3139
|
+
const lines = [];
|
|
3140
|
+
let currentLine = "";
|
|
3141
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
3142
|
+
const token = tokens[i];
|
|
3143
|
+
const testLine = currentLine ? breakMode === "letters" ? currentLine + token : currentLine + " " + token : token;
|
|
3144
|
+
const metrics = ctx.measureText(testLine);
|
|
3145
|
+
if (metrics.width > width && currentLine !== "") {
|
|
3146
|
+
lines.push(currentLine);
|
|
3147
|
+
currentLine = token;
|
|
3148
|
+
} else {
|
|
3149
|
+
currentLine = testLine;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
if (currentLine) {
|
|
3153
|
+
lines.push(currentLine);
|
|
3154
|
+
}
|
|
3155
|
+
const lineHeight = ctx.__textLeading ?? ctx.__textSize * 1.2;
|
|
3156
|
+
let linesToDraw = lines;
|
|
3157
|
+
if (overflow > 0 && lines.length * lineHeight > overflow) {
|
|
3158
|
+
const maxLines = Math.floor(overflow / lineHeight);
|
|
3159
|
+
linesToDraw = lines.slice(0, maxLines);
|
|
3160
|
+
}
|
|
3161
|
+
linesToDraw.forEach((line, index) => {
|
|
3162
|
+
const lineY = y + index * lineHeight;
|
|
3163
|
+
let lineX = x;
|
|
3164
|
+
if (justification === "center") {
|
|
3165
|
+
lineX = x + width / 2;
|
|
3166
|
+
} else if (justification === "right") {
|
|
3167
|
+
lineX = x + width;
|
|
3168
|
+
} else if (justification === "justified" && index < linesToDraw.length - 1) {
|
|
3169
|
+
const words = line.split(/\s+/);
|
|
3170
|
+
if (words.length > 1) {
|
|
3171
|
+
const textWidth = ctx.measureText(words.join("")).width;
|
|
3172
|
+
const totalSpaceWidth = width - textWidth;
|
|
3173
|
+
const spaceWidth = totalSpaceWidth / (words.length - 1);
|
|
3174
|
+
let currentX = x;
|
|
3175
|
+
words.forEach((word, wordIndex) => {
|
|
3176
|
+
if (ctx.checkTransparency("fill")) {
|
|
3177
|
+
ctx.fillText(word, currentX, lineY);
|
|
3178
|
+
}
|
|
3179
|
+
if (ctx.checkTransparency("stroke")) {
|
|
3180
|
+
ctx.strokeText(word, currentX, lineY);
|
|
3181
|
+
}
|
|
3182
|
+
currentX += ctx.measureText(word).width + spaceWidth;
|
|
3183
|
+
});
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
if (ctx.checkTransparency("fill")) {
|
|
3188
|
+
ctx.fillText(line, lineX, lineY);
|
|
3189
|
+
}
|
|
3190
|
+
if (ctx.checkTransparency("stroke")) {
|
|
3191
|
+
ctx.strokeText(line, lineX, lineY);
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
ctx.textAlign = originalAlign;
|
|
3195
|
+
ctx.textBaseline = originalBaseline;
|
|
1589
3196
|
},
|
|
1590
3197
|
// DO NOT use putImageData for images you can draw : https://www.measurethat.net/Benchmarks/Show/9510/0/putimagedata-vs-drawimage
|
|
1591
3198
|
image: (ctx) => (image, x, y, arg3, arg4, arg5, arg6, arg7, arg8) => {
|
|
@@ -1635,11 +3242,8 @@ var KlintFunctions = {
|
|
|
1635
3242
|
return ctx.getImageData(0, 0, ctx.width, ctx.height);
|
|
1636
3243
|
},
|
|
1637
3244
|
updatePixels: (ctx) => (pixels) => {
|
|
1638
|
-
const
|
|
1639
|
-
|
|
1640
|
-
ctx.width,
|
|
1641
|
-
ctx.height
|
|
1642
|
-
);
|
|
3245
|
+
const pixelArray = pixels instanceof Uint8ClampedArray ? new Uint8ClampedArray(pixels) : new Uint8ClampedArray(pixels);
|
|
3246
|
+
const imageData = new ImageData(pixelArray, ctx.width, ctx.height);
|
|
1643
3247
|
ctx.putImageData(imageData, 0, 0);
|
|
1644
3248
|
},
|
|
1645
3249
|
readPixels: (ctx) => (x, y, w = 1, h = 1) => {
|
|
@@ -1655,7 +3259,7 @@ var KlintFunctions = {
|
|
|
1655
3259
|
ctx.globalAlpha = ctx.constrain(value, 0, 1);
|
|
1656
3260
|
},
|
|
1657
3261
|
blend: (ctx) => (blend) => {
|
|
1658
|
-
ctx.globalCompositeOperation = blend;
|
|
3262
|
+
ctx.globalCompositeOperation = blend === "default" ? "source-over" : blend;
|
|
1659
3263
|
},
|
|
1660
3264
|
setCanvasOrigin: (ctx) => (type) => {
|
|
1661
3265
|
ctx.__canvasOrigin = type;
|
|
@@ -1693,10 +3297,73 @@ var KlintFunctions = {
|
|
|
1693
3297
|
if (ctx.__canvasOrigin === "center") {
|
|
1694
3298
|
ctx.translate(ctx.width * 0.5, ctx.height * 0.5);
|
|
1695
3299
|
}
|
|
3300
|
+
},
|
|
3301
|
+
clipTo: (ctx) => (callback, fillRule) => {
|
|
3302
|
+
const originalFill = ctx.fill;
|
|
3303
|
+
const originalStroke = ctx.stroke;
|
|
3304
|
+
const originalDrawIfVisible = ctx.drawIfVisible;
|
|
3305
|
+
const originalBeginPath = ctx.beginPath;
|
|
3306
|
+
ctx.fill = () => {
|
|
3307
|
+
};
|
|
3308
|
+
ctx.stroke = () => {
|
|
3309
|
+
};
|
|
3310
|
+
ctx.drawIfVisible = () => {
|
|
3311
|
+
};
|
|
3312
|
+
let allowBeginPath = true;
|
|
3313
|
+
ctx.beginPath = () => {
|
|
3314
|
+
if (allowBeginPath) {
|
|
3315
|
+
originalBeginPath.call(ctx);
|
|
3316
|
+
allowBeginPath = false;
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
ctx.beginPath();
|
|
3320
|
+
callback(ctx);
|
|
3321
|
+
ctx.clip(fillRule || ctx.__fillRule || "nonzero");
|
|
3322
|
+
ctx.beginPath = originalBeginPath;
|
|
3323
|
+
ctx.drawIfVisible = originalDrawIfVisible;
|
|
3324
|
+
ctx.fill = originalFill;
|
|
3325
|
+
ctx.stroke = originalStroke;
|
|
3326
|
+
},
|
|
3327
|
+
canIuseFilter: (ctx) => () => {
|
|
3328
|
+
return ctx.filter !== void 0 && ctx.filter !== null;
|
|
3329
|
+
},
|
|
3330
|
+
blur: (ctx) => (radius) => {
|
|
3331
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3332
|
+
ctx.filter = `blur(${radius}px)`;
|
|
3333
|
+
},
|
|
3334
|
+
SVGfilter: (ctx) => (url) => {
|
|
3335
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3336
|
+
if (!url || !url.startsWith("url(")) return;
|
|
3337
|
+
ctx.filter = url;
|
|
3338
|
+
},
|
|
3339
|
+
dropShadow: (ctx) => (offsetX, offsetY, blurRadius, color) => {
|
|
3340
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3341
|
+
ctx.filter = `drop-shadow(${offsetX}px ${offsetY}px ${blurRadius}px ${color})`;
|
|
3342
|
+
},
|
|
3343
|
+
grayscale: (ctx) => (amount) => {
|
|
3344
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3345
|
+
const value = ctx.constrain(amount, 0, 1);
|
|
3346
|
+
ctx.filter = `grayscale(${value})`;
|
|
3347
|
+
},
|
|
3348
|
+
hue: (ctx) => (angle) => {
|
|
3349
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3350
|
+
const degrees = angle * 180 / Math.PI;
|
|
3351
|
+
ctx.filter = `hue-rotate(${degrees}deg)`;
|
|
3352
|
+
},
|
|
3353
|
+
invert: (ctx) => (amount) => {
|
|
3354
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3355
|
+
const value = ctx.constrain(amount, 0, 1);
|
|
3356
|
+
ctx.filter = `invert(${value})`;
|
|
3357
|
+
},
|
|
3358
|
+
filterOpacity: (ctx) => (value) => {
|
|
3359
|
+
if (ctx.filter === void 0 || ctx.filter === null) return;
|
|
3360
|
+
const amount = ctx.constrain(value, 0, 1);
|
|
3361
|
+
ctx.filter = `opacity(${amount})`;
|
|
1696
3362
|
}
|
|
1697
3363
|
};
|
|
1698
3364
|
|
|
1699
3365
|
// src/useKlint.tsx
|
|
3366
|
+
var import_meta2 = {};
|
|
1700
3367
|
var DEFAULT_MOUSE_STATE = {
|
|
1701
3368
|
x: 0,
|
|
1702
3369
|
y: 0,
|
|
@@ -1730,13 +3397,50 @@ var DEFAULT_GESTURE_STATE = {
|
|
|
1730
3397
|
lastX: 0,
|
|
1731
3398
|
lastY: 0
|
|
1732
3399
|
};
|
|
3400
|
+
var DEFAULT_KEYBOARD_STATE = {
|
|
3401
|
+
pressedKeys: /* @__PURE__ */ new Set(),
|
|
3402
|
+
modifiers: {
|
|
3403
|
+
alt: false,
|
|
3404
|
+
shift: false,
|
|
3405
|
+
ctrl: false,
|
|
3406
|
+
meta: false
|
|
3407
|
+
},
|
|
3408
|
+
lastKey: null,
|
|
3409
|
+
lastKeyTime: 0
|
|
3410
|
+
};
|
|
1733
3411
|
function useKlint() {
|
|
1734
3412
|
const contextRef = (0, import_react2.useRef)(null);
|
|
1735
3413
|
const mouseRef = (0, import_react2.useRef)(null);
|
|
1736
3414
|
const scrollRef = (0, import_react2.useRef)(null);
|
|
1737
3415
|
const gestureRef = (0, import_react2.useRef)(null);
|
|
3416
|
+
const keyboardRef = (0, import_react2.useRef)(null);
|
|
1738
3417
|
const useDev = () => {
|
|
1739
|
-
|
|
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
|
+
(0, import_react2.useEffect)(() => {
|
|
3439
|
+
if (process.env.NODE_ENV === "development" && contextRef.current) {
|
|
3440
|
+
console.log("[Klint] hot updated - clearing context state");
|
|
3441
|
+
contextRef.current.__isReadyToDraw = true;
|
|
3442
|
+
}
|
|
3443
|
+
});
|
|
1740
3444
|
};
|
|
1741
3445
|
const KlintImage = () => {
|
|
1742
3446
|
const imagesRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
@@ -1810,6 +3514,8 @@ function useKlint() {
|
|
|
1810
3514
|
if (!contextRef.current?.canvas) return;
|
|
1811
3515
|
const canvas = contextRef.current.canvas;
|
|
1812
3516
|
const ctx = contextRef.current;
|
|
3517
|
+
const controller = new AbortController();
|
|
3518
|
+
const { signal } = controller;
|
|
1813
3519
|
const updateMousePosition = (e) => {
|
|
1814
3520
|
const rect = canvas.getBoundingClientRect();
|
|
1815
3521
|
const dpr = window.devicePixelRatio || 1;
|
|
@@ -1848,20 +3554,13 @@ function useKlint() {
|
|
|
1848
3554
|
const handleClick = (e) => {
|
|
1849
3555
|
if (clickCallbackRef.current) clickCallbackRef.current(ctx, e);
|
|
1850
3556
|
};
|
|
1851
|
-
canvas.addEventListener("mousemove", updateMousePosition);
|
|
1852
|
-
canvas.addEventListener("mousedown", handleMouseDown);
|
|
1853
|
-
canvas.addEventListener("mouseup", handleMouseUp);
|
|
1854
|
-
canvas.addEventListener("mouseenter", handleMouseEnter);
|
|
1855
|
-
canvas.addEventListener("mouseleave", handleMouseLeave);
|
|
1856
|
-
canvas.addEventListener("click", handleClick);
|
|
1857
|
-
return () =>
|
|
1858
|
-
canvas.removeEventListener("mousemove", updateMousePosition);
|
|
1859
|
-
canvas.removeEventListener("mousedown", handleMouseDown);
|
|
1860
|
-
canvas.removeEventListener("mouseup", handleMouseUp);
|
|
1861
|
-
canvas.removeEventListener("mouseenter", handleMouseEnter);
|
|
1862
|
-
canvas.removeEventListener("mouseleave", handleMouseLeave);
|
|
1863
|
-
canvas.removeEventListener("click", handleClick);
|
|
1864
|
-
};
|
|
3557
|
+
canvas.addEventListener("mousemove", updateMousePosition, { signal });
|
|
3558
|
+
canvas.addEventListener("mousedown", handleMouseDown, { signal });
|
|
3559
|
+
canvas.addEventListener("mouseup", handleMouseUp, { signal });
|
|
3560
|
+
canvas.addEventListener("mouseenter", handleMouseEnter, { signal });
|
|
3561
|
+
canvas.addEventListener("mouseleave", handleMouseLeave, { signal });
|
|
3562
|
+
canvas.addEventListener("click", handleClick, { signal });
|
|
3563
|
+
return () => controller.abort();
|
|
1865
3564
|
});
|
|
1866
3565
|
return {
|
|
1867
3566
|
mouse: mouseRef.current,
|
|
@@ -1881,6 +3580,8 @@ function useKlint() {
|
|
|
1881
3580
|
if (!contextRef.current?.canvas) return;
|
|
1882
3581
|
const canvas = contextRef.current.canvas;
|
|
1883
3582
|
const ctx = contextRef.current;
|
|
3583
|
+
const controller = new AbortController();
|
|
3584
|
+
const { signal } = controller;
|
|
1884
3585
|
const handleScroll = (e) => {
|
|
1885
3586
|
e.preventDefault();
|
|
1886
3587
|
if (!scrollRef.current) return;
|
|
@@ -1893,8 +3594,8 @@ function useKlint() {
|
|
|
1893
3594
|
scrollCallbackRef.current(ctx, scrollRef.current, e);
|
|
1894
3595
|
}
|
|
1895
3596
|
};
|
|
1896
|
-
canvas.addEventListener("wheel", handleScroll);
|
|
1897
|
-
return () =>
|
|
3597
|
+
canvas.addEventListener("wheel", handleScroll, { signal });
|
|
3598
|
+
return () => controller.abort();
|
|
1898
3599
|
});
|
|
1899
3600
|
return {
|
|
1900
3601
|
scroll: scrollRef.current,
|
|
@@ -2040,18 +3741,19 @@ function useKlint() {
|
|
|
2040
3741
|
touchEndCallbackRef.current(ctx, e, gestureRef.current);
|
|
2041
3742
|
}
|
|
2042
3743
|
};
|
|
3744
|
+
const controller = new AbortController();
|
|
3745
|
+
const { signal } = controller;
|
|
2043
3746
|
canvas.addEventListener("touchstart", handleTouchStart, {
|
|
2044
|
-
passive: false
|
|
3747
|
+
passive: false,
|
|
3748
|
+
signal
|
|
2045
3749
|
});
|
|
2046
|
-
canvas.addEventListener("touchmove", handleTouchMove, {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
canvas.removeEventListener("touchcancel", handleTouchCancel);
|
|
2054
|
-
};
|
|
3750
|
+
canvas.addEventListener("touchmove", handleTouchMove, {
|
|
3751
|
+
passive: false,
|
|
3752
|
+
signal
|
|
3753
|
+
});
|
|
3754
|
+
canvas.addEventListener("touchend", handleTouchEnd, { signal });
|
|
3755
|
+
canvas.addEventListener("touchcancel", handleTouchCancel, { signal });
|
|
3756
|
+
return () => controller.abort();
|
|
2055
3757
|
}, []);
|
|
2056
3758
|
return {
|
|
2057
3759
|
gesture: gestureRef.current,
|
|
@@ -2064,6 +3766,315 @@ function useKlint() {
|
|
|
2064
3766
|
onTouchEnd: (callback) => touchEndCallbackRef.current = callback
|
|
2065
3767
|
};
|
|
2066
3768
|
};
|
|
3769
|
+
const KlintKeyboard = () => {
|
|
3770
|
+
if (!keyboardRef.current) {
|
|
3771
|
+
keyboardRef.current = { ...DEFAULT_KEYBOARD_STATE };
|
|
3772
|
+
}
|
|
3773
|
+
const keyPressedCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
3774
|
+
const keyReleasedCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
3775
|
+
const keyComboCallbackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
3776
|
+
(0, import_react2.useEffect)(() => {
|
|
3777
|
+
if (!contextRef.current) return;
|
|
3778
|
+
const ctx = contextRef.current;
|
|
3779
|
+
const controller = new AbortController();
|
|
3780
|
+
const { signal } = controller;
|
|
3781
|
+
const updateModifiers = (e) => {
|
|
3782
|
+
if (!keyboardRef.current) return;
|
|
3783
|
+
keyboardRef.current.modifiers.alt = e.altKey;
|
|
3784
|
+
keyboardRef.current.modifiers.shift = e.shiftKey;
|
|
3785
|
+
keyboardRef.current.modifiers.ctrl = e.ctrlKey;
|
|
3786
|
+
keyboardRef.current.modifiers.meta = e.metaKey;
|
|
3787
|
+
};
|
|
3788
|
+
const normalizeKey2 = (key) => {
|
|
3789
|
+
const keyMap = {
|
|
3790
|
+
" ": "Space",
|
|
3791
|
+
Control: "Ctrl",
|
|
3792
|
+
Escape: "Esc"
|
|
3793
|
+
};
|
|
3794
|
+
return keyMap[key] || key;
|
|
3795
|
+
};
|
|
3796
|
+
const handleKeyDown = (e) => {
|
|
3797
|
+
if (!keyboardRef.current) return;
|
|
3798
|
+
const normalizedKey = normalizeKey2(e.key);
|
|
3799
|
+
keyboardRef.current.pressedKeys.add(normalizedKey);
|
|
3800
|
+
keyboardRef.current.lastKey = normalizedKey;
|
|
3801
|
+
keyboardRef.current.lastKeyTime = performance.now();
|
|
3802
|
+
updateModifiers(e);
|
|
3803
|
+
const keyCallback = keyPressedCallbackRef.current.get(normalizedKey);
|
|
3804
|
+
if (keyCallback) {
|
|
3805
|
+
keyCallback(ctx, e);
|
|
3806
|
+
}
|
|
3807
|
+
const pressedKeysArray = Array.from(
|
|
3808
|
+
keyboardRef.current.pressedKeys
|
|
3809
|
+
).sort();
|
|
3810
|
+
const comboKey = pressedKeysArray.join("+");
|
|
3811
|
+
const comboCallback = keyComboCallbackRef.current.get(comboKey);
|
|
3812
|
+
if (comboCallback) {
|
|
3813
|
+
comboCallback(ctx, e);
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
const handleKeyUp = (e) => {
|
|
3817
|
+
if (!keyboardRef.current) return;
|
|
3818
|
+
const normalizedKey = normalizeKey2(e.key);
|
|
3819
|
+
keyboardRef.current.pressedKeys.delete(normalizedKey);
|
|
3820
|
+
updateModifiers(e);
|
|
3821
|
+
const keyCallback = keyReleasedCallbackRef.current.get(normalizedKey);
|
|
3822
|
+
if (keyCallback) {
|
|
3823
|
+
keyCallback(ctx, e);
|
|
3824
|
+
}
|
|
3825
|
+
};
|
|
3826
|
+
window.addEventListener("keydown", handleKeyDown, { signal });
|
|
3827
|
+
window.addEventListener("keyup", handleKeyUp, { signal });
|
|
3828
|
+
return () => controller.abort();
|
|
3829
|
+
}, []);
|
|
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
|
+
return {
|
|
3842
|
+
keyboard: keyboardRef.current,
|
|
3843
|
+
// Register callback for single key press
|
|
3844
|
+
keyPressed: (key, callback) => {
|
|
3845
|
+
keyPressedCallbackRef.current.set(normalizeKey(key), callback);
|
|
3846
|
+
},
|
|
3847
|
+
// Register callback for single key release
|
|
3848
|
+
keyReleased: (key, callback) => {
|
|
3849
|
+
keyReleasedCallbackRef.current.set(normalizeKey(key), callback);
|
|
3850
|
+
},
|
|
3851
|
+
// Register callback for key combination (e.g., ['Alt', 'Shift'])
|
|
3852
|
+
keyCombo: (keys, callback) => {
|
|
3853
|
+
const comboKey = createComboKey(keys);
|
|
3854
|
+
keyComboCallbackRef.current.set(comboKey, callback);
|
|
3855
|
+
},
|
|
3856
|
+
// Utility functions
|
|
3857
|
+
isPressed: (key) => {
|
|
3858
|
+
return keyboardRef.current?.pressedKeys.has(normalizeKey(key)) || false;
|
|
3859
|
+
},
|
|
3860
|
+
arePressed: (keys) => {
|
|
3861
|
+
if (!keyboardRef.current) return false;
|
|
3862
|
+
return keys.every(
|
|
3863
|
+
(key) => keyboardRef.current.pressedKeys.has(normalizeKey(key))
|
|
3864
|
+
);
|
|
3865
|
+
},
|
|
3866
|
+
// Clear all callbacks
|
|
3867
|
+
clearCallbacks: () => {
|
|
3868
|
+
keyPressedCallbackRef.current.clear();
|
|
3869
|
+
keyReleasedCallbackRef.current.clear();
|
|
3870
|
+
keyComboCallbackRef.current.clear();
|
|
3871
|
+
}
|
|
3872
|
+
};
|
|
3873
|
+
};
|
|
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
|
+
};
|
|
2067
4078
|
const KlintWindow = () => {
|
|
2068
4079
|
const resizeCallbackRef = (0, import_react2.useRef)(
|
|
2069
4080
|
null
|
|
@@ -2074,6 +4085,8 @@ function useKlint() {
|
|
|
2074
4085
|
(0, import_react2.useEffect)(() => {
|
|
2075
4086
|
if (!contextRef.current) return;
|
|
2076
4087
|
const ctx = contextRef.current;
|
|
4088
|
+
const controller = new AbortController();
|
|
4089
|
+
const { signal } = controller;
|
|
2077
4090
|
const handleResize = () => {
|
|
2078
4091
|
if (resizeCallbackRef.current) resizeCallbackRef.current(ctx);
|
|
2079
4092
|
};
|
|
@@ -2089,19 +4102,13 @@ function useKlint() {
|
|
|
2089
4102
|
visibilityChangeCallbackRef.current(ctx, isVisible);
|
|
2090
4103
|
}
|
|
2091
4104
|
};
|
|
2092
|
-
window.addEventListener("resize", handleResize);
|
|
2093
|
-
window.addEventListener("blur", handleBlur);
|
|
2094
|
-
window.addEventListener("focus", handleFocus);
|
|
2095
|
-
document.addEventListener("visibilitychange", handleVisibilityChange
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
window.removeEventListener("focus", handleFocus);
|
|
2100
|
-
document.removeEventListener(
|
|
2101
|
-
"visibilitychange",
|
|
2102
|
-
handleVisibilityChange
|
|
2103
|
-
);
|
|
2104
|
-
};
|
|
4105
|
+
window.addEventListener("resize", handleResize, { signal });
|
|
4106
|
+
window.addEventListener("blur", handleBlur, { signal });
|
|
4107
|
+
window.addEventListener("focus", handleFocus, { signal });
|
|
4108
|
+
document.addEventListener("visibilitychange", handleVisibilityChange, {
|
|
4109
|
+
signal
|
|
4110
|
+
});
|
|
4111
|
+
return () => controller.abort();
|
|
2105
4112
|
}, []);
|
|
2106
4113
|
return {
|
|
2107
4114
|
onResize: (callback) => resizeCallbackRef.current = callback,
|
|
@@ -2124,20 +4131,27 @@ function useKlint() {
|
|
|
2124
4131
|
context.__textWeight = "normal";
|
|
2125
4132
|
context.__textStyle = "normal";
|
|
2126
4133
|
context.__textSize = 72;
|
|
4134
|
+
context.__textLeading = void 0;
|
|
2127
4135
|
context.__textAlignment = {
|
|
2128
4136
|
horizontal: "left",
|
|
2129
4137
|
vertical: "top"
|
|
2130
4138
|
};
|
|
4139
|
+
context.__fillRule = "nonzero";
|
|
2131
4140
|
context.__offscreens = /* @__PURE__ */ new Map();
|
|
2132
4141
|
context.__isPlaying = true;
|
|
2133
4142
|
context.__currentContext = context;
|
|
2134
4143
|
context.Color = new Color_default();
|
|
2135
4144
|
context.createVector = (x = 0, y = 0) => new Vector_default(x, y);
|
|
2136
|
-
context.
|
|
2137
|
-
context.
|
|
2138
|
-
context.Time = new Time_default(context);
|
|
4145
|
+
context.Vector = new Vector_default();
|
|
4146
|
+
context.Easing = new Easing_default();
|
|
2139
4147
|
context.Text = new Text_default(context);
|
|
2140
4148
|
context.Thing = new Thing_default(context);
|
|
4149
|
+
context.Grid = new Grid_default(context);
|
|
4150
|
+
context.Strip = new Strip_default(context);
|
|
4151
|
+
context.Noise = new Noise_default(context);
|
|
4152
|
+
context.Hotspot = new Hotspot_default(context);
|
|
4153
|
+
context.Performance = new Performance_default(context);
|
|
4154
|
+
context.SSR = new SSR_default(context);
|
|
2141
4155
|
Object.entries(KlintCoreFunctions).forEach(([name, fn]) => {
|
|
2142
4156
|
context[name] = fn(context);
|
|
2143
4157
|
});
|
|
@@ -2168,6 +4182,30 @@ function useKlint() {
|
|
|
2168
4182
|
contextRef.current.__isPlaying = !contextRef.current.__isPlaying;
|
|
2169
4183
|
}
|
|
2170
4184
|
}, []);
|
|
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
|
+
};
|
|
2171
4209
|
return {
|
|
2172
4210
|
context: {
|
|
2173
4211
|
context: contextRef.current,
|
|
@@ -2176,8 +4214,11 @@ function useKlint() {
|
|
|
2176
4214
|
KlintMouse,
|
|
2177
4215
|
KlintScroll,
|
|
2178
4216
|
KlintGesture,
|
|
4217
|
+
KlintKeyboard,
|
|
2179
4218
|
KlintWindow,
|
|
2180
4219
|
KlintImage,
|
|
4220
|
+
KlintTimeline,
|
|
4221
|
+
KlintPerformance,
|
|
2181
4222
|
togglePlay,
|
|
2182
4223
|
useDev
|
|
2183
4224
|
};
|
|
@@ -2224,10 +4265,21 @@ var useStorage = (initialProps = {}) => {
|
|
|
2224
4265
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2225
4266
|
0 && (module.exports = {
|
|
2226
4267
|
CONFIG_PROPS,
|
|
4268
|
+
Color,
|
|
2227
4269
|
EPSILON,
|
|
4270
|
+
Easing,
|
|
4271
|
+
Grid,
|
|
4272
|
+
Hotspot,
|
|
2228
4273
|
Klint,
|
|
2229
4274
|
KlintCoreFunctions,
|
|
2230
4275
|
KlintFunctions,
|
|
4276
|
+
Noise,
|
|
4277
|
+
Performance,
|
|
4278
|
+
SSR,
|
|
4279
|
+
Strip,
|
|
4280
|
+
Text,
|
|
4281
|
+
Thing,
|
|
4282
|
+
Vector,
|
|
2231
4283
|
useKlint,
|
|
2232
4284
|
useProps,
|
|
2233
4285
|
useStorage
|