UNPKG

@inweb/viewer-visualize

Version:

JavaScript library for rendering CAD and BIM files in a browser using VisualizeJS

303 lines (233 loc) 9.48 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 { Point2d, Point3d, Vector3d } from "../Common/Geometry"; import { Viewer } from "../../Viewer"; export class OrbitAction { private _m_module: any; private _subject: Viewer; private _beginInteractivity: () => void; private _endInteractivity: () => void; private m_viewCenter: any; private m_startPoint: Point2d; constructor(m_module: any, subject: Viewer, beginInteractivity: () => void, endInteractivity: () => void) { this._m_module = m_module; this._subject = subject; this._beginInteractivity = beginInteractivity; this._endInteractivity = endInteractivity; } public beginAction(x: number, y: number) { this.m_viewCenter = this.getCenter(); this.m_startPoint = { x, y }; const view = this.getViewer().activeView; view.delete(); this._beginInteractivity(); } public action(x: number, y: number) { const view = this.getViewer().activeView; const corners = view.vportRect; const size = Math.max(Math.abs(corners[2] - corners[0]), Math.abs(corners[3] - corners[1])); const distX = ((this.m_startPoint.x - x) * Math.PI) / size; const distY = ((this.m_startPoint.y - y) * Math.PI) / size; this.m_startPoint.x = x; this.m_startPoint.y = y; const xOrbit = distY; const yOrbit = distX; const viewParams = { position: view.viewPosition, target: view.viewTarget, upVector: view.upVector, viewFieldWidth: view.viewFieldWidth, viewFieldHeight: view.viewFieldHeight, perspective: view.perspective, }; view.delete(); const sideVector = this.getSideVector(viewParams); if (xOrbit !== 0.0) { this.calculateXOrbit(viewParams, -xOrbit, sideVector); } if (yOrbit !== 0.0) { this.calculateYOrbit(viewParams, yOrbit, sideVector); } sideVector.delete(); const extView = this.getViewer().getActiveTvExtendedView(); extView.setView( viewParams.position, viewParams.target, viewParams.upVector, viewParams.viewFieldWidth, viewParams.viewFieldHeight, viewParams.perspective ); extView.delete(); this._subject.activeDragger()?.updatePreview?.(); this._subject.emitEvent({ type: "orbit", }); this._subject.emitEvent({ type: "changecamera", }); } public endAction() { this._endInteractivity(); } private getSideVector(viewParams: any): any { const pUpV = this.toVector(viewParams.upVector); const pTarget = this.toPoint(viewParams.target); const pPosition = this.toPoint(viewParams.position); const direct = pTarget.sub(pPosition); const vDirect = direct.asVector(); // Protect against zero or near-zero vector when pTarget and pPosition are the same if (vDirect.length() < 1e-10) { // Return a default side vector (X axis) when direction is degenerate const defaultSide = this.toVector(this._m_module.Vector3d.kXAxis); this.deleteAll([direct, pUpV, pTarget, pPosition, vDirect]); return defaultSide; } const vCross = pUpV.crossProduct(vDirect); // Also protect against zero cross product (parallel vectors) if (vCross.length() < 1e-10) { // Return a default side vector when up and direction are parallel const defaultSide = this.toVector(this._m_module.Vector3d.kXAxis); this.deleteAll([direct, pUpV, pTarget, pPosition, vDirect, vCross]); return defaultSide; } const sideVector = vCross.normalize(); this.deleteAll([direct, pUpV, pTarget, pPosition, vDirect, vCross]); return sideVector; } private calculateXOrbit(viewParams: any, delta: number, sideVector: Vector3d): void { { const pPoint = this.toPoint(viewParams.position); const pCenter = this.toPoint(this.m_viewCenter); const rotatePoint = pPoint.rotateByBasePoint(delta, sideVector, pCenter); viewParams.position = rotatePoint.toArray(); this.deleteAll([pPoint, pCenter, rotatePoint]); } { const pTarget = this.toPoint(viewParams.target); const pCenter = this.toPoint(this.m_viewCenter); const rotatePoint = pTarget.rotateByBasePoint(delta, sideVector, pCenter); viewParams.target = rotatePoint.toArray(); this.deleteAll([pTarget, pCenter, rotatePoint]); } { const pPoint = this.toPoint(viewParams.position); const pTarget = this.toPoint(viewParams.target); const pUp = pTarget.sub(pPoint); const vUp = pUp.asVector(); // Protect against zero or near-zero vector when pPoint and pTarget are the same if (vUp.length() < 1e-10) { // Skip up vector calculation if points are too close this.deleteAll([pPoint, pTarget, pUp, vUp]); return; } const crossProduct = vUp.crossProduct(sideVector); // Protect against zero cross product (parallel vectors) if (crossProduct.length() < 1e-10) { // Keep current up vector if cross product is degenerate this.deleteAll([pPoint, pTarget, pUp, vUp, crossProduct]); return; } const crossProductNormal = crossProduct.normalize(); viewParams.upVector = crossProductNormal.toArray(); this.deleteAll([pPoint, pTarget, pUp, vUp, crossProduct, crossProductNormal]); } } private calculateYOrbit(viewParams: any, delta: number, sideVector: any): void { { const pPoint = this.toPoint(viewParams.position); const pCenter = this.toPoint(this.m_viewCenter); const zAxis = this.toVector(this._m_module.Vector3d.kZAxis); const rotatePoint = pPoint.rotateByBasePoint(delta, zAxis, pCenter); viewParams.position = rotatePoint.toArray(); this.deleteAll([zAxis, pPoint, pCenter, rotatePoint]); } { const pTarget = this.toPoint(viewParams.target); const pCenter = this.toPoint(this.m_viewCenter); const zAxis = this.toVector(this._m_module.Vector3d.kZAxis); const rotatePoint = pTarget.rotateByBasePoint(delta, zAxis, pCenter); viewParams.target = rotatePoint.toArray(); this.deleteAll([zAxis, pTarget, pCenter, rotatePoint]); } { const zAxis = this.toVector(this._m_module.Vector3d.kZAxis); const pTarget = this.toPoint(viewParams.target); const pPoint = this.toPoint(viewParams.position); const side = sideVector.rotateBy(delta, zAxis); const pUp = pTarget.sub(pPoint); const vUp = pUp.asVector(); // Protect against zero or near-zero vector when pPoint and pTarget are the same if (vUp.length() < 1e-10) { // Skip up vector calculation if points are too close this.deleteAll([zAxis, pTarget, pPoint, side, pUp, vUp]); return; } const cross = vUp.crossProduct(side); // Protect against zero cross product (parallel vectors) if (cross.length() < 1e-10) { // Keep current up vector if cross product is degenerate this.deleteAll([zAxis, pTarget, pPoint, side, pUp, vUp, cross]); return; } const crossNormal = cross.normalize(); viewParams.upVector = crossNormal.toArray(); this.deleteAll([zAxis, pTarget, pPoint, side, pUp, vUp, cross, crossNormal]); } } private getCenter(): any { const viewer = this.getViewer(); let center; const pSet = viewer.getSelected(); if (!pSet.isNull() && pSet.numItems() !== 0) { const itr = pSet.getIterator(); let ext, entId, extSelected; for (; !itr.done(); itr.step()) { entId = itr.getEntity(); ext = entId.getWCSExtents(); if (extSelected) extSelected.addExt(ext); else extSelected = ext; } center = extSelected.center(); extSelected.delete(); itr.delete(); } else { center = viewer.getActiveExtents().center(); } return center; } private getViewer(): any { return this._m_module.getViewer(); } private toVector(geVector): Vector3d { return this._m_module.Vector3d.createFromArray(geVector); } private toPoint(gePoint: number[]): Point3d { return this._m_module.Point3d.createFromArray(gePoint); } private deleteAll(objects): void { for (const obj of objects) { obj?.delete?.(); } } }