# 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: ```ts selectionMode: boolean selectedElementIds: Set // chave: `${modelId}:${ifcId|uuid}` hiddenElementIds: Set isolatedElementIds: Set | 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) ```ts 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 + . ``` 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.ts` — **novo** - `src/stores/useModelStore.ts` — slice de seleção + persistência - `src/components/three/ModelViewer.tsx` — `SelectionHandler`, 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.