fix camera

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-10-23 12:44:33 -03:00
1 parent 204e9f8a39
commit 76ac1df116
3 files changed
+77 -1

No files matched your search

+48 -1
View File
@@ -59,11 +59,20 @@ function useClippingPlanes(): THREE.Plane[] {
}
// Ângulo polar "de pé, olhando na horizontal" (não de cima nem de baixo) —
// usado tanto pro enquadramento inicial quanto pelos botões Anterior/
// Posterior, pra sempre ancorar numa vista de frente/costas limpa em vez de
// um ângulo 3/4 arbitrário.
const EYE_LEVEL_POLAR = Math.PI / 2;
const ANTERIOR_AZIMUTH = 0;
const POSTERIOR_AZIMUTH = Math.PI;
function CameraController({ modelFiles }: { modelFiles: string[] }) {
const controlsRef = useRef<CameraControls>(null);
const { scene, camera } = useThree();
const focusRequest = useAtlasStore((s) => s.focusRequest);
const regionFocusRequest = useAtlasStore((s) => s.regionFocusRequest);
const viewAngleRequest = useAtlasStore((s) => s.viewAngleRequest);
const setContentBox = useAtlasStore((s) => s.setContentBox);
// focusRequest/regionFocusRequest moram no store GLOBAL (compartilhado
@@ -75,6 +84,13 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
// da câmera já existe.
const seenFocusNonce = useRef(focusRequest?.nonce);
const seenRegionNonce = useRef(regionFocusRequest?.nonce);
const seenViewAngleNonce = useRef(viewAngleRequest?.nonce);
// Último box enquadrado (corpo inteiro, região ou estrutura) — os botões
// Anterior/Posterior giram a câmera em torno DESSE enquadramento, não
// sempre do corpo inteiro, senão trocar de lado enquanto o usuário está
// olhando uma região específica (ex: "Pelve") chutaria o zoom pra fora.
const lastFitBoxRef = useRef<THREE.Box3 | null>(null);
// Malhas reais (Z-Anatomy) não seguem a mesma escala/origem que os
// modelos esquemáticos antigos (podem vir em cm, ou centralizadas em
@@ -122,7 +138,17 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
camera.near = Math.max(size / 1000, 0.001);
camera.far = size * 100;
if (camera instanceof THREE.PerspectiveCamera) camera.updateProjectionMatrix();
controlsRef.current.fitToBox(box, true, {
lastFitBoxRef.current = box.clone();
// fitToBox arredonda o ângulo ATUAL da câmera pro eixo mais próximo
// (frente/trás/lado/cima/baixo) em vez de manter um 3/4 arbitrário —
// na carga inicial isso dependia de quão perto a posição default
// [1.2, 1.4, 1.6] ficava de um múltiplo de 90°, o que podia (ex: após
// qualquer ajuste futuro nesse valor) arredondar pra uma vista de
// cima/baixo e enquadrar só os pés ou só a cabeça. Fixamos o ângulo
// explicitamente numa vista frontal, na altura dos olhos, ANTES do
// primeiro fitToBox, pra carga inicial ser sempre determinística.
controlsRef.current.rotateTo(ANTERIOR_AZIMUTH, EYE_LEVEL_POLAR, false);
controlsRef.current.fitToBox(box, false, {
paddingLeft: 0.2,
paddingRight: 0.2,
paddingTop: 0.2,
@@ -146,6 +172,7 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
if (!obj) return;
const box = computeVisibleBoundingBox(obj);
if (box.isEmpty()) return;
lastFitBoxRef.current = box.clone();
controlsRef.current.fitToBox(box, true, {
paddingLeft: 0.4,
paddingRight: 0.4,
@@ -162,6 +189,7 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
if (total.isEmpty()) return;
const box = regionSubBox(total, regionFocusRequest.region);
if (box.isEmpty()) return;
lastFitBoxRef.current = box.clone();
controlsRef.current.fitToBox(box, true, {
paddingLeft: 0.3,
paddingRight: 0.3,
@@ -170,6 +198,25 @@ function CameraController({ modelFiles }: { modelFiles: string[] }) {
});
}, [regionFocusRequest, scene]);
// Vista Anterior/Posterior — gira a câmera 180° em torno do último
// enquadramento (corpo inteiro, região ou estrutura), sem perder o zoom
// atual, pra comparar a face anterior e posterior de uma mesma parte.
useEffect(() => {
if (!viewAngleRequest || !controlsRef.current) return;
if (viewAngleRequest.nonce === seenViewAngleNonce.current) return;
seenViewAngleNonce.current = viewAngleRequest.nonce;
const box = lastFitBoxRef.current ?? computeVisibleBoundingBox(scene);
if (box.isEmpty()) return;
const azimuth = viewAngleRequest.angle === "anterior" ? ANTERIOR_AZIMUTH : POSTERIOR_AZIMUTH;
controlsRef.current.rotateTo(azimuth, EYE_LEVEL_POLAR, true);
controlsRef.current.fitToBox(box, true, {
paddingLeft: 0.25,
paddingRight: 0.25,
paddingTop: 0.25,
paddingBottom: 0.25,
});
}, [viewAngleRequest, scene]);
return (
<CameraControls
ref={controlsRef}