@almadar/shell-web 1.0.0 → 1.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/LICENSE +72 -0
- package/dist/AvlGlyph-KZMJDBEB.js +5 -0
- package/dist/AvlStateMachine-KVZMFHFU.js +8 -0
- package/dist/AvlTransitionDetail-7DVGV27I.js +10 -0
- package/dist/AvlTransitionExplainer-RMJ5LQLK.js +11 -0
- package/dist/BehaviorView-EYRPME67.js +10 -0
- package/dist/{FileTree-BCFnBEuQ.d.ts → FileTree-B3GDAjaQ.d.ts} +131 -4
- package/dist/FlowCanvas-J54627TC.js +23 -0
- package/dist/{FlowCanvas-3JRMNJLV.css → FlowCanvas-R4LL3KRI.css} +190 -192
- package/dist/{GameAudioProvider-ceM_n0ey.d.ts → GameAudioProvider-BgWOOYT3.d.ts} +15 -20
- package/dist/{KernelEngineContext-CcxGYtlr.d.ts → KernelEngineContext-BPx-q0iw.d.ts} +15 -2
- package/dist/{LayersPanel-M2LHWUFF.js → LayersPanel-PSXI4EHG.js} +4 -4
- package/dist/ModuleCard-TNRLL45H.js +6 -0
- package/dist/{OrbInspector-MHLJ6OEE.css → OrbInspector-2EGVZLOJ.css} +190 -192
- package/dist/OrbInspector-UBEQWWZ2.js +23 -0
- package/dist/{parseLessonSegments-GpCeMrb2.d.ts → SegmentRenderer-BAQ6Jwf6.d.ts} +4 -20
- package/dist/{Select-B-iDdnvE.d.ts → Select-BJ8Fgc8A.d.ts} +1 -1
- package/dist/{UISlotContext-Dl_ewwCI.d.ts → UISlotContext-7r0SQdTL.d.ts} +1 -1
- package/dist/UISlotRenderer-5OURMAP2.js +23 -0
- package/dist/{UISlotRenderer-KVFO6J7R.css → UISlotRenderer-SJBSQGBQ.css} +190 -192
- package/dist/auth/react/index.js +7 -6
- package/dist/avl/index.css +190 -192
- package/dist/avl/index.d.ts +19 -7
- package/dist/avl/index.js +170 -39
- package/dist/{chunk-AE7VCQB6.js → chunk-2LNXLMK3.js} +2 -2
- package/dist/{chunk-REXYVBUO.js → chunk-3DA36FOA.js} +1 -1
- package/dist/{chunk-UXLE3IPR.js → chunk-3UQNFRYV.js} +207 -9
- package/dist/{chunk-AGXD77SL.js → chunk-3YUMGEEW.js} +1 -1
- package/dist/{chunk-K4MOPSEL.js → chunk-6RXP5ZXE.js} +7 -8
- package/dist/{chunk-E6KA4J6N.js → chunk-7UCL26QB.js} +4 -5
- package/dist/{chunk-57DC34LY.js → chunk-AL2XXMYR.js} +2 -2
- package/dist/{chunk-YHMRX6YA.js → chunk-ANTJUQRD.js} +53 -13
- package/dist/{chunk-P4ZFUQFF.js → chunk-AZ74UPG6.js} +4 -1
- package/dist/{chunk-ROICKAU7.js → chunk-B23EZH2P.js} +3 -3
- package/dist/{chunk-CPLN6NBQ.js → chunk-CCO2ZH7F.js} +3 -4
- package/dist/{chunk-4YXBKHHM.js → chunk-D5FJU4XC.js} +87 -16
- package/dist/{chunk-HH5KSMH5.js → chunk-DPDKBY6Z.js} +1 -1
- package/dist/{chunk-A2LR6VQQ.js → chunk-ECC3WQIB.js} +9 -93
- package/dist/{chunk-VO2TTLS5.js → chunk-GHAF7QUD.js} +1 -1
- package/dist/{chunk-TXAUOLWZ.js → chunk-HA2BYO5E.js} +1 -1
- package/dist/{chunk-ILQXJ2AR.js → chunk-JEN4EIUF.js} +1 -1
- package/dist/{chunk-UTDC2NEA.js → chunk-KTYOYEVA.js} +1 -1
- package/dist/{chunk-X3DMMX27.js → chunk-LINJRTNB.js} +2 -2
- package/dist/{chunk-TUPZWTRE.js → chunk-LKL5RULQ.js} +467 -141
- package/dist/{chunk-UU57VNS2.js → chunk-PWXZN5G5.js} +2 -2
- package/dist/{chunk-SP5CDJ6K.js → chunk-RDPELAFQ.js} +4 -4
- package/dist/{chunk-PRXQGGCO.js → chunk-S3LS3B7N.js} +1 -1
- package/dist/{chunk-LHP2TK7O.js → chunk-TAEOMNDK.js} +3611 -4737
- package/dist/{chunk-3S54FWRC.js → chunk-UFQLHCFJ.js} +1 -1
- package/dist/{chunk-AB3X7QTE.js → chunk-V5SVGUYN.js} +2 -3
- package/dist/{chunk-3VZ36ZKO.js → chunk-ZDRDJKYB.js} +5 -2
- package/dist/components/index.css +190 -192
- package/dist/components/index.d.ts +568 -190
- package/dist/components/index.js +27 -30
- package/dist/context/index.d.ts +2 -2
- package/dist/context/index.js +5 -5
- package/dist/core.d.ts +21 -4
- package/dist/core.js +1 -1
- package/dist/{fx-CmQjnHmT.d.ts → fx-Bx5RqplG.d.ts} +10 -7
- package/dist/hooks/index.d.ts +8 -8
- package/dist/hooks/index.js +6 -6
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -2
- package/dist/{katexPlugin-FA7NX4PE.css → katexPlugin-BUHVNRBJ.css} +1 -1
- package/dist/{kernel-client-TZKGHJRY.js → kernel-client-NMZ56JVU.js} +60 -6
- package/dist/{kernel-wire-wNdAPfhJ.d.ts → kernel-wire-CRsHtLsf.d.ts} +8 -1
- package/dist/lib/index.d.ts +3 -4
- package/dist/lib/index.js +435 -4
- package/dist/providers/index.css +190 -192
- package/dist/providers/index.d.ts +6 -8
- package/dist/providers/index.js +21 -25
- package/dist/renderer/index.d.ts +3 -79
- package/dist/renderer/index.js +2 -2
- package/dist/runtime/index.css +190 -192
- package/dist/runtime/index.d.ts +9 -2
- package/dist/runtime/index.js +20 -23
- package/dist/ssr/index.cjs +105 -28
- package/dist/ssr/index.js +106 -29
- package/dist/test-support.js +2 -2
- package/dist/{useUISlots-CiobNFGb.d.ts → useUISlots-BLcGd__D.d.ts} +2 -0
- package/dist/{paintDispatch-CuJLsr9Y.d.ts → verificationRegistry-BLuQU1JV.d.ts} +127 -15
- package/package.json +10 -38
- package/src/core/locales/ar.json +10 -1
- package/src/core/locales/en.json +10 -1
- package/src/core/locales/sl.json +10 -1
- package/tailwind-preset.cjs +8 -4
- package/dist/AvlGlyph-EPJKFEO3.js +0 -6
- package/dist/AvlStateMachine-RKZIKNGC.js +0 -9
- package/dist/AvlTransitionDetail-63SD6GZF.js +0 -11
- package/dist/AvlTransitionExplainer-ZBTLOM3T.js +0 -12
- package/dist/BehaviorView-YS5G4GNB.js +0 -11
- package/dist/Canvas3DHost-BkZQB5l2.d.ts +0 -552
- package/dist/FlowCanvas-AUMTWMNG.js +0 -26
- package/dist/ModuleCard-QHDGK5XK.js +0 -7
- package/dist/OrbInspector-QCPEPUPH.js +0 -26
- package/dist/UISlotRenderer-C7WWOK43.js +0 -26
- package/dist/avl-3d-context-BycmO6tG.d.ts +0 -201
- package/dist/avl-schema-parser-CtJxJ9KU.d.ts +0 -132
- package/dist/avl-zoom-state-CKMVuBFg.d.ts +0 -17
- package/dist/chunk-4T3P43NS.js +0 -1133
- package/dist/chunk-DZBQA2OW.js +0 -176
- package/dist/chunk-EOWMARUB.js +0 -16
- package/dist/chunk-GFXCPUMX.js +0 -1334
- package/dist/chunk-ILZ7ORX7.js +0 -432
- package/dist/three/index.css +0 -503
- package/dist/three/index.d.ts +0 -1185
- package/dist/three/index.js +0 -3908
- package/dist/verificationRegistry-CVrcxG1s.d.ts +0 -122
- /package/dist/{katexPlugin-NCDKOOQY.js → katexPlugin-WGJ4JFZU.js} +0 -0
package/dist/chunk-4T3P43NS.js
DELETED
|
@@ -1,1133 +0,0 @@
|
|
|
1
|
-
import { resolveThemeColor, parseComputedColor } from './chunk-UXLE3IPR.js';
|
|
2
|
-
import { updateAssetStatus } from './chunk-ILZ7ORX7.js';
|
|
3
|
-
import { useTheme, useThemeScope } from './chunk-4YXBKHHM.js';
|
|
4
|
-
import { createContext, useContext, useState, useLayoutEffect, useEffect } from 'react';
|
|
5
|
-
import { createLogger } from '@almadar/logger';
|
|
6
|
-
import { DIAGRAM_TONES, kernelEvaluator, DIAGRAM_TONE_COLOR_KEYS, DIAGRAM_TEXT_SIZES } from '@almadar/core';
|
|
7
|
-
|
|
8
|
-
// src/lib/drawable/contract.ts
|
|
9
|
-
function isValidScenePos(pos) {
|
|
10
|
-
return Number.isFinite(pos?.x) && Number.isFinite(pos?.y);
|
|
11
|
-
}
|
|
12
|
-
function spriteAssetState(node) {
|
|
13
|
-
if (!isValidScenePos(node.position)) return "invalid-position";
|
|
14
|
-
return typeof node.asset?.url === "string" && node.asset.url.length > 0 ? "ready" : "asset-unset";
|
|
15
|
-
}
|
|
16
|
-
var SPRITE_FALLBACK_FILL = "#9b8f7f";
|
|
17
|
-
var SPRITE_FALLBACK_STROKE = "#5e564b";
|
|
18
|
-
function spriteRect(projector, node, natural) {
|
|
19
|
-
const tw = projector.tileWidth;
|
|
20
|
-
const fallbackW = projector.worldPixelDirect ? natural?.w ?? 0 : tw;
|
|
21
|
-
const fallbackH = projector.worldPixelDirect ? natural?.h ?? 0 : tw;
|
|
22
|
-
const w = node.width !== void 0 ? node.width * tw : fallbackW;
|
|
23
|
-
const h = node.height !== void 0 ? node.height * tw : fallbackH;
|
|
24
|
-
const anchor = node.anchor ?? "top-left";
|
|
25
|
-
const p = projector.anchorPoint(node.position, anchor);
|
|
26
|
-
return {
|
|
27
|
-
x: anchor === "top-left" ? p.x : p.x - w / 2,
|
|
28
|
-
y: anchor === "ground" ? p.y - h : anchor === "center" ? p.y - h / 2 : p.y,
|
|
29
|
-
w,
|
|
30
|
-
h
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
var DrawableRegistryContext = createContext(null);
|
|
34
|
-
var NUMERIC_TRACKS = [
|
|
35
|
-
"offsetX",
|
|
36
|
-
"offsetY",
|
|
37
|
-
"rotate",
|
|
38
|
-
"opacity",
|
|
39
|
-
"radiusX",
|
|
40
|
-
"radiusY",
|
|
41
|
-
"width",
|
|
42
|
-
"height",
|
|
43
|
-
"strokeWidth",
|
|
44
|
-
"strokeDashOffset",
|
|
45
|
-
"blur"
|
|
46
|
-
];
|
|
47
|
-
function isAnimatedShape(node) {
|
|
48
|
-
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
49
|
-
}
|
|
50
|
-
var lerp = (a, b, k) => a + (b - a) * k;
|
|
51
|
-
function applyShapeAnimation(node, timeMs) {
|
|
52
|
-
const anim = node.animation;
|
|
53
|
-
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
|
|
54
|
-
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
55
|
-
const cycle = timeMs / anim.durationMs;
|
|
56
|
-
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
57
|
-
const out = { ...node };
|
|
58
|
-
const trackValue = (key) => {
|
|
59
|
-
const defined = frames.filter((f) => f[key] !== void 0);
|
|
60
|
-
if (defined.length === 0) return void 0;
|
|
61
|
-
let prev;
|
|
62
|
-
let next;
|
|
63
|
-
for (const f of defined) {
|
|
64
|
-
if (f.at <= t) prev = f;
|
|
65
|
-
else if (!next) next = f;
|
|
66
|
-
}
|
|
67
|
-
if (!prev) return defined[0][key];
|
|
68
|
-
if (!next) return prev[key];
|
|
69
|
-
const span = next.at - prev.at;
|
|
70
|
-
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
71
|
-
const a = prev[key];
|
|
72
|
-
const b = next[key];
|
|
73
|
-
if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
|
|
74
|
-
return a;
|
|
75
|
-
};
|
|
76
|
-
for (const key of NUMERIC_TRACKS) {
|
|
77
|
-
const v = trackValue(key);
|
|
78
|
-
if (v !== void 0) out[key] = v;
|
|
79
|
-
}
|
|
80
|
-
const fill = trackValue("fill");
|
|
81
|
-
if (fill !== void 0) out.fill = fill;
|
|
82
|
-
const stroke = trackValue("stroke");
|
|
83
|
-
if (stroke !== void 0) out.stroke = stroke;
|
|
84
|
-
const shadowFrames = frames.filter((f) => f.shadow !== void 0);
|
|
85
|
-
if (shadowFrames.length > 0) {
|
|
86
|
-
const sh = trackValue("shadow");
|
|
87
|
-
if (sh !== void 0) {
|
|
88
|
-
let prevSh;
|
|
89
|
-
let nextSh;
|
|
90
|
-
for (const f of shadowFrames) {
|
|
91
|
-
if (f.at <= t) prevSh = f;
|
|
92
|
-
else if (!nextSh) nextSh = f;
|
|
93
|
-
}
|
|
94
|
-
if (prevSh?.shadow && nextSh?.shadow) {
|
|
95
|
-
const span = nextSh.at - prevSh.at;
|
|
96
|
-
const k = span > 0 ? (t - prevSh.at) / span : 1;
|
|
97
|
-
out.shadow = { color: prevSh.shadow.color, blur: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
|
|
98
|
-
} else {
|
|
99
|
-
out.shadow = sh;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return out;
|
|
104
|
-
}
|
|
105
|
-
function gradientStyle(g, ox, oy, scale) {
|
|
106
|
-
if (g.kind === "linear") {
|
|
107
|
-
if (!g.from || !g.to) return void 0;
|
|
108
|
-
return {
|
|
109
|
-
kind: "linear",
|
|
110
|
-
x1: ox + g.from.x * scale,
|
|
111
|
-
y1: oy + g.from.y * scale,
|
|
112
|
-
x2: ox + g.to.x * scale,
|
|
113
|
-
y2: oy + g.to.y * scale,
|
|
114
|
-
stops: g.stops
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
if (g.kind === "conic") {
|
|
118
|
-
if (!g.center) return void 0;
|
|
119
|
-
return {
|
|
120
|
-
kind: "conic",
|
|
121
|
-
cx: ox + g.center.x * scale,
|
|
122
|
-
cy: oy + g.center.y * scale,
|
|
123
|
-
angle: g.angle ?? 0,
|
|
124
|
-
stops: g.stops
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (!g.center || g.radius === void 0) return void 0;
|
|
128
|
-
return {
|
|
129
|
-
kind: "radial",
|
|
130
|
-
cx: ox + g.center.x * scale,
|
|
131
|
-
cy: oy + g.center.y * scale,
|
|
132
|
-
r: g.radius * scale,
|
|
133
|
-
stops: g.stops
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
function patternStyle(p, unit) {
|
|
137
|
-
if (p.kind === "image") {
|
|
138
|
-
if (!p.url) return void 0;
|
|
139
|
-
return { kind: "image", url: p.url, scale: (p.scale ?? 1) / unit };
|
|
140
|
-
}
|
|
141
|
-
return { kind: "noise", scale: (p.scale ?? 2) / unit, alpha: p.alpha, color: p.color };
|
|
142
|
-
}
|
|
143
|
-
var paintShape = (painter, rawNode, dctx) => {
|
|
144
|
-
const node = dctx.time > 0 ? applyShapeAnimation(rawNode, dctx.time) : rawNode;
|
|
145
|
-
if (!isValidScenePos(node.position)) return;
|
|
146
|
-
painter.save();
|
|
147
|
-
if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
|
|
148
|
-
const origin = dctx.projector.project(node.position);
|
|
149
|
-
const tileWidth = dctx.projector.tileWidth;
|
|
150
|
-
const groupScale = dctx.groupScale ?? 1;
|
|
151
|
-
const strokePx = (node.strokeWidth ?? 1) / groupScale;
|
|
152
|
-
if (node.blendMode) painter.setBlend(node.blendMode);
|
|
153
|
-
if (node.strokeDash && node.strokeDash.length > 0) {
|
|
154
|
-
painter.setLineDash(
|
|
155
|
-
node.strokeDash.map((v) => v / groupScale),
|
|
156
|
-
(node.strokeDashOffset ?? 0) / groupScale
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / groupScale);
|
|
160
|
-
if (node.rotate) {
|
|
161
|
-
const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
|
|
162
|
-
const px = origin.x + pivot.x * tileWidth;
|
|
163
|
-
const py = origin.y + pivot.y * tileWidth;
|
|
164
|
-
painter.translate(px, py);
|
|
165
|
-
painter.rotate(node.rotate);
|
|
166
|
-
painter.translate(-px, -py);
|
|
167
|
-
}
|
|
168
|
-
if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth * groupScale });
|
|
169
|
-
const fill = node.fill === "none" ? void 0 : node.fill;
|
|
170
|
-
const stroke = node.stroke === "none" ? void 0 : node.stroke;
|
|
171
|
-
const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
|
|
172
|
-
const pxStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, origin.x, origin.y, tileWidth) ?? stroke : stroke;
|
|
173
|
-
const patFill = node.fillPattern ? patternStyle(node.fillPattern, groupScale) : void 0;
|
|
174
|
-
switch (node.shape) {
|
|
175
|
-
case "cell": {
|
|
176
|
-
const pts = dctx.projector.cellPath(node.position);
|
|
177
|
-
if (pxFill) painter.fillPoly(pts, pxFill);
|
|
178
|
-
if (patFill) painter.fillPoly(pts, patFill);
|
|
179
|
-
if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
case "rect": {
|
|
183
|
-
const p = dctx.projector.anchorPoint(node.position, node.anchor ?? "top-left");
|
|
184
|
-
const tw = dctx.projector.tileWidth;
|
|
185
|
-
const x = p.x + (node.offsetX ?? 0) * tw;
|
|
186
|
-
const y = p.y + (node.offsetY ?? 0) * tw;
|
|
187
|
-
const w = (node.width ?? 0) * tw;
|
|
188
|
-
const h = (node.height ?? 0) * tw;
|
|
189
|
-
if (pxFill) painter.fillRect(x, y, w, h, pxFill);
|
|
190
|
-
if (patFill) painter.fillRect(x, y, w, h, patFill);
|
|
191
|
-
if (pxStroke) painter.strokeRect(x, y, w, h, pxStroke, strokePx);
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case "ellipse": {
|
|
195
|
-
const p = dctx.projector.anchorPoint(node.position, node.anchor ?? "ground");
|
|
196
|
-
const tw = dctx.projector.tileWidth;
|
|
197
|
-
const cx = p.x + (node.offsetX ?? 0) * tw;
|
|
198
|
-
const cy = p.y + (node.offsetY ?? 0) * tw;
|
|
199
|
-
const rx = (node.radiusX ?? 0) * tw;
|
|
200
|
-
const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
|
|
201
|
-
if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
|
|
202
|
-
if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
|
|
203
|
-
if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
case "poly": {
|
|
207
|
-
const pts = (node.points ?? []).map((pt) => ({
|
|
208
|
-
x: origin.x + ((node.offsetX ?? 0) + pt.x) * tileWidth,
|
|
209
|
-
y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
|
|
210
|
-
}));
|
|
211
|
-
if (pxFill) painter.fillPoly(pts, pxFill);
|
|
212
|
-
if (patFill) painter.fillPoly(pts, patFill);
|
|
213
|
-
if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
case "path": {
|
|
217
|
-
if (!node.d) break;
|
|
218
|
-
painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
|
|
219
|
-
painter.scale(tileWidth, tileWidth);
|
|
220
|
-
if (node.strokeDash && node.strokeDash.length > 0) {
|
|
221
|
-
painter.setLineDash(
|
|
222
|
-
node.strokeDash.map((v) => v / (groupScale * tileWidth)),
|
|
223
|
-
(node.strokeDashOffset ?? 0) / (groupScale * tileWidth)
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / (groupScale * tileWidth));
|
|
227
|
-
const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
|
|
228
|
-
const localStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, 0, 0, 1) ?? stroke : stroke;
|
|
229
|
-
const localPat = node.fillPattern ? patternStyle(node.fillPattern, groupScale * tileWidth) : void 0;
|
|
230
|
-
if (localFill) painter.fillPath(node.d, localFill);
|
|
231
|
-
if (localPat) painter.fillPath(node.d, localPat);
|
|
232
|
-
if (localStroke) painter.strokePath(node.d, localStroke, strokePx / tileWidth);
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
painter.restore();
|
|
237
|
-
};
|
|
238
|
-
function DrawShape(props) {
|
|
239
|
-
const register = useContext(DrawableRegistryContext);
|
|
240
|
-
if (register) {
|
|
241
|
-
const { opacity, ...rest } = props;
|
|
242
|
-
const node = {
|
|
243
|
-
...rest,
|
|
244
|
-
type: "draw-shape",
|
|
245
|
-
...opacity !== void 0 && opacity > 0 ? { opacity } : {}
|
|
246
|
-
};
|
|
247
|
-
register(node);
|
|
248
|
-
}
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
function isAnimatedGroup(node) {
|
|
252
|
-
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
253
|
-
}
|
|
254
|
-
function DrawGroup(props) {
|
|
255
|
-
const register = useContext(DrawableRegistryContext);
|
|
256
|
-
if (register) register({ ...props, type: "draw-group" });
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// src/lib/imageCache.ts
|
|
261
|
-
var RETRY_AFTER_MS = 5e3;
|
|
262
|
-
var cache = /* @__PURE__ */ new Map();
|
|
263
|
-
function startLoad(url, onReady, existing) {
|
|
264
|
-
const img = new Image();
|
|
265
|
-
img.crossOrigin = "anonymous";
|
|
266
|
-
const entry = existing ?? { img, status: "pending" };
|
|
267
|
-
entry.img = img;
|
|
268
|
-
if (onReady) entry.onReady = onReady;
|
|
269
|
-
img.onload = () => {
|
|
270
|
-
entry.status = "loaded";
|
|
271
|
-
updateAssetStatus(url, "loaded");
|
|
272
|
-
entry.onReady?.();
|
|
273
|
-
};
|
|
274
|
-
img.onerror = () => {
|
|
275
|
-
entry.status = "failed";
|
|
276
|
-
entry.failedAt = Date.now();
|
|
277
|
-
updateAssetStatus(url, "failed");
|
|
278
|
-
entry.onReady?.();
|
|
279
|
-
};
|
|
280
|
-
img.src = url;
|
|
281
|
-
return entry;
|
|
282
|
-
}
|
|
283
|
-
function getOrLoadImage(url, onReady) {
|
|
284
|
-
if (!url || typeof window === "undefined") return null;
|
|
285
|
-
const existing = cache.get(url);
|
|
286
|
-
if (existing) {
|
|
287
|
-
if (existing.status === "loaded") return existing.img;
|
|
288
|
-
if (existing.status === "pending" && onReady) existing.onReady = onReady;
|
|
289
|
-
if (existing.status === "failed" && Date.now() - (existing.failedAt ?? 0) >= RETRY_AFTER_MS) {
|
|
290
|
-
existing.failedAt = Date.now();
|
|
291
|
-
startLoad(url, onReady, existing);
|
|
292
|
-
}
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
|
-
const entry = startLoad(url, onReady);
|
|
296
|
-
cache.set(url, entry);
|
|
297
|
-
updateAssetStatus(url, "pending");
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
function getImageStatus(url) {
|
|
301
|
-
return cache.get(url)?.status;
|
|
302
|
-
}
|
|
303
|
-
function computeWorldMatrices(bones, pose) {
|
|
304
|
-
return JSON.parse(kernelEvaluator().skinWorldMatrices(JSON.stringify(bones), JSON.stringify(pose)));
|
|
305
|
-
}
|
|
306
|
-
function skinVertices(mesh, bindWorld, curWorld) {
|
|
307
|
-
return JSON.parse(kernelEvaluator().skinVertices(JSON.stringify(mesh), JSON.stringify(bindWorld), JSON.stringify(curWorld)));
|
|
308
|
-
}
|
|
309
|
-
function triangleAffine(src, dst) {
|
|
310
|
-
return JSON.parse(kernelEvaluator().skinTriangleAffine(JSON.stringify(src), JSON.stringify(dst)));
|
|
311
|
-
}
|
|
312
|
-
var meshCache = /* @__PURE__ */ new Map();
|
|
313
|
-
function isRigBundle(json) {
|
|
314
|
-
return json.mesh !== void 0 || json.bones !== void 0;
|
|
315
|
-
}
|
|
316
|
-
function getRigBundle(url, onReady) {
|
|
317
|
-
if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
|
|
318
|
-
meshCache.set(url, void 0);
|
|
319
|
-
fetch(url).then((r) => {
|
|
320
|
-
if (!r.ok) throw new Error(`rigbundle fetch failed: HTTP ${r.status}`);
|
|
321
|
-
return r.json();
|
|
322
|
-
}).then((json) => {
|
|
323
|
-
const ok = isRigBundle(json) || Array.isArray(json.vertices);
|
|
324
|
-
if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
|
|
325
|
-
meshCache.set(url, json);
|
|
326
|
-
onReady();
|
|
327
|
-
}).catch(() => {
|
|
328
|
-
meshCache.set(url, null);
|
|
329
|
-
onReady();
|
|
330
|
-
});
|
|
331
|
-
return void 0;
|
|
332
|
-
}
|
|
333
|
-
function skinMeshFailed(url) {
|
|
334
|
-
return meshCache.get(url) === null;
|
|
335
|
-
}
|
|
336
|
-
function vertexBounds(points) {
|
|
337
|
-
return JSON.parse(kernelEvaluator().skinVertexBounds(JSON.stringify(points)));
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// src/components/game/atoms/DrawSkinnedMesh.tsx
|
|
341
|
-
var skinnedLog = createLogger("almadar:ui:draw-skinned-mesh");
|
|
342
|
-
var loggedMissing = /* @__PURE__ */ new Set();
|
|
343
|
-
function warnMissingOnce(reason, node) {
|
|
344
|
-
const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
|
|
345
|
-
if (loggedMissing.has(key)) return;
|
|
346
|
-
loggedMissing.add(key);
|
|
347
|
-
skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
|
|
348
|
-
}
|
|
349
|
-
var WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
|
|
350
|
-
function dominantBone(v) {
|
|
351
|
-
let best = -1;
|
|
352
|
-
let bestW = 0;
|
|
353
|
-
for (const { bone, w } of v.weights) {
|
|
354
|
-
if (w > bestW) {
|
|
355
|
-
bestW = w;
|
|
356
|
-
best = bone;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return best;
|
|
360
|
-
}
|
|
361
|
-
function resolveSkinPose(node, clip) {
|
|
362
|
-
const effectiveClip = clip ?? node.clip;
|
|
363
|
-
if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
|
|
364
|
-
return effectiveClip.frames[node.frame % effectiveClip.frames.length];
|
|
365
|
-
}
|
|
366
|
-
return node.pose ?? {};
|
|
367
|
-
}
|
|
368
|
-
function resolveRig(node, dctx) {
|
|
369
|
-
const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
|
|
370
|
-
if (node.meshUrl && fetched === void 0) return void 0;
|
|
371
|
-
const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
|
|
372
|
-
const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
|
|
373
|
-
if (!mesh) return void 0;
|
|
374
|
-
const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
|
|
375
|
-
if (bones.length === 0) return void 0;
|
|
376
|
-
const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
|
|
377
|
-
return { mesh, bones, clip };
|
|
378
|
-
}
|
|
379
|
-
var pixelUVByMesh = /* @__PURE__ */ new WeakMap();
|
|
380
|
-
function meshHasPixelUVs(mesh) {
|
|
381
|
-
const cached = pixelUVByMesh.get(mesh);
|
|
382
|
-
if (cached !== void 0) return cached;
|
|
383
|
-
let pixel = false;
|
|
384
|
-
for (const v of mesh.vertices) {
|
|
385
|
-
if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
|
|
386
|
-
pixel = true;
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
pixelUVByMesh.set(mesh, pixel);
|
|
391
|
-
return pixel;
|
|
392
|
-
}
|
|
393
|
-
function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
|
|
394
|
-
const bindWorld = computeWorldMatrices(bones, {});
|
|
395
|
-
const curWorld = computeWorldMatrices(bones, pose);
|
|
396
|
-
const skinned = skinVertices(mesh, bindWorld, curWorld);
|
|
397
|
-
const origin = dctx.projector.project(node.position);
|
|
398
|
-
const tw = dctx.projector.tileWidth;
|
|
399
|
-
painter.save();
|
|
400
|
-
if (opacity !== 1) painter.setAlpha(opacity);
|
|
401
|
-
for (const [i0, i1, i2] of mesh.triangles) {
|
|
402
|
-
const v0 = mesh.vertices[i0];
|
|
403
|
-
const v1 = mesh.vertices[i1];
|
|
404
|
-
const v2 = mesh.vertices[i2];
|
|
405
|
-
const p0 = skinned[i0];
|
|
406
|
-
const p1 = skinned[i1];
|
|
407
|
-
const p2 = skinned[i2];
|
|
408
|
-
if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
|
|
409
|
-
const dst = [
|
|
410
|
-
[origin.x + p0[0] * tw, origin.y + p0[1] * tw],
|
|
411
|
-
[origin.x + p1[0] * tw, origin.y + p1[1] * tw],
|
|
412
|
-
[origin.x + p2[0] * tw, origin.y + p2[1] * tw]
|
|
413
|
-
];
|
|
414
|
-
if (node.weightsOverlay || !tex) {
|
|
415
|
-
if (!node.weightsOverlay) continue;
|
|
416
|
-
const bone = dominantBone(v0);
|
|
417
|
-
painter.fillPoly(
|
|
418
|
-
dst.map(([x, y]) => ({ x, y })),
|
|
419
|
-
WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
|
|
420
|
-
);
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
const pixelUV = meshHasPixelUVs(mesh);
|
|
424
|
-
const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
|
|
425
|
-
[v0.u * tex.width, v0.v * tex.height],
|
|
426
|
-
[v1.u * tex.width, v1.v * tex.height],
|
|
427
|
-
[v2.u * tex.width, v2.v * tex.height]
|
|
428
|
-
];
|
|
429
|
-
const affine = triangleAffine(uv, dst);
|
|
430
|
-
if (!affine) continue;
|
|
431
|
-
painter.save();
|
|
432
|
-
painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
|
|
433
|
-
painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
|
|
434
|
-
painter.restore();
|
|
435
|
-
}
|
|
436
|
-
painter.restore();
|
|
437
|
-
}
|
|
438
|
-
var paintSkinnedMesh = (painter, node, dctx) => {
|
|
439
|
-
if (!isValidScenePos(node.position)) return;
|
|
440
|
-
const rig = resolveRig(node, dctx);
|
|
441
|
-
if (!rig) {
|
|
442
|
-
if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce("mesh-failed", node);
|
|
443
|
-
else if (!node.mesh && !node.meshUrl) warnMissingOnce("mesh-missing", node);
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
|
|
447
|
-
if (!node.weightsOverlay && !tex) {
|
|
448
|
-
if (getImageStatus(node.asset.url) === "failed") warnMissingOnce("texture-failed", node);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
|
|
452
|
-
paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
|
|
453
|
-
}
|
|
454
|
-
paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
|
|
455
|
-
};
|
|
456
|
-
function DrawSkinnedMesh(props) {
|
|
457
|
-
const register = useContext(DrawableRegistryContext);
|
|
458
|
-
if (register) register({ ...props, type: "draw-skinned-mesh" });
|
|
459
|
-
return null;
|
|
460
|
-
}
|
|
461
|
-
var MESH_SEGMENTS_DEFAULT = 24;
|
|
462
|
-
function polyhedronBounds(vertices) {
|
|
463
|
-
if (!vertices || vertices.length < 3) return null;
|
|
464
|
-
const min = [Infinity, Infinity, Infinity];
|
|
465
|
-
const max = [-Infinity, -Infinity, -Infinity];
|
|
466
|
-
for (const v of vertices) {
|
|
467
|
-
if (v.length < 3 || !v.every((c) => Number.isFinite(c))) return null;
|
|
468
|
-
for (let axis = 0; axis < 3; axis++) {
|
|
469
|
-
min[axis] = Math.min(min[axis], v[axis]);
|
|
470
|
-
max[axis] = Math.max(max[axis], v[axis]);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return { min, max };
|
|
474
|
-
}
|
|
475
|
-
function clampSegments(segments) {
|
|
476
|
-
const s = segments ?? MESH_SEGMENTS_DEFAULT;
|
|
477
|
-
return Math.min(64, Math.max(3, Math.round(s)));
|
|
478
|
-
}
|
|
479
|
-
function isAnimatedMesh(node) {
|
|
480
|
-
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
481
|
-
}
|
|
482
|
-
var lerp2 = (a, b, k) => a + (b - a) * k;
|
|
483
|
-
var NUMERIC_TRACKS2 = [
|
|
484
|
-
"offsetX",
|
|
485
|
-
"offsetY",
|
|
486
|
-
"offsetZ",
|
|
487
|
-
"rotateX",
|
|
488
|
-
"rotateY",
|
|
489
|
-
"rotateZ",
|
|
490
|
-
"scale",
|
|
491
|
-
"opacity",
|
|
492
|
-
"emissiveIntensity"
|
|
493
|
-
];
|
|
494
|
-
function applyMeshAnimation(node, timeMs) {
|
|
495
|
-
const anim = node.animation;
|
|
496
|
-
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
|
|
497
|
-
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
498
|
-
const cycle = timeMs / anim.durationMs;
|
|
499
|
-
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
500
|
-
const trackValue = (key) => {
|
|
501
|
-
const defined = frames.filter((f) => f[key] !== void 0);
|
|
502
|
-
if (defined.length === 0) return void 0;
|
|
503
|
-
let prev;
|
|
504
|
-
let next;
|
|
505
|
-
for (const f of defined) {
|
|
506
|
-
if (f.at <= t) prev = f;
|
|
507
|
-
else if (!next) next = f;
|
|
508
|
-
}
|
|
509
|
-
if (!prev) return defined[0][key];
|
|
510
|
-
if (!next) return prev[key];
|
|
511
|
-
const span = next.at - prev.at;
|
|
512
|
-
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
513
|
-
const a = prev[key];
|
|
514
|
-
const b = next[key];
|
|
515
|
-
if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
|
|
516
|
-
return a;
|
|
517
|
-
};
|
|
518
|
-
const num = {};
|
|
519
|
-
for (const key of NUMERIC_TRACKS2) {
|
|
520
|
-
const v = trackValue(key);
|
|
521
|
-
if (v !== void 0) num[key] = v;
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
|
|
525
|
-
rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
|
|
526
|
-
scale: num.scale ?? 1,
|
|
527
|
-
opacity: num.opacity,
|
|
528
|
-
emissiveIntensity: num.emissiveIntensity,
|
|
529
|
-
color: trackValue("color"),
|
|
530
|
-
emissive: trackValue("emissive")
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
function DrawMesh(props) {
|
|
534
|
-
const register = useContext(DrawableRegistryContext);
|
|
535
|
-
if (register) register({ ...props, type: "draw-mesh" });
|
|
536
|
-
return null;
|
|
537
|
-
}
|
|
538
|
-
var DEFAULT_TICK_MS = 500;
|
|
539
|
-
var DEFAULT_TEXT_COLOR = "#ffe066";
|
|
540
|
-
var DEFAULT_FONT_FAMILY = "system-ui, sans-serif";
|
|
541
|
-
function expandFxLayer(node, epochNowMs, dim, tileWidth, fontFamily) {
|
|
542
|
-
const layer = {
|
|
543
|
-
...node,
|
|
544
|
-
tickMs: node.tickMs ?? DEFAULT_TICK_MS,
|
|
545
|
-
textColor: node.textColor ?? DEFAULT_TEXT_COLOR,
|
|
546
|
-
fontFamily: fontFamily ?? DEFAULT_FONT_FAMILY
|
|
547
|
-
};
|
|
548
|
-
return JSON.parse(kernelEvaluator().expandFxLayer(JSON.stringify(layer), epochNowMs, dim, tileWidth));
|
|
549
|
-
}
|
|
550
|
-
function DrawFxLayer(_props) {
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
var log = createLogger("almadar:ui:canvas-theme");
|
|
554
|
-
var toneVar = (tone) => `--color-${DIAGRAM_TONE_COLOR_KEYS[tone]}`;
|
|
555
|
-
var SERIES_TONES = ["series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8"];
|
|
556
|
-
var TONE_SET = new Set(DIAGRAM_TONES);
|
|
557
|
-
function isDiagramTone(value) {
|
|
558
|
-
return TONE_SET.has(value);
|
|
559
|
-
}
|
|
560
|
-
function colorCss(c) {
|
|
561
|
-
return c.a >= 1 ? c.css : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;
|
|
562
|
-
}
|
|
563
|
-
function oneOf(value, allowed, fallback, name) {
|
|
564
|
-
const hit = allowed.find((a) => a === value);
|
|
565
|
-
if (hit !== void 0) return hit;
|
|
566
|
-
if (value !== "") log.warn("invalid-theme-keyword", { var: name, value, allowed });
|
|
567
|
-
return fallback;
|
|
568
|
-
}
|
|
569
|
-
var PLAIN_LENGTH = /^(-?[\d.]+)(px)?$/;
|
|
570
|
-
var ScopeReader = class {
|
|
571
|
-
constructor(scope) {
|
|
572
|
-
this.scope = scope;
|
|
573
|
-
this.style = getComputedStyle(scope);
|
|
574
|
-
const doc = scope.ownerDocument;
|
|
575
|
-
this.probe = doc.createElement("span");
|
|
576
|
-
this.probe.style.position = "absolute";
|
|
577
|
-
this.probe.style.visibility = "hidden";
|
|
578
|
-
this.probe.style.pointerEvents = "none";
|
|
579
|
-
scope.appendChild(this.probe);
|
|
580
|
-
}
|
|
581
|
-
scope;
|
|
582
|
-
style;
|
|
583
|
-
probe;
|
|
584
|
-
dispose() {
|
|
585
|
-
this.probe.remove();
|
|
586
|
-
}
|
|
587
|
-
raw(name) {
|
|
588
|
-
return this.style.getPropertyValue(name).trim();
|
|
589
|
-
}
|
|
590
|
-
/** A length var in px: a plain value parses directly, a derived one (`calc`, `var`) through the probe. */
|
|
591
|
-
length(name, fallback) {
|
|
592
|
-
const raw = this.raw(name);
|
|
593
|
-
const plain = PLAIN_LENGTH.exec(raw);
|
|
594
|
-
if (plain) return Number(plain[1]);
|
|
595
|
-
if (raw === "") return fallback;
|
|
596
|
-
this.probe.style.width = `var(${name})`;
|
|
597
|
-
const computed = PLAIN_LENGTH.exec(getComputedStyle(this.probe).width.trim());
|
|
598
|
-
this.probe.style.width = "";
|
|
599
|
-
if (computed) return Number(computed[1]);
|
|
600
|
-
log.warn("unresolvable-theme-length", { var: name, raw });
|
|
601
|
-
return fallback;
|
|
602
|
-
}
|
|
603
|
-
number(name, fallback) {
|
|
604
|
-
const raw = this.raw(name);
|
|
605
|
-
if (raw === "") return fallback;
|
|
606
|
-
const n = Number(raw);
|
|
607
|
-
if (Number.isFinite(n)) return n;
|
|
608
|
-
log.warn("unresolvable-theme-number", { var: name, raw });
|
|
609
|
-
return fallback;
|
|
610
|
-
}
|
|
611
|
-
/** A duration var in ms (`250ms`, `0.25s`, or a `var()` alias resolved through the probe). */
|
|
612
|
-
durationMs(name, fallback) {
|
|
613
|
-
const parse = (v) => {
|
|
614
|
-
const m = /^([\d.]+)(ms|s)$/.exec(v.trim());
|
|
615
|
-
return m ? Number(m[1]) * (m[2] === "s" ? 1e3 : 1) : null;
|
|
616
|
-
};
|
|
617
|
-
const raw = this.raw(name);
|
|
618
|
-
const direct = parse(raw);
|
|
619
|
-
if (direct !== null) return direct;
|
|
620
|
-
if (raw === "") return fallback;
|
|
621
|
-
this.probe.style.transitionDuration = `var(${name})`;
|
|
622
|
-
const computed = parse(getComputedStyle(this.probe).transitionDuration.split(",")[0] ?? "");
|
|
623
|
-
this.probe.style.transitionDuration = "";
|
|
624
|
-
return computed ?? fallback;
|
|
625
|
-
}
|
|
626
|
-
color(name) {
|
|
627
|
-
const raw = this.raw(name);
|
|
628
|
-
return parseComputedColor(raw) ?? resolveThemeColor(`var(${name})`, this.scope);
|
|
629
|
-
}
|
|
630
|
-
shadow(name) {
|
|
631
|
-
const raw = this.raw(name);
|
|
632
|
-
if (raw === "" || raw === "none") return null;
|
|
633
|
-
this.probe.style.boxShadow = `var(${name})`;
|
|
634
|
-
const computed = getComputedStyle(this.probe).boxShadow.trim() || raw;
|
|
635
|
-
this.probe.style.boxShadow = "";
|
|
636
|
-
const colorMatch = /(rgba?\([^)]*\)|#[0-9a-f]{3,8}|oklch\([^)]*\))/i.exec(computed);
|
|
637
|
-
const lengths = computed.replace(colorMatch?.[0] ?? "", "").trim().split(/\s+/).map((t) => PLAIN_LENGTH.exec(t)).filter((m) => m !== null).map((m) => Number(m[1]));
|
|
638
|
-
if (lengths.length < 2) return null;
|
|
639
|
-
const color = colorMatch ? parseComputedColor(colorMatch[0]) : null;
|
|
640
|
-
return { offsetX: lengths[0], offsetY: lengths[1], blur: lengths[2] ?? 0, color: color ? colorCss(color) : "rgba(0, 0, 0, 0.25)" };
|
|
641
|
-
}
|
|
642
|
-
family(name, fallback) {
|
|
643
|
-
return this.raw(name) || fallback;
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
function dashList(raw, fallback) {
|
|
647
|
-
if (raw === "") return fallback;
|
|
648
|
-
const parts = raw.split(/[\s,]+/).map(Number);
|
|
649
|
-
return parts.every((n) => Number.isFinite(n) && n >= 0) ? parts : fallback;
|
|
650
|
-
}
|
|
651
|
-
function prefersReducedMotion(scope) {
|
|
652
|
-
const view = scope.ownerDocument.defaultView;
|
|
653
|
-
return typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
654
|
-
}
|
|
655
|
-
function resolveCanvasTheme(scope) {
|
|
656
|
-
const r = new ScopeReader(scope);
|
|
657
|
-
try {
|
|
658
|
-
const tones = {};
|
|
659
|
-
for (const tone of DIAGRAM_TONES) {
|
|
660
|
-
const c = r.color(toneVar(tone));
|
|
661
|
-
tones[tone] = c ? colorCss(c) : "currentColor";
|
|
662
|
-
}
|
|
663
|
-
const surface = (name) => {
|
|
664
|
-
const c = r.color(name);
|
|
665
|
-
return c ? colorCss(c) : "transparent";
|
|
666
|
-
};
|
|
667
|
-
const ground = r.color("--surface-diagram");
|
|
668
|
-
const text = {};
|
|
669
|
-
const TEXT_FALLBACK = { xs: 12, sm: 14, base: 16, lg: 18, xl: 20, "2xl": 24 };
|
|
670
|
-
for (const size of DIAGRAM_TEXT_SIZES) text[size] = r.length(`--text-${size}`, TEXT_FALLBACK[size]);
|
|
671
|
-
const body = r.family("--font-family-body", r.family("--font-family", "system-ui, sans-serif"));
|
|
672
|
-
return {
|
|
673
|
-
ground: ground ? colorCss(ground) : "transparent",
|
|
674
|
-
surfaces: {
|
|
675
|
-
muted: surface("--color-muted"),
|
|
676
|
-
card: surface("--color-card"),
|
|
677
|
-
onPrimary: surface("--color-primary-foreground")
|
|
678
|
-
},
|
|
679
|
-
tones,
|
|
680
|
-
series: SERIES_TONES.map((t) => tones[t]),
|
|
681
|
-
strokes: {
|
|
682
|
-
thin: r.length("--diagram-stroke-thin", 1),
|
|
683
|
-
normal: r.length("--diagram-stroke-normal", 1.5),
|
|
684
|
-
bold: r.length("--diagram-stroke-bold", 3)
|
|
685
|
-
},
|
|
686
|
-
lineCap: oneOf(r.raw("--diagram-line-cap"), ["butt", "round", "square"], "round", "--diagram-line-cap"),
|
|
687
|
-
lineJoin: oneOf(r.raw("--diagram-line-join"), ["miter", "round", "bevel"], "round", "--diagram-line-join"),
|
|
688
|
-
dash: dashList(r.raw("--diagram-dash"), [6, 4]),
|
|
689
|
-
dot: dashList(r.raw("--diagram-dot"), [1, 4]),
|
|
690
|
-
fillStyle: oneOf(r.raw("--diagram-fill-style"), ["solid", "tint", "outline", "hatch"], "tint", "--diagram-fill-style"),
|
|
691
|
-
fillOpacity: r.number("--diagram-fill-opacity", 0.18),
|
|
692
|
-
roughness: r.number("--diagram-roughness", 0),
|
|
693
|
-
glow: r.number("--diagram-glow", 0),
|
|
694
|
-
shadow: r.shadow("--diagram-shadow"),
|
|
695
|
-
marker: oneOf(r.raw("--diagram-marker"), ["triangle", "open", "line", "dot"], "triangle", "--diagram-marker"),
|
|
696
|
-
faces: {
|
|
697
|
-
body,
|
|
698
|
-
display: r.family("--font-family-display", body),
|
|
699
|
-
mono: r.family("--font-family-mono", "ui-monospace, monospace")
|
|
700
|
-
},
|
|
701
|
-
label: {
|
|
702
|
-
font: oneOf(r.raw("--diagram-label-font"), ["body", "display", "mono"], "body", "--diagram-label-font"),
|
|
703
|
-
size: oneOf(r.raw("--diagram-label-size"), DIAGRAM_TEXT_SIZES, "sm", "--diagram-label-size"),
|
|
704
|
-
weight: r.raw("--diagram-label-weight") || "500",
|
|
705
|
-
uppercase: oneOf(r.raw("--diagram-label-case"), ["none", "uppercase"], "none", "--diagram-label-case") === "uppercase"
|
|
706
|
-
},
|
|
707
|
-
text,
|
|
708
|
-
corner: r.length("--diagram-corner", 4),
|
|
709
|
-
motion: {
|
|
710
|
-
enabled: r.raw("--motion-enable") !== "off" && !prefersReducedMotion(scope),
|
|
711
|
-
fastMs: r.durationMs("--duration-fast", 150),
|
|
712
|
-
normalMs: r.durationMs("--duration-normal", 250),
|
|
713
|
-
slowMs: r.durationMs("--duration-slow", 400),
|
|
714
|
-
easing: r.raw("--easing-standard") || "ease"
|
|
715
|
-
},
|
|
716
|
-
scene: {
|
|
717
|
-
roughness: r.number("--material-roughness", 0.6),
|
|
718
|
-
metalness: r.number("--material-metalness", 0),
|
|
719
|
-
flat: r.number("--material-flat", 0) >= 1,
|
|
720
|
-
outline: r.number("--material-outline", 0),
|
|
721
|
-
ambient: r.number("--light-ambient", 0.55),
|
|
722
|
-
key: r.number("--light-key", 1),
|
|
723
|
-
keyColor: (() => {
|
|
724
|
-
const c = r.color("--light-key-color");
|
|
725
|
-
return c ? colorCss(c) : "#ffffff";
|
|
726
|
-
})(),
|
|
727
|
-
fog: r.number("--scene-fog", 0)
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
} finally {
|
|
731
|
-
r.dispose();
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
function withSeries(theme, series, scope) {
|
|
735
|
-
if (!theme || !series || series.length === 0 || !scope) return theme;
|
|
736
|
-
const colors = series.map((c, i) => markColor(theme, c, scope, SERIES_TONES[i % SERIES_TONES.length]));
|
|
737
|
-
const tones = { ...theme.tones };
|
|
738
|
-
SERIES_TONES.forEach((tone, i) => {
|
|
739
|
-
tones[tone] = colors[i % colors.length];
|
|
740
|
-
});
|
|
741
|
-
return { ...theme, tones, series: SERIES_TONES.map((t) => tones[t]) };
|
|
742
|
-
}
|
|
743
|
-
function markColor(theme, value, scope, fallback) {
|
|
744
|
-
if (value === void 0 || value === "") return theme.tones[fallback];
|
|
745
|
-
if (isDiagramTone(value)) return theme.tones[value];
|
|
746
|
-
if (value.startsWith("var(") || value.startsWith("color-mix(")) {
|
|
747
|
-
const resolved = resolveThemeColor(value, scope);
|
|
748
|
-
return resolved ? colorCss(resolved) : theme.tones[fallback];
|
|
749
|
-
}
|
|
750
|
-
return value;
|
|
751
|
-
}
|
|
752
|
-
function markStrokeWidth(theme, literal, role, fallback = "normal") {
|
|
753
|
-
return literal ?? theme.strokes[role ?? fallback];
|
|
754
|
-
}
|
|
755
|
-
function labelFont(theme, roles, literalPx, literalFamily) {
|
|
756
|
-
const px = literalPx ?? theme.text[roles.textSize ?? theme.label.size];
|
|
757
|
-
const family = literalFamily ?? theme.faces[roles.font ?? theme.label.font];
|
|
758
|
-
return { font: `${theme.label.weight} ${px}px ${family}`, px };
|
|
759
|
-
}
|
|
760
|
-
function labelText(theme, text, textCase = "theme") {
|
|
761
|
-
return theme.label.uppercase && textCase === "theme" ? text.toUpperCase() : text;
|
|
762
|
-
}
|
|
763
|
-
function fillLabel(ctx, theme, text, x, y) {
|
|
764
|
-
if (!text) return;
|
|
765
|
-
ctx.save();
|
|
766
|
-
ctx.strokeStyle = theme.ground;
|
|
767
|
-
ctx.lineWidth = Math.max(2, theme.text.xs * 0.3);
|
|
768
|
-
ctx.lineJoin = "round";
|
|
769
|
-
ctx.strokeText(text, x, y);
|
|
770
|
-
ctx.restore();
|
|
771
|
-
ctx.fillText(text, x, y);
|
|
772
|
-
}
|
|
773
|
-
function outlineStroke(stroke, fill, style, fillTone) {
|
|
774
|
-
return outlineCarriesFill(fill, style, fillTone) && fill !== void 0 ? fill : stroke;
|
|
775
|
-
}
|
|
776
|
-
function outlineCarriesFill(fill, style, fillTone) {
|
|
777
|
-
return style === "outline" && fill !== void 0 && fillTone !== "fill";
|
|
778
|
-
}
|
|
779
|
-
function applyLineCharacter(ctx, theme, strokeColor) {
|
|
780
|
-
ctx.lineCap = theme.lineCap;
|
|
781
|
-
ctx.lineJoin = theme.lineJoin;
|
|
782
|
-
if (theme.glow > 0) {
|
|
783
|
-
ctx.shadowColor = strokeColor;
|
|
784
|
-
ctx.shadowBlur = theme.glow;
|
|
785
|
-
ctx.shadowOffsetX = 0;
|
|
786
|
-
ctx.shadowOffsetY = 0;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
function markDash(theme, dash) {
|
|
790
|
-
if (dash === "dashed") return theme.dash;
|
|
791
|
-
if (dash === "dotted") return theme.dot;
|
|
792
|
-
return [];
|
|
793
|
-
}
|
|
794
|
-
function wobble(x, y, k) {
|
|
795
|
-
const s = Math.sin(x * 12.9898 + y * 78.233 + k * 37.719) * 43758.5453;
|
|
796
|
-
return (s - Math.floor(s)) * 2 - 1;
|
|
797
|
-
}
|
|
798
|
-
function tracePolyline(ctx, theme, points, closed) {
|
|
799
|
-
ctx.beginPath();
|
|
800
|
-
if (points.length === 0) return;
|
|
801
|
-
const amp = theme.roughness;
|
|
802
|
-
const pts = closed ? [...points, points[0]] : [...points];
|
|
803
|
-
ctx.moveTo(pts[0].x + amp * wobble(pts[0].x, pts[0].y, 0), pts[0].y + amp * wobble(pts[0].y, pts[0].x, 1));
|
|
804
|
-
for (let i = 1; i < pts.length; i++) {
|
|
805
|
-
const a = pts[i - 1];
|
|
806
|
-
const b = pts[i];
|
|
807
|
-
if (amp === 0) {
|
|
808
|
-
ctx.lineTo(b.x, b.y);
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
const steps = Math.max(1, Math.round(Math.hypot(b.x - a.x, b.y - a.y) / 24));
|
|
812
|
-
for (let s = 1; s <= steps; s++) {
|
|
813
|
-
const t = s / steps;
|
|
814
|
-
const x = a.x + (b.x - a.x) * t;
|
|
815
|
-
const y = a.y + (b.y - a.y) * t;
|
|
816
|
-
ctx.lineTo(x + amp * wobble(x, y, 2), y + amp * wobble(y, x, 3));
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
if (closed) ctx.closePath();
|
|
820
|
-
}
|
|
821
|
-
function arcPoints(cx, cy, rx, ry, startRad = 0, endRad = Math.PI * 2) {
|
|
822
|
-
const steps = Math.max(12, Math.round(Math.abs(endRad - startRad) * Math.max(rx, ry) / 8));
|
|
823
|
-
const out = [];
|
|
824
|
-
for (let i = 0; i <= steps; i++) {
|
|
825
|
-
const t = startRad + (endRad - startRad) * i / steps;
|
|
826
|
-
out.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });
|
|
827
|
-
}
|
|
828
|
-
return out;
|
|
829
|
-
}
|
|
830
|
-
function fillMark(ctx, theme, color, style = theme.fillStyle) {
|
|
831
|
-
if (style === "outline") return;
|
|
832
|
-
ctx.save();
|
|
833
|
-
ctx.shadowBlur = 0;
|
|
834
|
-
ctx.shadowColor = "transparent";
|
|
835
|
-
if (theme.shadow) {
|
|
836
|
-
ctx.shadowColor = theme.shadow.color;
|
|
837
|
-
ctx.shadowOffsetX = theme.shadow.offsetX;
|
|
838
|
-
ctx.shadowOffsetY = theme.shadow.offsetY;
|
|
839
|
-
ctx.shadowBlur = theme.shadow.blur;
|
|
840
|
-
}
|
|
841
|
-
if (style === "hatch") {
|
|
842
|
-
ctx.clip();
|
|
843
|
-
const span = ctx.canvas.width + ctx.canvas.height;
|
|
844
|
-
ctx.strokeStyle = color;
|
|
845
|
-
ctx.lineWidth = theme.strokes.thin;
|
|
846
|
-
ctx.setLineDash([]);
|
|
847
|
-
ctx.beginPath();
|
|
848
|
-
for (let d = -span; d < span; d += 6) {
|
|
849
|
-
ctx.moveTo(d, 0);
|
|
850
|
-
ctx.lineTo(d + span, span);
|
|
851
|
-
}
|
|
852
|
-
ctx.stroke();
|
|
853
|
-
} else {
|
|
854
|
-
ctx.globalAlpha *= style === "tint" ? theme.fillOpacity : 1;
|
|
855
|
-
ctx.fillStyle = color;
|
|
856
|
-
ctx.fill();
|
|
857
|
-
}
|
|
858
|
-
ctx.restore();
|
|
859
|
-
}
|
|
860
|
-
function drawMarker(ctx, theme, x1, y1, x2, y2, color, width) {
|
|
861
|
-
const size = Math.max(8, width * 4);
|
|
862
|
-
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
863
|
-
const left = { x: x2 - size * Math.cos(angle - Math.PI / 6), y: y2 - size * Math.sin(angle - Math.PI / 6) };
|
|
864
|
-
const right = { x: x2 - size * Math.cos(angle + Math.PI / 6), y: y2 - size * Math.sin(angle + Math.PI / 6) };
|
|
865
|
-
ctx.save();
|
|
866
|
-
ctx.strokeStyle = color;
|
|
867
|
-
ctx.fillStyle = color;
|
|
868
|
-
ctx.lineWidth = width;
|
|
869
|
-
ctx.setLineDash([]);
|
|
870
|
-
switch (theme.marker) {
|
|
871
|
-
case "triangle": {
|
|
872
|
-
ctx.beginPath();
|
|
873
|
-
ctx.moveTo(x2, y2);
|
|
874
|
-
ctx.lineTo(left.x, left.y);
|
|
875
|
-
ctx.lineTo(right.x, right.y);
|
|
876
|
-
ctx.closePath();
|
|
877
|
-
ctx.fill();
|
|
878
|
-
break;
|
|
879
|
-
}
|
|
880
|
-
case "open":
|
|
881
|
-
case "line": {
|
|
882
|
-
ctx.beginPath();
|
|
883
|
-
ctx.moveTo(left.x, left.y);
|
|
884
|
-
ctx.lineTo(x2, y2);
|
|
885
|
-
ctx.lineTo(right.x, right.y);
|
|
886
|
-
if (theme.marker === "open") ctx.stroke();
|
|
887
|
-
else {
|
|
888
|
-
ctx.moveTo(x2 - size * 0.5 * Math.cos(angle + Math.PI / 2), y2 - size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
889
|
-
ctx.lineTo(x2 + size * 0.5 * Math.cos(angle + Math.PI / 2), y2 + size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
890
|
-
ctx.stroke();
|
|
891
|
-
}
|
|
892
|
-
break;
|
|
893
|
-
}
|
|
894
|
-
case "dot": {
|
|
895
|
-
ctx.beginPath();
|
|
896
|
-
ctx.arc(x2, y2, Math.max(2.5, width * 1.5), 0, Math.PI * 2);
|
|
897
|
-
ctx.fill();
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
ctx.restore();
|
|
902
|
-
}
|
|
903
|
-
function useCanvasTheme(scopeRef, scopePresent = true) {
|
|
904
|
-
const { appliedTheme } = useTheme();
|
|
905
|
-
const scopeKey = JSON.stringify(useThemeScope());
|
|
906
|
-
const [state, setState] = useState({ theme: null, version: 0 });
|
|
907
|
-
useLayoutEffect(() => {
|
|
908
|
-
const scope = scopeRef.current;
|
|
909
|
-
if (!scope) return;
|
|
910
|
-
const theme = resolveCanvasTheme(scope);
|
|
911
|
-
setState((prev) => ({ theme, version: prev.version + 1 }));
|
|
912
|
-
}, [appliedTheme, scopeKey, scopeRef, scopePresent]);
|
|
913
|
-
return state;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// src/lib/drawable/themeDrawables.ts
|
|
917
|
-
function opt(value, resolve) {
|
|
918
|
-
return value === void 0 ? void 0 : resolve(value);
|
|
919
|
-
}
|
|
920
|
-
function themeShape(node, resolve) {
|
|
921
|
-
return {
|
|
922
|
-
...node,
|
|
923
|
-
...node.fill !== void 0 ? { fill: resolve(node.fill) } : {},
|
|
924
|
-
...node.stroke !== void 0 ? { stroke: resolve(node.stroke) } : {},
|
|
925
|
-
...node.gradient ? { gradient: { ...node.gradient, stops: node.gradient.stops.map((s) => ({ ...s, color: resolve(s.color) })) } } : {},
|
|
926
|
-
...node.fillPattern?.color !== void 0 ? { fillPattern: { ...node.fillPattern, color: resolve(node.fillPattern.color) } } : {},
|
|
927
|
-
...node.shadow ? { shadow: { ...node.shadow, color: resolve(node.shadow.color) } } : {},
|
|
928
|
-
...node.animation ? {
|
|
929
|
-
animation: {
|
|
930
|
-
...node.animation,
|
|
931
|
-
keyframes: node.animation.keyframes.map((k) => ({
|
|
932
|
-
...k,
|
|
933
|
-
...k.fill !== void 0 ? { fill: resolve(k.fill) } : {},
|
|
934
|
-
...k.stroke !== void 0 ? { stroke: resolve(k.stroke) } : {}
|
|
935
|
-
}))
|
|
936
|
-
}
|
|
937
|
-
} : {}
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
function themeText(node, resolve) {
|
|
941
|
-
return { ...node, color: resolve(node.color) };
|
|
942
|
-
}
|
|
943
|
-
function themeMaterial(material, resolve) {
|
|
944
|
-
if (!material) return material;
|
|
945
|
-
return {
|
|
946
|
-
...material,
|
|
947
|
-
...material.color !== void 0 ? { color: resolve(material.color) } : {},
|
|
948
|
-
...material.emissive !== void 0 ? { emissive: resolve(material.emissive) } : {}
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
function resolveDrawableColors(nodes, resolve) {
|
|
952
|
-
return nodes.map((node) => {
|
|
953
|
-
switch (node.type) {
|
|
954
|
-
case "draw-shape":
|
|
955
|
-
return themeShape(node, resolve);
|
|
956
|
-
case "draw-text":
|
|
957
|
-
return themeText(node, resolve);
|
|
958
|
-
case "draw-group":
|
|
959
|
-
return { ...node, items: resolveDrawableColors(node.items, resolve) };
|
|
960
|
-
case "draw-shape-layer":
|
|
961
|
-
return { ...node, items: node.items.map((item) => themeShape(item, resolve)) };
|
|
962
|
-
case "draw-text-layer":
|
|
963
|
-
return { ...node, items: node.items.map((item) => themeText(item, resolve)) };
|
|
964
|
-
case "draw-fx-layer":
|
|
965
|
-
return {
|
|
966
|
-
...node,
|
|
967
|
-
...node.textColor !== void 0 ? { textColor: resolve(node.textColor) } : {},
|
|
968
|
-
...node.presets ? {
|
|
969
|
-
presets: node.presets.map((p) => ({
|
|
970
|
-
...p,
|
|
971
|
-
...p.color !== void 0 ? { color: resolve(p.color) } : {},
|
|
972
|
-
...p.color2 !== void 0 ? { color2: resolve(p.color2) } : {}
|
|
973
|
-
}))
|
|
974
|
-
} : {}
|
|
975
|
-
};
|
|
976
|
-
case "draw-mesh":
|
|
977
|
-
return {
|
|
978
|
-
...node,
|
|
979
|
-
material: themeMaterial(node.material, resolve),
|
|
980
|
-
...node.outline ? { outline: { ...node.outline, ...node.outline.color !== void 0 ? { color: resolve(node.outline.color) } : {} } } : {},
|
|
981
|
-
...node.animation ? {
|
|
982
|
-
animation: {
|
|
983
|
-
...node.animation,
|
|
984
|
-
keyframes: node.animation.keyframes.map((k) => ({
|
|
985
|
-
...k,
|
|
986
|
-
...k.color !== void 0 ? { color: resolve(k.color) } : {},
|
|
987
|
-
...k.emissive !== void 0 ? { emissive: opt(k.emissive, resolve) } : {}
|
|
988
|
-
}))
|
|
989
|
-
}
|
|
990
|
-
} : {}
|
|
991
|
-
};
|
|
992
|
-
default:
|
|
993
|
-
return node;
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
function applySceneMaterials(nodes, theme) {
|
|
998
|
-
return nodes.map((node) => {
|
|
999
|
-
if (node.type === "draw-group") return { ...node, items: applySceneMaterials(node.items, theme) };
|
|
1000
|
-
if (node.type !== "draw-mesh") return node;
|
|
1001
|
-
const m = node.material ?? {};
|
|
1002
|
-
const pbr = (m.kind ?? "standard") === "standard" || m.kind === "physical";
|
|
1003
|
-
return {
|
|
1004
|
-
...node,
|
|
1005
|
-
material: {
|
|
1006
|
-
...m,
|
|
1007
|
-
...pbr && m.roughness === void 0 ? { roughness: theme.scene.roughness } : {},
|
|
1008
|
-
...pbr && m.metalness === void 0 ? { metalness: theme.scene.metalness } : {},
|
|
1009
|
-
...m.flatShading === void 0 && theme.scene.flat ? { flatShading: true } : {}
|
|
1010
|
-
},
|
|
1011
|
-
...node.outline === void 0 && theme.scene.outline > 0 ? { outline: { color: theme.tones.ink, width: theme.scene.outline * 0.02 } } : {}
|
|
1012
|
-
};
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// src/lib/drawable/hitTest.ts
|
|
1017
|
-
function shapeDrawnItem(n) {
|
|
1018
|
-
if (n.shape !== "rect") return { pos: n.position, id: n.id };
|
|
1019
|
-
return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
|
|
1020
|
-
}
|
|
1021
|
-
function skinnedMeshDrawnItem(n) {
|
|
1022
|
-
const mesh = n.mesh;
|
|
1023
|
-
if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
|
|
1024
|
-
const bindWorld = computeWorldMatrices(n.bones, {});
|
|
1025
|
-
const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
|
|
1026
|
-
const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
|
|
1027
|
-
if (!bounds) return { pos: n.position, id: n.id };
|
|
1028
|
-
return {
|
|
1029
|
-
pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
|
|
1030
|
-
id: n.id,
|
|
1031
|
-
anchor: "top-left",
|
|
1032
|
-
width: bounds.maxX - bounds.minX,
|
|
1033
|
-
height: bounds.maxY - bounds.minY
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
function collectDrawnItems(nodes) {
|
|
1037
|
-
const out = [];
|
|
1038
|
-
for (const n of nodes) {
|
|
1039
|
-
switch (n.type) {
|
|
1040
|
-
case "draw-sprite":
|
|
1041
|
-
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
|
|
1042
|
-
break;
|
|
1043
|
-
case "draw-shape":
|
|
1044
|
-
if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
|
|
1045
|
-
break;
|
|
1046
|
-
case "draw-skinned-mesh":
|
|
1047
|
-
if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
|
|
1048
|
-
break;
|
|
1049
|
-
case "draw-text":
|
|
1050
|
-
case "draw-group":
|
|
1051
|
-
case "draw-mesh":
|
|
1052
|
-
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
|
|
1053
|
-
break;
|
|
1054
|
-
case "draw-sprite-layer":
|
|
1055
|
-
for (const it of n.items) {
|
|
1056
|
-
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
|
|
1057
|
-
}
|
|
1058
|
-
break;
|
|
1059
|
-
case "draw-shape-layer":
|
|
1060
|
-
for (const it of n.items) {
|
|
1061
|
-
if (isValidScenePos(it.position)) out.push(shapeDrawnItem(it));
|
|
1062
|
-
}
|
|
1063
|
-
break;
|
|
1064
|
-
case "draw-text-layer":
|
|
1065
|
-
for (const it of n.items) {
|
|
1066
|
-
if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id });
|
|
1067
|
-
}
|
|
1068
|
-
break;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
return out;
|
|
1072
|
-
}
|
|
1073
|
-
function buildHitIndex(items) {
|
|
1074
|
-
const m = /* @__PURE__ */ new Map();
|
|
1075
|
-
for (const it of items) {
|
|
1076
|
-
if (it.id !== void 0) m.set(`${it.pos.x},${it.pos.y}`, it.id);
|
|
1077
|
-
}
|
|
1078
|
-
return m;
|
|
1079
|
-
}
|
|
1080
|
-
function withPreviewPosition(nodes, id, pos) {
|
|
1081
|
-
return nodes.map((n) => {
|
|
1082
|
-
if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
|
|
1083
|
-
if (!n.items.some((it) => it.id === id)) return n;
|
|
1084
|
-
return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
|
|
1085
|
-
}
|
|
1086
|
-
if (n.type === "draw-group") {
|
|
1087
|
-
if (!Array.isArray(n.items)) return n;
|
|
1088
|
-
return { ...n, items: withPreviewPosition(n.items, id, pos) };
|
|
1089
|
-
}
|
|
1090
|
-
if (n.id === id && "position" in n) {
|
|
1091
|
-
return { ...n, position: pos };
|
|
1092
|
-
}
|
|
1093
|
-
return n;
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
function hitTestSprites(items, projector, point) {
|
|
1097
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
1098
|
-
const it = items[i];
|
|
1099
|
-
if (it.id === void 0) continue;
|
|
1100
|
-
const r = spriteRect(projector, { position: it.pos, anchor: it.anchor, width: it.width, height: it.height });
|
|
1101
|
-
const test = it.rotation ? rotatePoint(point, { x: r.x + r.w / 2, y: r.y + r.h / 2 }, -it.rotation) : point;
|
|
1102
|
-
if (test.x >= r.x && test.x <= r.x + r.w && test.y >= r.y && test.y <= r.y + r.h) return it.id;
|
|
1103
|
-
}
|
|
1104
|
-
return void 0;
|
|
1105
|
-
}
|
|
1106
|
-
function rotatePoint(p, center, radians) {
|
|
1107
|
-
const cos = Math.cos(radians);
|
|
1108
|
-
const sin = Math.sin(radians);
|
|
1109
|
-
const dx = p.x - center.x;
|
|
1110
|
-
const dy = p.y - center.y;
|
|
1111
|
-
return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
|
|
1112
|
-
}
|
|
1113
|
-
function useOnScreen(ref, margin = "200px") {
|
|
1114
|
-
const [intersecting, setIntersecting] = useState(true);
|
|
1115
|
-
const [pageVisible, setPageVisible] = useState(true);
|
|
1116
|
-
useEffect(() => {
|
|
1117
|
-
const el = ref.current;
|
|
1118
|
-
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
1119
|
-
const observer = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting), { rootMargin: margin });
|
|
1120
|
-
observer.observe(el);
|
|
1121
|
-
return () => observer.disconnect();
|
|
1122
|
-
}, [ref, margin]);
|
|
1123
|
-
useEffect(() => {
|
|
1124
|
-
if (typeof document === "undefined") return;
|
|
1125
|
-
const update = () => setPageVisible(document.visibilityState !== "hidden");
|
|
1126
|
-
update();
|
|
1127
|
-
document.addEventListener("visibilitychange", update);
|
|
1128
|
-
return () => document.removeEventListener("visibilitychange", update);
|
|
1129
|
-
}, []);
|
|
1130
|
-
return intersecting && pageVisible;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
export { DrawFxLayer, DrawGroup, DrawMesh, DrawShape, DrawSkinnedMesh, DrawableRegistryContext, SPRITE_FALLBACK_FILL, SPRITE_FALLBACK_STROKE, applyLineCharacter, applyMeshAnimation, applySceneMaterials, applyShapeAnimation, arcPoints, buildHitIndex, clampSegments, collectDrawnItems, drawMarker, expandFxLayer, fillLabel, fillMark, getImageStatus, getOrLoadImage, hitTestSprites, isAnimatedGroup, isAnimatedMesh, isAnimatedShape, isValidScenePos, labelFont, labelText, markColor, markDash, markStrokeWidth, outlineCarriesFill, outlineStroke, paintShape, paintSkinnedMesh, polyhedronBounds, resolveDrawableColors, spriteAssetState, spriteRect, tracePolyline, useCanvasTheme, useOnScreen, withPreviewPosition, withSeries };
|