@almadar/ui 5.139.0 → 5.141.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.
@@ -4,7 +4,7 @@ var React3 = require('react');
4
4
  var providers = require('@almadar/ui/providers');
5
5
  var logger = require('@almadar/logger');
6
6
  var fiber = require('@react-three/fiber');
7
- var THREE9 = require('three');
7
+ var THREE10 = require('three');
8
8
  var drei = require('@react-three/drei');
9
9
  var jsxRuntime = require('react/jsx-runtime');
10
10
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
@@ -38,7 +38,7 @@ function _interopNamespace(e) {
38
38
  }
39
39
 
40
40
  var React3__default = /*#__PURE__*/_interopDefault(React3);
41
- var THREE9__namespace = /*#__PURE__*/_interopNamespace(THREE9);
41
+ var THREE10__namespace = /*#__PURE__*/_interopNamespace(THREE10);
42
42
 
43
43
  var __defProp = Object.defineProperty;
44
44
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -189,6 +189,7 @@ function collectDrawnItems(nodes) {
189
189
  case "draw-shape":
190
190
  case "draw-text":
191
191
  case "draw-group":
192
+ case "draw-mesh":
192
193
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
193
194
  break;
194
195
  case "draw-sprite-layer":
@@ -514,7 +515,7 @@ function Lighting3D({
514
515
  "directionalLightHelper",
515
516
  {
516
517
  args: [
517
- new THREE9__namespace.DirectionalLight(directionalColor, directionalIntensity),
518
+ new THREE10__namespace.DirectionalLight(directionalColor, directionalIntensity),
518
519
  5
519
520
  ]
520
521
  }
@@ -541,8 +542,8 @@ function FollowCamera3D({
541
542
  offset
542
543
  }) {
543
544
  const { camera } = fiber.useThree();
544
- const look = React3.useRef(new THREE9__namespace.Vector3(target[0], target[1], target[2]));
545
- const goal = React3.useRef(new THREE9__namespace.Vector3());
545
+ const look = React3.useRef(new THREE10__namespace.Vector3(target[0], target[1], target[2]));
546
+ const goal = React3.useRef(new THREE10__namespace.Vector3());
546
547
  fiber.useFrame((_, delta) => {
547
548
  const t = Math.min(1, delta * 5);
548
549
  goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
@@ -553,6 +554,17 @@ function FollowCamera3D({
553
554
  return null;
554
555
  }
555
556
 
557
+ // lib/drawable/projector3d.ts
558
+ function create3DProjector(opts = {}) {
559
+ const cellSize = opts.cellSize ?? 1;
560
+ const offsetX = opts.offsetX ?? 0;
561
+ const offsetZ = opts.offsetZ ?? 0;
562
+ return {
563
+ cellSize,
564
+ toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
565
+ };
566
+ }
567
+
556
568
  // lib/atlasSlice.ts
557
569
  var atlasCache = /* @__PURE__ */ new Map();
558
570
  function isTilesheet(a) {
@@ -687,12 +699,12 @@ function ModelLoader({
687
699
  if (!loadedModel) return null;
688
700
  const cloned = SkeletonUtils.clone(loadedModel);
689
701
  cloned.updateMatrixWorld(true);
690
- const tintColor = tint ? new THREE9__namespace.Color(tint) : null;
702
+ const tintColor = tint ? new THREE10__namespace.Color(tint) : null;
691
703
  cloned.traverse((child) => {
692
- if (child instanceof THREE9__namespace.Mesh) {
704
+ if (child instanceof THREE10__namespace.Mesh) {
693
705
  child.castShadow = castShadow;
694
706
  child.receiveShadow = receiveShadow;
695
- if (tintColor && child.material instanceof THREE9__namespace.MeshStandardMaterial) {
707
+ if (tintColor && child.material instanceof THREE10__namespace.MeshStandardMaterial) {
696
708
  const mat = child.material.clone();
697
709
  mat.color.multiply(tintColor);
698
710
  child.material = mat;
@@ -701,7 +713,7 @@ function ModelLoader({
701
713
  });
702
714
  return cloned;
703
715
  }, [loadedModel, castShadow, receiveShadow, tint]);
704
- const mixer = React3.useMemo(() => model ? new THREE9__namespace.AnimationMixer(model) : null, [model]);
716
+ const mixer = React3.useMemo(() => model ? new THREE10__namespace.AnimationMixer(model) : null, [model]);
705
717
  React3.useEffect(() => {
706
718
  if (!mixer || !animation || clips.length === 0) return;
707
719
  const wanted = animation.toLowerCase();
@@ -718,8 +730,8 @@ function ModelLoader({
718
730
  });
719
731
  const normFactor = React3.useMemo(() => {
720
732
  if (!model) return 1;
721
- const box = new THREE9__namespace.Box3().setFromObject(model);
722
- const size = new THREE9__namespace.Vector3();
733
+ const box = new THREE10__namespace.Box3().setFromObject(model);
734
+ const size = new THREE10__namespace.Vector3();
723
735
  box.getSize(size);
724
736
  const maxDim = Math.max(size.x, size.y, size.z);
725
737
  if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
@@ -809,7 +821,7 @@ var warnUnsupported3d = (kind) => {
809
821
  warnedUnsupported.add(kind);
810
822
  mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
811
823
  };
812
- var CrossOriginTextureLoader = class extends THREE9__namespace.TextureLoader {
824
+ var CrossOriginTextureLoader = class extends THREE10__namespace.TextureLoader {
813
825
  constructor() {
814
826
  super();
815
827
  this.crossOrigin = "anonymous";
@@ -827,7 +839,7 @@ function useBillboardTexture(url) {
827
839
  url,
828
840
  (texture) => {
829
841
  if (!active) return;
830
- texture.colorSpace = THREE9__namespace.SRGBColorSpace;
842
+ texture.colorSpace = THREE10__namespace.SRGBColorSpace;
831
843
  setState({ texture, error: false });
832
844
  },
833
845
  void 0,
@@ -859,7 +871,7 @@ function useAtlasFrame(asset) {
859
871
  return { frame: { x: r.sx, y: r.sy, w: r.sw, h: r.sh }, ready: true };
860
872
  }, [asset, tick]);
861
873
  }
862
- function SpriteBillboard({ node, world, cellSize = 1 }) {
874
+ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
863
875
  const { texture, error: textureError } = useBillboardTexture(node.asset.url);
864
876
  const { frame: atlasFrame, ready: atlasReady } = useAtlasFrame(node.asset);
865
877
  const frame = node.frame ?? atlasFrame;
@@ -880,7 +892,7 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
880
892
  }, [texture, frame, node.height, node.width, anchor, cellSize]);
881
893
  const groundGeometry = React3__default.default.useMemo(() => {
882
894
  if (anchor !== "top-left" || !texture || !atlasReady) return null;
883
- const g = new THREE9__namespace.PlaneGeometry(size.width, size.height);
895
+ const g = new THREE10__namespace.PlaneGeometry(size.width, size.height);
884
896
  g.rotateX(-Math.PI / 2);
885
897
  return g;
886
898
  }, [anchor, texture, atlasReady, size.width, size.height]);
@@ -888,15 +900,15 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
888
900
  if (anchor === "top-left") {
889
901
  return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [world[0] + size.width / 2, 0.02, world[2] + size.height / 2], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [
890
902
  /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [size.width, size.height] }),
891
- /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE9__namespace.DoubleSide })
903
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10__namespace.DoubleSide })
892
904
  ] }) });
893
905
  }
894
906
  return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { children: [
895
907
  /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [size.width, size.height] }),
896
- /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE9__namespace.DoubleSide })
908
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10__namespace.DoubleSide })
897
909
  ] }) });
