From 2e45153b8faa3cd0c56224ef952a910fac69306b Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:49:54 +0000 Subject: [PATCH 1/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index a7ad651..a6d95f7 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -712,6 +712,7 @@ function SelectionHandler() { if (o.geometry instanceof THREE.PlaneGeometry) return false; return true; }); + console.log('[Selection] click', { intersects: intersects.length, hit: !!hit, hitName: hit?.object?.name, hitUserData: hit?.object?.userData }); if (!hit) return; // Walk up to find ifcElement node AND the GLBModel root (carrying modelId) @@ -723,11 +724,13 @@ function SelectionHandler() { if (!modelId && cur.userData?.modelId) modelId = cur.userData.modelId as string; cur = cur.parent; } + console.log('[Selection] walked', { foundIfcElement: !!element, modelId, elementName: element?.name }); // Non-IFC fallback: pick the mesh itself if (!element) element = hit.object; if (!modelId) modelId = useModelStore.getState().activeModelId; - if (!modelId || !element) return; + if (!modelId || !element) { console.log('[Selection] aborted: no modelId/element'); return; } const key = elementKey(modelId, element); + console.log('[Selection] toggling', key); useModelStore.getState().toggleElementSelection(key); }; From 7e30722c2a8efbd0064e35928dc16b6ccfc2d164 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:52:30 +0000 Subject: [PATCH 2/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index a6d95f7..ff58083 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -759,6 +759,7 @@ function VisibilityApplier() { useEffect(() => { const st = useModelStore.getState(); const { hiddenElementKeys, isolatedElementKeys, selectedElementKeys } = st; + let touched = 0, highlighted = 0; scene.traverse((root) => { const modelId = root.userData?.modelId as string | undefined; From 145bc5127242d09434b18ac0164b10c85286c0d9 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:52:49 +0000 Subject: [PATCH 3/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index ff58083..501f71f 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -767,12 +767,14 @@ function VisibilityApplier() { // For each ifcElement descendant inside this model root root.traverse((el) => { if (!el.userData?.ifcElement) return; + touched++; const key = elementKey(modelId, el); const hidden = hiddenElementKeys.has(key); const isolated = isolatedElementKeys ? !isolatedElementKeys.has(key) : false; el.visible = !(hidden || isolated); const selected = selectedElementKeys.has(key); + if (selected) highlighted++; el.traverse((m) => { if (!(m instanceof THREE.Mesh)) return; const mats = Array.isArray(m.material) ? m.material : [m.material]; @@ -796,6 +798,7 @@ function VisibilityApplier() { }); }); }); + console.log('[VisibilityApplier] ran', { touched, highlighted, selectedKeys: Array.from(selectedElementKeys) }); }, [scene, nonce, models, opacity, renderMode, checklist]); return null; From 3532bcf820e20d1c8eb4763dbe0b17bc2d7d4c93 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:55:38 +0000 Subject: [PATCH 4/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/stores/useModelStore.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/useModelStore.ts b/src/stores/useModelStore.ts index 11182e9..8970a0c 100644 --- a/src/stores/useModelStore.ts +++ b/src/stores/useModelStore.ts @@ -524,7 +524,9 @@ export const useModelStore = create((set, get) => ({ toggleElementSelection: (key) => set((s) => { const next = new Set(s.selectedElementKeys); if (next.has(key)) next.delete(key); else next.add(key); - return { selectedElementKeys: next, visibilityNonce: s.visibilityNonce + 1 }; + const newNonce = s.visibilityNonce + 1; + console.log('[Store] toggleElementSelection', { key, size: next.size, newNonce }); + return { selectedElementKeys: next, visibilityNonce: newNonce }; }), clearElementSelection: () => set((s) => ({ selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 })), hideSelectedElements: () => set((s) => { From 2503b36ea2da7c613a2984f02c885ccb9bd4e631 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:56:41 +0000 Subject: [PATCH 5/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/stores/useModelStore.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stores/useModelStore.ts b/src/stores/useModelStore.ts index 8970a0c..11182e9 100644 --- a/src/stores/useModelStore.ts +++ b/src/stores/useModelStore.ts @@ -524,9 +524,7 @@ export const useModelStore = create((set, get) => ({ toggleElementSelection: (key) => set((s) => { const next = new Set(s.selectedElementKeys); if (next.has(key)) next.delete(key); else next.add(key); - const newNonce = s.visibilityNonce + 1; - console.log('[Store] toggleElementSelection', { key, size: next.size, newNonce }); - return { selectedElementKeys: next, visibilityNonce: newNonce }; + return { selectedElementKeys: next, visibilityNonce: s.visibilityNonce + 1 }; }), clearElementSelection: () => set((s) => ({ selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 })), hideSelectedElements: () => set((s) => { From 38963d7e23a2f6ff505c8f1347aaaa25ba8b31da Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:56:58 +0000 Subject: [PATCH 6/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index 501f71f..4e98c9d 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -759,37 +759,37 @@ function VisibilityApplier() { useEffect(() => { const st = useModelStore.getState(); const { hiddenElementKeys, isolatedElementKeys, selectedElementKeys } = st; - let touched = 0, highlighted = 0; scene.traverse((root) => { const modelId = root.userData?.modelId as string | undefined; if (!modelId) return; - // For each ifcElement descendant inside this model root root.traverse((el) => { if (!el.userData?.ifcElement) return; - touched++; const key = elementKey(modelId, el); const hidden = hiddenElementKeys.has(key); const isolated = isolatedElementKeys ? !isolatedElementKeys.has(key) : false; el.visible = !(hidden || isolated); const selected = selectedElementKeys.has(key); - if (selected) highlighted++; el.traverse((m) => { if (!(m instanceof THREE.Mesh)) return; const mats = Array.isArray(m.material) ? m.material : [m.material]; mats.forEach((mat) => { if (!(mat instanceof THREE.MeshStandardMaterial)) return; if (selected) { - if (m.userData.__origEmissive === undefined) { + if (m.userData.__origColor === undefined) { + m.userData.__origColor = mat.color.getHex(); m.userData.__origEmissive = mat.emissive.getHex(); m.userData.__origEmissiveIntensity = mat.emissiveIntensity; } + mat.color.set('#f59e0b'); mat.emissive.set('#f59e0b'); - mat.emissiveIntensity = 0.6; - } else if (m.userData.__origEmissive !== undefined) { + mat.emissiveIntensity = 1.0; + } else if (m.userData.__origColor !== undefined) { + mat.color.setHex(m.userData.__origColor as number); mat.emissive.setHex(m.userData.__origEmissive as number); mat.emissiveIntensity = (m.userData.__origEmissiveIntensity as number) ?? 0; + delete m.userData.__origColor; delete m.userData.__origEmissive; delete m.userData.__origEmissiveIntensity; } @@ -798,7 +798,6 @@ function VisibilityApplier() { }); }); }); - console.log('[VisibilityApplier] ran', { touched, highlighted, selectedKeys: Array.from(selectedElementKeys) }); }, [scene, nonce, models, opacity, renderMode, checklist]); return null; From e9046b6aa7e91a16a7f1bd1601f616f3fc41b8b1 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:57:15 +0000 Subject: [PATCH 7/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 38 +++++++++++++--------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index 4e98c9d..4f0657e 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -773,28 +773,26 @@ function VisibilityApplier() { const selected = selectedElementKeys.has(key); el.traverse((m) => { if (!(m instanceof THREE.Mesh)) return; - const mats = Array.isArray(m.material) ? m.material : [m.material]; - mats.forEach((mat) => { - if (!(mat instanceof THREE.MeshStandardMaterial)) return; - if (selected) { - if (m.userData.__origColor === undefined) { - m.userData.__origColor = mat.color.getHex(); - m.userData.__origEmissive = mat.emissive.getHex(); - m.userData.__origEmissiveIntensity = mat.emissiveIntensity; + if (selected) { + if (!m.userData.__origMaterial) { + m.userData.__origMaterial = m.material; + const src = Array.isArray(m.material) ? m.material[0] : m.material; + const cloned = (src as THREE.Material).clone() as THREE.MeshStandardMaterial; + if (cloned instanceof THREE.MeshStandardMaterial) { + cloned.color.set('#f59e0b'); + cloned.emissive.set('#f59e0b'); + cloned.emissiveIntensity = 0.8; + cloned.needsUpdate = true; } - mat.color.set('#f59e0b'); - mat.emissive.set('#f59e0b'); - mat.emissiveIntensity = 1.0; - } else if (m.userData.__origColor !== undefined) { - mat.color.setHex(m.userData.__origColor as number); - mat.emissive.setHex(m.userData.__origEmissive as number); - mat.emissiveIntensity = (m.userData.__origEmissiveIntensity as number) ?? 0; - delete m.userData.__origColor; - delete m.userData.__origEmissive; - delete m.userData.__origEmissiveIntensity; + m.material = cloned; } - mat.needsUpdate = true; - }); + } else if (m.userData.__origMaterial) { + // Dispose the cloned highlight material + const cur = Array.isArray(m.material) ? m.material[0] : m.material; + (cur as THREE.Material)?.dispose?.(); + m.material = m.userData.__origMaterial as THREE.Material; + delete m.userData.__origMaterial; + } }); }); }); From 1e56e584e30bf358f9a85164fc17f6662358dc0f Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 16:57:29 +0000 Subject: [PATCH 8/8] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/ModelViewer.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index 4f0657e..81200d6 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -712,10 +712,8 @@ function SelectionHandler() { if (o.geometry instanceof THREE.PlaneGeometry) return false; return true; }); - console.log('[Selection] click', { intersects: intersects.length, hit: !!hit, hitName: hit?.object?.name, hitUserData: hit?.object?.userData }); if (!hit) return; - // Walk up to find ifcElement node AND the GLBModel root (carrying modelId) let cur: THREE.Object3D | null = hit.object; let element: THREE.Object3D | null = null; let modelId: string | null = null; @@ -724,13 +722,10 @@ function SelectionHandler() { if (!modelId && cur.userData?.modelId) modelId = cur.userData.modelId as string; cur = cur.parent; } - console.log('[Selection] walked', { foundIfcElement: !!element, modelId, elementName: element?.name }); - // Non-IFC fallback: pick the mesh itself if (!element) element = hit.object; if (!modelId) modelId = useModelStore.getState().activeModelId; - if (!modelId || !element) { console.log('[Selection] aborted: no modelId/element'); return; } + if (!modelId || !element) return; const key = elementKey(modelId, element); - console.log('[Selection] toggling', key); useModelStore.getState().toggleElementSelection(key); };