UNPKG

@inweb/viewer-three

Version:

JavaScript library for rendering CAD and BIM files in a browser using Three.js

158 lines (123 loc) 5.95 kB
/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, 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-2025 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 { Object3D, Vector2, Vector3 } from "three"; import type { IComponent } from "@inweb/viewer-core"; import type { Viewer } from "../Viewer"; import type { IModelImpl } from "../models/IModelImpl"; import type { HighlighterComponent } from "./HighlighterComponent"; import { Snapper } from "../measurement/Snapper"; export class SelectionComponent implements IComponent { protected viewer: Viewer; protected downPosition: Vector2; protected highlighter: HighlighterComponent; constructor(viewer: Viewer) { this.viewer = viewer; this.downPosition = new Vector2(); this.viewer.addEventListener("pointerdown", this.onPointerDown); this.viewer.addEventListener("pointerup", this.onPointerUp); this.viewer.addEventListener("dblclick", this.onDoubleClick); this.viewer.addEventListener("initialize", this.initHighlighter); } dispose() { this.viewer.removeEventListener("pointerdown", this.onPointerDown); this.viewer.removeEventListener("pointerup", this.onPointerUp); this.viewer.removeEventListener("dblclick", this.onDoubleClick); this.viewer.removeEventListener("initialize", this.initHighlighter); } onPointerDown = (event: PointerEvent) => { if (!event.isPrimary || event.button !== 0) return; this.getMousePosition(event, this.downPosition); }; onPointerUp = (event: PointerEvent) => { if (!event.isPrimary) return; const upPosition = this.getMousePosition(event, new Vector2()); if (upPosition.distanceTo(this.downPosition) !== 0) return; const snapper = new Snapper(this.viewer.camera, this.viewer.renderer, this.viewer.canvas); snapper.threshold = this.viewer.extents.getSize(new Vector3()).length() / 10000; let intersections = []; this.viewer.models.forEach((model) => { const objects = model.getVisibleObjects(); const intersects = snapper.getPointerIntersects(upPosition, objects); if (intersects.length > 0) intersections.push({ ...intersects[0], model }); }); intersections = intersections.sort((a, b) => a.distance - b.distance); if (!event.shiftKey) this.clearSelection(); if (intersections.length > 0) { const model = intersections[0].model; const handles = model.getHandlesByObjects(intersections[0].object); const objects = model.getObjectsByHandles(handles); if (!event.shiftKey) this.select(objects, model); else this.toggleSelection(objects, model); } this.viewer.update(); this.viewer.emitEvent({ type: "select", data: undefined, handles: this.viewer.getSelected() }); this.viewer.emitEvent({ type: "select2", data: undefined, handles: this.viewer.getSelected2() }); }; onDoubleClick = (event: MouseEvent) => { if (event.button !== 0) return; this.viewer.executeCommand("zoomToSelected"); }; getMousePosition(event: MouseEvent, target: Vector2): Vector2 { return target.set(event.clientX, event.clientY); } select(objects: Object3D | Object3D[], model?: IModelImpl) { if (!model) { this.viewer.models.forEach((model) => this.select(objects, model)); return; } if (!Array.isArray(objects)) objects = [objects]; if (objects.length === 0) return; model.highlightObjects(objects); this.highlighter.highlight(objects); objects.forEach((object) => this.viewer.selected.push(object)); objects.forEach((object) => (object.userData.isSelected = true)); } deselect(objects: Object3D | Object3D[], model?: IModelImpl) { if (!model) { this.viewer.models.forEach((model) => this.select(objects, model)); return; } if (!Array.isArray(objects)) objects = [objects]; if (objects.length === 0) return; model.unhighlightObjects(objects); this.highlighter.unhighlight(objects); this.viewer.selected = this.viewer.selected.filter((x) => !objects.includes(x)); objects.forEach((object) => (object.userData.isSelected = false)); } toggleSelection(objects: Object3D | Object3D[], model?: IModelImpl) { if (!Array.isArray(objects)) objects = [objects]; if (objects.length === 0) return; if (objects[0].userData.isSelected) this.deselect(objects, model); else this.select(objects, model); } clearSelection() { if (this.viewer.selected.length === 0) return; this.viewer.models.forEach((model) => model.unhighlightObjects(this.viewer.selected)); this.highlighter.unhighlight(this.viewer.selected); this.viewer.selected.forEach((object) => (object.userData.isSelected = false)); this.viewer.selected.length = 0; } initHighlighter = () => { this.highlighter = this.viewer.getComponent("HighlighterComponent") as HighlighterComponent; }; }