898
910
  }
899
- texture.magFilter = texture.minFilter = THREE9__namespace.NearestFilter;
911
+ texture.magFilter = texture.minFilter = THREE10__namespace.NearestFilter;
900
912
  texture.needsUpdate = true;
901
913
  if (frame) {
902
914
  texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
@@ -909,8 +921,8 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
909
921
  map: texture,
910
922
  transparent: true,
911
923
  alphaTest: 0.1,
912
- side: THREE9__namespace.DoubleSide,
913
- opacity: node.opacity ?? 1
924
+ side: THREE10__namespace.DoubleSide,
925
+ opacity: (node.opacity ?? 1) * groupOpacity
914
926
  }
915
927
  ) }) });
916
928
  }
@@ -922,13 +934,13 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
922
934
  map: texture,
923
935
  transparent: true,
924
936
  alphaTest: 0.1,
925
- side: THREE9__namespace.DoubleSide,
937
+ side: THREE10__namespace.DoubleSide,
926
938
  opacity: node.opacity ?? 1
927
939
  }
928
940
  )
929
941
  ] }) });
930
942
  }
931
- function Sprite3D({ node, projector }) {
943
+ function Sprite3D({ node, projector, groupOpacity = 1 }) {
932
944
  const asset = node.asset;
933
945
  if (!asset?.url || !isValidScenePos(node.position)) return null;
934
946
  if (asset.dimension === "3d") {
@@ -942,7 +954,7 @@ function Sprite3D({ node, projector }) {
942
954
  {
943
955
  url: asset.url,
944
956
  scale,
945
- rotation: [0, node.rotation ?? 0, 0],
957
+ rotation: [0, (node.rotation ?? 0) * 180 / Math.PI, 0],
946
958
  animation: node.animation,
947
959
  fallbackGeometry: "box",
948
960
  castShadow: true,
@@ -950,9 +962,9 @@ function Sprite3D({ node, projector }) {
950
962
  }
951
963
  ) });
952
964
  }
953
- return /* @__PURE__ */ jsxRuntime.jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize });
965
+ return /* @__PURE__ */ jsxRuntime.jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize, groupOpacity });
954
966
  }
955
- function Shape3D({ node, projector }) {
967
+ function Shape3D({ node, projector, groupOpacity = 1 }) {
956
968
  if (!isValidScenePos(node.position)) return null;
957
969
  const world = projector.toWorld(node.position);
958
970
  const color = node.fill ?? node.stroke ?? "#ffffff";
@@ -972,7 +984,7 @@ function Shape3D({ node, projector }) {
972
984
  }
973
985
  case "poly": {
974
986
  if (!node.points || node.points.length === 0) return null;
975
- const s = new THREE9__namespace.Shape();
987
+ const s = new THREE10__namespace.Shape();
976
988
  node.points.forEach((p, i) => {
977
989
  if (i === 0) s.moveTo(p.x, p.y);
978
990
  else s.lineTo(p.x, p.y);
@@ -990,7 +1002,7 @@ function Shape3D({ node, projector }) {
990
1002
  }
991
1003
  return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [world[0], world[1] + 0.02, world[2]], children: [
992
1004
  geometry,
993
- /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color, transparent: true, opacity: node.opacity ?? 1, side: THREE9__namespace.DoubleSide })
1005
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE10__namespace.DoubleSide })
994
1006
  ] });
995
1007
  }
996
1008
  function Text3D({ node, projector }) {
@@ -1009,37 +1021,246 @@ function Text3D({ node, projector }) {
1009
1021
  }
1010
1022
  ) });
1011
1023
  }
