fix exame fisico4

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-10 20:34:30 -03:00
1 parent 6b5adc3403
commit 43df670a55
2 files changed
+45 -16

No files matched your search

+31 -16
View File
@@ -45,14 +45,26 @@ const REGION_CANDIDATES: Record<string, string[]> = {
tibiaL: ["esqueletico-tibia-esquerdo"],
};
function unionFromSlugs(scene: THREE.Object3D, slugs: string[]): THREE.Box3 | null {
// Cada Box3.setFromObject percorre a geometria toda do osso — cacheamos o
// resultado por slug pra não recalcular a cada tentativa de retry (o
// esqueleto não se move, então o box de um osso já achado nunca muda).
function boxForSlug(scene: THREE.Object3D, slug: string, cache: Map<string, THREE.Box3>): THREE.Box3 | null {
const cached = cache.get(slug);
if (cached) return cached;
const obj = scene.getObjectByName(slug);
if (!obj) return null;
const box = new THREE.Box3().setFromObject(obj);
if (box.isEmpty()) return null;
cache.set(slug, box);
return box;
}
function unionFromSlugs(scene: THREE.Object3D, slugs: string[], cache: Map<string, THREE.Box3>): THREE.Box3 | null {
const box = new THREE.Box3();
let found = false;
for (const slug of slugs) {
const obj = scene.getObjectByName(slug);
if (!obj) continue;
const objBox = new THREE.Box3().setFromObject(obj);
if (objBox.isEmpty()) continue;
const objBox = boxForSlug(scene, slug, cache);
if (!objBox) continue;
box.union(objBox);
found = true;
}
@@ -61,10 +73,10 @@ function unionFromSlugs(scene: THREE.Object3D, slugs: string[]): THREE.Box3 | nu
type AnchorBoxes = Partial<Record<ExamAnchor, THREE.Box3>>;
function computeAnchorBoxes(scene: THREE.Object3D): AnchorBoxes {
const thorax = unionFromSlugs(scene, REGION_CANDIDATES.thorax);
const pelvis = unionFromSlugs(scene, REGION_CANDIDATES.pelvis);
const skull = unionFromSlugs(scene, REGION_CANDIDATES.skull);
function computeAnchorBoxes(scene: THREE.Object3D, cache: Map<string, THREE.Box3>): AnchorBoxes {
const thorax = unionFromSlugs(scene, REGION_CANDIDATES.thorax, cache);
const pelvis = unionFromSlugs(scene, REGION_CANDIDATES.pelvis, cache);
const skull = unionFromSlugs(scene, REGION_CANDIDATES.skull, cache);
const boxes: AnchorBoxes = {};
if (thorax) boxes.thorax = thorax;
@@ -96,7 +108,7 @@ function computeAnchorBoxes(scene: THREE.Object3D): AnchorBoxes {
}
for (const key of ["radiusR", "radiusL", "tibiaR", "tibiaL"] as const) {
const found = unionFromSlugs(scene, REGION_CANDIDATES[key]);
const found = unionFromSlugs(scene, REGION_CANDIDATES[key], cache);
if (found) boxes[key] = found;
}
@@ -131,16 +143,19 @@ export function ExamMarkers({
let cancelled = false;
let attempts = 0;
let timer: ReturnType<typeof setTimeout>;
const cache = new Map<string, THREE.Box3>();
function tryCompute() {
if (cancelled) return;
const boxes = computeAnchorBoxes(scene);
const boxes = computeAnchorBoxes(scene, cache);
setAnchors(boxes);
// "thorax" é o anchor mais usado (a maioria dos pontos cardio/resp
// dependem dele) — se ainda não achou, continua tentando (o glb do
// esqueleto pode ainda estar carregando); só desiste depois de 10s.
attempts += 1;
if (!boxes.thorax && attempts < 50) {
timer = setTimeout(tryCompute, 200);
// Só continua tentando o que ainda falta (thorax/pelvis/head) — uma
// vez achados, cada Box3 fica em cache e não é recalculado de novo,
// então parar assim que os 3 principais existirem evita trabalho
// repetido (percorrer geometria) rodando junto com o resto da cena.
const gotCore = boxes.thorax && boxes.pelvis && boxes.head;
if (!gotCore && attempts < 30) {
timer = setTimeout(tryCompute, 300);
} else if (!boxes.thorax) {
console.warn(
"ExamMarkers: não encontrou o tórax (esterno/costelas/vértebras) — os pontos vão usar o corpo inteiro como referência aproximada. Anchors encontrados:",
+14
View File
@@ -104,6 +104,16 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
const regionFocusRequest = useAtlasStore((s) => s.regionFocusRequest);
const setContentBox = useAtlasStore((s) => s.setContentBox);
// focusRequest/regionFocusRequest moram no store GLOBAL (compartilhado
// entre páginas) — se o usuário clicou "Cabeça" no Atlas e depois foi pro
// Exame Físico, um <Scene> novo montaria aqui e replayaria esse clique
// antigo, zoomando numa região errada sem o usuário ter clicado em nada
// nesta página. Guardamos o nonce já visto NA MONTAGEM (valor inicial do
// useRef) pra só reagir a cliques que aconteçam DEPOIS que esta instância
// da câmera já existe.
const seenFocusNonce = useRef(focusRequest?.nonce);
const seenRegionNonce = useRef(regionFocusRequest?.nonce);
// Malhas reais (Z-Anatomy) não seguem a mesma escala/origem que os
// modelos esquemáticos antigos (podem vir em cm, ou centralizadas em
// outro ponto) — em vez de assumir uma posição de câmera fixa, sempre
@@ -137,6 +147,8 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
useEffect(() => {
if (!focusRequest || !controlsRef.current) return;
if (focusRequest.nonce === seenFocusNonce.current) return;
seenFocusNonce.current = focusRequest.nonce;
const obj = scene.getObjectByName(focusRequest.slug);
if (!obj) return;
const box = computeVisibleBoundingBox(obj);
@@ -151,6 +163,8 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
useEffect(() => {
if (!regionFocusRequest || !controlsRef.current) return;
if (regionFocusRequest.nonce === seenRegionNonce.current) return;
seenRegionNonce.current = regionFocusRequest.nonce;
const total = computeVisibleBoundingBox(scene);
if (total.isEmpty()) return;
const box = regionSubBox(total, regionFocusRequest.region);