6daf7a76f3
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
5.4 KiB
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
- Mouse não cria pontos: o
MeasureClickHandlerusa o evento DOMclickno canvas, que conflita com oonClickinterno do R3F no<group>doGLBModel(setActive). OstopPropagationdo R3F não pára o evento DOM, mas o caminho que chega lá ignora se o mouse arrastou, e opointerdownglobal dePositionDragHandler(que está sempre montado) chamasetPointerCapturequandopositionModeestá ligado — em modo Medir não captura, mas o handlerclickpuro é frágil. Vamos trocar porpointerdown/uppróprios com gate de "não foi arrasto". - Sem feedback de "ponto A pendente" claro no AR — o ponto fica pequeno demais quando escala é 1:50.
- 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. - AR só tem gatilho — sem desfazer último ponto, sem limpar, sem indicação visual de "armando 2º ponto".
- Snap de vértice só funciona em desktop (
SmartSnapHandlerlê mouse DOM). No AR não há snap.
O que vai ser construído
A) Mouse (desktop)
- Substituir
MeasureClickHandlerpor handlerpointerdown→pointerupno canvas:- Marca posição em
pointerdown; se empointerupo mouse moveu < 4 px emeasureModeestá on → adiciona ponto. - Usa
snapPointse houver; senão raycast. - Ignora botão direito (reservado para OrbitControls eventual no futuro).
- Marca posição em
- Cursor
crosshairapenas quandomeasureMode. - 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) eHoverDetector(furo/aresta) só funcionam pelo mouse. Vamos:- Manter o snap de vértice via
findNearestVertex(já existe emSmartMeasure.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.
- Manter o snap de vértice via
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 emuseFrameviasetScalarno mesh — alvo: ~6 px de raio na tela em qualquer escala/zoom.- Label
<Html distanceFactor={...}>: substituir odistanceFactor={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
Linedo drei comlineWidthconstante em pixels (já é o comportamento padrão). - No AR usar
<Text>do@react-three/drei(já importado emXRHudInWorld) 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" quandomeasurePoints.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).