UNPKG

@inweb/viewer-three

Version:

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

165 lines (130 loc) 5.74 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 { Euler, Quaternion, Object3D, Vector3 } from "three"; import { CommandEvent, IComponent, RenderEvent } from "@inweb/viewer-core"; import type { Viewer } from "../Viewer"; export const defaultViewPositions = { top: new Vector3(0, 0, 1), bottom: new Vector3(0, 0, -1), left: new Vector3(-1, 0, 0), right: new Vector3(1, 0, 0), front: new Vector3(0, 1, 0), back: new Vector3(0, -1, 0), sw: new Vector3(-0.5, -0.5, 1.0).normalize(), se: new Vector3(0.5, -0.5, 1.0).normalize(), ne: new Vector3(0.5, 0.5, 1.0).normalize(), nw: new Vector3(-0.5, 0.5, 1.0).normalize(), }; export class ViewPositionComponent implements IComponent { private position: string; private center: Vector3; private targetPosition: Vector3; private targetQuaternion: Quaternion; private radius: number; private q1: Quaternion; private q2: Quaternion; private animating: boolean; private viewer: Viewer; constructor(viewer: Viewer) { this.animating = false; // this.center = new THREE.Vector3(); this.targetPosition = new Vector3(); this.targetQuaternion = new Quaternion(); this.q1 = new Quaternion(); this.q2 = new Quaternion(); this.viewer = viewer; this.viewer.addEventListener("render", this.onRender); this.viewer.addEventListener("command", this.onCommand); console.log("--- ViewPositionComponent.constructor"); } dispose() { this.viewer.off("render", this.onRender); this.viewer.off("viewposition", this.onCommand); console.log("--- ViewPositionComponent.dispose"); } onCommand = (event: CommandEvent) => { if (this.animating) return; this.position = event.data === "setDefaultViewPosition" ? event.args[0] : event.data; console.log("--- ViewPositionComponent:onCommand", this.position); switch (this.position) { case "left": this.targetPosition.set(-1, 0, 0); this.targetQuaternion.setFromEuler(new Euler(0, -Math.PI * 0.5, 0)); break; case "right": this.targetPosition.set(1, 0, 0); this.targetQuaternion.setFromEuler(new Euler(0, Math.PI * 0.5, 0)); break; case "front": this.targetPosition.set(0, 1, 0); this.targetQuaternion.setFromEuler(new Euler(-Math.PI * 0.5, 0, 0)); // this.targetQuaternion.setFromEuler(new Euler(Math.PI * 0.5, 0, 0)); break; case "back": this.targetPosition.set(0, -1, 0); this.targetQuaternion.setFromEuler(new Euler(Math.PI * 0.5, 0, 0)); break; case "top": default: this.targetPosition.set(0, 0, 1); this.targetQuaternion.setFromEuler(new Euler()); break; case "bottom": this.targetPosition.set(0, 0, -1); this.targetQuaternion.setFromEuler(new Euler(0, Math.PI, 0)); break; } this.center = this.viewer.extents.getCenter(new Vector3()); this.radius = this.viewer.camera.position.distanceTo(this.center); this.targetPosition.multiplyScalar(this.radius).add(this.center); const dummy = new Object3D(); dummy.position.copy(this.center); dummy.lookAt(this.viewer.camera.position); this.q1.copy(dummy.quaternion); dummy.lookAt(this.targetPosition); this.q2.copy(dummy.quaternion); this.animating = true; // this.viewer.camera.position.set(0, 0, 1).applyQuaternion(this.q2).multiplyScalar(this.radius).add(this.center); // this.viewer.camera.quaternion.copy(this.targetQuaternion); // this.viewer.camera.updateProjectionMatrix(); this.viewer.update(); }; onRender = (event: RenderEvent) => { if (!this.animating) return; const turnRate = 2 * Math.PI; // turn rate in angles per second const step = (event.deltaTime * turnRate) / 10; // animate position by doing a slerp and then scaling the position on the unit sphere this.q1.rotateTowards(this.q2, step); this.viewer.camera.position.set(0, 0, 1).applyQuaternion(this.q1).multiplyScalar(this.radius).add(this.center); // animate orientation this.viewer.camera.quaternion.rotateTowards(this.targetQuaternion, step); this.viewer.update(); if (this.q1.angleTo(this.q2) === 0) { this.animating = false; // console.log("--- ViewPositionComponent.onRender", event.deltaTime, step, this.animating); this.viewer.emit({ type: "viewposition", data: this.position }); return; } // console.log("--- ViewPositionComponent.onRender", event.deltaTime, step, this.animating); }; }