Files
SteelXR2/.lovable/plan.md
T
gpt-engineer-app[bot] 6daf7a76f3 Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
2026-05-21 12:54:31 +00:00

5.4 KiB

Objetivo

Tornar a ferramenta Medir confiável tanto no desktop (mouse) quanto no modo AR (controles Touch Plus), com snap de pontos a vértices/arestas, marcadores visíveis em qualquer escala e labels com texto sempre legível.

Problemas identificados

  1. Mouse não cria pontos: o MeasureClickHandler usa o evento DOM click no canvas, que conflita com o onClick interno do R3F no <group> do GLBModel (setActive). O stopPropagation do R3F não pára o evento DOM, mas o caminho que chega lá ignora se o mouse arrastou, e o pointerdown global de PositionDragHandler (que está sempre montado) chama setPointerCapture quando positionMode está ligado — em modo Medir não captura, mas o handler click puro é frágil. Vamos trocar por pointerdown/up próprios com gate de "não foi arrasto".
  2. Sem feedback de "ponto A pendente" claro no AR — o ponto fica pequeno demais quando escala é 1:50.
  3. Tamanho dos marcadores e do texto é fixo em unidades de mundo (sphereGeometry [0.003], fontSize 0.008), então some quando a peça está em 1:1 grande ou fica enorme em 1:100.
  4. AR só tem gatilho — sem desfazer último ponto, sem limpar, sem indicação visual de "armando 2º ponto".
  5. Snap de vértice só funciona em desktop (SmartSnapHandler lê mouse DOM). No AR não há snap.

O que vai ser construído

A) Mouse (desktop)

  • Substituir MeasureClickHandler por handler pointerdownpointerup no canvas:
    • Marca posição em pointerdown; se em pointerup o mouse moveu < 4 px e measureMode está on → adiciona ponto.
    • Usa snapPoint se houver; senão raycast.
    • Ignora botão direito (reservado para OrbitControls eventual no futuro).
  • Cursor crosshair apenas quando measureMode.
  • Adicionar tecla Esc para cancelar 1º ponto pendente, tecla Z (ou Ctrl+Z) para desfazer última medição.

B) Snap inteligente compartilhado

  • Atualmente SmartSnapHandler (vértices) e HoverDetector (furo/aresta) só funcionam pelo mouse. Vamos:
    • Manter o snap de vértice via findNearestVertex (já existe em SmartMeasure.ts).
    • Adicionar snap a aresta mais próxima (já há findNearestEdgeSegment) como fallback quando não há vértice perto — prioridade: vértice > aresta > ponto bruto do raycast.
    • Expor uma função utilitária resolveSnap(hitPoint, hitObject, camera, canvasSize) reutilizada pelo mouse e pelo AR.

C) AR (joysticks)

Reescrever XRControllerMeasure.tsx para:

  • Gatilho direito (trigger): adiciona ponto (com snap).
  • Botão A (gamepad button 4) direito: desfaz último ponto / última medição.
  • Botão B (gamepad button 5) direito: limpa todas as medições.
  • Gatilho esquerdo: alterna snap to vertex ON/OFF (com hysteresis para evitar toggling) — útil em peças orgânicas.
  • Mira laser visível: render de uma linha fina saindo do controle direito até o primeiro hit, com uma esfera no ponto que será marcado (verde se snap, amarelo se ponto bruto). Tamanho proporcional à distância do controle ao alvo.
  • Hysteresis já existe para o gatilho; aplicar o mesmo para A/B.

D) Marcadores e labels proporcionais

  • PointMarker: raio passa a ser função da distância da câmera ao ponto, calculado em useFrame via setScalar no mesh — alvo: ~6 px de raio na tela em qualquer escala/zoom.
  • Label <Html distanceFactor={...}>: substituir o distanceFactor={1} fixo por um valor calculado a partir da distância câmera↔ponto (mantém o texto com ~12 px na tela).
  • Linha verde da medição: usar Line do drei com lineWidth constante em pixels (já é o comportamento padrão).
  • No AR usar <Text> do @react-three/drei (já importado em XRHudInWorld) em vez de <Html> para os labels — render correto em XR.

E) Botões de UI

  • Desktop (ViewerControls): adicionar botão "Desfazer ponto" ao lado do "Limpar medições" quando measurePoints.length === 1.
  • AR HUD (XRHudInWorld): já existe "✕ Medidas". Adicionar legenda visual abaixo: "Gatilho R: marcar · A: desfazer · B: limpar · Gatilho L: snap".

Arquivos afetados

Arquivo Mudança
src/components/three/ModelViewer.tsx Reescrever MeasureClickHandler (pointerdown/up + gate de arrasto); PointMarker e MeasurementOverlay com tamanho/distanceFactor dinâmicos por frame
src/components/three/SmartMeasure.ts Adicionar helper resolveSnap() que combina vértice + aresta
src/components/three/XRControllerMeasure.tsx Adicionar handlers A/B + gatilho esquerdo, render de laser+esfera, snap via raycast no frame XR
src/components/three/XRHudInWorld.tsx Legenda de atalhos dos controles na aba Tools
src/components/ViewerControls.tsx Botão "Desfazer ponto"; atalhos Esc/Z
src/stores/useModelStore.ts Action undoLastMeasurePoint() (e undoLastMeasurement() se nenhum ponto pendente)

Critérios de aceite

  • Clicar em uma peça com Medir ON cria marcador amarelo; segundo clique fecha e gera label verde em mm reais (já compensado por escala — lógica do store mantida).
  • Snap a vértice mostra anel amarelo (SnapRing) e clique usa esse ponto.
  • Marcadores e labels permanecem do mesmo tamanho aparente em qualquer escala (1:1, 1:10, 1:100).
  • No AR, gatilho direito marca ponto sob o laser; A desfaz; B limpa; gatilho esquerdo alterna snap.
  • Esc cancela 1º ponto pendente; Z desfaz última medição (desktop).