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"],
|
||||
};
|
||||
|
||||
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:",
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user