1012
- function Drawable3D({ node, projector }) {
1024
+ var MESH_SEGMENTS_DEFAULT = 24;
1025
+ function clampSegments(segments) {
1026
+ const s = segments ?? MESH_SEGMENTS_DEFAULT;
1027
+ return Math.min(64, Math.max(3, Math.round(s)));
1028
+ }
1029
+ function isAnimatedMesh(node) {
1030
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
1031
+ }
1032
+ var lerp = (a, b, k) => a + (b - a) * k;
1033
+ var NUMERIC_TRACKS = [
1034
+ "offsetX",
1035
+ "offsetY",
1036
+ "offsetZ",
1037
+ "rotateX",
1038
+ "rotateY",
1039
+ "rotateZ",
1040
+ "scale",
1041
+ "opacity",
1042
+ "emissiveIntensity"
1043
+ ];
1044
+ function applyMeshAnimation(node, timeMs) {
1045
+ const anim = node.animation;
1046
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
1047
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
1048
+ const cycle = timeMs / anim.durationMs;
1049
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
1050
+ const trackValue = (key) => {
1051
+ const defined = frames.filter((f) => f[key] !== void 0);
1052
+ if (defined.length === 0) return void 0;
1053
+ let prev;
1054
+ let next;
1055
+ for (const f of defined) {
1056
+ if (f.at <= t) prev = f;
1057
+ else if (!next) next = f;
1058
+ }
1059
+ if (!prev) return defined[0][key];
1060
+ if (!next) return prev[key];
1061
+ const span = next.at - prev.at;
1062
+ const k = span > 0 ? (t - prev.at) / span : 1;
1063
+ const a = prev[key];
1064
+ const b = next[key];
1065
+ if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
1066
+ return a;
1067
+ };
1068
+ const num = {};
1069
+ for (const key of NUMERIC_TRACKS) {
1070
+ const v = trackValue(key);
1071
+ if (v !== void 0) num[key] = v;
1072
+ }
1073
+ return {
1074
+ offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
1075
+ rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
1076
+ scale: num.scale ?? 1,
1077
+ opacity: num.opacity,
1078
+ emissiveIntensity: num.emissiveIntensity,
1079
+ color: trackValue("color"),
1080
+ emissive: trackValue("emissive")
1081
+ };
1082
+ }
1083
+ function meshGeometry(node, cellSize) {
1084
+ const seg = clampSegments(node.segments);
1085
+ const r = (node.radius ?? 0.4) * cellSize;
1086
+ const w = (node.width ?? 1) * cellSize;
1087
+ const h = (node.height ?? 1) * cellSize;
1088
+ const d = (node.depth ?? node.width ?? 1) * cellSize;
1089
+ switch (node.shape) {
1090
+ case "box":
1091
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [w, h, d] }), lift: h / 2, size: Math.max(w, h, d) };
1092
+ case "sphere":
1093
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [r, seg, seg] }), lift: r, size: r * 2 };
1094
+ case "capsule":
1095
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [r, h, Math.max(2, Math.round(seg / 2)), seg] }), lift: h / 2 + r, size: h + r * 2 };
1096
+ case "cylinder":
1097
+ return {
1098
+ element: /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [(node.radiusTop ?? node.radius ?? 0.4) * cellSize, (node.radiusBottom ?? node.radius ?? 0.4) * cellSize, h, seg] }),
1099
+ lift: h / 2,
1100
+ size: Math.max(h, r * 2)
1101
+ };
1102
+ case "cone":
1103
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [r, h, seg] }), lift: h / 2, size: Math.max(h, r * 2) };
1104
+ case "torus": {
1105
+ const tube = (node.tube ?? (node.radius ?? 0.4) / 3) * cellSize;
1106
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
1107
+ }
1108
+ case "plane":
1109
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d) };
1110
+ case "circle":
1111
+ return { element: /* @__PURE__ */ jsxRuntime.jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2 };
1112
+ default:
1113
+ return null;
1114
+ }
1115
+ }
1116
+ var SIDE_MAP = {
1117
+ front: THREE10__namespace.FrontSide,
1118
+ back: THREE10__namespace.BackSide,
1119
+ double: THREE10__namespace.DoubleSide
1120
+ };
1121
+ function meshMaterial(mat, opacity, ref) {
1122
+ const m = mat ?? {};
1123
+ const common = {
1124
+ ref,
1125
+ color: m.color ?? "#ffffff",
1126
+ transparent: opacity < 1,
1127
+ opacity,
1128
+ side: SIDE_MAP[m.side ?? "front"]
1129
+ };
1130
+ const emissive = m.emissive ? { emissive: m.emissive, emissiveIntensity: m.emissiveIntensity ?? 1 } : {};
1131
+ switch (m.kind ?? "standard") {
1132
+ case "basic":
1133
+ return /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { ...common });
1134
+ case "toon":
1135
+ return /* @__PURE__ */ jsxRuntime.jsx("meshToonMaterial", { ...common, ...emissive });
1136
+ case "physical":
1137
+ return /* @__PURE__ */ jsxRuntime.jsx(
1138
+ "meshPhysicalMaterial",
1139
+ {
1140
+ ...common,
1141
+ ...emissive,
1142
+ metalness: m.metalness ?? 0,
1143
+ roughness: m.roughness ?? 0.5,
1144
+ transmission: m.transmission ?? 0,
1145
+ ior: m.ior ?? 1.5,
1146
+ flatShading: m.flatShading ?? false
1147
+ }
1148
+ );
1149
+ case "standard":
1150
+ default:
1151
+ return /* @__PURE__ */ jsxRuntime.jsx(
1152
+ "meshStandardMaterial",
1153
+ {
1154
+ ...common,
1155
+ ...emissive,
1156
+ metalness: m.metalness ?? 0,
1157
+ roughness: m.roughness ?? 0.5,
1158
+ flatShading: m.flatShading ?? false
1159
+ }
1160
+ );
1161
+ }
1162
+ }
1163
+ function Mesh3D({
1164
+ node,
1165
+ projector,
1166
+ groupOpacity = 1
1167
+ }) {
1168
+ const groupRef = React3.useRef(null);
1169
+ const meshRef = React3.useRef(null);
1170
+ const materialRef = React3.useRef(null);
1171
+ const animated = isAnimatedMesh(node);
1172
+ const validPos = isValidScenePos(node.position);
1173
+ const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
1174
+ const geo = React3.useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
1175
+ fiber.useFrame(({ clock }) => {
1176
+ if (!animated || !groupRef.current) return;
1177
+ const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
1178
+ if (!state) return;
1179
+ const [wx, wy, wz] = baseWorld;
1180
+ groupRef.current.position.set(
1181
+ wx + state.offset[0] * projector.cellSize,
1182
+ wy + state.offset[2] * projector.cellSize,
1183
+ wz + state.offset[1] * projector.cellSize
1184
+ );
1185
+ groupRef.current.scale.setScalar(state.scale);
1186
+ if (meshRef.current) {
1187
+ const base = node.rotation ?? [0, 0, 0];
1188
+ meshRef.current.rotation.set(base[0] + state.rotate[0], base[1] + state.rotate[1], base[2] + state.rotate[2]);
1189
+ }
1190
+ const mat = materialRef.current;
1191
+ if (mat) {
1192
+ const effective = (state.opacity ?? node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
1193
+ mat.opacity = effective;
1194
+ mat.transparent = effective < 1;
1195
+ if (state.color && mat.color) mat.color.set(state.color);
1196
+ if (state.emissive && mat.emissive) mat.emissive.set(state.emissive);
1197
+ if (state.emissiveIntensity !== void 0 && mat.emissiveIntensity !== void 0) {
1198
+ mat.emissiveIntensity = state.emissiveIntensity;
1199
+ }
1200
+ }
1201
+ });
1202
+ if (!validPos || !geo) return null;
1203
+ const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
1204
+ const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
1205
+ const rotation = node.rotation ?? [0, 0, 0];
1206
+ const outlineScale = geo.size > 0 ? 1 + (node.outline?.width ?? 0.05) * projector.cellSize / geo.size : 1;
1207
+ return /* @__PURE__ */ jsxRuntime.jsxs("group", { ref: groupRef, position: baseWorld, children: [
1208
+ /* @__PURE__ */ jsxRuntime.jsxs(
1209
+ "mesh",
1210
+ {
1211
+ ref: meshRef,
1212
+ position: [0, lift, 0],
1213
+ rotation,
1214
+ castShadow: node.castShadow ?? true,
1215
+ receiveShadow: node.receiveShadow ?? true,
1216
+ children: [
1217
+ geo.element,
1218
+ meshMaterial(node.material, opacity, materialRef)
1219
+ ]
1220
+ }
1221
+ ),
1222
+ node.outline && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
1223
+ geo.element,
1224
+ /* @__PURE__ */ jsxRuntime.jsx(
1225
+ "meshBasicMaterial",
1226
+ {
1227
+ color: node.outline.color ?? "#101014",
1228
+ side: THREE10__namespace.BackSide,
1229
+ transparent: opacity < 1,
1230
+ opacity
1231
+ }
1232
+ )
1233
+ ] })
1234
+ ] });
1235
+ }
1236
+ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1013
1237
  switch (node.type) {
1014
1238
  case "draw-sprite":
1015
- return /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node, projector });
1239
+ return /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node, projector, groupOpacity });
1016
1240
  case "draw-shape":
