import { useEffect, useMemo, useState } from "react"; import { useSystemStructures } from "../hooks/useSystemStructures.js"; import { useAtlasStore } from "../store/useAtlasStore.js"; import { Scene } from "../components/viewer/Scene.js"; import { ExamMarkers } from "../components/viewer/ExamMarkers.js"; import { LayerToggle } from "../components/LayerToggle.js"; import { QuickRegionNav } from "../components/QuickRegionNav.js"; import { EXAM_POINTS, TECHNIQUES, type ExamTechnique, type ExamSide } from "../data/physicalExam.js"; // Módulo de estudo pro exame físico, sobre o corpo 3D REAL do Z-Anatomy // (mesmo visualizador do Atlas — camadas, navegação por região, câmera // tudo igual), com pontos clicáveis mostrando ONDE e COMO se faz cada // manobra (inspeção/palpação/percussão/ausculta). Reaproveita o mesmo // do Atlas (via prop "children", ver Scene.tsx) em vez de um corpo // geométrico à parte. // O esqueleto serve de referência pra posicionar os pontos de exame (ver // ExamMarkers.tsx) mesmo que o usuário não tenha marcado essa camada — // por isso é sempre buscado/carregado aqui, independente do toggle. const ALWAYS_LOAD = ["esqueletico"]; // Visualmente, um corpo com a pele/regiões de superfície faz muito mais // sentido pra exame físico do que o esqueleto nu (é na pele que se // ausculta/palpa/percute) — esse é o layer mostrado por padrão ao entrar // nesta página; o usuário pode trocar/adicionar outras camadas no painel. const DEFAULT_VISIBLE = ["regioes"]; export default function PhysicalExamPage() { const visibleSystems = useAtlasStore((s) => s.visibleSystems); const setVisibleSystems = useAtlasStore((s) => s.setVisibleSystems); const fetchSystems = useMemo( () => new Set([...visibleSystems, ...ALWAYS_LOAD]), [visibleSystems], ); const { bySlug } = useSystemStructures(fetchSystems); const selectedStructureId = useAtlasStore((s) => s.selectedStructureId); const [activeTechniques, setActiveTechniques] = useState>( () => new Set(TECHNIQUES.map((t) => t.key)), ); const [selectedId, setSelectedId] = useState(null); const [viewSide, setViewSide] = useState("anterior"); const [leftOpen, setLeftOpen] = useState(false); const [rightOpen, setRightOpen] = useState(false); // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => setVisibleSystems(DEFAULT_VISIBLE), []); useEffect(() => { if (selectedStructureId) { setRightOpen(true); setLeftOpen(false); } }, [selectedStructureId]); function toggleTechnique(key: ExamTechnique) { setActiveTechniques((prev) => { const next = new Set(prev); if (next.has(key)) next.delete(key); else next.add(key); return next; }); } const visiblePoints = useMemo( () => EXAM_POINTS.filter((p) => activeTechniques.has(p.technique)), [activeTechniques], ); const selected = EXAM_POINTS.find((p) => p.id === selectedId) ?? null; const techniqueColor = (t: ExamTechnique) => TECHNIQUES.find((x) => x.key === t)?.color ?? "#fff"; return (
{(leftOpen || rightOpen) && (
{ setLeftOpen(false); setRightOpen(false); }} /> )}
{ setSelectedId(id); setRightOpen(true); }} onSideChange={setViewSide} />
Vista: {viewSide === "anterior" ? "Anterior" : "Posterior"}
); }