fix exame fisico4
This commit is contained in:
1 parent
6b5adc3403
commit
43df670a55
2 files changed
+45
-16
No files matched your search
@@ -45,14 +45,26 @@ const REGION_CANDIDATES: Record<string, string[]> = {
|
|||||||
tibiaL: ["esqueletico-tibia-esquerdo"],
|
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();
|
const box = new THREE.Box3();
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const slug of slugs) {
|
for (const slug of slugs) {
|
||||||
const obj = scene.getObjectByName(slug);
|
const objBox = boxForSlug(scene, slug, cache);
|
||||||
if (!obj) continue;
|
if (!objBox) continue;
|
||||||
const objBox = new THREE.Box3().setFromObject(obj);
|
|
||||||
if (objBox.isEmpty()) continue;
|
|
||||||
box.union(objBox);
|
box.union(objBox);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@@ -61,10 +73,10 @@ function unionFromSlugs(scene: THREE.Object3D, slugs: string[]): THREE.Box3 | nu
|
|||||||
|
|
||||||
type AnchorBoxes = Partial<Record<ExamAnchor, THREE.Box3>>;
|
type AnchorBoxes = Partial<Record<ExamAnchor, THREE.Box3>>;
|
||||||
|
|
||||||
function computeAnchorBoxes(scene: THREE.Object3D): AnchorBoxes {
|
function computeAnchorBoxes(scene: THREE.Object3D, cache: Map<string, THREE.Box3>): AnchorBoxes {
|
||||||
const thorax = unionFromSlugs(scene, REGION_CANDIDATES.thorax);
|
const thorax = unionFromSlugs(scene, REGION_CANDIDATES.thorax, cache);
|
||||||
const pelvis = unionFromSlugs(scene, REGION_CANDIDATES.pelvis);
|
const pelvis = unionFromSlugs(scene, REGION_CANDIDATES.pelvis, cache);
|
||||||
const skull = unionFromSlugs(scene, REGION_CANDIDATES.skull);
|
const skull = unionFromSlugs(scene, REGION_CANDIDATES.skull, cache);
|
||||||
|
|
||||||
const boxes: AnchorBoxes = {};
|
const boxes: AnchorBoxes = {};
|
||||||
if (thorax) boxes.thorax = thorax;
|
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) {
|
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;
|
if (found) boxes[key] = found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,16 +143,19 @@ export function ExamMarkers({
|
|||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
let timer: ReturnType<typeof setTimeout>;
|
let timer: ReturnType<typeof setTimeout>;
|
||||||
|
const cache = new Map<string, THREE.Box3>();
|
||||||
function tryCompute() {
|
function tryCompute() {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
const boxes = computeAnchorBoxes(scene);
|
const boxes = computeAnchorBoxes(scene, cache);
|
||||||
setAnchors(boxes);
|
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;
|
attempts += 1;
|
||||||
if (!boxes.thorax && attempts < 50) {
|
// Só continua tentando o que ainda falta (thorax/pelvis/head) — uma
|
||||||
timer = setTimeout(tryCompute, 200);
|
// 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) {
|
} else if (!boxes.thorax) {
|
||||||
console.warn(
|
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:",
|
"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:",
|
||||||
|
|||||||
@@ -104,6 +104,16 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
|
|||||||
const regionFocusRequest = useAtlasStore((s) => s.regionFocusRequest);
|
const regionFocusRequest = useAtlasStore((s) => s.regionFocusRequest);
|
||||||
const setContentBox = useAtlasStore((s) => s.setContentBox);
|
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
|
// Malhas reais (Z-Anatomy) não seguem a mesma escala/origem que os
|
||||||
// modelos esquemáticos antigos (podem vir em cm, ou centralizadas em
|
// 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
|
// outro ponto) — em vez de assumir uma posição de câmera fixa, sempre
|
||||||
@@ -137,6 +147,8 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!focusRequest || !controlsRef.current) return;
|
if (!focusRequest || !controlsRef.current) return;
|
||||||
|
if (focusRequest.nonce === seenFocusNonce.current) return;
|
||||||
|
seenFocusNonce.current = focusRequest.nonce;
|
||||||
const obj = scene.getObjectByName(focusRequest.slug);
|
const obj = scene.getObjectByName(focusRequest.slug);
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
const box = computeVisibleBoundingBox(obj);
|
const box = computeVisibleBoundingBox(obj);
|
||||||
@@ -151,6 +163,8 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!regionFocusRequest || !controlsRef.current) return;
|
if (!regionFocusRequest || !controlsRef.current) return;
|
||||||
|
if (regionFocusRequest.nonce === seenRegionNonce.current) return;
|
||||||
|
seenRegionNonce.current = regionFocusRequest.nonce;
|
||||||
const total = computeVisibleBoundingBox(scene);
|
const total = computeVisibleBoundingBox(scene);
|
||||||
if (total.isEmpty()) return;
|
if (total.isEmpty()) return;
|
||||||
const box = regionSubBox(total, regionFocusRequest.region);
|
const box = regionSubBox(total, regionFocusRequest.region);
|
||||||
|
|||||||
Reference in new issue
Block a user