1017
- return /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node, projector });
1241
+ return /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node, projector, groupOpacity });
1018
1242
  case "draw-text":
1019
1243
  return /* @__PURE__ */ jsxRuntime.jsx(Text3D, { node, projector });
1244
+ case "draw-mesh":
1245
+ return /* @__PURE__ */ jsxRuntime.jsx(Mesh3D, { node, projector, groupOpacity });
1020
1246
  case "draw-sprite-layer":
1021
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node: item, projector }, i)) });
1247
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node: item, projector, groupOpacity }, i)) });
1022
1248
  case "draw-shape-layer":
1023
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node: item, projector }, i)) });
1249
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node: item, projector, groupOpacity }, i)) });
1024
1250
  case "draw-text-layer":
1025
1251
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Text3D, { node: item, projector }, i)) });
1026
- case "draw-group":
1027
- warnUnsupported3d("draw-group");
1028
- return null;
1252
+ case "draw-group": {
1253
+ if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
1254
+ if (node.clip) warnUnsupported3d("draw-group:clip");
1255
+ const world = projector.toWorld(node.position);
1256
+ const inner = create3DProjector({ cellSize: projector.cellSize });
1257
+ const opacity = (node.opacity ?? 1) * groupOpacity;
1258
+ const s = node.scale ?? 1;
1259
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position: world, rotation: [0, -(node.rotate ?? 0), 0], scale: [s, s, s], children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Drawable3D, { node: item, projector: inner, groupOpacity: opacity }, i)) });
1260
+ }
1029
1261
  }
1030
1262
  }
1031
1263
 
1032
- // lib/drawable/projector3d.ts
1033
- function create3DProjector(opts = {}) {
1034
- const cellSize = opts.cellSize ?? 1;
1035
- const offsetX = opts.offsetX ?? 0;
1036
- const offsetZ = opts.offsetZ ?? 0;
1037
- return {
1038
- cellSize,
1039
- toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
1040
- };
1041
- }
1042
-
1043
1264
  // lib/drawable/three/game3dTheme.ts
