Files
SteelXR/.lovable/plan.md
T
gpt-engineer-app[bot] ad37b2c37a Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
2026-05-21 14:48:19 +00:00

5.1 KiB

Seleção de elementos IFC — Esconder, Isolar e Exportar GLB

Adicionar um modo de seleção por elemento (viga, chapa, etc.) que permite ao usuário escolher partes do modelo e então esconder, isolar ou exportar como GLB reduzido para reuso. Funciona no desktop e no AR/XR (Quest 3) em uma única fase.

Fluxo do usuário

  1. Usuário ativa botão "Selecionar" no ViewerControls (e no XRHud).
  2. Clica em elementos do modelo — cada clique alterna seleção (highlight laranja/emissive).
  3. Contador mostra "N elementos selecionados".
  4. Três ações disponíveis:
    • Esconder selecionados → some com eles
    • Isolar selecionados → esconde todo o resto
    • Mostrar tudo → restaura
  5. Botão extra "Exportar GLB" salva arquivo .glb contendo apenas os elementos selecionados, baixável pelo navegador. Esse GLB pode ser recarregado no app pelo fluxo normal de import.
  6. Estado de visibilidade é persistido por fileName no localStorage, igual ao PLACEMENT_KEY, sobrevivendo a recarregar a página e a sair/entrar do AR.

Granularidade

Seleção opera no elemento IFC inteiro (uma viga, uma chapa). O converter de IFC preserva a hierarquia: cada IfcProduct vira um Group ou Mesh nomeado no GLB. O picking sobe na árvore até encontrar o ancestral marcado como "elemento IFC raiz" (campo userData.ifcElement = true adicionado durante a conversão), garantindo que clicar em qualquer face seleciona a viga toda.

Detalhes técnicos

1. src/lib/convertIFC.ts

  • Durante a conversão IFC→GLB, marcar cada IfcProduct resultante com obj.userData.ifcElement = true e userData.ifcId = expressId. Sem isso, a árvore three.js fica plana e não dá pra distinguir "elemento" de "submesh".

2. src/stores/useModelStore.ts

Novo slice de seleção:

selectionMode: boolean
selectedElementIds: Set<string>  // chave: `${modelId}:${ifcId|uuid}`
hiddenElementIds: Set<string>
isolatedElementIds: Set<string> | null  // null = sem isolamento
toggleSelection(key), clearSelection(), 
hideSelected(), isolateSelected(), showAll()

Persistência de hiddenElementIds/isolatedElementIds por fileName em localStorage (chave nova tsxr_visibility_v1), aplicada ao carregar o modelo.

3. src/components/three/ModelViewer.tsx

  • Novo componente SelectionHandler: ativo quando selectionMode === true. Raycaster no onPointerDown, sobe a árvore via object.parent até achar userData.ifcElement === true, faz toggle no store. Desabilita OrbitControls durante o clique para não conflitar.
  • Aplicar visibilidade: useEffect percorre scene.traverse, define obj.visible conforme hiddenElementIds/isolatedElementIds.
  • Highlight de seleção: material emissive laranja (#f59e0b, mesma cor de holes) com emissiveIntensity = 0.4, restaurado ao deselecionar. Guardar material original em userData.originalMaterial.
  • selectionMode é mutuamente exclusivo com measureMode e positionMode (mesma lógica que esses dois já têm entre si).

4. src/components/three/XRControllerMeasure.tsx (ou novo XRControllerSelect.tsx)

  • Quando selectionMode === true em XR: usar o mesmo ray do controller, no selectstart (trigger) faz toggle de seleção em vez de pontos de medição.
  • Tip do laser muda para laranja durante o modo seleção.

5. src/components/ViewerControls.tsx + src/components/XRHud.tsx

Novos botões:

  • Selecionar (toggle, ícone MousePointerClick)
  • Esconder / Isolar / Mostrar tudo (visíveis quando selectionMode ou há ocultos)
  • Exportar GLB (visível quando há seleção)
  • Badge com contador "N selecionados"

6. src/lib/exportSelectionGLB.ts (novo)

import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
// Clona apenas os elementos selecionados em uma nova Scene,
// preserva transformações mundiais, exporta como .glb binário,
// dispara download via Blob + <a download>.

Nome do arquivo: {original}_selecao_{N}elem.glb. O GLB resultante pode ser aberto pelo próprio app via fluxo normal de import (já aceita .glb).

7. Performance

  • Aplicar visibilidade via obj.visible = false (não remove geometria — reversível e barato).
  • Frustum culling do three.js já ignora invisíveis no render → ganho real de FPS no Quest mesmo sem exportar.

Arquivos tocados

  • src/lib/convertIFC.ts — marcar userData.ifcElement
  • src/lib/exportSelectionGLB.tsnovo
  • src/stores/useModelStore.ts — slice de seleção + persistência
  • src/components/three/ModelViewer.tsxSelectionHandler, highlight, aplicar visibilidade
  • src/components/three/XRControllerMeasure.tsx — handler de seleção em XR
  • src/components/ViewerControls.tsx — botões desktop
  • src/components/XRHud.tsx — botões XR

Fora do escopo (decidido)

  • Não exportar IFC reduzido (decidido: arquivo é só pra este app, GLB resolve e é trivial).
  • Não suportar seleção por face/sub-mesh (decidido: elemento inteiro).
  • Não salvar conjuntos nomeados de seleção ("layout A", "layout B") — pode virar Fase 2 se você pedir.