import { Suspense, useEffect, useMemo, useRef } from "react"; import { Canvas, useThree } from "@react-three/fiber"; import { CameraControls, Grid } from "@react-three/drei"; import * as THREE from "three"; import { useAtlasStore, type ClipAxis } from "../../store/useAtlasStore.js"; import { SystemModel } from "./SystemModel.js"; import type { Structure } from "../../lib/api.js"; const AXIS_CONFIG: Record = { sagittal: { normal: [1, 0, 0], min: -0.4, max: 0.4 }, coronal: { normal: [0, 0, 1], min: -0.3, max: 0.3 }, transverse: { normal: [0, -1, 0], min: -1.8, max: 0 }, }; function useClippingPlanes(): THREE.Plane[] { const { enabled, axis, position } = useAtlasStore((s) => s.clipping); return useMemo(() => { if (!enabled) return []; const cfg = AXIS_CONFIG[axis]; const t = (position + 1) / 2; // normalize -1..1 -> 0..1 const constant = cfg.min + t * (cfg.max - cfg.min); const normal = new THREE.Vector3(...cfg.normal); return [new THREE.Plane(normal, -constant)]; }, [enabled, axis, position]); } function FocusController() { const controlsRef = useRef(null); const { scene } = useThree(); const focusRequest = useAtlasStore((s) => s.focusRequest); useEffect(() => { if (!focusRequest || !controlsRef.current) return; const obj = scene.getObjectByName(focusRequest.slug); if (!obj) return; const box = new THREE.Box3().setFromObject(obj); if (box.isEmpty()) return; controlsRef.current.fitToBox(box, true, { paddingLeft: 0.4, paddingRight: 0.4, paddingTop: 0.4, paddingBottom: 0.4, }); }, [focusRequest, scene]); return ( ); } export function Scene({ bySlug }: { bySlug: Map }) { const visibleSystems = useAtlasStore((s) => s.visibleSystems); const selectedStructureId = useAtlasStore((s) => s.selectedStructureId); const selectStructure = useAtlasStore((s) => s.selectStructure); const clippingPlanes = useClippingPlanes(); const selectedSlug = selectedStructureId ? [...bySlug.values()].find((s) => s.id === selectedStructureId)?.slug ?? null : null; const modelFiles = useMemo( () => [...visibleSystems].map((slug) => `${slug}.glb`), [visibleSystems], ); return ( {modelFiles.map((modelFile) => ( { const structure = bySlug.get(slug); if (structure) selectStructure(structure.id); }} /> ))} ); }