@almadar/ui 5.148.0 → 5.149.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{GameAudioProvider-CPGwD49P.d.cts → GameAudioProvider-B48iXwz3.d.ts} +2 -50
- package/dist/{GameAudioProvider-CPGwD49P.d.ts → GameAudioProvider-CQAdPreB.d.cts} +2 -50
- package/dist/avl/index.cjs +438 -13
- package/dist/avl/index.js +438 -13
- package/dist/{avl-schema-parser-DVmrgdwc.d.cts → avl-schema-parser-CVzkNzg7.d.cts} +18 -9
- package/dist/{avl-schema-parser-BRJ77Yze.d.ts → avl-schema-parser-Cx_4SVg9.d.ts} +18 -9
- package/dist/{cn-BAn68sNO.d.cts → cn-BnAJZcNb.d.cts} +2 -8
- package/dist/{cn-CWxxLkri.d.ts → cn-DJTUjk1M.d.ts} +2 -8
- package/dist/components/index.cjs +448 -16
- package/dist/components/index.d.cts +269 -95
- package/dist/components/index.d.ts +269 -95
- package/dist/components/index.js +444 -18
- package/dist/lib/drawable/three/index.cjs +56 -29
- package/dist/lib/drawable/three/index.d.cts +4 -3
- package/dist/lib/drawable/three/index.d.ts +4 -3
- package/dist/lib/drawable/three/index.js +56 -29
- package/dist/lib/index.d.cts +3 -2
- package/dist/lib/index.d.ts +3 -2
- package/dist/{paintDispatch-Cb_hQj4Y.d.ts → paintDispatch-BjZjUbcb.d.cts} +63 -69
- package/dist/{paintDispatch-Cb_hQj4Y.d.cts → paintDispatch-CxdLjHQq.d.ts} +63 -69
- package/dist/providers/index.cjs +438 -13
- package/dist/providers/index.d.cts +2 -1
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +438 -13
- package/dist/runtime/index.cjs +438 -13
- package/dist/runtime/index.js +438 -13
- package/dist/types-B3nHgnMG.d.cts +51 -0
- package/dist/types-B3nHgnMG.d.ts +51 -0
- package/package.json +3 -3
package/dist/avl/index.js
CHANGED
|
@@ -14041,9 +14041,10 @@ function Canvas({
|
|
|
14041
14041
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
14042
14042
|
isLoading,
|
|
14043
14043
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
14044
|
-
...zoom !== void 0 ? {
|
|
14044
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
14045
14045
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14046
14046
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14047
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14047
14048
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14048
14049
|
unitScale,
|
|
14049
14050
|
backgroundColor,
|
|
@@ -17095,23 +17096,188 @@ var init_AuthLayout = __esm({
|
|
|
17095
17096
|
AuthLayout.displayName = "AuthLayout";
|
|
17096
17097
|
}
|
|
17097
17098
|
});
|
|
17098
|
-
|
|
17099
|
+
function LearningScene3D({
|
|
17100
|
+
className,
|
|
17101
|
+
width = 600,
|
|
17102
|
+
height = 400,
|
|
17103
|
+
title,
|
|
17104
|
+
backgroundColor,
|
|
17105
|
+
drawables,
|
|
17106
|
+
camera,
|
|
17107
|
+
lighting,
|
|
17108
|
+
post,
|
|
17109
|
+
showGrid = false,
|
|
17110
|
+
shadows,
|
|
17111
|
+
interactive,
|
|
17112
|
+
isLoading,
|
|
17113
|
+
error,
|
|
17114
|
+
onItemClick
|
|
17115
|
+
}) {
|
|
17116
|
+
const instanceId = useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
17117
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
17118
|
+
const onItemClickRef = useRef(onItemClick);
|
|
17119
|
+
onItemClickRef.current = onItemClick;
|
|
17120
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
17121
|
+
const unitId = event.payload?.unitId;
|
|
17122
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
17123
|
+
});
|
|
17124
|
+
const props3d = {
|
|
17125
|
+
drawables,
|
|
17126
|
+
isLoading,
|
|
17127
|
+
error: error ? error.message : null,
|
|
17128
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
17129
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
17130
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
17131
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
17132
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
17133
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
17134
|
+
backgroundColor,
|
|
17135
|
+
showGrid,
|
|
17136
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
17137
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
17138
|
+
lighting,
|
|
17139
|
+
post,
|
|
17140
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
17141
|
+
};
|
|
17142
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
17143
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17144
|
+
/* @__PURE__ */ jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
17145
|
+
] }) });
|
|
17146
|
+
}
|
|
17147
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
17148
|
+
return {
|
|
17149
|
+
type: "draw-mesh",
|
|
17150
|
+
...id !== void 0 ? { id } : {},
|
|
17151
|
+
shape: opts?.shape ?? "sphere",
|
|
17152
|
+
position: { x, y, z },
|
|
17153
|
+
radius,
|
|
17154
|
+
pivot: "center",
|
|
17155
|
+
segments: opts?.segments ?? 24,
|
|
17156
|
+
material: { color, ...opts?.material },
|
|
17157
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
17158
|
+
};
|
|
17159
|
+
}
|
|
17160
|
+
function axisRotation(from, to) {
|
|
17161
|
+
const wx = to[0] - from[0];
|
|
17162
|
+
const wy = to[2] - from[2];
|
|
17163
|
+
const wz = to[1] - from[1];
|
|
17164
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
17165
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
17166
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
17167
|
+
}
|
|
17168
|
+
function segmentLength(from, to) {
|
|
17169
|
+
const dx = to[0] - from[0];
|
|
17170
|
+
const dy = to[1] - from[1];
|
|
17171
|
+
const dz = to[2] - from[2];
|
|
17172
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
17173
|
+
}
|
|
17174
|
+
function cylinderBetween(from, to, radius, color) {
|
|
17175
|
+
const len = segmentLength(from, to);
|
|
17176
|
+
if (len < 1e-6) return null;
|
|
17177
|
+
return {
|
|
17178
|
+
type: "draw-mesh",
|
|
17179
|
+
shape: "cylinder",
|
|
17180
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
17181
|
+
radius,
|
|
17182
|
+
height: len,
|
|
17183
|
+
rotation: axisRotation(from, to),
|
|
17184
|
+
pivot: "center",
|
|
17185
|
+
segments: 12,
|
|
17186
|
+
material: { color }
|
|
17187
|
+
};
|
|
17188
|
+
}
|
|
17189
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
17190
|
+
const len = segmentLength(from, to);
|
|
17191
|
+
if (len < 1e-6) return null;
|
|
17192
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
17193
|
+
const k = (len - tipLen) / len;
|
|
17194
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
17195
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
17196
|
+
const tipStart = shaftEnd;
|
|
17197
|
+
const tipEnd = delta;
|
|
17198
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
17199
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
17200
|
+
const tip = {
|
|
17201
|
+
type: "draw-mesh",
|
|
17202
|
+
shape: "cone",
|
|
17203
|
+
position: {
|
|
17204
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
17205
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
17206
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
17207
|
+
},
|
|
17208
|
+
radius: shaftRadius * 3,
|
|
17209
|
+
height: tipLenActual,
|
|
17210
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
17211
|
+
pivot: "center",
|
|
17212
|
+
segments: 12,
|
|
17213
|
+
material: { color }
|
|
17214
|
+
};
|
|
17215
|
+
return {
|
|
17216
|
+
type: "draw-group",
|
|
17217
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
17218
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
17219
|
+
};
|
|
17220
|
+
}
|
|
17221
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
17222
|
+
return {
|
|
17223
|
+
type: "draw-text",
|
|
17224
|
+
text,
|
|
17225
|
+
position: { x, y, z },
|
|
17226
|
+
color: opts?.color ?? "#111827"
|
|
17227
|
+
};
|
|
17228
|
+
}
|
|
17229
|
+
function labelColorForBackground(backgroundColor) {
|
|
17230
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
17231
|
+
if (!m) return "#111827";
|
|
17232
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
17233
|
+
const r2 = parseInt(hex.slice(0, 2), 16) / 255;
|
|
17234
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
17235
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
17236
|
+
const luminance = 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
17237
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
17238
|
+
}
|
|
17239
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
17240
|
+
if (!onShapeClick) return void 0;
|
|
17241
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
17242
|
+
}
|
|
17243
|
+
var Canvas3DHost2;
|
|
17244
|
+
var init_learningScene3D = __esm({
|
|
17245
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
17246
|
+
"use client";
|
|
17247
|
+
init_atoms();
|
|
17248
|
+
init_Stack();
|
|
17249
|
+
init_useEventBus();
|
|
17250
|
+
Canvas3DHost2 = lazy(
|
|
17251
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
17252
|
+
);
|
|
17253
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
17254
|
+
}
|
|
17255
|
+
});
|
|
17256
|
+
var biologyLog, BiologyCanvas;
|
|
17099
17257
|
var init_BiologyCanvas = __esm({
|
|
17100
17258
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
17101
17259
|
"use client";
|
|
17102
17260
|
init_atoms();
|
|
17103
17261
|
init_Stack();
|
|
17104
17262
|
init_LearningCanvas();
|
|
17263
|
+
init_learningScene3D();
|
|
17264
|
+
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
17105
17265
|
BiologyCanvas = ({
|
|
17106
17266
|
className,
|
|
17107
17267
|
width = 600,
|
|
17108
17268
|
height = 400,
|
|
17109
17269
|
title,
|
|
17110
17270
|
backgroundColor,
|
|
17271
|
+
mode = "2d",
|
|
17272
|
+
camera,
|
|
17273
|
+
lighting,
|
|
17274
|
+
post,
|
|
17111
17275
|
nodes = [],
|
|
17112
17276
|
edges = [],
|
|
17113
17277
|
shapes = [],
|
|
17114
|
-
|
|
17278
|
+
showGrid,
|
|
17279
|
+
shadows,
|
|
17280
|
+
interactive,
|
|
17115
17281
|
animate = false,
|
|
17116
17282
|
onShapeClick,
|
|
17117
17283
|
isLoading,
|
|
@@ -17172,6 +17338,75 @@ var init_BiologyCanvas = __esm({
|
|
|
17172
17338
|
out.push(...shapes);
|
|
17173
17339
|
return out;
|
|
17174
17340
|
}, [nodes, edges, shapes]);
|
|
17341
|
+
const drawables3D = useMemo(() => {
|
|
17342
|
+
if (mode !== "3d") return [];
|
|
17343
|
+
if (shapes.length > 0) {
|
|
17344
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
17345
|
+
}
|
|
17346
|
+
const out = [];
|
|
17347
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
17348
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
17349
|
+
for (const n of nodes) {
|
|
17350
|
+
if (n.id) nodeById.set(n.id, n);
|
|
17351
|
+
}
|
|
17352
|
+
for (const e of edges) {
|
|
17353
|
+
const a = nodeById.get(e.from);
|
|
17354
|
+
const b = nodeById.get(e.to);
|
|
17355
|
+
if (!a || !b) continue;
|
|
17356
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
17357
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
17358
|
+
if (edge) out.push(edge);
|
|
17359
|
+
if (e.label) {
|
|
17360
|
+
out.push(
|
|
17361
|
+
billboardLabel(
|
|
17362
|
+
e.label,
|
|
17363
|
+
(a.x + b.x) / 2,
|
|
17364
|
+
(a.y + b.y) / 2,
|
|
17365
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
17366
|
+
{ color: labelColor }
|
|
17367
|
+
)
|
|
17368
|
+
);
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
for (const n of nodes) {
|
|
17372
|
+
const radius = n.radius ?? 0.5;
|
|
17373
|
+
const nz = n.z ?? 0;
|
|
17374
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
17375
|
+
if (n.label) {
|
|
17376
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
17377
|
+
}
|
|
17378
|
+
}
|
|
17379
|
+
return out;
|
|
17380
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
17381
|
+
const nodeIndexById = useMemo(() => {
|
|
17382
|
+
const m = /* @__PURE__ */ new Map();
|
|
17383
|
+
nodes.forEach((n, i) => {
|
|
17384
|
+
if (n.id) m.set(n.id, i);
|
|
17385
|
+
});
|
|
17386
|
+
return m;
|
|
17387
|
+
}, [nodes]);
|
|
17388
|
+
if (mode === "3d") {
|
|
17389
|
+
return /* @__PURE__ */ jsx(
|
|
17390
|
+
LearningScene3D,
|
|
17391
|
+
{
|
|
17392
|
+
className,
|
|
17393
|
+
width,
|
|
17394
|
+
height,
|
|
17395
|
+
title,
|
|
17396
|
+
backgroundColor,
|
|
17397
|
+
drawables: drawables3D,
|
|
17398
|
+
camera,
|
|
17399
|
+
lighting,
|
|
17400
|
+
post,
|
|
17401
|
+
showGrid,
|
|
17402
|
+
shadows,
|
|
17403
|
+
interactive,
|
|
17404
|
+
isLoading,
|
|
17405
|
+
error,
|
|
17406
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
17407
|
+
}
|
|
17408
|
+
);
|
|
17409
|
+
}
|
|
17175
17410
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
17176
17411
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17177
17412
|
/* @__PURE__ */ jsx(
|
|
@@ -17181,7 +17416,7 @@ var init_BiologyCanvas = __esm({
|
|
|
17181
17416
|
height,
|
|
17182
17417
|
backgroundColor,
|
|
17183
17418
|
shapes: derivedShapes,
|
|
17184
|
-
interactive,
|
|
17419
|
+
interactive: interactive ?? false,
|
|
17185
17420
|
animate,
|
|
17186
17421
|
onShapeClick,
|
|
17187
17422
|
isLoading,
|
|
@@ -24021,24 +24256,41 @@ var init_ChatBar = __esm({
|
|
|
24021
24256
|
ChatBar.displayName = "ChatBar";
|
|
24022
24257
|
}
|
|
24023
24258
|
});
|
|
24024
|
-
|
|
24259
|
+
function bondPerpendicular(a, b) {
|
|
24260
|
+
const dx = b[0] - a[0];
|
|
24261
|
+
const dy = b[1] - a[1];
|
|
24262
|
+
const px = dy;
|
|
24263
|
+
const py = -dx;
|
|
24264
|
+
const len = Math.sqrt(px * px + py * py);
|
|
24265
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
24266
|
+
return [px / len, py / len, 0];
|
|
24267
|
+
}
|
|
24268
|
+
var chemistryLog, ChemistryCanvas;
|
|
24025
24269
|
var init_ChemistryCanvas = __esm({
|
|
24026
24270
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
24027
24271
|
"use client";
|
|
24028
24272
|
init_atoms();
|
|
24029
24273
|
init_Stack();
|
|
24030
24274
|
init_LearningCanvas();
|
|
24275
|
+
init_learningScene3D();
|
|
24276
|
+
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
24031
24277
|
ChemistryCanvas = ({
|
|
24032
24278
|
className,
|
|
24033
24279
|
width = 600,
|
|
24034
24280
|
height = 400,
|
|
24035
24281
|
title,
|
|
24036
24282
|
backgroundColor,
|
|
24283
|
+
mode = "2d",
|
|
24284
|
+
camera,
|
|
24285
|
+
lighting,
|
|
24286
|
+
post,
|
|
24037
24287
|
atoms = [],
|
|
24038
24288
|
bonds = [],
|
|
24039
24289
|
arrows = [],
|
|
24040
24290
|
shapes = [],
|
|
24041
|
-
|
|
24291
|
+
showGrid,
|
|
24292
|
+
shadows,
|
|
24293
|
+
interactive,
|
|
24042
24294
|
animate = false,
|
|
24043
24295
|
onShapeClick,
|
|
24044
24296
|
isLoading,
|
|
@@ -24117,6 +24369,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
24117
24369
|
out.push(...shapes);
|
|
24118
24370
|
return out;
|
|
24119
24371
|
}, [atoms, bonds, arrows, shapes]);
|
|
24372
|
+
const drawables3D = useMemo(() => {
|
|
24373
|
+
if (mode !== "3d") return [];
|
|
24374
|
+
if (shapes.length > 0) {
|
|
24375
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
24376
|
+
}
|
|
24377
|
+
const out = [];
|
|
24378
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
24379
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
24380
|
+
for (const a of atoms) {
|
|
24381
|
+
if (a.id) atomById.set(a.id, a);
|
|
24382
|
+
}
|
|
24383
|
+
for (const b of bonds) {
|
|
24384
|
+
const a = atomById.get(b.from);
|
|
24385
|
+
const c = atomById.get(b.to);
|
|
24386
|
+
if (!a || !c) continue;
|
|
24387
|
+
const color = b.color ?? "#6b7280";
|
|
24388
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
24389
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
24390
|
+
const perp = bondPerpendicular(from, to);
|
|
24391
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
24392
|
+
const step = bondRadius * 2.2;
|
|
24393
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
24394
|
+
for (const off of offsets) {
|
|
24395
|
+
const bond = cylinderBetween(
|
|
24396
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
24397
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
24398
|
+
bondRadius,
|
|
24399
|
+
color
|
|
24400
|
+
);
|
|
24401
|
+
if (bond) out.push(bond);
|
|
24402
|
+
}
|
|
24403
|
+
}
|
|
24404
|
+
for (const a of arrows) {
|
|
24405
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
24406
|
+
const len = a.length ?? 60;
|
|
24407
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
24408
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
24409
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
24410
|
+
if (arrow) out.push(arrow);
|
|
24411
|
+
if (a.label) {
|
|
24412
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
24413
|
+
}
|
|
24414
|
+
}
|
|
24415
|
+
for (const a of atoms) {
|
|
24416
|
+
const radius = a.radius ?? 0.45;
|
|
24417
|
+
const az = a.z ?? 0;
|
|
24418
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
24419
|
+
if (a.element) {
|
|
24420
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
24421
|
+
}
|
|
24422
|
+
}
|
|
24423
|
+
return out;
|
|
24424
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
24425
|
+
const atomIndexById = useMemo(() => {
|
|
24426
|
+
const m = /* @__PURE__ */ new Map();
|
|
24427
|
+
atoms.forEach((a, i) => {
|
|
24428
|
+
if (a.id) m.set(a.id, i);
|
|
24429
|
+
});
|
|
24430
|
+
return m;
|
|
24431
|
+
}, [atoms]);
|
|
24432
|
+
if (mode === "3d") {
|
|
24433
|
+
return /* @__PURE__ */ jsx(
|
|
24434
|
+
LearningScene3D,
|
|
24435
|
+
{
|
|
24436
|
+
className,
|
|
24437
|
+
width,
|
|
24438
|
+
height,
|
|
24439
|
+
title,
|
|
24440
|
+
backgroundColor,
|
|
24441
|
+
drawables: drawables3D,
|
|
24442
|
+
camera,
|
|
24443
|
+
lighting,
|
|
24444
|
+
post,
|
|
24445
|
+
showGrid,
|
|
24446
|
+
shadows,
|
|
24447
|
+
interactive,
|
|
24448
|
+
isLoading,
|
|
24449
|
+
error,
|
|
24450
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
24451
|
+
}
|
|
24452
|
+
);
|
|
24453
|
+
}
|
|
24120
24454
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
24121
24455
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
24122
24456
|
/* @__PURE__ */ jsx(
|
|
@@ -24126,7 +24460,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
24126
24460
|
height,
|
|
24127
24461
|
backgroundColor,
|
|
24128
24462
|
shapes: derivedShapes,
|
|
24129
|
-
interactive,
|
|
24463
|
+
interactive: interactive ?? false,
|
|
24130
24464
|
animate,
|
|
24131
24465
|
onShapeClick,
|
|
24132
24466
|
isLoading,
|
|
@@ -30431,19 +30765,25 @@ var init_MathCanvas = __esm({
|
|
|
30431
30765
|
};
|
|
30432
30766
|
}
|
|
30433
30767
|
});
|
|
30434
|
-
var PhysicsCanvas;
|
|
30768
|
+
var physicsLog2, PhysicsCanvas;
|
|
30435
30769
|
var init_PhysicsCanvas = __esm({
|
|
30436
30770
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
30437
30771
|
"use client";
|
|
30438
30772
|
init_atoms();
|
|
30439
30773
|
init_Stack();
|
|
30440
30774
|
init_LearningCanvas();
|
|
30775
|
+
init_learningScene3D();
|
|
30776
|
+
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
30441
30777
|
PhysicsCanvas = ({
|
|
30442
30778
|
className,
|
|
30443
30779
|
width = 600,
|
|
30444
30780
|
height = 400,
|
|
30445
30781
|
title,
|
|
30446
30782
|
backgroundColor,
|
|
30783
|
+
mode = "2d",
|
|
30784
|
+
camera,
|
|
30785
|
+
lighting,
|
|
30786
|
+
post,
|
|
30447
30787
|
bodies = [],
|
|
30448
30788
|
constraints = [],
|
|
30449
30789
|
showVelocity = true,
|
|
@@ -30451,7 +30791,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
30451
30791
|
velocityScale = 20,
|
|
30452
30792
|
forceScale = 20,
|
|
30453
30793
|
shapes = [],
|
|
30454
|
-
|
|
30794
|
+
showGrid,
|
|
30795
|
+
shadows,
|
|
30796
|
+
interactive,
|
|
30455
30797
|
animate = false,
|
|
30456
30798
|
onShapeClick,
|
|
30457
30799
|
isLoading,
|
|
@@ -30523,6 +30865,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
30523
30865
|
out.push(...shapes);
|
|
30524
30866
|
return out;
|
|
30525
30867
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
30868
|
+
const drawables3D = useMemo(() => {
|
|
30869
|
+
if (mode !== "3d") return [];
|
|
30870
|
+
if (shapes.length > 0) {
|
|
30871
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
30872
|
+
}
|
|
30873
|
+
const out = [];
|
|
30874
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
30875
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
30876
|
+
for (const b of bodies) {
|
|
30877
|
+
if (b.id) bodyById.set(b.id, b);
|
|
30878
|
+
}
|
|
30879
|
+
for (const c of constraints) {
|
|
30880
|
+
const a = bodyById.get(c.from);
|
|
30881
|
+
const b = bodyById.get(c.to);
|
|
30882
|
+
if (!a || !b) continue;
|
|
30883
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
30884
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
30885
|
+
if (rod) out.push(rod);
|
|
30886
|
+
}
|
|
30887
|
+
for (const b of bodies) {
|
|
30888
|
+
const radius = b.radius ?? 0.5;
|
|
30889
|
+
const bz = b.z ?? 0;
|
|
30890
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
30891
|
+
if (b.label) {
|
|
30892
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
30893
|
+
}
|
|
30894
|
+
const vx = b.vx ?? 0;
|
|
30895
|
+
const vy = b.vy ?? 0;
|
|
30896
|
+
const vz = b.vz ?? 0;
|
|
30897
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
30898
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
30899
|
+
const arrow = arrowBetween(
|
|
30900
|
+
[b.x, b.y, bz],
|
|
30901
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
30902
|
+
"#16a34a",
|
|
30903
|
+
arrowRadius
|
|
30904
|
+
);
|
|
30905
|
+
if (arrow) out.push(arrow);
|
|
30906
|
+
}
|
|
30907
|
+
const fx = b.fx ?? 0;
|
|
30908
|
+
const fy = b.fy ?? 0;
|
|
30909
|
+
const fz = b.fz ?? 0;
|
|
30910
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
30911
|
+
const arrow = arrowBetween(
|
|
30912
|
+
[b.x, b.y, bz],
|
|
30913
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
30914
|
+
"#dc2626",
|
|
30915
|
+
arrowRadius
|
|
30916
|
+
);
|
|
30917
|
+
if (arrow) out.push(arrow);
|
|
30918
|
+
}
|
|
30919
|
+
}
|
|
30920
|
+
return out;
|
|
30921
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
30922
|
+
const bodyIndexById = useMemo(() => {
|
|
30923
|
+
const m = /* @__PURE__ */ new Map();
|
|
30924
|
+
bodies.forEach((b, i) => {
|
|
30925
|
+
if (b.id) m.set(b.id, i);
|
|
30926
|
+
});
|
|
30927
|
+
return m;
|
|
30928
|
+
}, [bodies]);
|
|
30929
|
+
if (mode === "3d") {
|
|
30930
|
+
return /* @__PURE__ */ jsx(
|
|
30931
|
+
LearningScene3D,
|
|
30932
|
+
{
|
|
30933
|
+
className,
|
|
30934
|
+
width,
|
|
30935
|
+
height,
|
|
30936
|
+
title,
|
|
30937
|
+
backgroundColor,
|
|
30938
|
+
drawables: drawables3D,
|
|
30939
|
+
camera,
|
|
30940
|
+
lighting,
|
|
30941
|
+
post,
|
|
30942
|
+
showGrid,
|
|
30943
|
+
shadows,
|
|
30944
|
+
interactive,
|
|
30945
|
+
isLoading,
|
|
30946
|
+
error,
|
|
30947
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
30948
|
+
}
|
|
30949
|
+
);
|
|
30950
|
+
}
|
|
30526
30951
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
30527
30952
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
30528
30953
|
/* @__PURE__ */ jsx(
|
|
@@ -30532,7 +30957,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
30532
30957
|
height,
|
|
30533
30958
|
backgroundColor,
|
|
30534
30959
|
shapes: derivedShapes,
|
|
30535
|
-
interactive,
|
|
30960
|
+
interactive: interactive ?? false,
|
|
30536
30961
|
animate,
|
|
30537
30962
|
onShapeClick,
|
|
30538
30963
|
isLoading,
|
|
@@ -31150,12 +31575,12 @@ var init_MapView = __esm({
|
|
|
31150
31575
|
shadowSize: [41, 41]
|
|
31151
31576
|
});
|
|
31152
31577
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31153
|
-
const { useEffect: useEffect64, useRef:
|
|
31578
|
+
const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
|
|
31154
31579
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31155
31580
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31156
31581
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31157
31582
|
const map = useMap();
|
|
31158
|
-
const prevRef =
|
|
31583
|
+
const prevRef = useRef65({ centerLat, centerLng, zoom });
|
|
31159
31584
|
useEffect64(() => {
|
|
31160
31585
|
const prev = prevRef.current;
|
|
31161
31586
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -41569,7 +41994,7 @@ function getEnumOptions(field) {
|
|
|
41569
41994
|
}));
|
|
41570
41995
|
}
|
|
41571
41996
|
const validation = field.validation;
|
|
41572
|
-
if (validation?.enum &&
|
|
41997
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
41573
41998
|
return validation.enum.map((v) => ({
|
|
41574
41999
|
value: v,
|
|
41575
42000
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnimationName, Asset, ScenePos, EventEmit, JsonObject, JsonValue } from '@almadar/core';
|
|
2
2
|
import React__default from 'react';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
|
-
import { D as DrawableNode } from './paintDispatch-
|
|
4
|
+
import { D as DrawableNode } from './paintDispatch-BjZjUbcb.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Sprite Sheet Animation Types
|
|
@@ -307,10 +307,11 @@ interface CameraState {
|
|
|
307
307
|
* (forwarded as `followTarget`), falling back to the scene centre.
|
|
308
308
|
*
|
|
309
309
|
* Interaction: keyboard maps to semantic events (device-agnostic input). Pointer
|
|
310
|
-
* click
|
|
311
|
-
* the
|
|
312
|
-
*
|
|
313
|
-
*
|
|
310
|
+
* click on a tagged drawable (a descriptor carrying `DrawableBase.id`) raycasts to
|
|
311
|
+
* the mesh and walks up to the tagged ancestor → `unitClickEvent {unitId,x,z}`;
|
|
312
|
+
* a click that hits no tagged mesh falls back to the ground-plane cell raycast →
|
|
313
|
+
* `tileClickEvent`. Pointer hover/feature events are accepted but not yet
|
|
314
|
+
* emitted (docs/Almadar_Std_Gaps.md S-CANVAS3D-HOVER-DEAD).
|
|
314
315
|
*
|
|
315
316
|
* @packageDocumentation
|
|
316
317
|
*/
|
|
@@ -422,8 +423,9 @@ interface Canvas3DHostProps {
|
|
|
422
423
|
terrain?: string;
|
|
423
424
|
elevation?: number;
|
|
424
425
|
}>;
|
|
425
|
-
/** Declarative event: unit click. Emitted `{ unitId, x, z }` when the raycast
|
|
426
|
-
*
|
|
426
|
+
/** Declarative event: unit click. Emitted `{ unitId, x, z }` when the raycast hits a
|
|
427
|
+
* mesh tagged with a descriptor `id` (or lands on a ground cell whose descriptor
|
|
428
|
+
* carries an `id`). */
|
|
427
429
|
unitClickEvent?: EventEmit<{
|
|
428
430
|
unitId: string;
|
|
429
431
|
x: number;
|
|
@@ -477,8 +479,12 @@ interface Canvas3DHostProps {
|
|
|
477
479
|
loadingMessage?: string;
|
|
478
480
|
/** Unit draw-size multiplier (accepted for API parity; sizing is drawable-authored). */
|
|
479
481
|
unitScale?: number;
|
|
480
|
-
/** Board zoom
|
|
481
|
-
|
|
482
|
+
/** Board zoom — the neutral `Camera.zoom`. Dollies the camera: the framing
|
|
483
|
+
* distance is divided by `zoom` (undefined/1 = default framing; <= 0 ignored). */
|
|
484
|
+
zoom?: number;
|
|
485
|
+
/** Enable the orbit camera controls. Default true; `follow`/`chase` modes always
|
|
486
|
+
* disable them (the follow camera is authoritative). */
|
|
487
|
+
controlsEnabled?: boolean;
|
|
482
488
|
/** Maps a keydown `e.code` → the board's SEMANTIC event (device-agnostic input). */
|
|
483
489
|
keyMap?: Record<string, string>;
|
|
484
490
|
/** Maps a keyup `e.code` → the board's SEMANTIC event. */
|
|
@@ -494,6 +500,9 @@ interface Canvas3DHostProps {
|
|
|
494
500
|
fov?: number;
|
|
495
501
|
/** Orbit the mode's framing position around the vertical axis through the target, in radians — the neutral `Camera.azimuth`. */
|
|
496
502
|
azimuth?: number;
|
|
503
|
+
/** Orbit height angle above the ground plane, in radians — the neutral `Camera.elevation`
|
|
504
|
+
* (perspective mode). Omitted → the mode's fixed framing height. */
|
|
505
|
+
elevation?: number;
|
|
497
506
|
/** 3D scene light rig as data — ambient/directional/hemisphere/point lights + optional
|
|
498
507
|
* 'room' environment. Omitted → the standard fixed rig (this host's current
|
|
499
508
|
* hardcoded lights, unchanged). */
|