UNPKG

@inweb/viewer-visualize

Version:

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

1,290 lines (1,242 loc) 158 kB
import { CANVAS_EVENTS, draggersRegistry, commandsRegistry, componentsRegistry, Loader, loadersRegistry, Options } from "@inweb/viewer-core"; export * from "@inweb/viewer-core"; import { EventEmitter2 } from "@inweb/eventemitter2"; import { Markup } from "@inweb/markup"; export * from "@inweb/markup"; 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: min, max: 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); } } } 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: 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: 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"; } 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; this.size = 10; 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(); 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"; } } 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: p1, p2: p2, angle: angle, width: 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) > 1e-4) { 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; 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); } 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); } 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 "Meters": eUnit = this.m_module.Units.kMeters; break; case "Feet": eUnit = this.m_module.Units.kFeet; break; case "Inches": eUnit = this.m_module.Units.kInches; break; case "Yards": eUnit = this.m_module.Units.kYards; break; case "Kilometers": eUnit = this.m_module.Units.kKilometers; break; case "Miles": eUnit = this.m_module.Units.kMiles; break; case "Micrometers": eUnit = this.m_module.Units.kMicrometers; break; case "MicroInches": eUnit = this.m_module.Units.kMicroInches; break; } return eUnit; } } const FocalLengthConst = 42; const calcFocalLength = (lensLength, fieldWidth, fieldHeight) => lensLength / FocalLengthConst * Math.sqrt(fieldWidth * fieldWidth + fieldHeight * fieldHeight); class OdaWalkDragger extends OdBaseDragger { constructor(subject) { super(subject); this.viewer = undefined; this.multiplier = 5; this.speed = 1; this.keyPressMap = new Set; this.keydown = this.keydown.bind(this); this.keyup = this.keyup.bind(this); this.lastFrameTS = 0; this.animationId = undefined; this.processMovement = this.processMovement.bind(this); this.deltaAngle = Math.PI / 3600; this.autoSelect = true; } initialize() { super.initialize(); this.viewer = this.getViewer(); window.addEventListener("keydown", this.keydown, false); window.addEventListener("keyup", this.keyup, false); this.oldWCSEnableValue = this.viewer.getEnableWCS(); this.viewer.setEnableWCS(false); const view = this.viewer.activeView; const maxDimension = this.getMaxDimension(view); this.speed = maxDimension / 3e4; this.subject.emitEvent({ type: "walkstart" }); this.viewParams = this.getViewParams(); this.setViewParams(this.viewParams); const model = this.viewer.getActiveModel(); this.cameraId = model.appendCamera("Camera0"); this.setupCamera(view); model.delete(); this.cameraWalker = new this.m_module.OdTvCameraWalker; this.cameraWalker.setCamera(this.cameraId); this.subject.update(); this.enableZoomWheelPreviousValue = this.subject.options.enableZoomWheel; this.subject.options.enableZoomWheel = false; } dispose() { var _a; this.oldWCSEnableValue = this.oldWCSEnableValue !== undefined ? this.oldWCSEnableValue : this.subject.options.showWCS; this.viewer.setEnableWCS(this.oldWCSEnableValue); super.dispose(); this.keyPressMap.clear(); window.removeEventListener("keydown", this.keydown); window.removeEventListener("keyup", this.keyup); if (this.animationId) { window.cancelAnimationFrame(this.animationId); this.animationId = undefined; } if (this.cameraId) { const model = this.viewer.getActiveModel(); model.removeEntity(this.cameraId); model.delete(); (_a = this.cameraWalker) === null || _a === undefined ? undefined : _a.delete(); } if (this.viewParams) { this.setViewParams(this.viewParams); const avp = this.viewer.activeView; avp.delete(); } this.subject.update(true); this.subject.options.enableZoomWheel = this.enableZoomWheelPreviousValue; } keydown(ev) { switch (ev.code) { case "NumpadSubtract": case "Minus": if (this.multiplier > 1) { this.multiplier = this.multiplier - 1; this.subject.emitEvent({ type: "walkspeedchange", data: this.multiplier }); } break; case "NumpadAdd": case "Equal": if (this.multiplier < 10) { this.multiplier = this.multiplier + 1; this.subject.emitEvent({ type: "walkspeedchange", data: this.multiplier }); } break; case "KeyW": case "KeyA": case "KeyS": case "KeyD": case "KeyQ": case "KeyE": this.keyPressMap.add(ev.code); if (!this.animationId) this.processMovement(0); break; } } keyup(ev) { this.keyPressMap.delete(ev.code); if (this.keyPressMap.size < 1 && this.animationId) { window.cancelAnimationFrame(this.animationId); this.animationId = undefined; this.lastFrameTS = 0; } } processMovement(timestamp) { this.animationId = requestAnimationFrame(this.processMovement); if (this.lastFrameTS !== 0) { const deltaTS = timestamp - this.lastFrameTS; const currentDelta = this.multiplier * deltaTS * this.speed; for (const keyCode of this.keyPressMap) { switch (keyCode) { case "KeyW": this.cameraWalker.moveForward(currentDelta); break; case "KeyS": this.cameraWalker.moveBackward(currentDelta); break; case "KeyA": this.cameraWalker.moveLeft(currentDelta); break; case "KeyD": this.cameraWalker.moveRight(currentDelta); break; case "KeyQ": this.cameraWalker.moveUp(currentDelta); break; case "KeyE": this.cameraWalker.moveDown(currentDelta); break; } } this.subject.update(); } this.lastFrameTS = timestamp; } start(x, y) { this.dragPosition = { x: x, y: y }; } drag(x, y) { if (this.cameraId && this.isDragging) { const dltX = x - this.dragPosition.x; const dltY = y - this.dragPosition.y; this.dragPosition = { x: x, y: y }; if (dltX !== 0) this.turnLeft(-dltX * this.deltaAngle); if (dltY !== 0) this.cameraWalker.turnDown(dltY * this.deltaAngle); this.subject.update(); } } turnLeft(angle) { const pCamera = this.cameraWalker.camera().openObjectAsCamera(); const dir = this.toVector(pCamera.direction()); const up = this.toVector(pCamera.upVector()); const pos = pCamera.position(); const rotMatrix = this.createMatrix3d(); const zAxisVector = [ 0, 0, 1 ]; rotMatrix.setToRotation(angle, zAxisVector, pos); dir.transformBy(rotMatrix); up.transformBy(rotMatrix); pCamera.setupCameraByDirection(pos, dir.toArray(), up.toArray()); pCamera.delete(); } setupCamera(view) { const pCamera = this.cameraId.openObjectAsCamera(); const target = view.viewTarget; pCamera.setDisplayGlyph(false); pCamera.setDisplayTarget(false); pCamera.setAutoAdjust(true); pCamera.setupCamera(view.viewPosition, target, view.upVector); pCamera.setNearClip(false, 1); pCamera.setFarClip(false, 0); pCamera.setViewParameters(view.viewFieldWidth, view.viewFieldHeight, true); const focalL = calcFocalLength(view.lensLength, view.viewFieldWidth, view.viewFieldHeight); const pTarget = this.toPoint(view.viewTarget); const viewDir = this.toPoint(view.viewPosition); const viewDirSub = viewDir.sub(pTarget); const viewDirVec = viewDirSub.asVector(); const viewDirVecNormal = viewDirVec.normalize(); const geViewDir = this.toGeVector(viewDirVecNormal); const newGeViewDir = [ geViewDir[0] * focalL, geViewDir[1] * focalL, geViewDir[2] * focalL ]; const pTarget2 = this.toPoint(view.viewTarget); const newGeViewDirPt = this.toPoint(newGeViewDir); const newPos = pTarget2.add(newGeViewDirPt); pCamera.setupCamera(this.toGePoint(newPos), view.viewTarget, view.upVector); this.deleteAll([ pTarget, viewDir, viewDirSub, viewDirVec, viewDirVecNormal, pTarget2, newGeViewDirPt, newPos ]); pCamera.assignView(view); pCamera.delete(); } getMaxDimension(view) { const [xmax, ymax, zmax] = view.sceneExtents.max(); const [xmin, ymin, zmin] = view.sceneExtents.min(); const volume = [ xmax - xmin, ymax - ymin, zmax - zmin ]; return Math.max(...volume); } } class OdBaseCuttingPlaneDragger extends OdBaseDragger { constructor(subject) { super(subject); this.press = false; const ext = this.getViewer().getActiveExtents(); const min = ext.min(); const max = ext.max(); this.m_size_x = Math.abs(max[0] - min[0]) / 2; this.m_size_y = Math.abs(max[1] - min[1]) / 2; this.m_size_z = Math.abs(max[2] - min[2]) / 2; this.m_center = this.toPoint(ext.center()); this.m_normal = this.createNormal(); const avp = this.getViewer().activeView; const plane = this.createPlane(); plane.set(this.toGePoint(this.m_center), this.m_normal); avp.addCuttingPlane(plane); this.index = avp.numCuttingPlanes() - 1; const {red: red, green: green, blue: blue} = this.subject.options.cuttingPlaneFillColor; avp.setEnableCuttingPlaneFill(true, red, green, blue); avp.setCuttingPlaneFillPatternEnabled(true, this.m_module.CuttingPlaneFillStyle.kHorizontalBars, 0, 0, 0); this.m_model = this.getModel(); this.createPreview(); this.deleteAll([ ext, avp, plane ]); } dispose() { super.dispose(); if (this.m_entity) { this.m_model.removeEntity(this.m_entity); this.deleteAll([ this.m_model, this.m_entity, this.planePreview, this.m_center ]); this.m_entity = null; this.planePreview = null; this.subject.update(); } } createNormal() { return [ 0, 0, 0 ]; } handleDelta(delta) { return delta; } getPlanePreviewCoordinate() { return []; } start(x, y) { this.press = true; this.m_last = this.screenToWorld(x, y); this.m_click = { x: x, y: y }; } drag(x, y) { if (this.press) { const point = this.screenToWorld(x, y); const delta = this.handleDelta(point.sub(this.m_last)); const oldCenter = this.m_center; this.m_center = oldCenter.add(delta); const oldLast = this.m_last; this.m_last = point; const avp = this.getViewer().activeView; const plane = this.createPlane(); const newPlane = plane.set(this.toGePoint(this.m_center), this.m_normal); const newCutting = avp.updateCuttingPlane(this.index, plane); this.drawPreview(); this.deleteAll([ avp, plane, oldCenter, delta, oldLast, newPlane, newCutting ]); const device = this.getViewer().getActiveDevice(); device.invalidate(device.getSize()); } } end(x, y) { this.press = false; if (x === this.m_click.x && y === this.m_click.y) { this.m_normal = [ this.m_normal[0] * -1, this.m_normal[1] * -1, this.m_normal[2] * -1 ]; const avp = this.getViewer().activeView; const plane = this.createPlane(); plane.set(this.toGePoint(this.m_center), this.m_normal); avp.updateCuttingPlane(this.index, plane); this.deleteAll([ avp, plane, this.m_last ]); const device = this.getViewer().getActiveDevice(); device.invalidate(device.getSize()); } } createPreview() { this.m_entity = this.m_model.appendEntity("&CuttingPlanePreview"); const GeometryTypes = this.m_module.GeometryTypes; const transparencyDef = new this.m_module.OdTvTransparencyDef; const colorDef = new this.m_module.OdTvColorDef(112, 112, 112); transparencyDef.setValue(.9); const entityPtr = this.m_entity.openObject(); entityPtr.setColor(colorDef, GeometryTypes.kFaces.value); colorDef.setColor(112, 112, 112); entityPtr.setColor(colorDef, GeometryTypes.kEdges.value); entityPtr.setLineWeight(5); entityPtr.setTransparency(transparencyDef, GeometryTypes.kFaces); transparencyDef.setValue(1); entityPtr.setTransparency(transparencyDef, GeometryTypes.kEdges); this.planePreview = entityPtr.appendPolygon(this.getPlanePreviewCoordinate()); const polygonPtr = this.planePreview.openAsPolygon(); polygonPtr.setFilled(true); this.deleteAll([ transparencyDef, colorDef, entityPtr, polygonPtr, GeometryTypes ]); this.subject.syncOverlay(); } drawPreview() { const polygonPtr = this.planePreview.openAsPolygon(); polygonPtr.setPoints(this.getPlanePreviewCoordinate()); this.deleteAll([ polygonPtr ]); this.subject.syncOverlay(); } } class OdCuttingPlaneXAxisDragger extends OdBaseCuttingPlaneDragger { createNormal() { return [ 1, 0, 0 ]; } handleDelta(delta) { delta.y = 0; delta.z = 0; return delta; } getPlanePreviewCoordinate() { return [ this.m_center.x, this.m_center.y - this.m_size_y, this.m_center.z - this.m_size_z, this.m_center.x, this.m_center.y + this.m_size_y, this.m_center.z - this.m_size_z, this.m_center.x, this.m_center.y + this.m_size_y, this.m_center.z + this.m_size_z, this.m_center.x, this.m_center.y - this.m_size_y, this.m_center.z + this.m_size_z ]; } } class OdCuttingPlaneYAxisDragger extends OdBaseCuttingPlaneDragger { createNormal() { return [ 0, 1, 0 ]; } handleDelta(delta) { delta.x = 0; delta.z = 0; return delta; } getPlanePreviewCoordinate() { return [ this.m_center.x - this.m_size_x, this.m_center.y, this.m_center.z - this.m_size_z, this.m_center.x + this.m_size_x, this.m_center.y, this.m_center.z - this.m_size_z, this.m_center.x + this.m_size_x, this.m_center.y, this.m_center.z + this.m_size_z, this.m_center.x - this.m_size_x, this.m_center.y, this.m_center.z + this.m_size_z ]; } } class OdCuttingPlaneZAxisDragger extends OdBaseCuttingPlaneDragger { createNormal() { return [ 0, 0, 1 ]; } handleDelta(delta) { delta.x = 0; delta.y = 0; return delta; } getPlanePreviewCoordinate() { return [ this.m_center.x - this.m_size_x, this.m_center.y - this.m_size_y, this.m_center.z, this.m_center.x + this.m_size_x, this.m_center.y - this.m_size_y, this.m_center.z, this.m_center.x + this.m_size_x, this.m_center.y + this.m_size_y, this.m_center.z, this.m_center.x - this.m_size_x, this.m_center.y + this.m_size_y, this.m_center.z ]; } } class OrbitAction { constructor(m_module, subject, beginInteractivity, endInteractivity) { this._m_module = m_module; this._subject = subject; this._beginInteractivity = beginInteractivity; this._endInteractivity = endInteractivity; } beginAction(x, y) { this.m_viewCenter = this.getCenter(); this.m_startPoint = { x: x, y: y }; const view = this.getViewer().activeView; view.delete(); this._beginInteractivity(); } action(x, y) { var _a, _b; 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) { this.calculateXOrbit(viewParams, -xOrbit, sideVector); } if (yOrbit !== 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(); (_b = (_a = this._subject.activeDragger()) === null || _a === undefined ? undefined : _a.updatePreview) === null || _b === undefined ? undefined : _b.call(_a); } endAction() { this._endInteractivity(); } getSideVector(viewParams) { 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(); const vCross = pUpV.crossProduct(vDirect); const sideVector = vCross.normalize(); this.deleteAll([ direct, pUpV, pTarget, pPosition, vDirect, vCross ]); return sideVector; } calculateXOrbit(viewParams, delta, sideVector) { { 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 pCenter = this.toPoint(this.m_viewCenter); const pUp = pTarget.sub(pPoint); const vUp = pUp.asVector(); const crossProduct = vUp.crossProduct(sideVector); const crossProductNormal = crossProduct.normalize(); viewParams.upVector = crossProductNormal.toArray(); this.deleteAll([ pPoint, pTarget, pCenter, pUp, vUp, crossProduct, crossProductNormal ]); } } calculateYOrbit(viewParams, delta, sideVector) { { 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(); const cross = vUp.crossProduct(side); const crossNormal = cross.normalize(); viewParams.upVector = crossNormal.toArray(); this.deleteAll([ zAxis, pTarget, pPoint, side, pUp, vUp, cross, crossNormal ]); } } getCenter() { 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()