From 7e8834fbfd0678233e67ea7c68f1cdd96b58216f Mon Sep 17 00:00:00 2001 From: Rodrigo Bertoldo de Sousa Martins Date: Thu, 10 Sep 2026 21:49:26 -0300 Subject: [PATCH] fix exame fisico --- frontend/src/components/viewer/Scene.tsx | 45 ++++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/viewer/Scene.tsx b/frontend/src/components/viewer/Scene.tsx index 74f5324..056e6fc 100644 --- a/frontend/src/components/viewer/Scene.tsx +++ b/frontend/src/components/viewer/Scene.tsx @@ -119,30 +119,61 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) { // outro ponto) — em vez de assumir uma posição de câmera fixa, sempre // que o conjunto de arquivos carregados muda, enquadra a câmera no // bounding box real de tudo que está na cena. + // + // O carregamento do .glb é assíncrono (Suspense) e pode levar bem mais + // que 1 frame — um único requestAnimationFrame às vezes media a cena + // ainda vazia/parcial (só 1-2 ossos montados) e nunca corrige depois, + // porque "modelFiles" não muda de novo uma vez carregado. Por isso agora + // fica tentando de novo a cada 200ms até o tamanho do conteúdo ficar + // ESTÁVEL (duas leituras seguidas iguais = terminou de montar), só então + // enquadra a câmera. useEffect(() => { - if (!controlsRef.current || modelFiles.length === 0) return; - const id = requestAnimationFrame(() => { + if (modelFiles.length === 0) return; + let cancelled = false; + let attempts = 0; + let lastSize = -1; + let timer: ReturnType; + + function tryFit() { + if (cancelled || !controlsRef.current) return; const box = computeVisibleBoundingBox(scene); - if (box.isEmpty()) return; + attempts += 1; + if (box.isEmpty()) { + if (attempts < 50) timer = setTimeout(tryFit, 200); + return; + } + const size = box.getSize(new THREE.Vector3()).length(); + const stable = Math.abs(size - lastSize) < lastSize * 0.01; + lastSize = size; + if (!stable && attempts < 50) { + timer = setTimeout(tryFit, 200); + return; + } + setContentBox({ min: [box.min.x, box.min.y, box.min.z], max: [box.max.x, box.max.y, box.max.z], }); - const size = box.getSize(new THREE.Vector3()).length(); // Ajusta os planos de corte da câmera ao tamanho real do conteúdo — // um "far" fixo pequeno cortaria (tornaria invisível) um modelo bem // maior do que o esperado. camera.near = Math.max(size / 1000, 0.001); camera.far = size * 100; if (camera instanceof THREE.PerspectiveCamera) camera.updateProjectionMatrix(); - controlsRef.current?.fitToBox(box, true, { + controlsRef.current.fitToBox(box, true, { paddingLeft: 0.2, paddingRight: 0.2, paddingTop: 0.2, paddingBottom: 0.2, }); - }); - return () => cancelAnimationFrame(id); + } + + const id = requestAnimationFrame(tryFit); + return () => { + cancelled = true; + cancelAnimationFrame(id); + clearTimeout(timer); + }; }, [modelFiles, scene, camera, setContentBox]); useEffect(() => {