Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
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
- Usuário ativa botão "Selecionar" no ViewerControls (e no XRHud).
- Clica em elementos do modelo — cada clique alterna seleção (highlight laranja/emissive).
- Contador mostra "N elementos selecionados".
- Três ações disponíveis:
- Esconder selecionados → some com eles
- Isolar selecionados → esconde todo o resto
- Mostrar tudo → restaura
- Botão extra "Exportar GLB" salva arquivo
.glbcontendo apenas os elementos selecionados, baixável pelo navegador. Esse GLB pode ser recarregado no app pelo fluxo normal de import. - Estado de visibilidade é persistido por
fileNamenolocalStorage, igual aoPLACEMENT_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
IfcProductresultante comobj.userData.ifcElement = trueeuserData.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 quandoselectionMode === true. Raycaster noonPointerDown, sobe a árvore viaobject.parentaté acharuserData.ifcElement === true, faz toggle no store. Desabilita OrbitControls durante o clique para não conflitar. - Aplicar visibilidade:
useEffectpercorrescene.traverse, defineobj.visibleconformehiddenElementIds/isolatedElementIds. - Highlight de seleção: material
emissivelaranja (#f59e0b, mesma cor de holes) comemissiveIntensity = 0.4, restaurado ao deselecionar. Guardar material original emuserData.originalMaterial. selectionModeé mutuamente exclusivo commeasureModeepositionMode(mesma lógica que esses dois já têm entre si).
4. src/components/three/XRControllerMeasure.tsx (ou novo XRControllerSelect.tsx)
- Quando
selectionMode === trueem XR: usar o mesmo ray do controller, noselectstart(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, íconeMousePointerClick)Esconder/Isolar/Mostrar tudo(visíveis quandoselectionModeou 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— marcaruserData.ifcElementsrc/lib/exportSelectionGLB.ts— novosrc/stores/useModelStore.ts— slice de seleção + persistênciasrc/components/three/ModelViewer.tsx—SelectionHandler, highlight, aplicar visibilidadesrc/components/three/XRControllerMeasure.tsx— handler de seleção em XRsrc/components/ViewerControls.tsx— botões desktopsrc/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.