UNPKG

@inweb/viewer-visualize

Version:

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

1,282 lines (1,258 loc) 867 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.ODA = global.ODA || {}, global.ODA.Visualize = global.ODA.Visualize || {}))); })(this, (function (exports) { 'use strict'; class CommandsRegistry { constructor() { this._commands = new Map; } registerCommand(id, handler, description, thisArg) { this._commands.set(id, { id: id, handler: handler, thisArg: thisArg, description: description }); } registerCommandAlias(id, alias) { this.registerCommand(alias, ((viewer, ...args) => this.executeCommand(id, viewer, ...args))); } getCommand(id) { return this._commands.get(id); } getCommands() { const map = new Map; this._commands.forEach(((value, key) => map.set(key, value))); return map; } executeCommand(id, viewer, ...args) { const command = this._commands.get(id); if (!command) { if (viewer) { const isDraggerCommand = viewer.draggers.includes(id); if (isDraggerCommand) return viewer.setActiveDragger(id); } console.warn(`Command '${id}' not found`); return undefined; } const {handler: handler, thisArg: thisArg} = command; const result = handler.apply(thisArg, [ viewer, ...args ]); viewer === null || viewer === undefined ? undefined : viewer.emit({ type: "command", data: id, args: args }); return result; } } const _commandsRegistry = new Map; function commandsRegistry(viewerType = "") { let result = _commandsRegistry.get(viewerType); if (!result) { result = new CommandsRegistry; _commandsRegistry.set(viewerType, result); } return result; } class Dragger { constructor(viewer) { this.name = ""; } dispose() {} } class DraggersRegistry { constructor() { this._providers = new Map; } registerDragger(name, provider) { this._providers.set(name, provider); } registerDraggerAlias(name, alias) { const provider = this._providers.get(name); if (provider) this.registerDragger(alias, (viewer => provider(viewer))); } getDraggers() { const map = new Map; this._providers.forEach(((value, key) => map.set(key, value))); return map; } createDragger(name, viewer) { const provider = this._providers.get(name); if (!provider) return null; const dragger = provider(viewer); dragger.name = name; return dragger; } } const _draggersRegistry = new Map; function draggersRegistry(viewerType = "") { let result = _draggersRegistry.get(viewerType); if (!result) { result = new DraggersRegistry; _draggersRegistry.set(viewerType, result); } return result; } class Component { constructor(viewer) { this.name = ""; } dispose() {} } class Components { constructor() { this._providers = new Map; } registerComponent(name, provider) { this._providers.set(name, provider); } registerComponentAlias(name, alias) { const provider = this._providers.get(name); if (provider) this.registerComponent(alias, (viewer => provider(viewer))); } getComponents() { const map = new Map; this._providers.forEach(((value, key) => map.set(key, value))); return map; } createComponent(name, viewer) { const provider = this._providers.get(name); if (!provider) return null; const component = provider(viewer); component.name = name; return component; } } const _components = new Map; function componentsRegistry(viewerType = "") { let result = _components.get(viewerType); if (!result) { result = new Components; _components.set(viewerType, result); } return result; } class Loader { constructor() { this.name = ""; this.abortController = new AbortController; } dispose() { this.abortController.abort(); this.abortController = undefined; } isSupport(file, format) { return false; } load(file, format, params) { return Promise.resolve(this); } cancel() { this.abortController.abort(); } } class Loaders { constructor() { this._providers = new Map; } registerLoader(name, provider) { this._providers.set(name, provider); } getLoader(name) { return this._providers.get(name); } getLoaders() { const map = new Map; this._providers.forEach(((value, key) => map.set(key, value))); return map; } createLoader(viewer, file, format) { let result = null; this._providers.forEach(((provider, key) => { const loader = provider(viewer); if (loader.isSupport(file, format)) { result = loader; result.name = key; } })); return result; } } const _loaders = new Map; function loadersRegistry(viewerType = "") { let result = _loaders.get(viewerType); if (!result) { result = new Loaders; _loaders.set(viewerType, result); } return result; } function defaultOptions() { return { showWCS: true, cameraAnimation: true, antialiasing: true, groundShadow: false, shadows: false, cameraAxisXSpeed: 4, cameraAxisYSpeed: 1, ambientOcclusion: false, enableStreamingMode: true, enablePartialMode: false, memoryLimit: 3294967296, cuttingPlaneFillColor: { red: 255, green: 152, blue: 0 }, edgesColor: { r: 255, g: 152, b: 0 }, facesColor: { r: 255, g: 152, b: 0 }, edgesVisibility: true, edgesOverlap: true, facesOverlap: false, facesTransparancy: 200, enableCustomHighlight: true, sceneGraph: false, edgeModel: true, reverseZoomWheel: false, enableZoomWheel: true, enableGestures: true, geometryType: "vsfx", rulerUnit: "Default" }; } class Options { constructor(emitter) { this._emitter = emitter; this._data = defaultOptions(); this.loadFromStorage(); } static defaults() { return defaultOptions(); } notifierChangeEvent() { console.warn("Options.notifierChangeEvent() has been deprecated since 25.3 and will be removed in a future release, use Options.change() instead."); this.change(); } change() { if (this._emitter !== undefined) { this.saveToStorage(); this._emitter.emit({ type: "optionschange", data: this }); } } saveToStorage() { if (typeof window !== "undefined") try { localStorage.setItem("od-client-settings", JSON.stringify(this.data)); } catch (error) { console.error("Cannot save client settings.", error); } } loadFromStorage() { if (typeof window !== "undefined") try { const item = localStorage.getItem("od-client-settings"); if (item) { const data = JSON.parse(item); this.data = { ...data }; } } catch (error) { console.error("Cannot load client settings.", error); } } resetToDefaults(fields) { if (fields !== undefined) { const defaults = Options.defaults(); const resetData = fields.reduce(((acc, field) => { acc[field] = defaults[field]; return acc; }), {}); this.data = { ...this.data, ...resetData }; } else { this.data = { ...this.data, ...Options.defaults() }; } } get data() { return this._data; } set data(value) { const enablePartialMode = value.enableStreamingMode ? value.enablePartialMode : false; const sceneGraph = enablePartialMode ? false : value.sceneGraph; this._data = { ...Options.defaults(), ...this._data, ...value, enablePartialMode: enablePartialMode, sceneGraph: sceneGraph }; this.change(); } get showWCS() { return this._data.showWCS; } set showWCS(value) { this._data.showWCS = value; this.change(); } get cameraAnimation() { return this._data.cameraAnimation; } set cameraAnimation(value) { this._data.cameraAnimation = value; this.change(); } get antialiasing() { return this._data.antialiasing; } set antialiasing(value) { this._data.antialiasing = value; this.change(); } get groundShadow() { return this._data.groundShadow; } set groundShadow(value) { this._data.groundShadow = value; this.change(); } get shadows() { return this._data.shadows; } set shadows(value) { this._data.shadows = value; this.change(); } get cameraAxisXSpeed() { return this._data.cameraAxisXSpeed; } set cameraAxisXSpeed(value) { this._data.cameraAxisXSpeed = value; this.change(); } get cameraAxisYSpeed() { return this._data.cameraAxisYSpeed; } set cameraAxisYSpeed(value) { this.cameraAxisYSpeed = value; this.change(); } get ambientOcclusion() { return this._data.ambientOcclusion; } set ambientOcclusion(value) { this._data.ambientOcclusion = value; this.change(); } get enableStreamingMode() { return this._data.enableStreamingMode; } set enableStreamingMode(value) { this._data.enableStreamingMode = value; if (!value) this._data.enablePartialMode = false; this.change(); } get enablePartialMode() { return this._data.enablePartialMode; } set enablePartialMode(value) { this._data.enablePartialMode = value; if (value) { this._data.enableStreamingMode = true; this._data.sceneGraph = false; } this.change(); } get memoryLimit() { return this._data.memoryLimit; } set memoryLimit(value) { this._data.memoryLimit = value; this.change(); } get cuttingPlaneFillColor() { return this._data.cuttingPlaneFillColor; } set cuttingPlaneFillColor(value) { this._data.cuttingPlaneFillColor = value; this.change(); } get edgesColor() { return this._data.edgesColor; } set edgesColor(value) { this._data.edgesColor = value; this.change(); } get facesColor() { return this._data.facesColor; } set facesColor(value) { this._data.facesColor = value; this.change(); } get edgesVisibility() { return this._data.edgesVisibility; } set edgesVisibility(value) { this._data.edgesVisibility = value; this.change(); } get edgesOverlap() { return this._data.edgesOverlap; } set edgesOverlap(value) { this._data.edgesOverlap = value; this.change(); } get facesOverlap() { return this._data.facesOverlap; } set facesOverlap(value) { this._data.facesOverlap = value; this.change(); } get facesTransparancy() { return this._data.facesTransparancy; } set facesTransparancy(value) { this._data.facesTransparancy = value; this.change(); } get enableCustomHighlight() { return this._data.enableCustomHighlight; } set enableCustomHighlight(value) { this._data.enableCustomHighlight = value; this.change(); } get sceneGraph() { return this._data.sceneGraph; } set sceneGraph(value) { this._data.sceneGraph = value; if (value) this._data.enablePartialMode = false; this.change(); } get edgeModel() { return Boolean(this._data.edgeModel); } set edgeModel(value) { this._data.edgeModel = Boolean(value); this.change(); } get reverseZoomWheel() { return this._data.reverseZoomWheel; } set reverseZoomWheel(value) { this._data.reverseZoomWheel = !!value; this.change(); } get enableZoomWheel() { return this._data.enableZoomWheel; } set enableZoomWheel(value) { this._data.enableZoomWheel = !!value; this.change(); } get enableGestures() { return this._data.enableGestures; } set enableGestures(value) { this._data.enableGestures = !!value; this.change(); } get geometryType() { return this._data.geometryType; } set geometryType(value) { this._data.geometryType = value; this.change(); } get rulerUnit() { return this._data.rulerUnit; } set rulerUnit(value) { this._data.rulerUnit = value; this.change(); } } const CanvasEvents = [ "click", "contextmenu", "dblclick", "mousedown", "mouseleave", "mousemove", "mouseup", "pointercancel", "pointerdown", "pointerleave", "pointermove", "pointerup", "touchcancel", "touchend", "touchmove", "touchstart", "wheel" ]; const CANVAS_EVENTS = CanvasEvents; class OdaGeAction { constructor(module) { this.setViewParams = (params) => { var _a; const extView = this.m_module.getViewer().getActiveTvExtendedView(); extView.setView(params.position, params.target, params.upVector, params.viewFieldWidth, params.viewFieldHeight, params.perspective); (_a = extView.delete) === null || _a === undefined ? undefined : _a.call(extView); }; this.getViewParams = () => { var _a; const view = this.m_module.getViewer().activeView; const obj = { position: view.viewPosition, target: view.viewTarget, upVector: view.upVector, viewFieldWidth: view.viewFieldWidth, viewFieldHeight: view.viewFieldHeight, perspective: view.perspective, }; (_a = view.delete) === null || _a === undefined ? undefined : _a.call(view); return obj; }; this.m_module = module; } getViewer() { return this.m_module.getViewer(); } getModel() { return this.getViewer().getMarkupModel(); } copyPoint(point) { const p = new this.m_module.Point3d(); p.set(point.x, point.y, point.z); return p; } createVector3d() { return new this.m_module.Vector3d(); } createPoint3d() { return new this.m_module.Point3d(); } createMatrix3d() { return new this.m_module.Matrix3d(); } createPlane() { return new this.m_module.OdTvPlane(); } toVector(geVector) { return this.m_module.Vector3d.createFromArray(geVector); } toGeVector(v) { return [v.x, v.y, v.z]; } toGePoint(point) { return [point.x, point.y, point.z]; } toPoint(gePoint) { return this.m_module.Point3d.createFromArray(gePoint); } screenToWorld(x, y) { return this.toPoint(this.m_module.getViewer().screenToWorld(x, y)); } toDoubleArray(points) { const p = []; for (let i = 0; i < points.length; i++) { p.push(points[i].x); p.push(points[i].y); p.push(points[i].z); } return p; } correctCameraTarget() { const params = this.getViewParams(); const ext = this.m_module.getViewer().getActiveExtents(); const { min, max } = ext; const target = this.toPoint(params.target); const contains = target.x >= min.x && target.y >= min.y && target.z >= min.z && target.x <= max.x && target.y <= max.y && target.z <= max.z; if (!contains) { params.target = ext.center(); this.setViewParams(params); } } } /////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// /* eslint-disable no-unused-vars */ const CLICK_DELTA = 5; const INTERACTIVITY_FPS = 24; class OdBaseDragger extends OdaGeAction { constructor(subject) { super(subject.visualizeJs); this.beginInteractivity = () => { const viewer = this.getViewer(); const view = viewer.activeView; if (view["beginInteractivity"]) { view.beginInteractivity(INTERACTIVITY_FPS); this.subject.update(); } view.delete(); }; this.endInteractivity = () => { const viewer = this.getViewer(); const view = viewer.activeView; if (view["endInteractivity"]) { view.endInteractivity(); const device = this.getViewer().getActiveDevice(); const canvas = this.m_module.canvas; device.invalidate([0, 0, canvas.width, canvas.height]); device.delete(); this.subject.update(); } view.delete(); }; this.subject = subject; this.needInputText = false; this.mouseDownPosition = { x: 0, y: 0 }; this.autoSelect = false; this.onmessage = (event) => this.subject.emitEvent(event); this.canvasEvents = CANVAS_EVENTS; } initialize() { this.canvasEvents = this.canvasEvents.filter((x) => typeof this[x] === "function"); this.canvasEvents.forEach((x) => (this[x] = this[x].bind(this))); this.canvasEvents.forEach((x) => this.subject.on(x, this[x])); this.getViewer().setEnableAutoSelect(!!this.autoSelect); } dispose() { this.canvasEvents.forEach((x) => this.subject.off(x, this[x])); } relativeCoords(event) { return { x: event.offsetX * window.devicePixelRatio, y: event.offsetY * window.devicePixelRatio }; } pointerdown(ev) { if (!ev.isPrimary || OdBaseDragger.isGestureActive) { return; } ev.target.setPointerCapture(ev.pointerId); const relCoord = this.relativeCoords(ev); this.isDragging = true; this.mouseDownPosition = { x: relCoord.x, y: relCoord.y }; this.start(relCoord.x, relCoord.y, ev.clientX, ev.clientY); this.subject.update(); } pointerup(ev) { if (OdBaseDragger.needSkipPointerUp) { return; } if (!ev.isPrimary) { return; } ev.target.releasePointerCapture(ev.pointerId); const relCoord = this.relativeCoords(ev); this.end(relCoord.x, relCoord.y, ev.clientX, ev.clientY); this.isDragging = false; this.subject.update(); } pointercancel(ev) { if (!ev.isPrimary) { return; } this.m_module.canvas.dispatchEvent(new PointerEvent("pointerup", ev)); } pointermove(ev) { if (!ev.isPrimary || OdBaseDragger.isGestureActive) { return; } const relCoord = this.relativeCoords(ev); this.drag(relCoord.x, relCoord.y, ev.clientX, ev.clientY); if (this.isDragging) { this.subject.update(); } } click(ev) { const viewer = this.getViewer(); const relCoord = this.relativeCoords(ev); const x = relCoord.x; const y = relCoord.y; const isNotDragging = Math.abs(x - this.mouseDownPosition.x) < CLICK_DELTA && Math.abs(y - this.mouseDownPosition.y) < CLICK_DELTA; if (viewer && viewer.getEnableAutoSelect() && isNotDragging) { viewer.unselect(); viewer.select(x, y, x, y); this.subject.update(); const selectionSet = viewer.getSelected(); const handles = this.subject.getSelected(); this.onmessage({ type: "select", data: selectionSet, handles }); } } dblclick(ev) { const viewer = this.getViewer(); const relCoord = this.relativeCoords(ev); const x = relCoord.x; const y = relCoord.y; const device = viewer.getActiveDevice(); const clickView = device.viewAt([x, y]); if (clickView && !clickView.active) { viewer.activeView = clickView; clickView.delete(); this.subject.update(); } else { if (viewer && viewer.getEnableAutoSelect()) { const pSelected = viewer.getSelected(); if (!pSelected.isNull() && pSelected.numItems() !== 0) { const itr = pSelected.getIterator(); const entity = itr.getEntity(); viewer.zoomToEntity(entity); this.onmessage({ type: "zoomtoentity", data: entity }); this.subject.update(); this.deleteAll([itr, entity]); } } } device.delete(); } start(x, y, absoluteX = 0, absoluteY = 0) { } drag(x, y, absoluteX = 0, absoluteY = 0) { } end(x, y, absoluteX = 0, absoluteY = 0) { } getActiveMarkupEntity(entityName) { return this.subject.addMarkupEntity(entityName); } syncOverlayView() { return this.subject.syncOverlay(); } deleteAll(objects) { var _a; for (const obj of objects) { (_a = obj === null || obj === undefined ? undefined : obj.delete) === null || _a === undefined ? undefined : _a.call(obj); } } updatePreview() { } static set isGestureActive(value) { if (OdBaseDragger._isGestureActive === value) { return; } OdBaseDragger._isGestureActive = value; if (OdBaseDragger._isGestureActive) { OdBaseDragger.needSkipPointerUp = true; } } static get isGestureActive() { return OdBaseDragger._isGestureActive; } static get needSkipPointerUp() { if (OdBaseDragger._needSkipPointerUp) { OdBaseDragger.needSkipPointerUp = false; return true; } return false; } static set needSkipPointerUp(value) { OdBaseDragger._needSkipPointerUp = value; } } OdBaseDragger._isGestureActive = false; OdBaseDragger._needSkipPointerUp = false; function createHtmlElementIfNeed(element, targetElement, dataTestId) { if (!element) { element = document.createElement("div"); element.setAttribute("data-testid", dataTestId); targetElement.appendChild(element); } return element; } function destroyHtmlElement(element, targetElement) { if (element) { targetElement.removeChild(element); } return null; } function worldToScreen(gePoint, moduleInstance, viewer) { const worldPoint = moduleInstance.Point3d.createFromArray(gePoint); const avp = viewer.activeView; const mtx = avp.worldToDeviceMatrix; const devicePoint = worldPoint.transformBy(mtx); const res = { x: devicePoint.x / window.devicePixelRatio, y: devicePoint.y / window.devicePixelRatio }; mtx.delete(); worldPoint.delete(); devicePoint.delete(); avp.delete(); return res; } function getDistance(gePoint1, gePoint2, moduleInstance) { const tvPoint1 = moduleInstance.Point3d.createFromArray(gePoint1); const tvPoint2 = moduleInstance.Point3d.createFromArray(gePoint2); const distance = tvPoint1.distanceTo(tvPoint2).toFixed(2); tvPoint1.delete(); tvPoint2.delete(); return distance; } function normalizeFloat(value) { return value < 0 ? Math.ceil(value) : Math.floor(value); } const lineSegmentsIntersect = (p1, p2, p3, p4) => { const a_dx = p2.x - p1.x; const a_dy = p2.y - p1.y; const b_dx = p4.x - p3.x; const b_dy = p4.y - p3.y; const s = (-a_dy * (p1.x - p3.x) + a_dx * (p1.y - p3.y)) / (-b_dx * a_dy + a_dx * b_dy); const t = (+b_dx * (p1.y - p3.y) - b_dy * (p1.x - p3.x)) / (-b_dx * a_dy + a_dx * b_dy); return s >= 0 && s <= 1 && t >= 0 && t <= 1 ? { x: normalizeFloat(p1.x + t * a_dx), y: normalizeFloat(p1.y + t * a_dy), } : false; }; function checkSegmentsIntersect(p1, p2, p3, p4, res) { const r = lineSegmentsIntersect(p1, p2, p3, p4); if (r) { res.push(r); } } function isInsideRect(p, width, height) { return p.x <= width && p.x >= 0 && p.y <= height && p.y >= 0; } function getDataForDrawLineWithFixed(p1, p2, width, height) { const pLU = { x: 0, y: 0 }; const pRU = { x: width, y: 0 }; const pLB = { x: 0, y: height }; const pRB = { x: width, y: height }; const intersects = []; checkSegmentsIntersect(p1, p2, pLU, pRU, intersects); checkSegmentsIntersect(p1, p2, pLU, pLB, intersects); checkSegmentsIntersect(p1, p2, pLB, pRB, intersects); checkSegmentsIntersect(p1, p2, pRB, pRU, intersects); let fixedP1 = null; let fixedP2 = null; if (intersects.length === 0) { fixedP1 = p1; fixedP2 = p2; } else if (intersects.length === 1) { if (isInsideRect(p1, width, height)) { fixedP1 = p1; fixedP2 = intersects[0]; } else { fixedP1 = intersects[0]; fixedP2 = p2; } } else { fixedP1 = intersects[0]; fixedP2 = intersects[1]; } const dx = fixedP2.x - fixedP1.x; const dy = fixedP2.y - fixedP1.y; let angle = (180 * Math.atan(dy / dx)) / Math.PI; if (dx < 0) { angle -= 180; } const size = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); return { angle, width: size, p1: fixedP1, p2: fixedP2 }; } function onSetCallback(element, cb) { if (element) { element.onclick = cb ? () => cb() : () => { }; } } function onSetSelectivity(element, enable) { element.style.pointerEvents = enable ? "auto" : "none"; } /////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// class MeasureLineItem { constructor(targetElement, viewer, moduleInstance) { this.htmlElemStartPoint = null; this.htmlElemEndPoint = null; this.htmlElemLine = null; this.htmlElemTitle = null; this.startPoint = null; this.endPoint = null; this.unit = ""; this.scale = 1.0; this.size = 10.0; this.lineThickness = 2; this.style = { border: "2px solid #FFFFFF", background: "#009bff", color: "white", boxShadow: "0 0 10px rgba(0,0,0,0.5)", }; this.htmlElemStartPoint = createHtmlElementIfNeed(this.htmlElemStartPoint, targetElement, "ruler-start"); this.htmlElemEndPoint = createHtmlElementIfNeed(this.htmlElemEndPoint, targetElement, "ruler-end"); this.htmlElemLine = createHtmlElementIfNeed(this.htmlElemLine, targetElement, "ruler-line"); this.htmlElemTitle = createHtmlElementIfNeed(this.htmlElemTitle, targetElement, "ruler-value"); this.viewer = viewer; this.moduleInstance = moduleInstance; this.targetElement = targetElement; this.isFinishDraw = false; } drawMeasureLine() { const pointSize = this.size; const rect = this.moduleInstance.canvas.getBoundingClientRect(); // draw start point if (this.startPoint) { this.htmlElemStartPoint = createHtmlElementIfNeed(this.htmlElemStartPoint, this.targetElement, "ruler-start"); const pScreenStart = worldToScreen(this.startPoint, this.moduleInstance, this.viewer); if (isInsideRect(pScreenStart, rect.width, rect.height)) { this.htmlElemStartPoint.style.display = "block"; this.htmlElemStartPoint.style.cursor = "pointer"; this.htmlElemStartPoint.style.position = "absolute"; this.htmlElemStartPoint.style.top = `${pScreenStart.y - pointSize / 2}px`; this.htmlElemStartPoint.style.left = `${pScreenStart.x - pointSize / 2}px`; this.htmlElemStartPoint.style.borderRadius = `${pointSize}px`; this.htmlElemStartPoint.style.border = this.style.border; this.htmlElemStartPoint.style.background = this.style.background; this.htmlElemStartPoint.style.zIndex = "2"; this.htmlElemStartPoint.style.width = `${pointSize}px`; this.htmlElemStartPoint.style.height = `${pointSize}px`; this.htmlElemStartPoint.style.boxShadow = this.style.boxShadow; } else { this.htmlElemStartPoint.style.display = "none"; } } // draw end point if (this.endPoint && this.isFinishDraw) { this.htmlElemEndPoint = createHtmlElementIfNeed(this.htmlElemEndPoint, this.targetElement, "ruler-end"); const pScreenEnd = worldToScreen(this.endPoint, this.moduleInstance, this.viewer); if (isInsideRect(pScreenEnd, rect.width, rect.height)) { this.htmlElemEndPoint.style.display = "block"; this.htmlElemEndPoint.style.cursor = "pointer"; this.htmlElemEndPoint.style.position = "absolute"; this.htmlElemEndPoint.style.top = `${pScreenEnd.y - pointSize / 2}px`; this.htmlElemEndPoint.style.left = `${pScreenEnd.x - pointSize / 2}px`; this.htmlElemEndPoint.style.borderRadius = `${pointSize}px`; this.htmlElemEndPoint.style.border = this.style.border; this.htmlElemEndPoint.style.background = this.style.background; this.htmlElemEndPoint.style.zIndex = "2"; this.htmlElemEndPoint.style.width = `${pointSize}px`; this.htmlElemEndPoint.style.height = `${pointSize}px`; this.htmlElemEndPoint.style.boxShadow = this.style.boxShadow; } else { this.htmlElemEndPoint.style.display = "none"; } } if (this.endPoint && this.startPoint) { const point1 = worldToScreen(this.startPoint, this.moduleInstance, this.viewer); const point2 = worldToScreen(this.endPoint, this.moduleInstance, this.viewer); const { p1, p2, angle, width } = getDataForDrawLineWithFixed(point1, point2, rect.width, rect.height); const dx = p2.x - p1.x; const dy = p2.y - p1.y; const height = this.lineThickness; if (isInsideRect(p1, rect.width, rect.height) && isInsideRect(p2, rect.width, rect.height)) { this.htmlElemLine = createHtmlElementIfNeed(this.htmlElemLine, this.targetElement, "ruler-line"); this.htmlElemLine.style.display = "block"; this.htmlElemLine.style.cursor = "pointer"; this.htmlElemLine.style.position = "absolute"; this.htmlElemLine.style.top = `${p1.y}px`; this.htmlElemLine.style.left = `${p1.x}px`; this.htmlElemLine.style.width = `${width}px`; this.htmlElemLine.style.transform = `rotate(${angle}deg)`; this.htmlElemLine.style.transformOrigin = `0px ${height / 2}px`; this.htmlElemLine.style.boxShadow = this.style.boxShadow; this.htmlElemLine.style.border = "none"; this.htmlElemLine.style.background = this.style.background; this.htmlElemLine.style.zIndex = "1"; this.htmlElemLine.style.height = `${height}px`; const distance = `${this.getDistance()} ${this.unit}`; const pX = p1.x + dx / 2; const pY = p1.y + dy / 2; const widthTitle = distance.length * 10; this.htmlElemTitle = createHtmlElementIfNeed(this.htmlElemTitle, this.targetElement, "ruler-value"); this.htmlElemTitle.style.display = "block"; this.htmlElemTitle.style.cursor = "pointer"; this.htmlElemTitle.style.font = "10px"; this.htmlElemTitle.style.color = "white"; this.htmlElemTitle.style.position = "Absolute"; this.htmlElemTitle.style.top = `${pY}px`; this.htmlElemTitle.style.left = `${pX - widthTitle / 2}px`; this.htmlElemTitle.style.width = `${widthTitle}px`; this.htmlElemTitle.style.transformOrigin = "0px 0px"; this.htmlElemTitle.style.borderRadius = "5px"; this.htmlElemTitle.style.boxShadow = this.style.boxShadow; this.htmlElemTitle.style.border = "none"; this.htmlElemTitle.style.background = this.style.background; this.htmlElemTitle.style.zIndex = "3"; this.htmlElemTitle.style.padding = "2px"; this.htmlElemTitle.style.textAlign = "center"; this.htmlElemTitle.innerHTML = `${distance}`; } else { this.htmlElemLine.style.display = "none"; this.htmlElemTitle.style.display = "none"; } } } getDistance() { let distance = getDistance(this.startPoint, this.endPoint, this.moduleInstance); if (Math.abs(this.scale - 1.0) > 10e-5) { distance = (distance / this.scale).toFixed(2); } return distance; } setStartPoint(gePoint) { this.startPoint = gePoint; this.drawMeasureLine(); } setEndPoint(gePoint, isFinish) { this.isFinishDraw = isFinish === undefined ? true : isFinish; this.endPoint = gePoint; this.drawMeasureLine(); } update() { this.drawMeasureLine(); } setSize(size) { this.size = size; this.drawMeasureLine(); } clear() { this.endPoint = null; this.startPoint = null; this.htmlElemStartPoint = destroyHtmlElement(this.htmlElemStartPoint, this.targetElement); this.htmlElemEndPoint = destroyHtmlElement(this.htmlElemEndPoint, this.targetElement); this.htmlElemLine = destroyHtmlElement(this.htmlElemLine, this.targetElement); this.htmlElemTitle = destroyHtmlElement(this.htmlElemTitle, this.targetElement); } setUnit(unit) { this.unit = unit; this.drawMeasureLine(); } setConversionFactor(scale) { this.scale = scale; this.drawMeasureLine(); } setStyle(style) { this.style = style; this.drawMeasureLine(); } setSelectionReactor(reactor) { onSetCallback(this.htmlElemStartPoint, reactor ? reactor.onStartPoint : null); onSetCallback(this.htmlElemEndPoint, reactor ? reactor.onEndPoint : null); onSetCallback(this.htmlElemTitle, reactor ? reactor.onTitle : null); } setSelectability(enable) { onSetSelectivity(this.htmlElemStartPoint, enable); onSetSelectivity(this.htmlElemEndPoint, enable); onSetSelectivity(this.htmlElemLine, enable); onSetSelectivity(this.htmlElemTitle, enable); } } function renameUnit(table, unit) { return table[unit] || unit; } class MeasureLineDragger extends OdBaseDragger { constructor(subject) { var _a; super(subject); this.lineThickness = 2; this.press = false; this.gripingRadius = 5.0; this.firstPoint = null; this.secondPoint = null; this.renameUnitTable = { Millimeters: "mm", Centimeters: "cm", Meters: "m", Feet: "ft", Inches: "in", Yards: "yd", Kilometers: "km", Miles: "mi", Micrometers: "µm", MicroInches: "µin", Default: "unit", }; this.items = []; this.canvasEvents.push("resize"); this.oldRulerUnit = (_a = subject.options.rulerUnit) !== null && _a !== undefined ? _a : "Default"; this.optionsChange = this.optionsChange.bind(this); } initialize() { super.initialize(); this.m_overlayElement = document.createElement("div"); this.m_overlayElement.style.background = "rgba(0,0,0,0)"; this.m_overlayElement.style.position = "fixed"; this.m_overlayElement.style.zIndex = "1"; this.m_overlayElement.style.pointerEvents = "none"; document.body.appendChild(this.m_overlayElement); this.subject.addEventListener("optionschange", this.optionsChange); this.resize(); } dispose() { super.dispose(); this.m_overlayElement.remove(); this.subject.removeEventListener("optionschange", this.optionsChange); } updatePreview() { this.items.forEach((item) => item.update()); } resize() { const rect = this.m_module.canvas.getBoundingClientRect(); this.m_overlayElement.style.top = `${rect.top}px`; this.m_overlayElement.style.left = `${rect.left}px`; this.m_overlayElement.style.width = `${rect.width}px`; this.m_overlayElement.style.height = `${rect.height}px`; } getSnapPointRadius() { const view = this.getViewer().activeView; const corners = view.viewDcCorners(); const pt1 = corners.lowerLeft; const pt2 = corners.upperRight; pt2[0] -= pt1[0]; pt2[1] -= pt1[1]; return Math.min(pt2[0], pt2[1]) / 120; } start(x, y) { this.createNewMeasureIfNeed(); const point = this.getViewer().getSnapPoint(x, y, this.gripingRadius); if (point) { this.firstPoint = point; this.previewMeasureLine.setStartPoint(this.firstPoint); } } drag(x, y) { this.createNewMeasureIfNeed(); const point = this.getViewer().getSnapPoint(x, y, this.gripingRadius); if (this.isDragging) { if (point) { if (this.firstPoint) { this.secondPoint = point; this.previewMeasureLine.setStartPoint(this.firstPoint); this.previewMeasureLine.setEndPoint(this.secondPoint, true); } else { this.firstPoint = point; this.previewMeasureLine.setStartPoint(this.firstPoint); } } else { this.secondPoint = null; this.previewMeasureLine.clear(); this.previewMeasureLine.setStartPoint(this.firstPoint); this.previewMeasureLine.setEndPoint(this.getViewer().screenToWorld(x, y), false); } } else { if (point) { this.previewMeasureLine.setStartPoint(point); } else { this.previewMeasureLine.clear(); } } } end() { if (this.firstPoint && this.secondPoint) { const newLineMeasure = this.createMeasureLine(); newLineMeasure.setStartPoint(this.firstPoint); newLineMeasure.setEndPoint(this.secondPoint, true); } this.firstPoint = null; this.secondPoint = null; this.previewMeasureLine.clear(); } createNewMeasureIfNeed() { if (!this.previewMeasureLine) { this.previewMeasureLine = this.createMeasureLine(); } } createMeasureLine() { const viewer = this.m_module.getViewer(); const item = new MeasureLineItem(this.m_overlayElement, viewer, this.m_module); item.lineThickness = this.lineThickness || item.lineThickness; const isDefaultUnit = !this.subject.options.rulerUnit || this.subject.options.rulerUnit === "Default"; item.setUnit(renameUnit(this.renameUnitTable, isDefaultUnit ? viewer.getUnit() : this.subject.options.rulerUnit)); if (!isDefaultUnit) { const fromUnit = this.getKUnitByName(viewer.getUnit()); const toUnit = this.getKUnitByName(this.subject.options.rulerUnit); const multiplier = viewer.getUnitsConversionCoef(fromUnit, toUnit); this.conversionFactor = 1 / multiplier; item.setConversionFactor(this.conversionFactor); } else { item.setConversionFactor(1.0); } this.items.push(item); return item; } optionsChange(event) { var _a; const options = event.data; const toUnitName = (_a = options.rulerUnit) !== null && _a !== undefined ? _a : "Default"; if (this.oldRulerUnit === toUnitName) return; this.oldRulerUnit = toUnitName; const drawingUnit = this.m_module.getViewer().getUnit(); const eToUnit = this.getKUnitByName(toUnitName); const eFromUnit = this.getKUnitByName(drawingUnit); this.items.forEach((item) => { if (toUnitName === "Default") { item.setUnit(renameUnit(this.renameUnitTable, drawingUnit)); item.setConversionFactor(1.0); } else { item.setUnit(renameUnit(this.renameUnitTable, toUnitName)); const multiplier = this.m_module.getViewer().getUnitsConversionCoef(eFromUnit, eToUnit); this.conversionFactor = 1 / multiplier; item.setConversionFactor(this.conversionFactor); } }); } getKUnitByName(unitName) { let eUnit = this.m_module.Units.kUserDefined; switch (unitName) { case "Millimeters": eUnit = this.m_module.Units.kMillimeters; break; case "Centimeters": eUnit = this.m_module.Units.kCentimeters; break; case "Me