diff --git a/.lovable/plan.md b/.lovable/plan.md index 15f5a1c..46d731b 100644 --- a/.lovable/plan.md +++ b/.lovable/plan.md @@ -1,81 +1,42 @@ + ## Objetivo -Alinhar o comportamento dos controles Touch Plus (Meta Quest 3) ao padrão pedido: +Adicionar alternância entre câmera **Perspectiva** e **Ortográfica** no viewer principal, com **Ortográfica como padrão**, e tornar a calibração mais confiável usando a vista ortogonal (sem distorção de perspectiva, normais alinhadas com os eixos da tela). -- **Grip** = único responsável por mover a peça (pan, zoom, orbit). -- **Trigger** = apenas seleção, medição e ações pontuais (nunca move/rotaciona a peça). -- **Thumbstick** = navegação do usuário (teletransporte/recuar/girar a câmera), nunca mais ajuste fino do modelo. -- **Botão Menu** = mantém comportamento nativo (não tocar). +## Por que isso resolve a calibração -## Mapeamento alvo +Em perspectiva, ao clicar numa face "de frente", a normal capturada pelo raycaster é a normal real do triângulo, mas o usuário tende a clicar em faces inclinadas achando que estão alinhadas — o que gera pares de vetores ruins. Em **ortográfica + vista alinhada ao cubo (FRENTE/TOPO/DIR)**, a face que aparece "de frente" é geometricamente perpendicular à câmera, então a chance de capturar a normal certa é muito maior. Também damos uma dica visual (snap angular) para validar o clique. -```text -Mão Botão Ação -───────────────────────────────────────────────────────── -Esquerda/Direita Grip (1 mão) Pan da peça (translação 6DOF, sem rotação) -Ambas as mãos Grip + afastar Zoom uniforme (escala) -Ambas as mãos Grip + giro oposto Orbitar/rotacionar a peça -Direita Trigger Selecionar componente / adicionar ponto de medida -Esquerda Trigger Alternar snap de vértice (já existe) -Direita Botão A Desfazer última medida (já existe) -Direita Botão B Limpar medidas (já existe) -Thumbstick Frente + soltar Teletransporte para o ponto apontado -Thumbstick Lados Snap-rotate da câmera (±30°) -Thumbstick Trás Recuar o usuário (passo) -``` +## Mudanças -## Mudanças no código +### 1. Toggle de câmera no viewer (`ModelViewer.tsx` + `ViewerControls.tsx`) +- Estado `cameraMode: 'ortho' | 'persp'` no `useModelStore` (default `'ortho'`, persistido em localStorage junto com as demais prefs visuais). +- Em `ModelViewer.tsx`, condicionar `` / `` (drei) como `makeDefault`, compartilhando posição/target com o `OrbitControls`. Ao trocar de modo, copiar `position`, `target` e calcular `zoom` ortográfico a partir da distância atual para evitar "pulo" visual. +- `OrbitControls` permanece, apenas com `enableRotate` igual; em ortho mantemos rotação livre (estilo CAD). Pan/zoom continuam funcionando. +- Botão de alternância adicionado em `ViewerControls.tsx` ao lado dos controles de render (Sólido / Bordas), com ícones (ex.: `Box` para perspectiva, `Square` para ortho) e tooltip. Atalho de teclado opcional `O`/`P`. -### 1. `src/components/three/XRGrabbable.tsx` +### 2. ViewCube já existente +- Continua funcionando igual, mas agora ao clicar numa face em modo ortográfico o resultado é uma vista verdadeiramente axial (sem foreshortening), o que é o comportamento esperado de CAD. -- **One-hand grip**: passar a aplicar **apenas translação** (pan). Hoje aplica matriz completa do grip → leva rotação junto. Vou capturar o offset de posição e atualizar só `group.position`, mantendo `quaternion` e `scale`. -- **Two-hand grip**: manter rotação pelo eixo entre as mãos + **forçar `allowScale = true` sempre** (zoom passa a vir do grip, não depende mais do toggle do HUD). -- Remover a prop `allowScale` da assinatura (ou ignorar e tratar como `true`). +### 3. Calibração refinada (`ViewCube.tsx` + `viewCubeBus.ts`) +- Ao iniciar a calibração, **forçar modo ortográfico** automaticamente (salvando o modo anterior para restaurar ao concluir/cancelar). +- Em cada passo "await-cube-N", após o usuário clicar a face do cubo, **animar a câmera principal** para essa vista (já temos `requestView`) — assim a próxima clique na peça é feito olhando a face de frente. +- Adicionar **snap de normal**: ao capturar a normal da face da peça, se ela estiver a <10° de um eixo principal (X/Y/Z), arredondar para esse eixo. Isso elimina ruído de faces ligeiramente chanfradas. +- Mostrar no hint: "olhando para FRENTE — clique na face frontal da peça" (texto dinâmico do passo). +- Manter o fluxo de 4 cliques + verificação opcional já aprovado. -### 2. `src/pages/XRSession.tsx` +### 4. Persistência +- `cameraMode` salvo globalmente (preferência do app, não por peça). +- `calibrationQuat` por peça continua como já está. -- Remover o estado `allowScale` e os toggles correspondentes nas duas instâncias de ``. -- Substituir `` por um novo `` (ver item 4) — o joystick não deve mais editar `fineTuning` do modelo. -- Garantir que `XRHitTestPlacement` só consome `onSelect` (trigger) quando `placementMode === true`; fora disso o trigger é roteado integralmente para `XRControllerMeasure`. +## Arquivos afetados -### 3. `src/components/XRHud.tsx` e `src/components/three/XRHudInWorld.tsx` +- `src/stores/useModelStore.ts` — campo `cameraMode` + setter. +- `src/components/three/ModelViewer.tsx` — alternância de câmera, sincronização de posição/zoom. +- `src/components/ViewerControls.tsx` — botão de alternância Persp/Ortho. +- `src/components/ViewCube.tsx` — força ortho durante calibração, anima vista após cada cube-click. +- `src/components/three/viewCubeBus.ts` — snap angular da normal capturada. -- Remover botão/toggle “Permitir escala” (`allowScale`) — agora é sempre via grip duplo. -- Remover/relabelar o toggle “Movimento livre” (`freeMove`) já que o joystick deixa de ajustar o modelo; manter apenas se ainda fizer sentido para outra função (caso não, remover prop também de `XRSession.tsx`). +## Pergunta -### 4. Novo `src/components/three/ControllerLocomotion.tsx` - -Componente que substitui `ControllerFineTuning`. Lê `useXR().session` e implementa: - -- **Joystick esquerdo (ou ambos, configurável)**: - - `axes[1] < -0.7` mantido por ~200 ms → dispara teleport: raycast a partir do controle direito contra `scene.children` no plano horizontal mais próximo; aplica `xrOrigin.position` para reposicionar. - - `axes[1] > 0.7` → recua o `XROrigin` em passos de 0.3 m por trigger. - - `axes[0]` cruzando ±0.7 (com histerese) → snap-rotate de ±30° no `XROrigin.rotation.y`. -- Usar a API `XROrigin` já montada em `XRSession.tsx` (referência via `useRef` ou store) para escrever pose. -- Desligar tudo se `lockedActive` (modelo travado) — locomoção do usuário ainda funciona, só não move o modelo. - -### 5. `src/components/three/XRControllerMeasure.tsx` - -- Sem mudanças funcionais — já usa apenas trigger e botões A/B. Apenas confirmar que o early-return `(!measureMode && !selectionMode)` continua: quando nenhum dos modos está ativo, o trigger não dispara nada relacionado à medição (e não interfere em `XRHitTestPlacement`, que respeita `placementMode`). - -### 6. `src/components/three/XRHitTestPlacement.tsx` - -- Garantir que o handler `onSelect` (trigger) verifica `placementMode` antes de colocar o modelo. Hoje o tap-plane invisível pode estar capturando o select mesmo fora do placement — adicionar guarda explícita e `visible={placementMode}`. - -## Validação - -1. **Desktop SimXR** (devkit): pressionar Q/W (grip simulado) move a peça; pressionar A/S (trigger simulado) não muda pose nenhuma. -2. **Quest 3 real (passthrough)**: - - Grip único arrasta a peça sem rotacionar. - - Dois grips afastando = zoom; dois grips em rotação oposta = orbit. - - Trigger direito fora dos modos medir/selecionar/colocar = nada acontece. - - Trigger direito em modo Selecionar = seleciona componente IFC. - - Trigger direito em modo Medir = adiciona ponto com snap. - - Joystick esquerdo para frente = teletransporte; lados = snap-rotate; trás = recua. -3. Logs `[XR][grab]` continuam mostrando início/fim de one-hand e two-hand. - -## Fora do escopo - -- Não mexer no botão Menu (Universal Menu do Quest é nativo). -- Não alterar handlers DOM/mouse do modo desktop (Viewer). -- Não tocar em RLS, store de dados ou backend. +Te parece bom seguir com **ortográfica como default** e **forçar ortho durante calibração** com snap automático nas normais? Ou prefere deixar o usuário trocar manualmente e a calibração apenas "sugerir" o modo ortho via toast?