1044
1265
  var GRID_COLORS_3D = {
1045
1266
  cell: "#444444",
@@ -1075,6 +1296,7 @@ var Canvas3DHost = React3.forwardRef(
1075
1296
  keyMap,
1076
1297
  keyUpMap,
1077
1298
  pixelsPerUnit,
1299
+ fov,
1078
1300
  children,
1079
1301
  drawables
1080
1302
  }, ref) => {
@@ -1164,7 +1386,7 @@ var Canvas3DHost = React3.forwardRef(
1164
1386
  getCameraPosition: () => {
1165
1387
  if (controlsRef.current) {
1166
1388
  const pos = controlsRef.current.object.position;
1167
- return new THREE9__namespace.Vector3(pos.x, pos.y, pos.z);
1389
+ return new THREE10__namespace.Vector3(pos.x, pos.y, pos.z);
1168
1390
  }
1169
1391
  return null;
1170
1392
  },
@@ -1203,18 +1425,19 @@ var Canvas3DHost = React3.forwardRef(
1203
1425
  const cx = cameraTarget[0];
1204
1426
  const cz = cameraTarget[2];
1205
1427
  const d = size * 1;
1428
+ const fovDeg = fov ?? 45;
1206
1429
  switch (cameraMode) {
1207
1430
  case "isometric":
1208
- return { position: [cx + d, d * 0.8, cz + d], fov: 45 };
1431
+ return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
1209
1432
  case "top-down":
1210
- return { position: [cx, d * 2, cz + d * 0.35], fov: 45 };
1433
+ return { position: [cx, d * 2, cz + d * 0.35], fov: fovDeg };
1211
1434
  case "follow":
1212
- return { position: [cx, d * 0.5, cz + d], fov: 45 };
1435
+ return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
1213
1436
  case "perspective":
1214
1437
  default:
1215
- return { position: [cx + d, d, cz + d], fov: 45 };
1438
+ return { position: [cx + d, d, cz + d], fov: fovDeg };
1216
1439
  }
1217
- }, [cameraMode, gridBounds, cellSize, cameraTarget]);
1440
+ }, [cameraMode, gridBounds, cellSize, cameraTarget, fov]);
1218
1441
  const followWorld = React3.useMemo(() => {
1219
1442
  if (followTarget) return drawableProjector.toWorld(followTarget);
1220
1443
  return cameraTarget;
@@ -1358,7 +1581,7 @@ var Canvas3DHost = React3.forwardRef(
1358
1581
  dampingFactor: 0.05,
1359
1582
  enableZoom: true,
1360
1583
  enablePan: true,
1361
- touches: { ONE: THREE9__namespace.TOUCH.ROTATE, TWO: THREE9__namespace.TOUCH.DOLLY_PAN },
1584
+ touches: { ONE: THREE10__namespace.TOUCH.ROTATE, TWO: THREE10__namespace.TOUCH.DOLLY_PAN },
1362
1585
  minDistance: 2,
1363
1586
  maxDistance: 100,
1364
1587
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -1381,15 +1604,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
1381
1604
  if (initializedRef.current) return;
1382
1605
  initializedRef.current = true;
1383
1606
  if (background.startsWith("#") || background.startsWith("rgb")) {
1384
- scene.background = new THREE9__namespace.Color(background);
1607
+ scene.background = new THREE10__namespace.Color(background);
1385
1608
  } else {
1386
- const loader = new THREE9__namespace.TextureLoader();
1609
+ const loader = new THREE10__namespace.TextureLoader();
1387
1610
  loader.load(background, (texture) => {
1388
1611
  scene.background = texture;
1389
1612
  });
1390
1613
  }
1391
1614
  if (fog) {
1392
- scene.fog = new THREE9__namespace.Fog(fog.color, fog.near, fog.far);
1615
+ scene.fog = new THREE10__namespace.Fog(fog.color, fog.near, fog.far);
1393
1616
  }
1394
1617
  return () => {
1395
1618
  scene.background = null;
@@ -1412,14 +1635,14 @@ var Camera3D = React3.forwardRef(
1412
1635
  }, ref) => {
1413
1636
  const { camera, set, viewport } = fiber.useThree();
1414
1637
  const controlsRef = React3.useRef(null);
1415
- const initialPosition = React3.useRef(new THREE9__namespace.Vector3(...position));
1416
- const initialTarget = React3.useRef(new THREE9__namespace.Vector3(...target));
1638
+ const initialPosition = React3.useRef(new THREE10__namespace.Vector3(...position));
1639
+ const initialTarget = React3.useRef(new THREE10__namespace.Vector3(...target));
1417
1640
  React3.useEffect(() => {
1418
1641
  let newCamera;
1419
1642
  if (mode === "isometric") {
1420
1643
  const aspect = viewport.aspect;
1421
1644
  const size = 10 / zoom;
1422
- newCamera = new THREE9__namespace.OrthographicCamera(
1645
+ newCamera = new THREE10__namespace.OrthographicCamera(
1423
1646
  -size * aspect,
1424
1647
  size * aspect,
1425
1648
  size,
@@ -1428,7 +1651,7 @@ var Camera3D = React3.forwardRef(
1428
1651
  1e3
1429
1652
  );
1430
1653
  } else {
1431
- newCamera = new THREE9__namespace.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1654
+ newCamera = new THREE10__namespace.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1432
1655
  }
1433
1656
  newCamera.position.copy(initialPosition.current);
1434
1657
  newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
@@ -1469,8 +1692,8 @@ var Camera3D = React3.forwardRef(
1469
1692
  }
1470
1693
  },
1471
1694
  getViewBounds: () => {
1472
- const min = new THREE9__namespace.Vector3(-10, -10, -10);
1473
- const max = new THREE9__namespace.Vector3(10, 10, 10);
1695
+ const min = new THREE10__namespace.Vector3(-10, -10, -10);
1696
+ const max = new THREE10__namespace.Vector3(10, 10, 10);
1474
1697
  return { min, max };
1475
1698
  }
1476
1699
  }));
@@ -1512,7 +1735,7 @@ var AssetLoader = class {
1512
1735
  __publicField(this, "textureCache");
1513
1736
  __publicField(this, "loadingPromises");
1514
1737
  this.objLoader = new OBJLoader_js.OBJLoader();
1515
- this.textureLoader = new THREE9__namespace.TextureLoader();
1738
+ this.textureLoader = new THREE10__namespace.TextureLoader();
1516
1739
  this.modelCache = /* @__PURE__ */ new Map();
1517
1740
  this.textureCache = /* @__PURE__ */ new Map();
1518
1741
  this.loadingPromises = /* @__PURE__ */ new Map();
@@ -1586,7 +1809,7 @@ var AssetLoader = class {
1586
1809
  return this.loadingPromises.get(`texture:${url}`);
1587
1810
  }
1588
1811
  const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
1589
- texture.colorSpace = THREE9__namespace.SRGBColorSpace;
1812
+ texture.colorSpace = THREE10__namespace.SRGBColorSpace;
1590
1813
  this.textureCache.set(url, texture);
1591
1814
  this.loadingPromises.delete(`texture:${url}`);
1592
1815
  return texture;
@@ -1660,7 +1883,7 @@ var AssetLoader = class {
1660
1883
  });
1661
1884
  this.modelCache.forEach((model) => {
1662
1885
  model.scene.traverse((child) => {
1663
- if (child instanceof THREE9__namespace.Mesh) {
1886
+ if (child instanceof THREE10__namespace.Mesh) {
1664
1887
  child.geometry.dispose();
1665
1888
  if (Array.isArray(child.material)) {
1666
1889
  child.material.forEach((m) => m.dispose());
@@ -1710,21 +1933,21 @@ function useThree4(options = {}) {
1710
1933
  const [isReady, setIsReady] = React3.useState(false);
1711
1934
  const [dimensions, setDimensions] = React3.useState({ width: 0, height: 0 });
1712
1935
  const initialCameraPosition = React3.useMemo(
1713
- () => new THREE9__namespace.Vector3(...opts.cameraPosition),
1936
+ () => new THREE10__namespace.Vector3(...opts.cameraPosition),
1714
1937
  []
1715
1938
  );
1716
1939
  React3.useEffect(() => {
1717
1940
  if (!containerRef.current) return;
1718
1941
  const container = containerRef.current;
1719
1942
  const { clientWidth, clientHeight } = container;
1720
- const scene = new THREE9__namespace.Scene();
1721
- scene.background = new THREE9__namespace.Color(opts.backgroundColor);
1943
+ const scene = new THREE10__namespace.Scene();
1944
+ scene.background = new THREE10__namespace.Color(opts.backgroundColor);
1722
1945
  sceneRef.current = scene;
1723
1946
  let camera;
1724
1947
  const aspect = clientWidth / clientHeight;
1725
1948
  if (opts.cameraMode === "isometric") {
1726
1949
  const size = 10;
1727
- camera = new THREE9__namespace.OrthographicCamera(
1950
+ camera = new THREE10__namespace.OrthographicCamera(
1728
1951
  -size * aspect,
1729
1952
  size * aspect,
1730
1953
  size,
@@ -1733,11 +1956,11 @@ function useThree4(options = {}) {
1733
1956
  1e3
1734
1957
  );
1735
1958
  } else {
1736
- camera = new THREE9__namespace.PerspectiveCamera(45, aspect, 0.1, 1e3);
1959
+ camera = new THREE10__namespace.PerspectiveCamera(45, aspect, 0.1, 1e3);
1737
1960
  }
1738
1961
  camera.position.copy(initialCameraPosition);
1739
1962
  cameraRef.current = camera;
1740
- const renderer = new THREE9__namespace.WebGLRenderer({
1963
+ const renderer = new THREE10__namespace.WebGLRenderer({
1741
1964
  antialias: true,
1742
1965
  alpha: true,
1743
1966
  canvas: canvasRef.current || void 0
@@ -1745,7 +1968,7 @@ function useThree4(options = {}) {
1745
1968
  renderer.setSize(clientWidth, clientHeight);
1746
1969
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
1747
1970
  renderer.shadowMap.enabled = opts.shadows;
1748
- renderer.shadowMap.type = THREE9__namespace.PCFSoftShadowMap;
1971
+ renderer.shadowMap.type = THREE10__namespace.PCFSoftShadowMap;
1749
1972
  rendererRef.current = renderer;
1750
1973
  const controls = new OrbitControls_js.OrbitControls(camera, renderer.domElement);
1751
1974
  controls.enableDamping = true;
@@ -1754,16 +1977,16 @@ function useThree4(options = {}) {
1754
1977
  controls.maxDistance = 100;
1755
1978
  controls.maxPolarAngle = Math.PI / 2 - 0.1;
1756
1979
  controlsRef.current = controls;
1757
- const ambientLight = new THREE9__namespace.AmbientLight(16777215, 0.6);
1980
+ const ambientLight = new THREE10__namespace.AmbientLight(16777215, 0.6);
1758
1981
  scene.add(ambientLight);
1759
- const directionalLight = new THREE9__namespace.DirectionalLight(16777215, 0.8);
1982
+ const directionalLight = new THREE10__namespace.DirectionalLight(16777215, 0.8);
1760
1983
  directionalLight.position.set(10, 20, 10);
1761
1984
  directionalLight.castShadow = opts.shadows;
1762
1985
  directionalLight.shadow.mapSize.width = 2048;
1763
1986
  directionalLight.shadow.mapSize.height = 2048;
1764
1987
  scene.add(directionalLight);
1765
1988
  if (opts.showGrid) {
1766
- const gridHelper = new THREE9__namespace.GridHelper(
1989
+ const gridHelper = new THREE10__namespace.GridHelper(
1767
1990
  opts.gridSize,
1768
1991
  opts.gridSize,
1769
1992
  4473924,
@@ -1781,10 +2004,10 @@ function useThree4(options = {}) {
1781
2004
  const handleResize = () => {
1782
2005
  const { clientWidth: width, clientHeight: height } = container;
1783
2006
  setDimensions({ width, height });
1784
- if (camera instanceof THREE9__namespace.PerspectiveCamera) {
2007
+ if (camera instanceof THREE10__namespace.PerspectiveCamera) {
1785
2008
  camera.aspect = width / height;
1786
2009
  camera.updateProjectionMatrix();
1787
- } else if (camera instanceof THREE9__namespace.OrthographicCamera) {
2010
+ } else if (camera instanceof THREE10__namespace.OrthographicCamera) {
1788
2011
  const aspect2 = width / height;
1789
2012
  const size = 10;
1790
2013
  camera.left = -size * aspect2;
@@ -1815,7 +2038,7 @@ function useThree4(options = {}) {
1815
2038
  let newCamera;
1816
2039
  if (opts.cameraMode === "isometric") {
1817
2040
  const size = 10;
1818
- newCamera = new THREE9__namespace.OrthographicCamera(
2041
+ newCamera = new THREE10__namespace.OrthographicCamera(
1819
2042
  -size * aspect,
1820
2043
  size * aspect,
1821
2044
  size,
@@ -1824,7 +2047,7 @@ function useThree4(options = {}) {
1824
2047
  1e3
1825
2048
  );
1826
2049
  } else {
1827
- newCamera = new THREE9__namespace.PerspectiveCamera(45, aspect, 0.1, 1e3);
2050
+ newCamera = new THREE10__namespace.PerspectiveCamera(45, aspect, 0.1, 1e3);
1828
2051
  }
1829
2052
  newCamera.position.copy(currentPos);
1830
2053
  cameraRef.current = newCamera;
@@ -2154,8 +2377,8 @@ function useSceneGraph() {
2154
2377
  }
2155
2378
  function useRaycaster(options) {
2156
2379
  const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
2157
- const raycaster = React3.useRef(new THREE9__namespace.Raycaster());
2158
- const mouse = React3.useRef(new THREE9__namespace.Vector2());
2380
+ const raycaster = React3.useRef(new THREE10__namespace.Raycaster());
2381
+ const mouse = React3.useRef(new THREE10__namespace.Vector2());
2159
2382
  const clientToNDC = React3.useCallback(
2160
2383
  (clientX, clientY) => {
2161
2384
  if (!canvas) {
@@ -2225,8 +2448,8 @@ function useRaycaster(options) {
2225
2448
  const ndc = clientToNDC(clientX, clientY);
2226
2449
  mouse.current.set(ndc.x, ndc.y);
2227
2450
  raycaster.current.setFromCamera(mouse.current, camera);
2228
- const plane = new THREE9__namespace.Plane(new THREE9__namespace.Vector3(0, 1, 0), 0);
2229
- const target = new THREE9__namespace.Vector3();
2451
+ const plane = new THREE10__namespace.Plane(new THREE10__namespace.Vector3(0, 1, 0), 0);
2452
+ const target = new THREE10__namespace.Vector3();
2230
2453
  const intersection = raycaster.current.ray.intersectPlane(plane, target);
2231
2454
  if (intersection) {
2232
2455
  const gridX = Math.round((target.x - offsetX) / cellSize);
@@ -2264,7 +2487,7 @@ function useRaycaster(options) {
2264
2487
  return {
2265
2488
  gridX: gridCoords.x,
2266
2489
  gridZ: gridCoords.z,
2267
- worldPosition: new THREE9__namespace.Vector3(
2490
+ worldPosition: new THREE10__namespace.Vector3(
2268
2491
  gridCoords.x * cellSize + offsetX,
2269
2492
  0,
2270
2493
  gridCoords.z * cellSize + offsetZ
@@ -2294,7 +2517,7 @@ var DEFAULT_CONFIG = {
2294
2517
  };
2295
2518
  function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
2296
2519
  const opts = { ...DEFAULT_CONFIG, ...config };
2297
- return new THREE9__namespace.Vector3(
2520
+ return new THREE10__namespace.Vector3(
2298
2521
  gridX * opts.cellSize + opts.offsetX,
2299
2522
  opts.elevation,
2300
2523
  gridZ * opts.cellSize + opts.offsetZ
@@ -2308,17 +2531,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
2308
2531
  };
2309
2532
  }
2310
2533
  function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
2311
- const raycaster = new THREE9__namespace.Raycaster();
2312
- const mouse = new THREE9__namespace.Vector2(mouseX, mouseY);
2534
+ const raycaster = new THREE10__namespace.Raycaster();
2535
+ const mouse = new THREE10__namespace.Vector2(mouseX, mouseY);
2313
2536
  raycaster.setFromCamera(mouse, camera);
2314
- const plane = new THREE9__namespace.Plane(new THREE9__namespace.Vector3(0, 1, 0), -planeY);
2315
- const target = new THREE9__namespace.Vector3();
2537
+ const plane = new THREE10__namespace.Plane(new THREE10__namespace.Vector3(0, 1, 0), -planeY);
2538
+ const target = new THREE10__namespace.Vector3();
2316
2539
  const intersection = raycaster.ray.intersectPlane(plane, target);
2317
2540
  return intersection ? target : null;
2318
2541
  }
2319
2542
  function raycastToObjects(camera, mouseX, mouseY, objects) {
2320
- const raycaster = new THREE9__namespace.Raycaster();
2321
- const mouse = new THREE9__namespace.Vector2(mouseX, mouseY);
2543
+ const raycaster = new THREE10__namespace.Raycaster();
2544
+ const mouse = new THREE10__namespace.Vector2(mouseX, mouseY);
2322
2545
  raycaster.setFromCamera(mouse, camera);
2323
2546
  const intersects = raycaster.intersectObjects(objects, true);
2324
2547
  return intersects.length > 0 ? intersects[0] : null;
@@ -2370,14 +2593,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
2370
2593
  return cells;
2371
2594
  }
2372
2595
  function createGridHighlight(color = 16776960, opacity = 0.3) {
2373
- const geometry = new THREE9__namespace.PlaneGeometry(0.95, 0.95);
2374
- const material = new THREE9__namespace.MeshBasicMaterial({
2596
+ const geometry = new THREE10__namespace.PlaneGeometry(0.95, 0.95);
2597
+ const material = new THREE10__namespace.MeshBasicMaterial({
2375
2598
  color,
2376
2599
  transparent: true,
2377
2600
  opacity,
2378
- side: THREE9__namespace.DoubleSide
2601
+ side: THREE10__namespace.DoubleSide
2379
2602
  });
2380
- const mesh = new THREE9__namespace.Mesh(geometry, material);
2603
+ const mesh = new THREE10__namespace.Mesh(geometry, material);
2381
2604
  mesh.rotation.x = -Math.PI / 2;
2382
2605
  mesh.position.y = 0.01;
2383
2606
  return mesh;
@@ -2390,31 +2613,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
2390
2613
  };
2391
2614
  }
2392
2615
  function isInFrustum(position, camera, padding = 0) {
2393
- const frustum = new THREE9__namespace.Frustum();
2394
- const projScreenMatrix = new THREE9__namespace.Matrix4();
2616
+ const frustum = new THREE10__namespace.Frustum();
2617
+ const projScreenMatrix = new THREE10__namespace.Matrix4();
2395
2618
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2396
2619
  frustum.setFromProjectionMatrix(projScreenMatrix);
2397
- const sphere = new THREE9__namespace.Sphere(position, padding);
2620
+ const sphere = new THREE10__namespace.Sphere(position, padding);
2398
2621
  return frustum.intersectsSphere(sphere);
2399
2622
  }
2400
2623
  function filterByFrustum(positions, camera, padding = 0) {
2401
- const frustum = new THREE9__namespace.Frustum();
2402
- const projScreenMatrix = new THREE9__namespace.Matrix4();
2624
+ const frustum = new THREE10__namespace.Frustum();
2625
+ const projScreenMatrix = new THREE10__namespace.Matrix4();
2403
2626
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2404
2627
  frustum.setFromProjectionMatrix(projScreenMatrix);
2405
2628
  return positions.filter((position) => {
2406
- const sphere = new THREE9__namespace.Sphere(position, padding);
2629
+ const sphere = new THREE10__namespace.Sphere(position, padding);
2407
2630
  return frustum.intersectsSphere(sphere);
2408
2631
  });
2409
2632
  }
2410
2633
  function getVisibleIndices(positions, camera, padding = 0) {
2411
- const frustum = new THREE9__namespace.Frustum();
2412
- const projScreenMatrix = new THREE9__namespace.Matrix4();
2634
+ const frustum = new THREE10__namespace.Frustum();
2635
+ const projScreenMatrix = new THREE10__namespace.Matrix4();
2413
2636
  const visible = /* @__PURE__ */ new Set();
2414
2637
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2415
2638
  frustum.setFromProjectionMatrix(projScreenMatrix);
2416
2639
  positions.forEach((position, index) => {
2417
- const sphere = new THREE9__namespace.Sphere(position, padding);
2640
+ const sphere = new THREE10__namespace.Sphere(position, padding);
2418
2641
  if (frustum.intersectsSphere(sphere)) {
2419
2642
  visible.add(index);
2420
2643
  }
@@ -2438,7 +2661,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
2438
2661
  return lodIndices;
2439
2662
  }
2440
2663
  function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
2441
- const dummy = new THREE9__namespace.Object3D();
2664
+ const dummy = new THREE10__namespace.Object3D();
2442
2665
  let visibleCount = 0;
2443
2666
  positions.forEach((position, index) => {
2444
2667
  if (visibleIndices.has(index)) {
@@ -3605,25 +3828,25 @@ function orbitRingPositions(count, radius, tilt) {
3605
3828
  return positions;
3606
3829
  }
3607
3830
  function arcCurve3D(from, to, offset) {
3608
- const start = new THREE9.Vector3(...from);
3609
- const end = new THREE9.Vector3(...to);
3610
- const mid = new THREE9.Vector3().addVectors(start, end).multiplyScalar(0.5);
3611
- const dir = new THREE9.Vector3().subVectors(end, start).normalize();
3612
- const up = new THREE9.Vector3(0, 1, 0);
3613
- const perp = new THREE9.Vector3().crossVectors(dir, up).normalize();
3831
+ const start = new THREE10.Vector3(...from);
3832
+ const end = new THREE10.Vector3(...to);
3833
+ const mid = new THREE10.Vector3().addVectors(start, end).multiplyScalar(0.5);
3834
+ const dir = new THREE10.Vector3().subVectors(end, start).normalize();
3835
+ const up = new THREE10.Vector3(0, 1, 0);
3836
+ const perp = new THREE10.Vector3().crossVectors(dir, up).normalize();
3614
3837
  if (perp.length() < 1e-3) {
3615
- perp.crossVectors(dir, new THREE9.Vector3(1, 0, 0)).normalize();
3838
+ perp.crossVectors(dir, new THREE10.Vector3(1, 0, 0)).normalize();
3616
3839
  }
3617
3840
  const control = mid.clone().add(perp.multiplyScalar(offset));
3618
3841
  control.y += Math.abs(offset) * 0.3;
3619
- return new THREE9.QuadraticBezierCurve3(start, control, end);
3842
+ return new THREE10.QuadraticBezierCurve3(start, control, end);
3620
3843
  }
3621
3844
  function selfLoopCurve3D(position, loopRadius) {
3622
- const base = new THREE9.Vector3(...position);
3623
- const start = base.clone().add(new THREE9.Vector3(-loopRadius * 0.3, 0, 0));
3624
- const end = base.clone().add(new THREE9.Vector3(loopRadius * 0.3, 0, 0));
3625
- const control = base.clone().add(new THREE9.Vector3(0, loopRadius, 0));
3626
- return new THREE9.QuadraticBezierCurve3(start, control, end);
3845
+ const base = new THREE10.Vector3(...position);
3846
+ const start = base.clone().add(new THREE10.Vector3(-loopRadius * 0.3, 0, 0));
3847
+ const end = base.clone().add(new THREE10.Vector3(loopRadius * 0.3, 0, 0));
3848
+ const control = base.clone().add(new THREE10.Vector3(0, loopRadius, 0));
3849
+ return new THREE10.QuadraticBezierCurve3(start, control, end);
3627
3850
  }
3628
3851
  function treeLayout3D(node, origin, horizontalSpacing, verticalSpacing = 2) {
3629
3852
  const results = [];
@@ -3790,7 +4013,7 @@ var Avl3DOrbitalNode = ({
3790
4013
  if (!groupRef.current) return;
3791
4014
  groupRef.current.rotation.y += delta * (hovered ? 0.4 : 0.2);
3792
4015
  groupRef.current.rotation.x += delta * 0.05;
3793
- currentScale.current = THREE9.MathUtils.damp(currentScale.current, targetScale, 6, delta);
4016
+ currentScale.current = THREE10.MathUtils.damp(currentScale.current, targetScale, 6, delta);
3794
4017
  });
3795
4018
  const baseBrightness = 0.3 + Math.min(pageCount, 5) * 0.05;
3796
4019
  const emissiveIntensity = hovered ? 0.8 : baseBrightness;
@@ -4432,7 +4655,7 @@ var Avl3DStateNode = ({
4432
4655
  const targetScale = hovered ? 1.08 : 1;
4433
4656
  const currentScale = React3.useRef(1);
4434
4657
  fiber.useFrame((_, delta) => {
4435
- currentScale.current = THREE9.MathUtils.damp(currentScale.current, targetScale, 6, delta);
4658
+ currentScale.current = THREE10.MathUtils.damp(currentScale.current, targetScale, 6, delta);
4436
4659
  });
4437
4660
  const scale = currentScale.current;
4438
4661
  return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
@@ -4612,9 +4835,9 @@ var Avl3DTransitionArc = ({
4612
4835
  const guardPt = curve.getPoint(0.3);
4613
4836
  const arrowPt = curve.getPoint(0.9);
4614
4837
  const arrowTangent = curve.getTangent(0.9);
4615
- const upVec = new THREE9.Vector3(0, 1, 0);
4616
- const tangentVec = new THREE9.Vector3(arrowTangent.x, arrowTangent.y, arrowTangent.z).normalize();
4617
- const quat = new THREE9.Quaternion().setFromUnitVectors(upVec, tangentVec);
4838
+ const upVec = new THREE10.Vector3(0, 1, 0);
4839
+ const tangentVec = new THREE10.Vector3(arrowTangent.x, arrowTangent.y, arrowTangent.z).normalize();
4840
+ const quat = new THREE10.Quaternion().setFromUnitVectors(upVec, tangentVec);
4618
4841
  const effectPositions2 = [];
4619
4842
  for (let ei = 0; ei < 4; ei++) {
4620
4843
  const t = 0.7 + ei * 0.05;
@@ -4838,14 +5061,14 @@ function operatorColor(label) {
4838
5061
  return "#4A90D9";
4839
5062
  }
4840
5063
  function edgeGeometry(parent, child) {
4841
- const p = new THREE9.Vector3(parent.x, parent.y, parent.z);
4842
- const c = new THREE9.Vector3(child.x, child.y, child.z);
4843
- const mid = new THREE9.Vector3().addVectors(p, c).multiplyScalar(0.5);
4844
- const dir = new THREE9.Vector3().subVectors(c, p);
5064
+ const p = new THREE10.Vector3(parent.x, parent.y, parent.z);
5065
+ const c = new THREE10.Vector3(child.x, child.y, child.z);
5066
+ const mid = new THREE10.Vector3().addVectors(p, c).multiplyScalar(0.5);
5067
+ const dir = new THREE10.Vector3().subVectors(c, p);
4845
5068
  const length = dir.length();
4846
- const yAxis = new THREE9.Vector3(0, 1, 0);
5069
+ const yAxis = new THREE10.Vector3(0, 1, 0);
4847
5070
  const angle = yAxis.angleTo(dir.normalize());
4848
- const axis = new THREE9.Vector3().crossVectors(yAxis, dir).normalize();
5071
+ const axis = new THREE10.Vector3().crossVectors(yAxis, dir).normalize();
4849
5072
  if (axis.length() < 1e-3) {
4850
5073
  return {
4851
5074
  position: [mid.x, mid.y, mid.z],
@@ -5221,12 +5444,12 @@ function useAvl3DConfig() {
5221
5444
  }
5222
5445
  function CameraController({ targetPosition, targetLookAt, animated }) {
5223
5446
  const { camera } = fiber.useThree();
5224
- const targetPosVec = React3.useRef(new THREE9__namespace.Vector3(...targetPosition));
5225
- const targetLookVec = React3.useRef(new THREE9__namespace.Vector3(...targetLookAt));
5447
+ const targetPosVec = React3.useRef(new THREE10__namespace.Vector3(...targetPosition));
5448
+ const targetLookVec = React3.useRef(new THREE10__namespace.Vector3(...targetLookAt));
5226
5449
  const isAnimating = React3.useRef(false);
5227
5450
  React3.useEffect(() => {
5228
- const newTarget = new THREE9__namespace.Vector3(...targetPosition);
5229
- const newLookAt = new THREE9__namespace.Vector3(...targetLookAt);
5451
+ const newTarget = new THREE10__namespace.Vector3(...targetPosition);
5452
+ const newLookAt = new THREE10__namespace.Vector3(...targetLookAt);
5230
5453
  if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
5231
5454
  targetPosVec.current.copy(newTarget);
5232
5455
  targetLookVec.current.copy(newLookAt);
@@ -5241,9 +5464,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
5241
5464
  fiber.useFrame((_, delta) => {
5242
5465
  if (!isAnimating.current) return;
5243
5466
  const speed = 3;
5244
- camera.position.x = THREE9__namespace.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
5245
- camera.position.y = THREE9__namespace.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
5246
- camera.position.z = THREE9__namespace.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
5467
+ camera.position.x = THREE10__namespace.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
5468
+ camera.position.y = THREE10__namespace.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
5469
+ camera.position.z = THREE10__namespace.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
5247
5470
  camera.lookAt(targetLookVec.current);
5248
5471
  const dist = camera.position.distanceTo(targetPosVec.current);
5249
5472
  if (dist < 0.05) {
@@ -5258,7 +5481,7 @@ function SceneFade({ animating, children }) {
5258
5481
  fiber.useFrame((_, delta) => {
5259
5482
  if (!groupRef.current) return;
5260
5483
  const target = animating ? 0 : 1;
5261
- opacityRef.current = THREE9__namespace.MathUtils.damp(opacityRef.current, target, 5, delta);
5484
+ opacityRef.current = THREE10__namespace.MathUtils.damp(opacityRef.current, target, 5, delta);
5262
5485
  groupRef.current.visible = opacityRef.current > 0.05;
5263
5486
  const s = 0.9 + opacityRef.current * 0.1;
5264
5487
  groupRef.current.scale.setScalar(s);