/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2023, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a // license agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2021 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// import * as THREE from "three"; import { IComponent } from "../IComponent"; import type { ThreejsViewer } from "../ThreejsViewer"; export class ObjectSelectionComponent implements IComponent { protected viewer: ThreejsViewer; protected raycaster: THREE.Raycaster; protected mouse: THREE.Vector2; protected selectionMaterial: THREE.MeshBasicMaterial; constructor(viewer: ThreejsViewer) { this.viewer = viewer; this.raycaster = new THREE.Raycaster(); this.mouse = new THREE.Vector2(); this.selectionMaterial = new THREE.MeshBasicMaterial({ color: "red" }); this.viewer.addEventListener("pointerdown", this.onPointerDown); this.viewer.addEventListener("pointerup", this.onPointerUp); } dispose() { this.viewer.removeEventListener("pointerdown", this.onPointerDown); this.viewer.removeEventListener("pointerup", this.onPointerUp); } onPointerUp = (event: PointerEvent) => { const x = (event.offsetX / this.viewer.canvas.clientWidth) * 2 - 1; const y = -(event.offsetY / this.viewer.canvas.clientHeight) * 2 + 1; if (this.mouse.x === x && this.mouse.y === y) { this.raycaster.setFromCamera(this.mouse, this.viewer.camera); const intersects = this.raycaster.intersectObjects(this.viewer.scene.children, true); this.clearSelection(); if (intersects.length > 0) { const object = intersects[0].object as THREE.Mesh; this.setSelectObject(object, true); } } }; onPointerDown = (event: PointerEvent) => { this.mouse.x = (event.offsetX / this.viewer.canvas.clientWidth) * 2 - 1; this.mouse.y = -(event.offsetY / this.viewer.canvas.clientHeight) * 2 + 1; }; clearSelection() { this.viewer.selectedObjects.forEach((object) => this.setSelectObject(object, false)); this.viewer.selectedObjects.length = 0; } setSelectObject(object, selected) { if (selected) { const index = this.viewer.selectedObjects.findIndex((o) => o === object); if (index === -1) { this.viewer.selectedObjects.push(object); this.highlightObject(object, true); this.viewer.emit("selectobject", object); } } else { const index = this.viewer.selectedObjects.findIndex((o) => o === object); if (index !== -1) { this.viewer.selectedObjects.splice(index, 1); this.highlightObject(object, false); this.viewer.emit("deselectobject", object); } } } highlightObject(object, enable) { if (enable) { if (!object.originalMaterial) { object.originalMaterial = object.material; object.material = this.selectionMaterial; } } else { if (object.originalMaterial) { object.material = object.originalMaterial; delete object.originalMaterial; } } } }