Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -662,7 +662,131 @@ function MeasureClickHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GridLayer() {
|
/** Pointer handler for element-selection mode. Clicks an IFC element, sub-mesh
|
||||||
|
* of a non-IFC model, or any pickable mesh group. Toggles selection in store. */
|
||||||
|
function SelectionHandler() {
|
||||||
|
const { camera, scene, gl } = useThree();
|
||||||
|
const selectionMode = useModelStore((s) => s.selectionMode);
|
||||||
|
const raycaster = useMemo(() => new THREE.Raycaster(), []);
|
||||||
|
const mouse = useMemo(() => new THREE.Vector2(), []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!selectionMode) return;
|
||||||
|
const prevCursor = gl.domElement.style.cursor;
|
||||||
|
gl.domElement.style.cursor = 'cell';
|
||||||
|
const canvas = gl.domElement;
|
||||||
|
let downX = 0, downY = 0, armed = false;
|
||||||
|
|
||||||
|
const onDown = (e: PointerEvent) => {
|
||||||
|
if (e.button !== 0) { armed = false; return; }
|
||||||
|
downX = e.clientX; downY = e.clientY; armed = true;
|
||||||
|
};
|
||||||
|
const onUp = (e: PointerEvent) => {
|
||||||
|
if (!armed) return;
|
||||||
|
armed = false;
|
||||||
|
if (Math.hypot(e.clientX - downX, e.clientY - downY) > 4) return;
|
||||||
|
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
raycaster.setFromCamera(mouse, camera);
|
||||||
|
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||||
|
const hit = intersects.find((i) => {
|
||||||
|
const o = i.object;
|
||||||
|
if (!(o instanceof THREE.Mesh)) return false;
|
||||||
|
if (o.userData.__edgeLine) return false;
|
||||||
|
if (o.geometry instanceof THREE.SphereGeometry) return false;
|
||||||
|
if (o.geometry instanceof THREE.RingGeometry) return false;
|
||||||
|
if (o.geometry instanceof THREE.PlaneGeometry) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
while (cur) {
|
||||||
|
if (!element && cur.userData?.ifcElement) element = cur;
|
||||||
|
if (!modelId && cur.userData?.modelId) modelId = cur.userData.modelId as string;
|
||||||
|
cur = cur.parent;
|
||||||
|
}
|
||||||
|
// Non-IFC fallback: pick the mesh itself
|
||||||
|
if (!element) element = hit.object;
|
||||||
|
if (!modelId) modelId = useModelStore.getState().activeModelId;
|
||||||
|
if (!modelId || !element) return;
|
||||||
|
const key = elementKey(modelId, element);
|
||||||
|
useModelStore.getState().toggleElementSelection(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.addEventListener('pointerdown', onDown);
|
||||||
|
canvas.addEventListener('pointerup', onUp);
|
||||||
|
return () => {
|
||||||
|
canvas.removeEventListener('pointerdown', onDown);
|
||||||
|
canvas.removeEventListener('pointerup', onUp);
|
||||||
|
gl.domElement.style.cursor = prevCursor;
|
||||||
|
};
|
||||||
|
}, [selectionMode, camera, scene, gl, raycaster, mouse]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Walks the scene each time visibility/selection state changes and applies
|
||||||
|
* per-element visibility + emissive highlight for selected elements. */
|
||||||
|
function VisibilityApplier() {
|
||||||
|
const { scene } = useThree();
|
||||||
|
const nonce = useModelStore((s) => s.visibilityNonce);
|
||||||
|
const models = useModelStore((s) => s.models);
|
||||||
|
const opacity = useModelStore((s) => s.opacity);
|
||||||
|
const renderMode = useModelStore((s) => s.renderMode);
|
||||||
|
const checklist = useModelStore((s) => s.checklist);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const st = useModelStore.getState();
|
||||||
|
const { hiddenElementKeys, isolatedElementKeys, selectedElementKeys } = st;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
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) {
|
||||||
|
m.userData.__origEmissive = mat.emissive.getHex();
|
||||||
|
m.userData.__origEmissiveIntensity = mat.emissiveIntensity;
|
||||||
|
}
|
||||||
|
mat.emissive.set('#f59e0b');
|
||||||
|
mat.emissiveIntensity = 0.6;
|
||||||
|
} else if (m.userData.__origEmissive !== undefined) {
|
||||||
|
mat.emissive.setHex(m.userData.__origEmissive as number);
|
||||||
|
mat.emissiveIntensity = (m.userData.__origEmissiveIntensity as number) ?? 0;
|
||||||
|
delete m.userData.__origEmissive;
|
||||||
|
delete m.userData.__origEmissiveIntensity;
|
||||||
|
}
|
||||||
|
mat.needsUpdate = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [scene, nonce, models, opacity, renderMode, checklist]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const showGrid = useModelStore((s) => s.showGrid);
|
const showGrid = useModelStore((s) => s.showGrid);
|
||||||
const gridY = useModelStore((s) => s.gridY);
|
const gridY = useModelStore((s) => s.gridY);
|
||||||
if (!showGrid) return null;
|
if (!showGrid) return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user