@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/runtime/index.cjs
CHANGED
|
@@ -10729,9 +10729,10 @@ function Canvas({
|
|
|
10729
10729
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
10730
10730
|
isLoading,
|
|
10731
10731
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
10732
|
-
...zoom !== void 0 ? {
|
|
10732
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
10733
10733
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
10734
10734
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
10735
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
10735
10736
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
10736
10737
|
unitScale,
|
|
10737
10738
|
backgroundColor,
|
|
@@ -14610,23 +14611,188 @@ var init_BehaviorView = __esm({
|
|
|
14610
14611
|
BehaviorView.displayName = "BehaviorView";
|
|
14611
14612
|
}
|
|
14612
14613
|
});
|
|
14613
|
-
|
|
14614
|
+
function LearningScene3D({
|
|
14615
|
+
className,
|
|
14616
|
+
width = 600,
|
|
14617
|
+
height = 400,
|
|
14618
|
+
title,
|
|
14619
|
+
backgroundColor,
|
|
14620
|
+
drawables,
|
|
14621
|
+
camera,
|
|
14622
|
+
lighting,
|
|
14623
|
+
post,
|
|
14624
|
+
showGrid = false,
|
|
14625
|
+
shadows,
|
|
14626
|
+
interactive,
|
|
14627
|
+
isLoading,
|
|
14628
|
+
error,
|
|
14629
|
+
onItemClick
|
|
14630
|
+
}) {
|
|
14631
|
+
const instanceId = React85.useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
14632
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
14633
|
+
const onItemClickRef = React85.useRef(onItemClick);
|
|
14634
|
+
onItemClickRef.current = onItemClick;
|
|
14635
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
14636
|
+
const unitId = event.payload?.unitId;
|
|
14637
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
14638
|
+
});
|
|
14639
|
+
const props3d = {
|
|
14640
|
+
drawables,
|
|
14641
|
+
isLoading,
|
|
14642
|
+
error: error ? error.message : null,
|
|
14643
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
14644
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
14645
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14646
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14647
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14648
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14649
|
+
backgroundColor,
|
|
14650
|
+
showGrid,
|
|
14651
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
14652
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
14653
|
+
lighting,
|
|
14654
|
+
post,
|
|
14655
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
14656
|
+
};
|
|
14657
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14658
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14659
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(React85.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
14660
|
+
] }) });
|
|
14661
|
+
}
|
|
14662
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
14663
|
+
return {
|
|
14664
|
+
type: "draw-mesh",
|
|
14665
|
+
...id !== void 0 ? { id } : {},
|
|
14666
|
+
shape: opts?.shape ?? "sphere",
|
|
14667
|
+
position: { x, y, z },
|
|
14668
|
+
radius,
|
|
14669
|
+
pivot: "center",
|
|
14670
|
+
segments: opts?.segments ?? 24,
|
|
14671
|
+
material: { color, ...opts?.material },
|
|
14672
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
14673
|
+
};
|
|
14674
|
+
}
|
|
14675
|
+
function axisRotation(from, to) {
|
|
14676
|
+
const wx = to[0] - from[0];
|
|
14677
|
+
const wy = to[2] - from[2];
|
|
14678
|
+
const wz = to[1] - from[1];
|
|
14679
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
14680
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
14681
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
14682
|
+
}
|
|
14683
|
+
function segmentLength(from, to) {
|
|
14684
|
+
const dx = to[0] - from[0];
|
|
14685
|
+
const dy = to[1] - from[1];
|
|
14686
|
+
const dz = to[2] - from[2];
|
|
14687
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
14688
|
+
}
|
|
14689
|
+
function cylinderBetween(from, to, radius, color) {
|
|
14690
|
+
const len = segmentLength(from, to);
|
|
14691
|
+
if (len < 1e-6) return null;
|
|
14692
|
+
return {
|
|
14693
|
+
type: "draw-mesh",
|
|
14694
|
+
shape: "cylinder",
|
|
14695
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
14696
|
+
radius,
|
|
14697
|
+
height: len,
|
|
14698
|
+
rotation: axisRotation(from, to),
|
|
14699
|
+
pivot: "center",
|
|
14700
|
+
segments: 12,
|
|
14701
|
+
material: { color }
|
|
14702
|
+
};
|
|
14703
|
+
}
|
|
14704
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
14705
|
+
const len = segmentLength(from, to);
|
|
14706
|
+
if (len < 1e-6) return null;
|
|
14707
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
14708
|
+
const k = (len - tipLen) / len;
|
|
14709
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
14710
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
14711
|
+
const tipStart = shaftEnd;
|
|
14712
|
+
const tipEnd = delta;
|
|
14713
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
14714
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
14715
|
+
const tip = {
|
|
14716
|
+
type: "draw-mesh",
|
|
14717
|
+
shape: "cone",
|
|
14718
|
+
position: {
|
|
14719
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
14720
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
14721
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
14722
|
+
},
|
|
14723
|
+
radius: shaftRadius * 3,
|
|
14724
|
+
height: tipLenActual,
|
|
14725
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
14726
|
+
pivot: "center",
|
|
14727
|
+
segments: 12,
|
|
14728
|
+
material: { color }
|
|
14729
|
+
};
|
|
14730
|
+
return {
|
|
14731
|
+
type: "draw-group",
|
|
14732
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
14733
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
14734
|
+
};
|
|
14735
|
+
}
|
|
14736
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
14737
|
+
return {
|
|
14738
|
+
type: "draw-text",
|
|
14739
|
+
text,
|
|
14740
|
+
position: { x, y, z },
|
|
14741
|
+
color: opts?.color ?? "#111827"
|
|
14742
|
+
};
|
|
14743
|
+
}
|
|
14744
|
+
function labelColorForBackground(backgroundColor) {
|
|
14745
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
14746
|
+
if (!m) return "#111827";
|
|
14747
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
14748
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
14749
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
14750
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
14751
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
14752
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
14753
|
+
}
|
|
14754
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
14755
|
+
if (!onShapeClick) return void 0;
|
|
14756
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
14757
|
+
}
|
|
14758
|
+
var Canvas3DHost2;
|
|
14759
|
+
var init_learningScene3D = __esm({
|
|
14760
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
14761
|
+
"use client";
|
|
14762
|
+
init_atoms();
|
|
14763
|
+
init_Stack();
|
|
14764
|
+
init_useEventBus();
|
|
14765
|
+
Canvas3DHost2 = React85.lazy(
|
|
14766
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
14767
|
+
);
|
|
14768
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
14769
|
+
}
|
|
14770
|
+
});
|
|
14771
|
+
var biologyLog, BiologyCanvas;
|
|
14614
14772
|
var init_BiologyCanvas = __esm({
|
|
14615
14773
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
14616
14774
|
"use client";
|
|
14617
14775
|
init_atoms();
|
|
14618
14776
|
init_Stack();
|
|
14619
14777
|
init_LearningCanvas();
|
|
14778
|
+
init_learningScene3D();
|
|
14779
|
+
biologyLog = logger.createLogger("almadar:ui:biology-canvas");
|
|
14620
14780
|
BiologyCanvas = ({
|
|
14621
14781
|
className,
|
|
14622
14782
|
width = 600,
|
|
14623
14783
|
height = 400,
|
|
14624
14784
|
title,
|
|
14625
14785
|
backgroundColor,
|
|
14786
|
+
mode = "2d",
|
|
14787
|
+
camera,
|
|
14788
|
+
lighting,
|
|
14789
|
+
post,
|
|
14626
14790
|
nodes = [],
|
|
14627
14791
|
edges = [],
|
|
14628
14792
|
shapes = [],
|
|
14629
|
-
|
|
14793
|
+
showGrid,
|
|
14794
|
+
shadows,
|
|
14795
|
+
interactive,
|
|
14630
14796
|
animate = false,
|
|
14631
14797
|
onShapeClick,
|
|
14632
14798
|
isLoading,
|
|
@@ -14687,6 +14853,75 @@ var init_BiologyCanvas = __esm({
|
|
|
14687
14853
|
out.push(...shapes);
|
|
14688
14854
|
return out;
|
|
14689
14855
|
}, [nodes, edges, shapes]);
|
|
14856
|
+
const drawables3D = React85.useMemo(() => {
|
|
14857
|
+
if (mode !== "3d") return [];
|
|
14858
|
+
if (shapes.length > 0) {
|
|
14859
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
14860
|
+
}
|
|
14861
|
+
const out = [];
|
|
14862
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
14863
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
14864
|
+
for (const n of nodes) {
|
|
14865
|
+
if (n.id) nodeById.set(n.id, n);
|
|
14866
|
+
}
|
|
14867
|
+
for (const e of edges) {
|
|
14868
|
+
const a = nodeById.get(e.from);
|
|
14869
|
+
const b = nodeById.get(e.to);
|
|
14870
|
+
if (!a || !b) continue;
|
|
14871
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
14872
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
14873
|
+
if (edge) out.push(edge);
|
|
14874
|
+
if (e.label) {
|
|
14875
|
+
out.push(
|
|
14876
|
+
billboardLabel(
|
|
14877
|
+
e.label,
|
|
14878
|
+
(a.x + b.x) / 2,
|
|
14879
|
+
(a.y + b.y) / 2,
|
|
14880
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
14881
|
+
{ color: labelColor }
|
|
14882
|
+
)
|
|
14883
|
+
);
|
|
14884
|
+
}
|
|
14885
|
+
}
|
|
14886
|
+
for (const n of nodes) {
|
|
14887
|
+
const radius = n.radius ?? 0.5;
|
|
14888
|
+
const nz = n.z ?? 0;
|
|
14889
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
14890
|
+
if (n.label) {
|
|
14891
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
14892
|
+
}
|
|
14893
|
+
}
|
|
14894
|
+
return out;
|
|
14895
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
14896
|
+
const nodeIndexById = React85.useMemo(() => {
|
|
14897
|
+
const m = /* @__PURE__ */ new Map();
|
|
14898
|
+
nodes.forEach((n, i) => {
|
|
14899
|
+
if (n.id) m.set(n.id, i);
|
|
14900
|
+
});
|
|
14901
|
+
return m;
|
|
14902
|
+
}, [nodes]);
|
|
14903
|
+
if (mode === "3d") {
|
|
14904
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14905
|
+
LearningScene3D,
|
|
14906
|
+
{
|
|
14907
|
+
className,
|
|
14908
|
+
width,
|
|
14909
|
+
height,
|
|
14910
|
+
title,
|
|
14911
|
+
backgroundColor,
|
|
14912
|
+
drawables: drawables3D,
|
|
14913
|
+
camera,
|
|
14914
|
+
lighting,
|
|
14915
|
+
post,
|
|
14916
|
+
showGrid,
|
|
14917
|
+
shadows,
|
|
14918
|
+
interactive,
|
|
14919
|
+
isLoading,
|
|
14920
|
+
error,
|
|
14921
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
14922
|
+
}
|
|
14923
|
+
);
|
|
14924
|
+
}
|
|
14690
14925
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14691
14926
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14692
14927
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -14696,7 +14931,7 @@ var init_BiologyCanvas = __esm({
|
|
|
14696
14931
|
height,
|
|
14697
14932
|
backgroundColor,
|
|
14698
14933
|
shapes: derivedShapes,
|
|
14699
|
-
interactive,
|
|
14934
|
+
interactive: interactive ?? false,
|
|
14700
14935
|
animate,
|
|
14701
14936
|
onShapeClick,
|
|
14702
14937
|
isLoading,
|
|
@@ -21419,24 +21654,41 @@ var init_ChatBar = __esm({
|
|
|
21419
21654
|
ChatBar.displayName = "ChatBar";
|
|
21420
21655
|
}
|
|
21421
21656
|
});
|
|
21422
|
-
|
|
21657
|
+
function bondPerpendicular(a, b) {
|
|
21658
|
+
const dx = b[0] - a[0];
|
|
21659
|
+
const dy = b[1] - a[1];
|
|
21660
|
+
const px = dy;
|
|
21661
|
+
const py = -dx;
|
|
21662
|
+
const len = Math.sqrt(px * px + py * py);
|
|
21663
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
21664
|
+
return [px / len, py / len, 0];
|
|
21665
|
+
}
|
|
21666
|
+
var chemistryLog, ChemistryCanvas;
|
|
21423
21667
|
var init_ChemistryCanvas = __esm({
|
|
21424
21668
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
21425
21669
|
"use client";
|
|
21426
21670
|
init_atoms();
|
|
21427
21671
|
init_Stack();
|
|
21428
21672
|
init_LearningCanvas();
|
|
21673
|
+
init_learningScene3D();
|
|
21674
|
+
chemistryLog = logger.createLogger("almadar:ui:chemistry-canvas");
|
|
21429
21675
|
ChemistryCanvas = ({
|
|
21430
21676
|
className,
|
|
21431
21677
|
width = 600,
|
|
21432
21678
|
height = 400,
|
|
21433
21679
|
title,
|
|
21434
21680
|
backgroundColor,
|
|
21681
|
+
mode = "2d",
|
|
21682
|
+
camera,
|
|
21683
|
+
lighting,
|
|
21684
|
+
post,
|
|
21435
21685
|
atoms = [],
|
|
21436
21686
|
bonds = [],
|
|
21437
21687
|
arrows = [],
|
|
21438
21688
|
shapes = [],
|
|
21439
|
-
|
|
21689
|
+
showGrid,
|
|
21690
|
+
shadows,
|
|
21691
|
+
interactive,
|
|
21440
21692
|
animate = false,
|
|
21441
21693
|
onShapeClick,
|
|
21442
21694
|
isLoading,
|
|
@@ -21515,6 +21767,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
21515
21767
|
out.push(...shapes);
|
|
21516
21768
|
return out;
|
|
21517
21769
|
}, [atoms, bonds, arrows, shapes]);
|
|
21770
|
+
const drawables3D = React85.useMemo(() => {
|
|
21771
|
+
if (mode !== "3d") return [];
|
|
21772
|
+
if (shapes.length > 0) {
|
|
21773
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
21774
|
+
}
|
|
21775
|
+
const out = [];
|
|
21776
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
21777
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
21778
|
+
for (const a of atoms) {
|
|
21779
|
+
if (a.id) atomById.set(a.id, a);
|
|
21780
|
+
}
|
|
21781
|
+
for (const b of bonds) {
|
|
21782
|
+
const a = atomById.get(b.from);
|
|
21783
|
+
const c = atomById.get(b.to);
|
|
21784
|
+
if (!a || !c) continue;
|
|
21785
|
+
const color = b.color ?? "#6b7280";
|
|
21786
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
21787
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
21788
|
+
const perp = bondPerpendicular(from, to);
|
|
21789
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
21790
|
+
const step = bondRadius * 2.2;
|
|
21791
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
21792
|
+
for (const off of offsets) {
|
|
21793
|
+
const bond = cylinderBetween(
|
|
21794
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
21795
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
21796
|
+
bondRadius,
|
|
21797
|
+
color
|
|
21798
|
+
);
|
|
21799
|
+
if (bond) out.push(bond);
|
|
21800
|
+
}
|
|
21801
|
+
}
|
|
21802
|
+
for (const a of arrows) {
|
|
21803
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
21804
|
+
const len = a.length ?? 60;
|
|
21805
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
21806
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
21807
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
21808
|
+
if (arrow) out.push(arrow);
|
|
21809
|
+
if (a.label) {
|
|
21810
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
21811
|
+
}
|
|
21812
|
+
}
|
|
21813
|
+
for (const a of atoms) {
|
|
21814
|
+
const radius = a.radius ?? 0.45;
|
|
21815
|
+
const az = a.z ?? 0;
|
|
21816
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
21817
|
+
if (a.element) {
|
|
21818
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
21819
|
+
}
|
|
21820
|
+
}
|
|
21821
|
+
return out;
|
|
21822
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
21823
|
+
const atomIndexById = React85.useMemo(() => {
|
|
21824
|
+
const m = /* @__PURE__ */ new Map();
|
|
21825
|
+
atoms.forEach((a, i) => {
|
|
21826
|
+
if (a.id) m.set(a.id, i);
|
|
21827
|
+
});
|
|
21828
|
+
return m;
|
|
21829
|
+
}, [atoms]);
|
|
21830
|
+
if (mode === "3d") {
|
|
21831
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
21832
|
+
LearningScene3D,
|
|
21833
|
+
{
|
|
21834
|
+
className,
|
|
21835
|
+
width,
|
|
21836
|
+
height,
|
|
21837
|
+
title,
|
|
21838
|
+
backgroundColor,
|
|
21839
|
+
drawables: drawables3D,
|
|
21840
|
+
camera,
|
|
21841
|
+
lighting,
|
|
21842
|
+
post,
|
|
21843
|
+
showGrid,
|
|
21844
|
+
shadows,
|
|
21845
|
+
interactive,
|
|
21846
|
+
isLoading,
|
|
21847
|
+
error,
|
|
21848
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
21849
|
+
}
|
|
21850
|
+
);
|
|
21851
|
+
}
|
|
21518
21852
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
21519
21853
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
21520
21854
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -21524,7 +21858,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
21524
21858
|
height,
|
|
21525
21859
|
backgroundColor,
|
|
21526
21860
|
shapes: derivedShapes,
|
|
21527
|
-
interactive,
|
|
21861
|
+
interactive: interactive ?? false,
|
|
21528
21862
|
animate,
|
|
21529
21863
|
onShapeClick,
|
|
21530
21864
|
isLoading,
|
|
@@ -27751,19 +28085,25 @@ var init_MathCanvas = __esm({
|
|
|
27751
28085
|
};
|
|
27752
28086
|
}
|
|
27753
28087
|
});
|
|
27754
|
-
var PhysicsCanvas;
|
|
28088
|
+
var physicsLog2, PhysicsCanvas;
|
|
27755
28089
|
var init_PhysicsCanvas = __esm({
|
|
27756
28090
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
27757
28091
|
"use client";
|
|
27758
28092
|
init_atoms();
|
|
27759
28093
|
init_Stack();
|
|
27760
28094
|
init_LearningCanvas();
|
|
28095
|
+
init_learningScene3D();
|
|
28096
|
+
physicsLog2 = logger.createLogger("almadar:ui:physics-canvas");
|
|
27761
28097
|
PhysicsCanvas = ({
|
|
27762
28098
|
className,
|
|
27763
28099
|
width = 600,
|
|
27764
28100
|
height = 400,
|
|
27765
28101
|
title,
|
|
27766
28102
|
backgroundColor,
|
|
28103
|
+
mode = "2d",
|
|
28104
|
+
camera,
|
|
28105
|
+
lighting,
|
|
28106
|
+
post,
|
|
27767
28107
|
bodies = [],
|
|
27768
28108
|
constraints = [],
|
|
27769
28109
|
showVelocity = true,
|
|
@@ -27771,7 +28111,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
27771
28111
|
velocityScale = 20,
|
|
27772
28112
|
forceScale = 20,
|
|
27773
28113
|
shapes = [],
|
|
27774
|
-
|
|
28114
|
+
showGrid,
|
|
28115
|
+
shadows,
|
|
28116
|
+
interactive,
|
|
27775
28117
|
animate = false,
|
|
27776
28118
|
onShapeClick,
|
|
27777
28119
|
isLoading,
|
|
@@ -27843,6 +28185,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
27843
28185
|
out.push(...shapes);
|
|
27844
28186
|
return out;
|
|
27845
28187
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
28188
|
+
const drawables3D = React85.useMemo(() => {
|
|
28189
|
+
if (mode !== "3d") return [];
|
|
28190
|
+
if (shapes.length > 0) {
|
|
28191
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
28192
|
+
}
|
|
28193
|
+
const out = [];
|
|
28194
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
28195
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
28196
|
+
for (const b of bodies) {
|
|
28197
|
+
if (b.id) bodyById.set(b.id, b);
|
|
28198
|
+
}
|
|
28199
|
+
for (const c of constraints) {
|
|
28200
|
+
const a = bodyById.get(c.from);
|
|
28201
|
+
const b = bodyById.get(c.to);
|
|
28202
|
+
if (!a || !b) continue;
|
|
28203
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
28204
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
28205
|
+
if (rod) out.push(rod);
|
|
28206
|
+
}
|
|
28207
|
+
for (const b of bodies) {
|
|
28208
|
+
const radius = b.radius ?? 0.5;
|
|
28209
|
+
const bz = b.z ?? 0;
|
|
28210
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
28211
|
+
if (b.label) {
|
|
28212
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
28213
|
+
}
|
|
28214
|
+
const vx = b.vx ?? 0;
|
|
28215
|
+
const vy = b.vy ?? 0;
|
|
28216
|
+
const vz = b.vz ?? 0;
|
|
28217
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
28218
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
28219
|
+
const arrow = arrowBetween(
|
|
28220
|
+
[b.x, b.y, bz],
|
|
28221
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
28222
|
+
"#16a34a",
|
|
28223
|
+
arrowRadius
|
|
28224
|
+
);
|
|
28225
|
+
if (arrow) out.push(arrow);
|
|
28226
|
+
}
|
|
28227
|
+
const fx = b.fx ?? 0;
|
|
28228
|
+
const fy = b.fy ?? 0;
|
|
28229
|
+
const fz = b.fz ?? 0;
|
|
28230
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
28231
|
+
const arrow = arrowBetween(
|
|
28232
|
+
[b.x, b.y, bz],
|
|
28233
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
28234
|
+
"#dc2626",
|
|
28235
|
+
arrowRadius
|
|
28236
|
+
);
|
|
28237
|
+
if (arrow) out.push(arrow);
|
|
28238
|
+
}
|
|
28239
|
+
}
|
|
28240
|
+
return out;
|
|
28241
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
28242
|
+
const bodyIndexById = React85.useMemo(() => {
|
|
28243
|
+
const m = /* @__PURE__ */ new Map();
|
|
28244
|
+
bodies.forEach((b, i) => {
|
|
28245
|
+
if (b.id) m.set(b.id, i);
|
|
28246
|
+
});
|
|
28247
|
+
return m;
|
|
28248
|
+
}, [bodies]);
|
|
28249
|
+
if (mode === "3d") {
|
|
28250
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28251
|
+
LearningScene3D,
|
|
28252
|
+
{
|
|
28253
|
+
className,
|
|
28254
|
+
width,
|
|
28255
|
+
height,
|
|
28256
|
+
title,
|
|
28257
|
+
backgroundColor,
|
|
28258
|
+
drawables: drawables3D,
|
|
28259
|
+
camera,
|
|
28260
|
+
lighting,
|
|
28261
|
+
post,
|
|
28262
|
+
showGrid,
|
|
28263
|
+
shadows,
|
|
28264
|
+
interactive,
|
|
28265
|
+
isLoading,
|
|
28266
|
+
error,
|
|
28267
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
28268
|
+
}
|
|
28269
|
+
);
|
|
28270
|
+
}
|
|
27846
28271
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
27847
28272
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
27848
28273
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -27852,7 +28277,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
27852
28277
|
height,
|
|
27853
28278
|
backgroundColor,
|
|
27854
28279
|
shapes: derivedShapes,
|
|
27855
|
-
interactive,
|
|
28280
|
+
interactive: interactive ?? false,
|
|
27856
28281
|
animate,
|
|
27857
28282
|
onShapeClick,
|
|
27858
28283
|
isLoading,
|
|
@@ -28470,12 +28895,12 @@ var init_MapView = __esm({
|
|
|
28470
28895
|
shadowSize: [41, 41]
|
|
28471
28896
|
});
|
|
28472
28897
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28473
|
-
const { useEffect: useEffect60, useRef:
|
|
28898
|
+
const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
|
|
28474
28899
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28475
28900
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28476
28901
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28477
28902
|
const map = useMap();
|
|
28478
|
-
const prevRef =
|
|
28903
|
+
const prevRef = useRef63({ centerLat, centerLng, zoom });
|
|
28479
28904
|
useEffect60(() => {
|
|
28480
28905
|
const prev = prevRef.current;
|
|
28481
28906
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -39140,7 +39565,7 @@ function getEnumOptions(field) {
|
|
|
39140
39565
|
}));
|
|
39141
39566
|
}
|
|
39142
39567
|
const validation = field.validation;
|
|
39143
|
-
if (validation?.enum &&
|
|
39568
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
39144
39569
|
return validation.enum.map((v) => ({
|
|
39145
39570
|
value: v,
|
|
39146
39571
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|