@vertexvis/viewer 0.18.2-testing.0 → 0.18.2-testing.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index-b99cd335.js +126 -1
- package/dist/esm/index-fd7d7b68.js +126 -1
- package/package.json +7 -7
- package/dist/components/_commonjsHelpers.js +0 -41
- package/dist/components/_commonjsHelpers.js.map +0 -1
- package/dist/components/browser.esm.js +0 -3173
- package/dist/components/browser.esm.js.map +0 -1
- package/dist/components/bundle.esm.js +0 -2250
- package/dist/components/bundle.esm.js.map +0 -1
- package/dist/components/bundle.esm2.js +0 -39740
- package/dist/components/bundle.esm2.js.map +0 -1
- package/dist/components/config.js +0 -81
- package/dist/components/config.js.map +0 -1
- package/dist/components/controller.js +0 -7627
- package/dist/components/controller.js.map +0 -1
- package/dist/components/controller2.js +0 -124
- package/dist/components/controller2.js.map +0 -1
- package/dist/components/controller3.js +0 -111
- package/dist/components/controller3.js.map +0 -1
- package/dist/components/cursors.js +0 -110
- package/dist/components/cursors.js.map +0 -1
- package/dist/components/dom.js +0 -43
- package/dist/components/dom.js.map +0 -1
- package/dist/components/dom2.js +0 -10
- package/dist/components/dom2.js.map +0 -1
- package/dist/components/elementRectObserver.js +0 -25
- package/dist/components/elementRectObserver.js.map +0 -1
- package/dist/components/entities.js +0 -179
- package/dist/components/entities.js.map +0 -1
- package/dist/components/errors.js +0 -80
- package/dist/components/errors.js.map +0 -1
- package/dist/components/events.js +0 -11
- package/dist/components/events.js.map +0 -1
- package/dist/components/index.d.ts +0 -26
- package/dist/components/index.js +0 -365
- package/dist/components/index.js.map +0 -1
- package/dist/components/index2.js +0 -63
- package/dist/components/index2.js.map +0 -1
- package/dist/components/interactions.js +0 -236
- package/dist/components/interactions.js.map +0 -1
- package/dist/components/mapper.js +0 -9811
- package/dist/components/mapper.js.map +0 -1
- package/dist/components/markup.js +0 -33
- package/dist/components/markup.js.map +0 -1
- package/dist/components/model.js +0 -134
- package/dist/components/model.js.map +0 -1
- package/dist/components/model2.js +0 -157
- package/dist/components/model2.js.map +0 -1
- package/dist/components/overlays.js +0 -76
- package/dist/components/overlays.js.map +0 -1
- package/dist/components/png-decoder.js +0 -2405
- package/dist/components/png-decoder.js.map +0 -1
- package/dist/components/regl-component.js +0 -12582
- package/dist/components/regl-component.js.map +0 -1
- package/dist/components/results.js +0 -24
- package/dist/components/results.js.map +0 -1
- package/dist/components/row.js +0 -32
- package/dist/components/row.js.map +0 -1
- package/dist/components/scene-tree-search.js +0 -146
- package/dist/components/scene-tree-search.js.map +0 -1
- package/dist/components/scene-tree-table-layout.js +0 -928
- package/dist/components/scene-tree-table-layout.js.map +0 -1
- package/dist/components/scene-tree-toolbar.js +0 -35
- package/dist/components/scene-tree-toolbar.js.map +0 -1
- package/dist/components/scene.js +0 -1508
- package/dist/components/scene.js.map +0 -1
- package/dist/components/stencil.js +0 -25
- package/dist/components/stencil.js.map +0 -1
- package/dist/components/streamAttributes.js +0 -40833
- package/dist/components/streamAttributes.js.map +0 -1
- package/dist/components/templates.js +0 -36
- package/dist/components/templates.js.map +0 -1
- package/dist/components/tslib.es6.js +0 -125
- package/dist/components/tslib.es6.js.map +0 -1
- package/dist/components/vertex-scene-tree-search.d.ts +0 -11
- package/dist/components/vertex-scene-tree-search.js +0 -11
- package/dist/components/vertex-scene-tree-search.js.map +0 -1
- package/dist/components/vertex-scene-tree-table-cell.d.ts +0 -11
- package/dist/components/vertex-scene-tree-table-cell.js +0 -209
- package/dist/components/vertex-scene-tree-table-cell.js.map +0 -1
- package/dist/components/vertex-scene-tree-table-column.d.ts +0 -11
- package/dist/components/vertex-scene-tree-table-column.js +0 -42
- package/dist/components/vertex-scene-tree-table-column.js.map +0 -1
- package/dist/components/vertex-scene-tree-table-header.d.ts +0 -11
- package/dist/components/vertex-scene-tree-table-header.js +0 -38
- package/dist/components/vertex-scene-tree-table-header.js.map +0 -1
- package/dist/components/vertex-scene-tree-table-layout.d.ts +0 -11
- package/dist/components/vertex-scene-tree-table-layout.js +0 -11
- package/dist/components/vertex-scene-tree-table-layout.js.map +0 -1
- package/dist/components/vertex-scene-tree-table-resize-divider.d.ts +0 -11
- package/dist/components/vertex-scene-tree-table-resize-divider.js +0 -57
- package/dist/components/vertex-scene-tree-table-resize-divider.js.map +0 -1
- package/dist/components/vertex-scene-tree-toolbar-group.d.ts +0 -11
- package/dist/components/vertex-scene-tree-toolbar-group.js +0 -38
- package/dist/components/vertex-scene-tree-toolbar-group.js.map +0 -1
- package/dist/components/vertex-scene-tree-toolbar.d.ts +0 -11
- package/dist/components/vertex-scene-tree-toolbar.js +0 -11
- package/dist/components/vertex-scene-tree-toolbar.js.map +0 -1
- package/dist/components/vertex-scene-tree.d.ts +0 -11
- package/dist/components/vertex-scene-tree.js +0 -803
- package/dist/components/vertex-scene-tree.js.map +0 -1
- package/dist/components/vertex-viewer-box-query-tool.d.ts +0 -11
- package/dist/components/vertex-viewer-box-query-tool.js +0 -352
- package/dist/components/vertex-viewer-box-query-tool.js.map +0 -1
- package/dist/components/vertex-viewer-button.d.ts +0 -11
- package/dist/components/vertex-viewer-button.js +0 -11
- package/dist/components/vertex-viewer-button.js.map +0 -1
- package/dist/components/vertex-viewer-default-toolbar.d.ts +0 -11
- package/dist/components/vertex-viewer-default-toolbar.js +0 -99
- package/dist/components/vertex-viewer-default-toolbar.js.map +0 -1
- package/dist/components/vertex-viewer-dom-element.d.ts +0 -11
- package/dist/components/vertex-viewer-dom-element.js +0 -11
- package/dist/components/vertex-viewer-dom-element.js.map +0 -1
- package/dist/components/vertex-viewer-dom-group.d.ts +0 -11
- package/dist/components/vertex-viewer-dom-group.js +0 -11
- package/dist/components/vertex-viewer-dom-group.js.map +0 -1
- package/dist/components/vertex-viewer-dom-renderer.d.ts +0 -11
- package/dist/components/vertex-viewer-dom-renderer.js +0 -11
- package/dist/components/vertex-viewer-dom-renderer.js.map +0 -1
- package/dist/components/vertex-viewer-hit-result-indicator.d.ts +0 -11
- package/dist/components/vertex-viewer-hit-result-indicator.js +0 -335
- package/dist/components/vertex-viewer-hit-result-indicator.js.map +0 -1
- package/dist/components/vertex-viewer-icon.d.ts +0 -11
- package/dist/components/vertex-viewer-icon.js +0 -11
- package/dist/components/vertex-viewer-icon.js.map +0 -1
- package/dist/components/vertex-viewer-layer.d.ts +0 -11
- package/dist/components/vertex-viewer-layer.js +0 -11
- package/dist/components/vertex-viewer-layer.js.map +0 -1
- package/dist/components/vertex-viewer-markup-arrow.d.ts +0 -11
- package/dist/components/vertex-viewer-markup-arrow.js +0 -11
- package/dist/components/vertex-viewer-markup-arrow.js.map +0 -1
- package/dist/components/vertex-viewer-markup-circle.d.ts +0 -11
- package/dist/components/vertex-viewer-markup-circle.js +0 -11
- package/dist/components/vertex-viewer-markup-circle.js.map +0 -1
- package/dist/components/vertex-viewer-markup-freeform.d.ts +0 -11
- package/dist/components/vertex-viewer-markup-freeform.js +0 -11
- package/dist/components/vertex-viewer-markup-freeform.js.map +0 -1
- package/dist/components/vertex-viewer-markup-tool.d.ts +0 -11
- package/dist/components/vertex-viewer-markup-tool.js +0 -257
- package/dist/components/vertex-viewer-markup-tool.js.map +0 -1
- package/dist/components/vertex-viewer-markup.d.ts +0 -11
- package/dist/components/vertex-viewer-markup.js +0 -362
- package/dist/components/vertex-viewer-markup.js.map +0 -1
- package/dist/components/vertex-viewer-measurement-details.d.ts +0 -11
- package/dist/components/vertex-viewer-measurement-details.js +0 -307
- package/dist/components/vertex-viewer-measurement-details.js.map +0 -1
- package/dist/components/vertex-viewer-measurement-distance.d.ts +0 -11
- package/dist/components/vertex-viewer-measurement-distance.js +0 -1067
- package/dist/components/vertex-viewer-measurement-distance.js.map +0 -1
- package/dist/components/vertex-viewer-measurement-line.d.ts +0 -11
- package/dist/components/vertex-viewer-measurement-line.js +0 -11
- package/dist/components/vertex-viewer-measurement-line.js.map +0 -1
- package/dist/components/vertex-viewer-measurement-overlays.d.ts +0 -11
- package/dist/components/vertex-viewer-measurement-overlays.js +0 -11
- package/dist/components/vertex-viewer-measurement-overlays.js.map +0 -1
- package/dist/components/vertex-viewer-measurement-precise.d.ts +0 -11
- package/dist/components/vertex-viewer-measurement-precise.js +0 -362
- package/dist/components/vertex-viewer-measurement-precise.js.map +0 -1
- package/dist/components/vertex-viewer-pin-group.d.ts +0 -11
- package/dist/components/vertex-viewer-pin-group.js +0 -11
- package/dist/components/vertex-viewer-pin-group.js.map +0 -1
- package/dist/components/vertex-viewer-pin-label-line.d.ts +0 -11
- package/dist/components/vertex-viewer-pin-label-line.js +0 -11
- package/dist/components/vertex-viewer-pin-label-line.js.map +0 -1
- package/dist/components/vertex-viewer-pin-label.d.ts +0 -11
- package/dist/components/vertex-viewer-pin-label.js +0 -11
- package/dist/components/vertex-viewer-pin-label.js.map +0 -1
- package/dist/components/vertex-viewer-pin-tool.d.ts +0 -11
- package/dist/components/vertex-viewer-pin-tool.js +0 -430
- package/dist/components/vertex-viewer-pin-tool.js.map +0 -1
- package/dist/components/vertex-viewer-spinner.d.ts +0 -11
- package/dist/components/vertex-viewer-spinner.js +0 -11
- package/dist/components/vertex-viewer-spinner.js.map +0 -1
- package/dist/components/vertex-viewer-toolbar-group.d.ts +0 -11
- package/dist/components/vertex-viewer-toolbar-group.js +0 -11
- package/dist/components/vertex-viewer-toolbar-group.js.map +0 -1
- package/dist/components/vertex-viewer-toolbar.d.ts +0 -11
- package/dist/components/vertex-viewer-toolbar.js +0 -11
- package/dist/components/vertex-viewer-toolbar.js.map +0 -1
- package/dist/components/vertex-viewer-transform-widget.d.ts +0 -11
- package/dist/components/vertex-viewer-transform-widget.js +0 -838
- package/dist/components/vertex-viewer-transform-widget.js.map +0 -1
- package/dist/components/vertex-viewer-view-cube.d.ts +0 -11
- package/dist/components/vertex-viewer-view-cube.js +0 -270
- package/dist/components/vertex-viewer-view-cube.js.map +0 -1
- package/dist/components/vertex-viewer.d.ts +0 -11
- package/dist/components/vertex-viewer.js +0 -3543
- package/dist/components/vertex-viewer.js.map +0 -1
- package/dist/components/viewer-button.js +0 -35
- package/dist/components/viewer-button.js.map +0 -1
- package/dist/components/viewer-dom-element.js +0 -250
- package/dist/components/viewer-dom-element.js.map +0 -1
- package/dist/components/viewer-dom-group.js +0 -214
- package/dist/components/viewer-dom-group.js.map +0 -1
- package/dist/components/viewer-dom-renderer.js +0 -295
- package/dist/components/viewer-dom-renderer.js.map +0 -1
- package/dist/components/viewer-icon.js +0 -87
- package/dist/components/viewer-icon.js.map +0 -1
- package/dist/components/viewer-layer.js +0 -46
- package/dist/components/viewer-layer.js.map +0 -1
- package/dist/components/viewer-markup-arrow.js +0 -235
- package/dist/components/viewer-markup-arrow.js.map +0 -1
- package/dist/components/viewer-markup-circle-components.js +0 -42
- package/dist/components/viewer-markup-circle-components.js.map +0 -1
- package/dist/components/viewer-markup-circle.js +0 -201
- package/dist/components/viewer-markup-circle.js.map +0 -1
- package/dist/components/viewer-markup-freeform.js +0 -251
- package/dist/components/viewer-markup-freeform.js.map +0 -1
- package/dist/components/viewer-measurement-line.js +0 -77
- package/dist/components/viewer-measurement-line.js.map +0 -1
- package/dist/components/viewer-measurement-overlays.js +0 -184
- package/dist/components/viewer-measurement-overlays.js.map +0 -1
- package/dist/components/viewer-pin-group.js +0 -207
- package/dist/components/viewer-pin-group.js.map +0 -1
- package/dist/components/viewer-pin-label-line.js +0 -43
- package/dist/components/viewer-pin-label-line.js.map +0 -1
- package/dist/components/viewer-pin-label.js +0 -427
- package/dist/components/viewer-pin-label.js.map +0 -1
- package/dist/components/viewer-spinner.js +0 -53
- package/dist/components/viewer-spinner.js.map +0 -1
- package/dist/components/viewer-toolbar-group.js +0 -42
- package/dist/components/viewer-toolbar-group.js.map +0 -1
- package/dist/components/viewer-toolbar.js +0 -61
- package/dist/components/viewer-toolbar.js.map +0 -1
- package/dist/components/viewport.js +0 -188
- package/dist/components/viewport.js.map +0 -1
- package/dist/components/wrappers_pb.js +0 -1926
- package/dist/components/wrappers_pb.js.map +0 -1
|
@@ -1,3543 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2023 Vertex Software LLC. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client';
|
|
5
|
-
import { p as point, v as vector3, c as angle, f as plane, r as ray, b as boundingBox, h as matrix2, d as dimensions } from './bundle.esm.js';
|
|
6
|
-
import { t as toProtoDuration, p as protoToDate, a as StreamApi, S as StreamRequestError, c as currentDateAsProtoTimestamp, W as WebSocketClientImpl } from './bundle.esm2.js';
|
|
7
|
-
import { E as EventDispatcher, m as mapper, c as color, o as objects, u as uri, a as async, b as uuid } from './browser.esm.js';
|
|
8
|
-
import { c as classnames } from './index2.js';
|
|
9
|
-
import { p as parseConfig } from './config.js';
|
|
10
|
-
import { C as CursorManager } from './cursors.js';
|
|
11
|
-
import { g as getMouseClientPosition, c as cssCursor } from './dom.js';
|
|
12
|
-
import { c as ImageLoadError, C as CustomError, W as WebsocketConnectionError, S as SceneRenderError, V as ViewerInitializationError, d as InteractionHandlerError, e as ComponentInitializationError } from './errors.js';
|
|
13
|
-
import { k as fromPbStencilBufferOrThrow, l as decodePng, m as DepthBuffer, E as EntityType, t as toProtobuf, n as toPbStreamAttributes, o as toPbRGBi, p as fromPbFrameOrThrow, q as fromPbStartStreamResponseOrThrow, r as fromPbReconnectResponseOrThrow, s as fromPbSyncTimeResponseOrThrow, u as fromPbRefreshTokenResponseOrThrow, x as copySummaryIfInvalid, y as toOrthographic, z as toPerspective } from './streamAttributes.js';
|
|
14
|
-
import { V as Viewport } from './viewport.js';
|
|
15
|
-
import { f as fromUrn, a as SynchronizedClock, S as Scene } from './scene.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A value that represents if a pixel does not contain a stencil value.
|
|
19
|
-
*/
|
|
20
|
-
const STENCIL_BUFFER_EMPTY_VALUE = 0;
|
|
21
|
-
/**
|
|
22
|
-
* A value that represents if a pixel contains a feature.
|
|
23
|
-
*/
|
|
24
|
-
const STENCIL_BUFFER_FEATURE_VALUE = 255;
|
|
25
|
-
/**
|
|
26
|
-
* The `StencilBufferManager` manages the stencil buffer state for the viewer.
|
|
27
|
-
* Stencil buffers are represented as images and contain additional information
|
|
28
|
-
* related to the rendered frame. Examples of information contained within a
|
|
29
|
-
* stencil buffer include: cross section and feature edge positions.
|
|
30
|
-
*
|
|
31
|
-
* This class contains methods for fetching a stencil buffer for the currently
|
|
32
|
-
* rendered frame, as well as helpers that components can use to get an
|
|
33
|
-
* up-to-date stencil buffer after any interactions are performed.
|
|
34
|
-
*/
|
|
35
|
-
class StencilBufferManager {
|
|
36
|
-
/**
|
|
37
|
-
* Constructs a new stencil buffer manager.
|
|
38
|
-
*
|
|
39
|
-
* **Note:** This class is only intended to be constructed by a viewer.
|
|
40
|
-
*
|
|
41
|
-
* @param viewer The viewer for this manager.
|
|
42
|
-
*/
|
|
43
|
-
constructor(viewer) {
|
|
44
|
-
this.viewer = viewer;
|
|
45
|
-
this.handleInteractionStarted = () => {
|
|
46
|
-
this.invalidateStencilBuffer();
|
|
47
|
-
this.pendingInteractionFinished = new Promise((resolve) => {
|
|
48
|
-
this.pendingInteractionFinishedResolver = resolve;
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
this.handleInteractionFinished = () => {
|
|
52
|
-
var _a;
|
|
53
|
-
(_a = this.pendingInteractionFinishedResolver) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
54
|
-
this.pendingInteractionFinished = undefined;
|
|
55
|
-
this.pendingInteractionFinishedResolver = undefined;
|
|
56
|
-
};
|
|
57
|
-
this.invalidateStencilBuffer = () => {
|
|
58
|
-
this.pendingStencilBuffer = undefined;
|
|
59
|
-
};
|
|
60
|
-
viewer.addEventListener('interactionStarted', this.handleInteractionStarted);
|
|
61
|
-
viewer.addEventListener('interactionFinished', this.handleInteractionFinished);
|
|
62
|
-
viewer.addEventListener('frameReceived', () => {
|
|
63
|
-
this.invalidateStencilBuffer();
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Fetches a stencil buffer for the last rendered frame. Returns `undefined`
|
|
68
|
-
* if the last frame does not have stencil information.
|
|
69
|
-
*/
|
|
70
|
-
async fetch() {
|
|
71
|
-
var _a;
|
|
72
|
-
const isReady = await this.viewer.isSceneReady();
|
|
73
|
-
const scene = isReady ? await this.viewer.scene() : undefined;
|
|
74
|
-
const hasStencil = scene != null
|
|
75
|
-
? scene.crossSectioning().current().sectionPlanes.length > 0 ||
|
|
76
|
-
this.viewer.featureLines != null
|
|
77
|
-
: false;
|
|
78
|
-
const camera = (_a = this.viewer.frame) === null || _a === void 0 ? void 0 : _a.scene.camera;
|
|
79
|
-
if (hasStencil && this.viewer.stream != null && camera != null) {
|
|
80
|
-
const res = await this.viewer.stream.getStencilBuffer({
|
|
81
|
-
includeDepthBuffer: true,
|
|
82
|
-
});
|
|
83
|
-
const { stencilBuffer: sBytes, depthBuffer: dBytes, imageAttributes, } = fromPbStencilBufferOrThrow(res);
|
|
84
|
-
const [stencilPng, depthPng] = await Promise.all([
|
|
85
|
-
decodePng(new Uint8Array(sBytes)),
|
|
86
|
-
decodePng(new Uint8Array(dBytes)),
|
|
87
|
-
]);
|
|
88
|
-
return StencilBuffer.fromPng(stencilPng, imageAttributes, sBytes, DepthBuffer.fromPng(depthPng, camera, imageAttributes));
|
|
89
|
-
}
|
|
90
|
-
else
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Returns a promise that resolves with the last generated stencil buffer, or
|
|
95
|
-
* fetches a new stencil buffer if the frame has changed since the last
|
|
96
|
-
* stencil buffer was fetched. Because the stencil buffer is cached by the
|
|
97
|
-
* manager, this method can be called multiple times without performing a
|
|
98
|
-
* network request.
|
|
99
|
-
*
|
|
100
|
-
* @see {@link StencilBufferManager.latestAfterInteraction} to wait for
|
|
101
|
-
* requesting a stencil buffer after an interaction has finished.
|
|
102
|
-
*/
|
|
103
|
-
latest() {
|
|
104
|
-
if (this.pendingStencilBuffer == null) {
|
|
105
|
-
this.pendingStencilBuffer = this.fetch();
|
|
106
|
-
}
|
|
107
|
-
return this.pendingStencilBuffer;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Returns a promise that resolves with the latest stencil buffer, once any
|
|
111
|
-
* interaction is being performed. If no interaction is being performed, then
|
|
112
|
-
* the promise will resolve immediately with the latest stencil buffer.
|
|
113
|
-
*
|
|
114
|
-
* This method is useful for components to fetch the most up-to-date stencil
|
|
115
|
-
* buffer. Because the stencil buffer is cached by the manager, components can
|
|
116
|
-
* call this method multiple times without performing a network request.
|
|
117
|
-
*
|
|
118
|
-
* @see {@link StencilBufferManager.latest} - used internally by this method.
|
|
119
|
-
*/
|
|
120
|
-
async latestAfterInteraction() {
|
|
121
|
-
await this.pendingInteractionFinished;
|
|
122
|
-
return this.latest();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Represents a stencil buffer that is managed by `StencilBufferManager`.
|
|
127
|
-
*
|
|
128
|
-
* @see {@link StencilBufferManager} for fetching a stencil buffer.
|
|
129
|
-
*/
|
|
130
|
-
class StencilBuffer {
|
|
131
|
-
/**
|
|
132
|
-
* Constructor.
|
|
133
|
-
*
|
|
134
|
-
* @param imageAttr The attributes of the stencil image.
|
|
135
|
-
* @param imageBytes The original bytes for the image of the stencil buffer.
|
|
136
|
-
* @param pixelBytes The PNG pixel data of the stencil buffer.
|
|
137
|
-
* @param imageChannels The number of color channels.
|
|
138
|
-
* @param depthBuffer The depth buffer associated with this stencil buffer.
|
|
139
|
-
*/
|
|
140
|
-
constructor(imageAttr, imageBytes, pixelBytes, imageChannels, depthBuffer) {
|
|
141
|
-
this.imageAttr = imageAttr;
|
|
142
|
-
this.imageBytes = imageBytes;
|
|
143
|
-
this.pixelBytes = pixelBytes;
|
|
144
|
-
this.imageChannels = imageChannels;
|
|
145
|
-
this.depthBuffer = depthBuffer;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Constructs a new stencil buffer from a decoded PNG.
|
|
149
|
-
*
|
|
150
|
-
* @param png The decoded PNG.
|
|
151
|
-
* @param imageAttr The attributes of the stencil image.
|
|
152
|
-
* * @param imageBytes The original bytes for the image of the stencil buffer.
|
|
153
|
-
* @param depthBuffer The depth buffer associated with this stencil buffer.
|
|
154
|
-
*/
|
|
155
|
-
static fromPng(png, imageAttr, imageBytes, depthBuffer) {
|
|
156
|
-
if (!(png.data instanceof Uint8Array)) {
|
|
157
|
-
throw new Error('Expected stencil PNG to have depth of 8-bit');
|
|
158
|
-
}
|
|
159
|
-
else if (png.channels !== 1) {
|
|
160
|
-
throw new Error('Expected stencil PNG to have 1 color channel');
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
return new StencilBuffer(imageAttr, imageBytes, png.data, png.channels, depthBuffer);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Returns the color at the given frame position.
|
|
168
|
-
*
|
|
169
|
-
* @param pt A position within the frame.
|
|
170
|
-
* @returns A color at the given position, or `undefined` if the color matches
|
|
171
|
-
* the stencil buffer's empty color.
|
|
172
|
-
* @see {@link Viewport.transformPointToFrame} to convert a viewport position
|
|
173
|
-
* to frame position.
|
|
174
|
-
*/
|
|
175
|
-
getValue(pt) {
|
|
176
|
-
const { width, height } = this.imageAttr.imageRect;
|
|
177
|
-
const offset = point.subtract(pt, this.imageAttr.imageRect);
|
|
178
|
-
const scale = 1 / this.imageAttr.imageScale;
|
|
179
|
-
const pixel = point.scale(offset, scale, scale);
|
|
180
|
-
if (pixel.x >= 0 && pixel.y >= 0 && pixel.x < width && pixel.y < height) {
|
|
181
|
-
const index = Math.floor(pixel.x) + Math.floor(pixel.y) * width;
|
|
182
|
-
const value = this.pixelBytes[index];
|
|
183
|
-
return value;
|
|
184
|
-
}
|
|
185
|
-
else
|
|
186
|
-
return 0;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Performs a hit test of the given point. Returns `true` if the point hits a
|
|
190
|
-
* pixel in the stencil.
|
|
191
|
-
*
|
|
192
|
-
* @param pt A point within the stencil's frame.
|
|
193
|
-
* @returns `true` if the point hits a pixel in the stencil, `false`
|
|
194
|
-
* otherwise.
|
|
195
|
-
*/
|
|
196
|
-
hitTest(pt) {
|
|
197
|
-
return this.getValue(pt) !== STENCIL_BUFFER_EMPTY_VALUE;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Returns the colored pixel nearest to the given `pt`. This method is useful
|
|
201
|
-
* for performing snapping operations. An optional predicate can be provided
|
|
202
|
-
* to filter the pixels that are evaluated.
|
|
203
|
-
*
|
|
204
|
-
* @param pt A position within the frame.
|
|
205
|
-
* @param radius The radius around `pt` to evaluate.
|
|
206
|
-
* @param predicate An optional predicate. If unspecified, any non-black
|
|
207
|
-
* pixels are considered.
|
|
208
|
-
* @returns A point within radius that matches the given predicate.
|
|
209
|
-
* @see {@link Viewport.transformPointToFrame} to convert a viewport position
|
|
210
|
-
* to frame position.
|
|
211
|
-
*/
|
|
212
|
-
snapToNearestPixel(pt, radius, predicate = () => true) {
|
|
213
|
-
const diameter = radius * 2;
|
|
214
|
-
const topLeft = point.create(pt.x - radius, pt.y - radius);
|
|
215
|
-
const pixels = [];
|
|
216
|
-
for (let i = 0; i < diameter * diameter; i++) {
|
|
217
|
-
const x = i % diameter;
|
|
218
|
-
const y = Math.floor(i / diameter);
|
|
219
|
-
const pixel = point.add(topLeft, { x, y });
|
|
220
|
-
if (point.distance(pixel, pt) <= radius) {
|
|
221
|
-
const value = this.getValue(pixel);
|
|
222
|
-
if (value === STENCIL_BUFFER_FEATURE_VALUE && predicate(value)) {
|
|
223
|
-
pixels.push(pixel);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
const sorted = pixels.sort((a, b) => point.distance(a, pt) - point.distance(b, pt));
|
|
228
|
-
const closest = sorted[0];
|
|
229
|
-
return closest != null
|
|
230
|
-
? point.create(Math.floor(closest.x) + 0.5, Math.floor(closest.y) + 0.5)
|
|
231
|
-
: pt;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function isPromise(obj) {
|
|
236
|
-
return (obj != null &&
|
|
237
|
-
obj['then'] instanceof Function &&
|
|
238
|
-
obj['catch'] instanceof Function &&
|
|
239
|
-
// NOTE: Should be removed if we do not target ES2018.
|
|
240
|
-
obj['finally'] instanceof Function);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
class FlyToPartKeyInteraction {
|
|
244
|
-
constructor(stream, configProvider, imageScaleProvider) {
|
|
245
|
-
this.stream = stream;
|
|
246
|
-
this.configProvider = configProvider;
|
|
247
|
-
this.imageScaleProvider = imageScaleProvider;
|
|
248
|
-
}
|
|
249
|
-
predicate(e) {
|
|
250
|
-
return e.altKey && !e.shiftKey;
|
|
251
|
-
}
|
|
252
|
-
async fn(e) {
|
|
253
|
-
var _a;
|
|
254
|
-
const scale = this.imageScaleProvider();
|
|
255
|
-
const hitResult = await this.stream.hitItems({
|
|
256
|
-
point: point.scale(e.position, (scale === null || scale === void 0 ? void 0 : scale.x) || 1, (scale === null || scale === void 0 ? void 0 : scale.y) || 1),
|
|
257
|
-
}, true);
|
|
258
|
-
if (((_a = hitResult.hitItems) === null || _a === void 0 ? void 0 : _a.hits) != null &&
|
|
259
|
-
hitResult.hitItems.hits.length > 0) {
|
|
260
|
-
await this.stream.flyTo({
|
|
261
|
-
itemId: hitResult.hitItems.hits[0].itemId,
|
|
262
|
-
animation: {
|
|
263
|
-
duration: toProtoDuration(this.configProvider().animation.durationMs),
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
console.debug(`No hit results found for fly to part [position={x: ${e.position.x}, y: ${e.position.y}}]`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* The `InteractionApi` provides methods that API developers can use to modify
|
|
275
|
-
* the internal state of an interaction.
|
|
276
|
-
*/
|
|
277
|
-
class InteractionApi {
|
|
278
|
-
constructor(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter) {
|
|
279
|
-
this.stream = stream;
|
|
280
|
-
this.cursors = cursors;
|
|
281
|
-
this.getConfig = getConfig;
|
|
282
|
-
this.getScene = getScene;
|
|
283
|
-
this.getFrame = getFrame;
|
|
284
|
-
this.getViewport = getViewport;
|
|
285
|
-
this.tapEmitter = tapEmitter;
|
|
286
|
-
this.doubleTapEmitter = doubleTapEmitter;
|
|
287
|
-
this.longPressEmitter = longPressEmitter;
|
|
288
|
-
this.interactionStartedEmitter = interactionStartedEmitter;
|
|
289
|
-
this.interactionFinishedEmitter = interactionFinishedEmitter;
|
|
290
|
-
this.tap = this.tap.bind(this);
|
|
291
|
-
this.doubleTap = this.doubleTap.bind(this);
|
|
292
|
-
this.longPress = this.longPress.bind(this);
|
|
293
|
-
this.emitTapEvent = this.emitTapEvent.bind(this);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Displays a cursor over the viewer with the given priority. Cursors with
|
|
297
|
-
* higher priority will take precedence over cursors with lower priorities if
|
|
298
|
-
* there's more than a single cursor added.
|
|
299
|
-
*
|
|
300
|
-
* @param cursor The cursor to add.
|
|
301
|
-
* @param priority The priority of the cursor.
|
|
302
|
-
* @returns A `Disposable` that can be used to remove the cursor.
|
|
303
|
-
*/
|
|
304
|
-
addCursor(cursor, priority) {
|
|
305
|
-
return this.cursors.add(cursor, priority);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Returns a 3D point in world space for the given 2D point in viewport space.
|
|
309
|
-
*
|
|
310
|
-
* @param point A point in 2D viewport space to transform.
|
|
311
|
-
* @returns A 3D point in world space.
|
|
312
|
-
*/
|
|
313
|
-
async getWorldPointFromViewport(point) {
|
|
314
|
-
const viewport = this.getViewport();
|
|
315
|
-
const frame = this.getFrame();
|
|
316
|
-
if (frame == null) {
|
|
317
|
-
throw new Error('Cannot get world point. Frame is undefined.');
|
|
318
|
-
}
|
|
319
|
-
const depthBuffer = await frame.depthBuffer();
|
|
320
|
-
return depthBuffer != null
|
|
321
|
-
? viewport.transformPointToWorldSpace(point, depthBuffer, 0.5)
|
|
322
|
-
: undefined;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Returns the entity at the given point in viewport space.
|
|
326
|
-
*
|
|
327
|
-
* @param point A point in viewport space.
|
|
328
|
-
* @returns The entity that was found.
|
|
329
|
-
*/
|
|
330
|
-
async getEntityTypeAtPoint(point) {
|
|
331
|
-
var _a;
|
|
332
|
-
const viewport = this.getViewport();
|
|
333
|
-
const featureMap = await ((_a = this.getFrame()) === null || _a === void 0 ? void 0 : _a.featureMap());
|
|
334
|
-
if (featureMap != null) {
|
|
335
|
-
const framePt = viewport.transformPointToFrame(point, featureMap);
|
|
336
|
-
return featureMap.getEntityType(framePt);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
return EntityType.NO_GEOMETRY;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Generates a ray from the given point, in viewport coordinates.
|
|
344
|
-
*
|
|
345
|
-
* @param point A point in viewport coordinates.
|
|
346
|
-
* @returns A ray representing the direction of the point in world
|
|
347
|
-
* coordinates.
|
|
348
|
-
*/
|
|
349
|
-
getRayFromPoint(point) {
|
|
350
|
-
const viewport = this.getViewport();
|
|
351
|
-
const frame = this.getFrame();
|
|
352
|
-
if (frame != null) {
|
|
353
|
-
return viewport.transformPointToRay(point, frame.image, frame.scene.camera);
|
|
354
|
-
}
|
|
355
|
-
else
|
|
356
|
-
throw new Error('Cannot get camera. Frame is undefined.');
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Emits a tap event with the provided position relative to the viewer
|
|
360
|
-
* canvas, along with the set of modifier keys held (if applicable).
|
|
361
|
-
*
|
|
362
|
-
* @param position An {x, y} coordinate marking the position of the tap.
|
|
363
|
-
* @param keyDetails A set of pressed keyboard keys that you want to include
|
|
364
|
-
* in the tap event.
|
|
365
|
-
*/
|
|
366
|
-
async tap(position, keyDetails = {}, buttons = 0) {
|
|
367
|
-
this.emitTapEvent(this.tapEmitter.emit, position, keyDetails, buttons);
|
|
368
|
-
}
|
|
369
|
-
async doubleTap(position, keyDetails = {}, buttons = 0) {
|
|
370
|
-
this.emitTapEvent(this.doubleTapEmitter.emit, position, keyDetails, buttons);
|
|
371
|
-
}
|
|
372
|
-
async longPress(position, keyDetails = {}, buttons = 0) {
|
|
373
|
-
this.emitTapEvent(this.longPressEmitter.emit, position, keyDetails, buttons);
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Marks the start of an interaction. This method must be called before
|
|
377
|
-
* performing any additional interaction operations. Use `endInteraction()` to
|
|
378
|
-
* mark the end of an interaction.
|
|
379
|
-
*/
|
|
380
|
-
async beginInteraction() {
|
|
381
|
-
if (!this.isInteracting()) {
|
|
382
|
-
this.interactionStartedEmitter.emit();
|
|
383
|
-
this.sceneLoadingPromise = this.getScene();
|
|
384
|
-
this.currentCamera = (await this.sceneLoadingPromise).camera();
|
|
385
|
-
this.sceneLoadingPromise = undefined;
|
|
386
|
-
await this.stream.beginInteraction();
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Invokes a function to transform the scene's camera and request a new image
|
|
391
|
-
* for the updated scene.
|
|
392
|
-
*
|
|
393
|
-
* @param t A function to transform the camera. Function will be passed the
|
|
394
|
-
* camera and scene viewport and is expected to return an updated camera.
|
|
395
|
-
*/
|
|
396
|
-
async transformCamera(t) {
|
|
397
|
-
var _a;
|
|
398
|
-
if (this.isInteracting()) {
|
|
399
|
-
const scene = await this.getScene();
|
|
400
|
-
const viewport = this.getViewport();
|
|
401
|
-
const frame = this.getFrame();
|
|
402
|
-
const depthBuffer = await (frame === null || frame === void 0 ? void 0 : frame.depthBuffer());
|
|
403
|
-
this.currentCamera =
|
|
404
|
-
this.currentCamera != null && viewport != null && frame != null
|
|
405
|
-
? t({
|
|
406
|
-
camera: this.currentCamera,
|
|
407
|
-
viewport,
|
|
408
|
-
scale: scene.scale(),
|
|
409
|
-
boundingBox: scene.boundingBox(),
|
|
410
|
-
frame,
|
|
411
|
-
depthBuffer,
|
|
412
|
-
})
|
|
413
|
-
: undefined;
|
|
414
|
-
await ((_a = this.currentCamera) === null || _a === void 0 ? void 0 : _a.render());
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
async twistCamera(...args) {
|
|
418
|
-
return this.transformCamera(({ camera, viewport }) => {
|
|
419
|
-
const axis = vector3.normalize(vector3.subtract(camera.lookAt, camera.position));
|
|
420
|
-
if (args.length === 1 && typeof args[0] === 'number') {
|
|
421
|
-
const angleInRadians = angle.toRadians(-args[0]);
|
|
422
|
-
return camera.rotateAroundAxis(angleInRadians, axis);
|
|
423
|
-
}
|
|
424
|
-
else if (args.length === 1) {
|
|
425
|
-
const center = point.create(viewport.width / 2, viewport.height / 2);
|
|
426
|
-
const currentAngle = angle.toDegrees(angle.fromPoints(center, args[0]));
|
|
427
|
-
const angleDelta = this.lastAngle != null ? currentAngle - this.lastAngle : 0;
|
|
428
|
-
this.lastAngle = currentAngle;
|
|
429
|
-
const axis = vector3.normalize(vector3.subtract(camera.lookAt, camera.position));
|
|
430
|
-
const angleInRadians = angle.toRadians(-angleDelta);
|
|
431
|
-
return camera.rotateAroundAxis(angleInRadians, axis);
|
|
432
|
-
}
|
|
433
|
-
return camera;
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Moves the camera's position and look at to the given screen coordinate.
|
|
438
|
-
*
|
|
439
|
-
* If the screen coordinate intersects with an object, the camera will track
|
|
440
|
-
* the hit point so the mouse position is always under the mouse.
|
|
441
|
-
*
|
|
442
|
-
* If the screen coordinate doesn't intersect with an object, then ???.
|
|
443
|
-
*
|
|
444
|
-
* @param screenPt A point in screen coordinates.
|
|
445
|
-
*/
|
|
446
|
-
async panCameraToScreenPoint(screenPt) {
|
|
447
|
-
return this.transformCamera(({ camera, frame, viewport, depthBuffer }) => {
|
|
448
|
-
// Capture the starting state of the pan.
|
|
449
|
-
if (this.panData == null) {
|
|
450
|
-
const startingCamera = camera.toFrameCamera();
|
|
451
|
-
const direction = startingCamera.direction;
|
|
452
|
-
const ray$1 = viewport.transformPointToRay(screenPt, frame.image, startingCamera);
|
|
453
|
-
const fallbackPlane = plane.fromNormalAndCoplanarPoint(direction, camera.lookAt);
|
|
454
|
-
const fallback = ray.intersectPlane(ray$1, fallbackPlane);
|
|
455
|
-
if (fallback == null) {
|
|
456
|
-
console.warn('Cannot determine fallback for pan. Ray does not intersect plane.');
|
|
457
|
-
return camera;
|
|
458
|
-
}
|
|
459
|
-
// Create a plane for the hit point that will be used to determine the
|
|
460
|
-
// delta of future mouse movements to the original hit point. Fallback
|
|
461
|
-
// to a plane placed at the look at point, in case there's no hit.
|
|
462
|
-
const hitPt = depthBuffer != null
|
|
463
|
-
? this.getWorldPoint(screenPt, depthBuffer, fallback)
|
|
464
|
-
: fallback;
|
|
465
|
-
const hitPlane = plane.fromNormalAndCoplanarPoint(direction, hitPt);
|
|
466
|
-
this.panData = { hitPt, hitPlane, startingCamera };
|
|
467
|
-
}
|
|
468
|
-
if (this.panData != null) {
|
|
469
|
-
const { hitPt, hitPlane, startingCamera } = this.panData;
|
|
470
|
-
// Use a ray that originates at the screen and intersects with the hit
|
|
471
|
-
// plane to determine the move distance.
|
|
472
|
-
const ray$1 = viewport.transformPointToRay(screenPt, frame.image, startingCamera);
|
|
473
|
-
const movePt = ray.intersectPlane(ray$1, hitPlane);
|
|
474
|
-
if (movePt != null) {
|
|
475
|
-
const delta = vector3.subtract(hitPt, movePt);
|
|
476
|
-
return camera.update(startingCamera).moveBy(delta);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return camera;
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Performs a view all operation for the scene's bounding box, and requests a
|
|
484
|
-
* new image for the updated scene.
|
|
485
|
-
*/
|
|
486
|
-
async viewAll() {
|
|
487
|
-
await (await this.getScene()).camera().viewAll().render();
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Performs a rotate operation of the scene around the camera's look at point,
|
|
491
|
-
* and requests a new image for the updated scene.
|
|
492
|
-
*
|
|
493
|
-
* @param delta A position delta `{x, y}` in the 2D coordinate space of the
|
|
494
|
-
* viewer.
|
|
495
|
-
*/
|
|
496
|
-
async rotateCamera(delta) {
|
|
497
|
-
return this.transformCamera(({ camera, viewport }) => {
|
|
498
|
-
const upVector = vector3.normalize(camera.up);
|
|
499
|
-
const lookAt = vector3.normalize(vector3.subtract(camera.lookAt, camera.position));
|
|
500
|
-
const crossX = vector3.cross(upVector, lookAt);
|
|
501
|
-
const crossY = vector3.cross(lookAt, crossX);
|
|
502
|
-
const mouseToWorld = vector3.normalize({
|
|
503
|
-
x: delta.x * crossX.x + delta.y * crossY.x,
|
|
504
|
-
y: delta.x * crossX.y + delta.y * crossY.y,
|
|
505
|
-
z: delta.x * crossX.z + delta.y * crossY.z,
|
|
506
|
-
});
|
|
507
|
-
const rotationAxis = vector3.cross(mouseToWorld, lookAt);
|
|
508
|
-
const epsilonX = (3.0 * Math.PI * delta.x) / viewport.width;
|
|
509
|
-
const epsilonY = (3.0 * Math.PI * delta.y) / viewport.height;
|
|
510
|
-
const angle = Math.abs(epsilonX) + Math.abs(epsilonY);
|
|
511
|
-
return camera.rotateAroundAxis(angle, rotationAxis);
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
async rotateCameraAtPoint(delta, point) {
|
|
515
|
-
return this.transformCamera(({ camera, viewport, boundingBox: boundingBox$1, depthBuffer }) => {
|
|
516
|
-
if (this.worldRotationPoint == null) {
|
|
517
|
-
const worldCenter = boundingBox.center(boundingBox$1);
|
|
518
|
-
this.worldRotationPoint =
|
|
519
|
-
depthBuffer != null
|
|
520
|
-
? this.getWorldPoint(point, depthBuffer, worldCenter)
|
|
521
|
-
: camera.lookAt;
|
|
522
|
-
}
|
|
523
|
-
const upVector = vector3.normalize(camera.up);
|
|
524
|
-
const vv = vector3.normalize(vector3.subtract(camera.lookAt, camera.position));
|
|
525
|
-
const crossX = vector3.cross(upVector, vv);
|
|
526
|
-
const crossY = vector3.cross(vv, crossX);
|
|
527
|
-
const mouseToWorld = vector3.normalize({
|
|
528
|
-
x: delta.x * crossX.x + delta.y * crossY.x,
|
|
529
|
-
y: delta.x * crossX.y + delta.y * crossY.y,
|
|
530
|
-
z: delta.x * crossX.z + delta.y * crossY.z,
|
|
531
|
-
});
|
|
532
|
-
const rotationAxis = vector3.cross(mouseToWorld, vv);
|
|
533
|
-
const epsilonX = (3.0 * Math.PI * delta.x) / viewport.width;
|
|
534
|
-
const epsilonY = (3.0 * Math.PI * delta.y) / viewport.height;
|
|
535
|
-
const angle = Math.abs(epsilonX) + Math.abs(epsilonY);
|
|
536
|
-
const updated = camera.rotateAroundAxisAtPoint(angle, this.worldRotationPoint, rotationAxis);
|
|
537
|
-
return updated.update({
|
|
538
|
-
// Scale the lookAt point to the same length as the distance to the
|
|
539
|
-
// center of the bounding box to maintain zoom and pan behavior.
|
|
540
|
-
lookAt: vector3.add(vector3.scale(Math.abs(camera.signedDistanceToBoundingBoxCenter()) /
|
|
541
|
-
vector3.magnitude(updated.viewVector), updated.viewVector), updated.position),
|
|
542
|
-
});
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
/**
|
|
546
|
-
* Performs a zoom operation of the scene's camera, and requests a new image
|
|
547
|
-
* for the updated scene.
|
|
548
|
-
*
|
|
549
|
-
* @param delta The distance to zoom. Positive values zoom in and negative
|
|
550
|
-
* values zoom out.
|
|
551
|
-
*/
|
|
552
|
-
async zoomCamera(delta) {
|
|
553
|
-
return this.transformCamera(({ camera, viewport }) => {
|
|
554
|
-
const vv = camera.viewVector;
|
|
555
|
-
const v = vector3.normalize(vv);
|
|
556
|
-
const distance = vector3.magnitude(vv);
|
|
557
|
-
const epsilon = (3 * distance * delta) / viewport.height;
|
|
558
|
-
const position = vector3.add(camera.position, vector3.scale(epsilon, v));
|
|
559
|
-
const newCamera = camera.update({ position });
|
|
560
|
-
return newCamera;
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Performs a pivot operation of the scene's camera, updating the lookAt
|
|
565
|
-
* while maintaining the position, and requests a new image for the
|
|
566
|
-
* updated scene.
|
|
567
|
-
*
|
|
568
|
-
* @param degreesLocalX The angle to rotate the lookAt point around the local x-axis
|
|
569
|
-
* @param degreesLocalY The angle to rotate the lookAt point around the local y-axis
|
|
570
|
-
*/
|
|
571
|
-
async pivotCamera(degreesLocalX, degreesLocalY) {
|
|
572
|
-
return this.transformCamera(({ camera }) => {
|
|
573
|
-
const { position, up, lookAt } = camera;
|
|
574
|
-
const normalizedUp = vector3.normalize(up);
|
|
575
|
-
const normalizedViewVector = vector3.normalize(camera.viewVector);
|
|
576
|
-
const xVector = vector3.cross(normalizedUp, normalizedViewVector);
|
|
577
|
-
const yVector = vector3.cross(normalizedViewVector, xVector);
|
|
578
|
-
const updatedLookAtX = vector3.rotateAboutAxis(angle.toRadians(degreesLocalX), lookAt, xVector, position);
|
|
579
|
-
const updatedLookAtY = vector3.rotateAboutAxis(angle.toRadians(degreesLocalY), updatedLookAtX, yVector, position);
|
|
580
|
-
return camera.update(Object.assign(Object.assign({}, camera), { lookAt: updatedLookAtY }));
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Marks the end of an interaction.
|
|
585
|
-
*/
|
|
586
|
-
async endInteraction() {
|
|
587
|
-
await this.sceneLoadingPromise;
|
|
588
|
-
if (this.isInteracting()) {
|
|
589
|
-
this.currentCamera = undefined;
|
|
590
|
-
this.worldRotationPoint = undefined;
|
|
591
|
-
this.panData = undefined;
|
|
592
|
-
this.zoomData = undefined;
|
|
593
|
-
this.resetLastAngle();
|
|
594
|
-
this.interactionFinishedEmitter.emit();
|
|
595
|
-
await this.stream.endInteraction();
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* resets the last recorded angle for a twist op
|
|
600
|
-
*/
|
|
601
|
-
resetLastAngle() {
|
|
602
|
-
this.lastAngle = undefined;
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Indicates if the API is in an interacting state.
|
|
606
|
-
*/
|
|
607
|
-
isInteracting() {
|
|
608
|
-
return this.currentCamera != null;
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Returns the pixel threshold that should be used to detect
|
|
612
|
-
* movement based on the type of pointer input being coarse or fine.
|
|
613
|
-
* This threshold is based on the configured `coarsePointerThreshold`
|
|
614
|
-
* or the `finePointerThreshold` respectively.
|
|
615
|
-
*
|
|
616
|
-
* @param isTouch - Whether the event is a touch or not, if false or
|
|
617
|
-
* undefined, a media query will be used to determine pointer type
|
|
618
|
-
* @returns The pixel threshold.
|
|
619
|
-
*/
|
|
620
|
-
pixelThreshold(isTouch) {
|
|
621
|
-
const pixelThreshold = this.isCoarseInputDevice(isTouch)
|
|
622
|
-
? this.getConfig().coarsePointerThreshold
|
|
623
|
-
: this.getConfig().finePointerThreshold;
|
|
624
|
-
return pixelThreshold * window.devicePixelRatio;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Performs a hit test at the given point and returns a list of hit results
|
|
628
|
-
* indicating any scene items that exist at the given point.
|
|
629
|
-
*
|
|
630
|
-
* @param pt A point, in viewport coordinates.
|
|
631
|
-
* @returns A promise that resolves with the list of hit results.
|
|
632
|
-
*/
|
|
633
|
-
async hitItems(pt) {
|
|
634
|
-
var _a;
|
|
635
|
-
const res = await (await this.getScene()).raycaster().hitItems(pt);
|
|
636
|
-
return (_a = res === null || res === void 0 ? void 0 : res.hits) !== null && _a !== void 0 ? _a : [];
|
|
637
|
-
}
|
|
638
|
-
emitTapEvent(emit, position, keyDetails = {}, buttons = 0) {
|
|
639
|
-
const { altKey = false, ctrlKey = false, metaKey = false, shiftKey = false, } = keyDetails;
|
|
640
|
-
emit({
|
|
641
|
-
position,
|
|
642
|
-
altKey,
|
|
643
|
-
ctrlKey,
|
|
644
|
-
metaKey,
|
|
645
|
-
shiftKey,
|
|
646
|
-
buttons,
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
isCoarseInputDevice(isTouch) {
|
|
650
|
-
return isTouch || window.matchMedia('(pointer: coarse)').matches;
|
|
651
|
-
}
|
|
652
|
-
getWorldPoint(point, depthBuffer, fallbackPoint) {
|
|
653
|
-
const viewport = this.getViewport();
|
|
654
|
-
const framePt = viewport.transformPointToFrame(point, depthBuffer);
|
|
655
|
-
const hasDepth = depthBuffer.hitTest(framePt);
|
|
656
|
-
return hasDepth
|
|
657
|
-
? viewport.transformPointToWorldSpace(point, depthBuffer)
|
|
658
|
-
: fallbackPoint;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
class InteractionApiOrthographic extends InteractionApi {
|
|
663
|
-
constructor(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter) {
|
|
664
|
-
super(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter);
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Returns a 3D point in world space for the given 2D point in viewport space.
|
|
668
|
-
*
|
|
669
|
-
* @param point A point in 2D viewport space to transform.
|
|
670
|
-
* @returns A 3D point in world space.
|
|
671
|
-
*/
|
|
672
|
-
async getWorldPointFromViewport(point) {
|
|
673
|
-
const viewport = this.getViewport();
|
|
674
|
-
const frame = this.getFrame();
|
|
675
|
-
if (frame == null) {
|
|
676
|
-
throw new Error('Cannot get world point. Frame is undefined.');
|
|
677
|
-
}
|
|
678
|
-
const depthBuffer = await frame.depthBuffer();
|
|
679
|
-
return depthBuffer != null
|
|
680
|
-
? viewport.transformPointToOrthographicWorldSpace(point, depthBuffer, 0.5)
|
|
681
|
-
: undefined;
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Performs a pan operation of the scene's camera, and requests a new image
|
|
685
|
-
* for the updated scene.
|
|
686
|
-
*
|
|
687
|
-
* @param delta A position delta `{x, y}` in the 2D coordinate space of the
|
|
688
|
-
* viewer.
|
|
689
|
-
*/
|
|
690
|
-
async panCameraByDelta(delta) {
|
|
691
|
-
return this.transformCamera(({ camera, viewport }) => {
|
|
692
|
-
const viewVector = camera.viewVector;
|
|
693
|
-
const normalizedUpVector = vector3.normalize(camera.up);
|
|
694
|
-
const normalizedViewVector = vector3.normalize(viewVector);
|
|
695
|
-
const throttledDelta = point.scale(delta, 0.5, 0.5);
|
|
696
|
-
const d = vector3.magnitude(viewVector);
|
|
697
|
-
const epsilonX = (throttledDelta.x * d) / viewport.width;
|
|
698
|
-
const epsilonY = (throttledDelta.y / viewport.width) * d;
|
|
699
|
-
const xvec = vector3.cross(normalizedUpVector, normalizedViewVector);
|
|
700
|
-
const yvec = vector3.cross(normalizedViewVector, xvec);
|
|
701
|
-
const offset = vector3.add(vector3.scale(epsilonX, xvec), vector3.scale(epsilonY, yvec));
|
|
702
|
-
return camera.moveBy(offset);
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
/**
|
|
706
|
-
* Moves the camera's position and look at to the given screen coordinate.
|
|
707
|
-
*
|
|
708
|
-
* If the screen coordinate intersects with an object, the camera will track
|
|
709
|
-
* the hit point so the mouse position is always under the mouse.
|
|
710
|
-
*
|
|
711
|
-
* If the screen coordinate doesn't intersect with an object, then ???.
|
|
712
|
-
*
|
|
713
|
-
* @param screenPt A point in screen coordinates.
|
|
714
|
-
*/
|
|
715
|
-
async panCameraToScreenPoint(screenPt) {
|
|
716
|
-
return this.transformCamera(({ camera, frame, viewport }) => {
|
|
717
|
-
// Capture the starting state of the pan.
|
|
718
|
-
if (this.panData == null) {
|
|
719
|
-
const startingCamera = camera.toFrameCamera();
|
|
720
|
-
const direction = startingCamera.direction;
|
|
721
|
-
const ray$1 = viewport.transformPointToOrthographicRay(screenPt, frame.image, startingCamera);
|
|
722
|
-
const hitPlane = plane.fromNormalAndCoplanarPoint(direction, camera.lookAt);
|
|
723
|
-
const hitPt = ray.intersectPlane(ray$1, hitPlane);
|
|
724
|
-
if (hitPt == null) {
|
|
725
|
-
console.warn('Cannot determine fallback for pan. Ray does not intersect plane.');
|
|
726
|
-
return camera;
|
|
727
|
-
}
|
|
728
|
-
this.panData = { hitPt, hitPlane, startingCamera };
|
|
729
|
-
}
|
|
730
|
-
if (this.panData != null) {
|
|
731
|
-
const { hitPt, hitPlane, startingCamera } = this.panData;
|
|
732
|
-
// Use a ray that originates at the screen and intersects with the hit
|
|
733
|
-
// plane to determine the move distance.
|
|
734
|
-
const ray$1 = viewport.transformPointToOrthographicRay(screenPt, frame.image, startingCamera);
|
|
735
|
-
const movePt = ray.intersectPlane(ray$1, hitPlane);
|
|
736
|
-
if (movePt != null) {
|
|
737
|
-
const delta = vector3.subtract(hitPt, movePt);
|
|
738
|
-
return camera.update({
|
|
739
|
-
lookAt: vector3.add(startingCamera.lookAt, delta),
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
return camera;
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
async zoomCameraToPoint(point$1, delta) {
|
|
747
|
-
return this.transformCamera(({ camera, viewport, frame, depthBuffer }) => {
|
|
748
|
-
if (this.orthographicZoomData == null ||
|
|
749
|
-
point.distance(point$1, this.orthographicZoomData.startingScreenPt) > 2) {
|
|
750
|
-
const frameCam = camera.toFrameCamera();
|
|
751
|
-
const dir = frameCam.direction;
|
|
752
|
-
const ray$1 = viewport.transformPointToOrthographicRay(point$1, frame.image, frameCam);
|
|
753
|
-
const fallbackPlane = plane.fromNormalAndCoplanarPoint(dir, frameCam.lookAt);
|
|
754
|
-
const fallbackPt = ray.intersectPlane(ray$1, fallbackPlane);
|
|
755
|
-
if (fallbackPt == null) {
|
|
756
|
-
console.warn('Cannot determine fallback point for zoom. Ray does not intersect plane.');
|
|
757
|
-
return camera;
|
|
758
|
-
}
|
|
759
|
-
const hitPt = depthBuffer != null
|
|
760
|
-
? this.getWorldPoint(point$1, depthBuffer, fallbackPt)
|
|
761
|
-
: fallbackPt;
|
|
762
|
-
const hitPlane = plane.fromNormalAndCoplanarPoint(dir, hitPt);
|
|
763
|
-
this.orthographicZoomData = {
|
|
764
|
-
hitPt,
|
|
765
|
-
hitPlane,
|
|
766
|
-
startingScreenPt: point$1,
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
if (this.orthographicZoomData != null) {
|
|
770
|
-
const { hitPt, hitPlane } = this.orthographicZoomData;
|
|
771
|
-
const relativeDelta = 2 * (camera.fovHeight / viewport.height) * delta * 2;
|
|
772
|
-
const fovHeight = Math.max(1, camera.fovHeight - relativeDelta);
|
|
773
|
-
const projectedLookAt = plane.projectPoint(hitPlane, camera.lookAt);
|
|
774
|
-
const diff = vector3.scale((camera.fovHeight - fovHeight) / camera.fovHeight, vector3.subtract(hitPt, projectedLookAt));
|
|
775
|
-
return camera.update({
|
|
776
|
-
lookAt: vector3.add(camera.lookAt, diff),
|
|
777
|
-
fovHeight: Math.max(1, camera.fovHeight - relativeDelta),
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
return camera;
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
getWorldPoint(point, depthBuffer, fallbackPoint) {
|
|
784
|
-
const viewport = this.getViewport();
|
|
785
|
-
const framePt = viewport.transformPointToFrame(point, depthBuffer);
|
|
786
|
-
const hasDepth = depthBuffer.hitTest(framePt);
|
|
787
|
-
return hasDepth
|
|
788
|
-
? viewport.transformPointToOrthographicWorldSpace(point, depthBuffer)
|
|
789
|
-
: fallbackPoint;
|
|
790
|
-
}
|
|
791
|
-
async transformCamera(t) {
|
|
792
|
-
var _a;
|
|
793
|
-
if (this.isInteracting()) {
|
|
794
|
-
const scene = await this.getScene();
|
|
795
|
-
const viewport = this.getViewport();
|
|
796
|
-
const frame = this.getFrame();
|
|
797
|
-
const depthBuffer = await (frame === null || frame === void 0 ? void 0 : frame.depthBuffer());
|
|
798
|
-
this.currentCamera =
|
|
799
|
-
this.currentCamera != null && viewport != null && frame != null
|
|
800
|
-
? t({
|
|
801
|
-
camera: this.currentCamera,
|
|
802
|
-
viewport,
|
|
803
|
-
scale: scene.scale(),
|
|
804
|
-
boundingBox: scene.boundingBox(),
|
|
805
|
-
frame,
|
|
806
|
-
depthBuffer,
|
|
807
|
-
})
|
|
808
|
-
: undefined;
|
|
809
|
-
await ((_a = this.currentCamera) === null || _a === void 0 ? void 0 : _a.render());
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const CAMERA_MIN_ZOOM_SCALAR = 0.2;
|
|
815
|
-
class InteractionApiPerspective extends InteractionApi {
|
|
816
|
-
constructor(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter) {
|
|
817
|
-
super(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter);
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Performs a pan operation of the scene's camera, and requests a new image
|
|
821
|
-
* for the updated scene.
|
|
822
|
-
*
|
|
823
|
-
* @param delta A position delta `{x, y}` in the 2D coordinate space of the
|
|
824
|
-
* viewer.
|
|
825
|
-
*/
|
|
826
|
-
async panCameraByDelta(delta) {
|
|
827
|
-
return this.transformCamera(({ camera, viewport }) => {
|
|
828
|
-
var _a;
|
|
829
|
-
const vv = camera.viewVector;
|
|
830
|
-
const u = vector3.normalize(camera.up);
|
|
831
|
-
const v = vector3.normalize(vv);
|
|
832
|
-
const throttledDelta = point.scale(delta, 0.25, 0.25);
|
|
833
|
-
const d = vector3.magnitude(vv) * Math.tan((_a = camera.fovY) !== null && _a !== void 0 ? _a : 45);
|
|
834
|
-
const epsilonX = (throttledDelta.x * d) / viewport.width;
|
|
835
|
-
const epsilonY = (throttledDelta.y / viewport.width) * d;
|
|
836
|
-
const xvec = vector3.cross(u, v);
|
|
837
|
-
const yvec = vector3.cross(v, xvec);
|
|
838
|
-
const offset = vector3.add(vector3.scale(epsilonX, xvec), vector3.scale(epsilonY, yvec));
|
|
839
|
-
return camera.moveBy(offset);
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
async zoomCameraToPoint(point, delta) {
|
|
843
|
-
return this.transformCamera(({ camera, viewport, frame, depthBuffer, boundingBox }) => {
|
|
844
|
-
const cam = frame.scene.camera;
|
|
845
|
-
const dir = cam.direction;
|
|
846
|
-
const frameCam = camera.toFrameCamera();
|
|
847
|
-
const ray$1 = viewport.transformPointToRay(point, frame.image, frameCam);
|
|
848
|
-
if (this.zoomData == null) {
|
|
849
|
-
const fallbackPlane = plane.fromNormalAndCoplanarPoint(dir, cam.lookAt);
|
|
850
|
-
const fallbackPt = ray.intersectPlane(ray$1, fallbackPlane);
|
|
851
|
-
if (fallbackPt == null) {
|
|
852
|
-
console.warn('Cannot determine fallback point for zoom. Ray does not intersect plane.');
|
|
853
|
-
return camera;
|
|
854
|
-
}
|
|
855
|
-
const hitPt = depthBuffer != null
|
|
856
|
-
? this.getWorldPoint(point, depthBuffer, fallbackPt)
|
|
857
|
-
: fallbackPt;
|
|
858
|
-
const hitPlane = plane.fromNormalAndCoplanarPoint(dir, hitPt);
|
|
859
|
-
this.zoomData = { hitPt, hitPlane };
|
|
860
|
-
}
|
|
861
|
-
if (this.zoomData != null) {
|
|
862
|
-
const { hitPlane } = this.zoomData;
|
|
863
|
-
const { position, distance, isPastHitPlane, keepCurrent } = this.computeZoomDistances(delta, camera, viewport, boundingBox, ray$1, this.zoomData);
|
|
864
|
-
if (isPastHitPlane && !keepCurrent) {
|
|
865
|
-
const viewVectorRay = ray.create({
|
|
866
|
-
origin: position,
|
|
867
|
-
direction: vector3.normalize(camera.viewVector),
|
|
868
|
-
});
|
|
869
|
-
return camera.update({
|
|
870
|
-
position,
|
|
871
|
-
lookAt: ray.at(viewVectorRay, distance),
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
else if (!keepCurrent) {
|
|
875
|
-
return camera.update({
|
|
876
|
-
position,
|
|
877
|
-
lookAt: plane.projectPoint(hitPlane, position),
|
|
878
|
-
});
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
return camera;
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
async transformCamera(t) {
|
|
885
|
-
var _a;
|
|
886
|
-
if (this.isInteracting()) {
|
|
887
|
-
const scene = await this.getScene();
|
|
888
|
-
const viewport = this.getViewport();
|
|
889
|
-
const frame = this.getFrame();
|
|
890
|
-
const depthBuffer = await (frame === null || frame === void 0 ? void 0 : frame.depthBuffer());
|
|
891
|
-
this.currentCamera =
|
|
892
|
-
this.currentCamera != null && viewport != null && frame != null
|
|
893
|
-
? t({
|
|
894
|
-
camera: this.currentCamera,
|
|
895
|
-
viewport,
|
|
896
|
-
scale: scene.scale(),
|
|
897
|
-
boundingBox: scene.boundingBox(),
|
|
898
|
-
frame,
|
|
899
|
-
depthBuffer,
|
|
900
|
-
})
|
|
901
|
-
: undefined;
|
|
902
|
-
await ((_a = this.currentCamera) === null || _a === void 0 ? void 0 : _a.render());
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
computeZoomDistances(delta, camera, viewport, boundingBox, pointRay, zoomData) {
|
|
906
|
-
const config = this.getConfig();
|
|
907
|
-
const { hitPt, hitPlane } = zoomData;
|
|
908
|
-
const minDistance = config.useMinimumPerspectiveZoomDistance
|
|
909
|
-
? this.computeZoomMinimumDistance(camera, boundingBox)
|
|
910
|
-
: -1;
|
|
911
|
-
const expectedDistance = vector3.distance(camera.position, hitPt);
|
|
912
|
-
const actualDistance = Math.max(minDistance, expectedDistance);
|
|
913
|
-
const epsilon = (6 * actualDistance * delta) / viewport.height;
|
|
914
|
-
const expectedPosition = ray.at(pointRay, epsilon);
|
|
915
|
-
const expectedViewVector = ray.create({
|
|
916
|
-
origin: expectedPosition,
|
|
917
|
-
direction: vector3.normalize(camera.viewVector),
|
|
918
|
-
});
|
|
919
|
-
const expectedIntersection = ray.intersectPlane(expectedViewVector, hitPlane);
|
|
920
|
-
if (expectedIntersection == null &&
|
|
921
|
-
config.useMinimumPerspectiveZoomDistance) {
|
|
922
|
-
const minDistanceEpsilon = (6 * minDistance * delta) / viewport.height;
|
|
923
|
-
const position = ray.at(pointRay, minDistanceEpsilon);
|
|
924
|
-
return {
|
|
925
|
-
position,
|
|
926
|
-
distance: minDistance,
|
|
927
|
-
isPastHitPlane: true,
|
|
928
|
-
keepCurrent: false,
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
else if (expectedIntersection == null) {
|
|
932
|
-
return {
|
|
933
|
-
position: camera.position,
|
|
934
|
-
distance: actualDistance,
|
|
935
|
-
isPastHitPlane: true,
|
|
936
|
-
keepCurrent: true,
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
return {
|
|
940
|
-
position: expectedPosition,
|
|
941
|
-
distance: actualDistance,
|
|
942
|
-
isPastHitPlane: false,
|
|
943
|
-
keepCurrent: false,
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
computeZoomMinimumDistance(camera, boundingBox) {
|
|
947
|
-
const xLength = Math.abs(boundingBox.min.x) + Math.abs(boundingBox.max.x);
|
|
948
|
-
const yLength = Math.abs(boundingBox.min.y) + Math.abs(boundingBox.max.y);
|
|
949
|
-
const zLength = Math.abs(boundingBox.min.z) + Math.abs(boundingBox.max.z);
|
|
950
|
-
const maxLength = Math.max(xLength, yLength, zLength);
|
|
951
|
-
const absDotX = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.right()));
|
|
952
|
-
const absDotY = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.up()));
|
|
953
|
-
const absDotZ = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.back()));
|
|
954
|
-
const scaledLengthX = xLength * absDotX;
|
|
955
|
-
const scaledLengthY = yLength * absDotY;
|
|
956
|
-
const scaledLengthZ = zLength * absDotZ;
|
|
957
|
-
const relevanceLengthX = maxLength / xLength;
|
|
958
|
-
const relevanceLengthY = maxLength / yLength;
|
|
959
|
-
const relevanceLengthZ = maxLength / zLength;
|
|
960
|
-
return (((scaledLengthX + scaledLengthY + scaledLengthZ) /
|
|
961
|
-
(relevanceLengthX + relevanceLengthY + relevanceLengthZ)) *
|
|
962
|
-
CAMERA_MIN_ZOOM_SCALAR);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
class FlyToPositionKeyInteraction {
|
|
967
|
-
constructor(stream, configProvider, imageScaleProvider, sceneProvider) {
|
|
968
|
-
this.stream = stream;
|
|
969
|
-
this.configProvider = configProvider;
|
|
970
|
-
this.imageScaleProvider = imageScaleProvider;
|
|
971
|
-
this.sceneProvider = sceneProvider;
|
|
972
|
-
}
|
|
973
|
-
predicate(e) {
|
|
974
|
-
return e.altKey && e.shiftKey;
|
|
975
|
-
}
|
|
976
|
-
async fn(e) {
|
|
977
|
-
var _a, _b;
|
|
978
|
-
const scale = this.imageScaleProvider();
|
|
979
|
-
const hitResult = await this.stream.hitItems({
|
|
980
|
-
point: point.scale(e.position, (scale === null || scale === void 0 ? void 0 : scale.x) || 1, (scale === null || scale === void 0 ? void 0 : scale.y) || 1),
|
|
981
|
-
}, true);
|
|
982
|
-
if (((_a = hitResult.hitItems) === null || _a === void 0 ? void 0 : _a.hits) != null &&
|
|
983
|
-
hitResult.hitItems.hits.length > 0 &&
|
|
984
|
-
hitResult.hitItems.hits[0].hitPoint != null) {
|
|
985
|
-
const camera = (await this.sceneProvider()).camera();
|
|
986
|
-
const hit = hitResult.hitItems.hits[0];
|
|
987
|
-
if (hit.hitPoint != null &&
|
|
988
|
-
hit.hitPoint.x != null &&
|
|
989
|
-
hit.hitPoint.y != null &&
|
|
990
|
-
hit.hitPoint.z != null) {
|
|
991
|
-
await this.stream.flyTo({
|
|
992
|
-
camera: toProtobuf(camera
|
|
993
|
-
.update({
|
|
994
|
-
lookAt: vector3.create(hit.hitPoint.x, hit.hitPoint.y, hit.hitPoint.z),
|
|
995
|
-
})
|
|
996
|
-
.toFrameCamera()),
|
|
997
|
-
animation: {
|
|
998
|
-
duration: toProtoDuration(this.configProvider().animation.durationMs),
|
|
999
|
-
},
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
else {
|
|
1003
|
-
console.debug(`No hit position found for fly to position [position={x: ${e.position.x}, y: ${e.position.y}}, hit-id={${(_b = hit.itemId) === null || _b === void 0 ? void 0 : _b.hex}}]`);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
console.debug(`No hit results found for fly to position [position={x: ${e.position.x}, y: ${e.position.y}}]`);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
const SCROLL_WHEEL_DELTA_PERCENTAGES = [0.2, 0.15, 0.25, 0.25, 0.15];
|
|
1013
|
-
const DEFAULT_FONT_SIZE = 16;
|
|
1014
|
-
const DEFAULT_LINE_HEIGHT = 1.2;
|
|
1015
|
-
class BaseInteractionHandler {
|
|
1016
|
-
constructor(downEvent, upEvent, moveEvent, rotateInteraction, rotatePointInteraction, zoomInteraction, panInteraction, twistInteraction, pivotInteraction, getConfig) {
|
|
1017
|
-
this.downEvent = downEvent;
|
|
1018
|
-
this.upEvent = upEvent;
|
|
1019
|
-
this.moveEvent = moveEvent;
|
|
1020
|
-
this.rotateInteraction = rotateInteraction;
|
|
1021
|
-
this.rotatePointInteraction = rotatePointInteraction;
|
|
1022
|
-
this.zoomInteraction = zoomInteraction;
|
|
1023
|
-
this.panInteraction = panInteraction;
|
|
1024
|
-
this.twistInteraction = twistInteraction;
|
|
1025
|
-
this.pivotInteraction = pivotInteraction;
|
|
1026
|
-
this.getConfig = getConfig;
|
|
1027
|
-
this.primaryInteraction = this.rotateInteraction;
|
|
1028
|
-
this.isDragging = false;
|
|
1029
|
-
this.keyboardControls = false;
|
|
1030
|
-
this.disableIndividualInteractions = false;
|
|
1031
|
-
this.primaryInteractionTypeChange = new EventDispatcher();
|
|
1032
|
-
this.handleDownEvent = this.handleDownEvent.bind(this);
|
|
1033
|
-
this.handleMouseWheel = this.handleMouseWheel.bind(this);
|
|
1034
|
-
this.handleWindowMove = this.handleWindowMove.bind(this);
|
|
1035
|
-
this.handleWindowUp = this.handleWindowUp.bind(this);
|
|
1036
|
-
this.handleDoubleClick = this.handleDoubleClick.bind(this);
|
|
1037
|
-
}
|
|
1038
|
-
initialize(element, api) {
|
|
1039
|
-
this.element = element;
|
|
1040
|
-
this.interactionApi = api;
|
|
1041
|
-
element.addEventListener(this.downEvent, this.handleDownEvent);
|
|
1042
|
-
element.addEventListener('mousedown', this.handleDoubleClick);
|
|
1043
|
-
element.addEventListener('wheel', this.handleMouseWheel);
|
|
1044
|
-
}
|
|
1045
|
-
dispose() {
|
|
1046
|
-
var _a, _b, _c;
|
|
1047
|
-
(_a = this.element) === null || _a === void 0 ? void 0 : _a.removeEventListener(this.downEvent, this.handleDownEvent);
|
|
1048
|
-
(_b = this.element) === null || _b === void 0 ? void 0 : _b.removeEventListener('mousedown', this.handleDoubleClick);
|
|
1049
|
-
(_c = this.element) === null || _c === void 0 ? void 0 : _c.removeEventListener('wheel', this.handleMouseWheel);
|
|
1050
|
-
this.element = undefined;
|
|
1051
|
-
}
|
|
1052
|
-
onPrimaryInteractionTypeChange(listener) {
|
|
1053
|
-
return this.primaryInteractionTypeChange.on(listener);
|
|
1054
|
-
}
|
|
1055
|
-
setCurrentInteractionType(type) {
|
|
1056
|
-
var _a;
|
|
1057
|
-
switch (type) {
|
|
1058
|
-
case 'rotate':
|
|
1059
|
-
this.currentInteraction = this.rotateInteraction;
|
|
1060
|
-
break;
|
|
1061
|
-
case 'zoom':
|
|
1062
|
-
this.currentInteraction = this.zoomInteraction;
|
|
1063
|
-
break;
|
|
1064
|
-
case 'pan':
|
|
1065
|
-
this.currentInteraction = this.panInteraction;
|
|
1066
|
-
break;
|
|
1067
|
-
case 'twist':
|
|
1068
|
-
this.currentInteraction = this.twistInteraction;
|
|
1069
|
-
break;
|
|
1070
|
-
case 'rotate-point':
|
|
1071
|
-
this.currentInteraction = this.rotatePointInteraction;
|
|
1072
|
-
break;
|
|
1073
|
-
case 'pivot':
|
|
1074
|
-
this.currentInteraction = this.pivotInteraction;
|
|
1075
|
-
break;
|
|
1076
|
-
default:
|
|
1077
|
-
this.currentInteraction = undefined;
|
|
1078
|
-
}
|
|
1079
|
-
if (this.draggingInteraction) {
|
|
1080
|
-
const point = this.draggingInteraction.getPosition();
|
|
1081
|
-
this.draggingInteraction =
|
|
1082
|
-
this.currentInteraction || this.primaryInteraction;
|
|
1083
|
-
(_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.resetLastAngle();
|
|
1084
|
-
this.draggingInteraction.setPosition(point);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
getPrimaryInteractionType() {
|
|
1088
|
-
return this.primaryInteraction.getType();
|
|
1089
|
-
}
|
|
1090
|
-
getCurrentInteractionType() {
|
|
1091
|
-
return (this.currentInteraction || this.primaryInteraction).getType();
|
|
1092
|
-
}
|
|
1093
|
-
setPrimaryInteractionType(type) {
|
|
1094
|
-
switch (type) {
|
|
1095
|
-
case 'rotate':
|
|
1096
|
-
this.primaryInteraction = this.rotateInteraction;
|
|
1097
|
-
this.lastPrimaryRotateInteraction = this.rotateInteraction;
|
|
1098
|
-
break;
|
|
1099
|
-
case 'rotate-point':
|
|
1100
|
-
this.primaryInteraction = this.rotatePointInteraction;
|
|
1101
|
-
this.lastPrimaryRotateInteraction = this.rotatePointInteraction;
|
|
1102
|
-
break;
|
|
1103
|
-
case 'zoom':
|
|
1104
|
-
this.primaryInteraction = this.zoomInteraction;
|
|
1105
|
-
break;
|
|
1106
|
-
case 'pan':
|
|
1107
|
-
this.primaryInteraction = this.panInteraction;
|
|
1108
|
-
break;
|
|
1109
|
-
case 'pivot':
|
|
1110
|
-
this.primaryInteraction = this.pivotInteraction;
|
|
1111
|
-
break;
|
|
1112
|
-
}
|
|
1113
|
-
this.primaryInteractionTypeChange.emit();
|
|
1114
|
-
}
|
|
1115
|
-
setDefaultKeyboardControls(keyboardControls) {
|
|
1116
|
-
this.keyboardControls = keyboardControls;
|
|
1117
|
-
}
|
|
1118
|
-
handleDownEvent(event) {
|
|
1119
|
-
// Prevent selection of any text while interacting with the model.
|
|
1120
|
-
event.preventDefault();
|
|
1121
|
-
this.interactionTimer = window.setTimeout(() => {
|
|
1122
|
-
this.downPosition = point.create(event.screenX, event.screenY);
|
|
1123
|
-
this.downPositionCanvas = this.getCanvasPosition(event);
|
|
1124
|
-
this.interactionTimer = undefined;
|
|
1125
|
-
// Perform the current movement in the case that the interaction timer elapses
|
|
1126
|
-
if (this.lastMoveEvent != null) {
|
|
1127
|
-
this.handleWindowMove(this.lastMoveEvent);
|
|
1128
|
-
}
|
|
1129
|
-
}, this.getConfig().interactions.interactionDelay);
|
|
1130
|
-
window.addEventListener(this.moveEvent, this.handleWindowMove);
|
|
1131
|
-
window.addEventListener(this.upEvent, this.handleWindowUp);
|
|
1132
|
-
}
|
|
1133
|
-
handleWindowMove(event) {
|
|
1134
|
-
if (this.interactionTimer == null) {
|
|
1135
|
-
if (this.disableIndividualInteractions) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
const position = point.create(event.screenX, event.screenY);
|
|
1139
|
-
const pixelThreshold = this.interactionApi != null
|
|
1140
|
-
? this.interactionApi.pixelThreshold(this.isTouch(event))
|
|
1141
|
-
: 2;
|
|
1142
|
-
if (this.downPosition != null &&
|
|
1143
|
-
point.distance(position, this.downPosition) >= pixelThreshold &&
|
|
1144
|
-
!this.isDragging) {
|
|
1145
|
-
this.beginDrag(event);
|
|
1146
|
-
this.isDragging = true;
|
|
1147
|
-
}
|
|
1148
|
-
if (this.isDragging) {
|
|
1149
|
-
this.drag(event);
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
this.lastMoveEvent = event;
|
|
1153
|
-
}
|
|
1154
|
-
async handleWindowUp(event) {
|
|
1155
|
-
if (this.isDragging) {
|
|
1156
|
-
this.endDrag(event);
|
|
1157
|
-
this.isDragging = false;
|
|
1158
|
-
}
|
|
1159
|
-
if (this.interactionTimer != null) {
|
|
1160
|
-
window.clearTimeout(this.interactionTimer);
|
|
1161
|
-
this.interactionTimer = undefined;
|
|
1162
|
-
}
|
|
1163
|
-
window.removeEventListener(this.moveEvent, this.handleWindowMove);
|
|
1164
|
-
window.removeEventListener(this.upEvent, this.handleWindowUp);
|
|
1165
|
-
this.lastMoveEvent = undefined;
|
|
1166
|
-
}
|
|
1167
|
-
async handleDoubleClick(event) {
|
|
1168
|
-
// event.detail is the number of clicks that have happened recently. If the number is 2, then the user double clicked.
|
|
1169
|
-
if (event.detail === 2 &&
|
|
1170
|
-
event.buttons === 4 &&
|
|
1171
|
-
this.interactionApi != null) {
|
|
1172
|
-
this.interactionApi.viewAll();
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
beginDrag(event) {
|
|
1176
|
-
var _a;
|
|
1177
|
-
if (this.keyboardControls && event.metaKey && event.shiftKey) {
|
|
1178
|
-
this.currentInteraction = this.rotatePointInteraction;
|
|
1179
|
-
}
|
|
1180
|
-
else if (this.keyboardControls && event.shiftKey && event.altKey) {
|
|
1181
|
-
this.currentInteraction = this.twistInteraction;
|
|
1182
|
-
}
|
|
1183
|
-
else if (this.keyboardControls && event.shiftKey) {
|
|
1184
|
-
this.currentInteraction = this.zoomInteraction;
|
|
1185
|
-
}
|
|
1186
|
-
else if (this.keyboardControls && (event.metaKey || event.ctrlKey)) {
|
|
1187
|
-
this.currentInteraction = this.panInteraction;
|
|
1188
|
-
}
|
|
1189
|
-
else if (this.keyboardControls && event.altKey) {
|
|
1190
|
-
this.currentInteraction = this.rotateInteraction;
|
|
1191
|
-
}
|
|
1192
|
-
if (event.buttons === 1) {
|
|
1193
|
-
this.draggingInteraction =
|
|
1194
|
-
this.currentInteraction || this.primaryInteraction;
|
|
1195
|
-
}
|
|
1196
|
-
else if (event.buttons === 2) {
|
|
1197
|
-
this.draggingInteraction = this.panInteraction;
|
|
1198
|
-
}
|
|
1199
|
-
else if (event.buttons === 4) {
|
|
1200
|
-
this.draggingInteraction =
|
|
1201
|
-
(_a = this.lastPrimaryRotateInteraction) !== null && _a !== void 0 ? _a : this.rotateInteraction;
|
|
1202
|
-
}
|
|
1203
|
-
if (this.draggingInteraction != null &&
|
|
1204
|
-
this.interactionApi != null &&
|
|
1205
|
-
this.element != null) {
|
|
1206
|
-
// Ensure any scroll wheel interactions have been ended prior to beginning
|
|
1207
|
-
// another interaction to prevent the interaction from being ended early.
|
|
1208
|
-
this.zoomInteraction.endDrag(event, this.interactionApi);
|
|
1209
|
-
this.draggingInteraction.beginDrag(event, this.downPositionCanvas || point.create(event.clientX, event.clientY), this.interactionApi, this.element);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
drag(event) {
|
|
1213
|
-
if (this.keyboardControls && event.altKey && event.shiftKey) {
|
|
1214
|
-
this.currentInteraction = this.twistInteraction;
|
|
1215
|
-
}
|
|
1216
|
-
else {
|
|
1217
|
-
this.currentInteraction = undefined;
|
|
1218
|
-
}
|
|
1219
|
-
this.draggingInteraction =
|
|
1220
|
-
this.currentInteraction ||
|
|
1221
|
-
this.draggingInteraction ||
|
|
1222
|
-
this.primaryInteraction;
|
|
1223
|
-
if (this.draggingInteraction != null && this.interactionApi != null) {
|
|
1224
|
-
this.draggingInteraction.drag(event, this.interactionApi);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
endDrag(event) {
|
|
1228
|
-
if (this.keyboardControls &&
|
|
1229
|
-
this.currentInteraction === this.twistInteraction) {
|
|
1230
|
-
this.currentInteraction = undefined;
|
|
1231
|
-
}
|
|
1232
|
-
if (this.draggingInteraction != null && this.interactionApi != null) {
|
|
1233
|
-
this.draggingInteraction.endDrag(event, this.interactionApi);
|
|
1234
|
-
this.draggingInteraction = undefined;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
handleMouseWheel(event) {
|
|
1238
|
-
event.preventDefault();
|
|
1239
|
-
if (this.element != null &&
|
|
1240
|
-
this.interactionApi != null &&
|
|
1241
|
-
event.buttons !== 4) {
|
|
1242
|
-
const delta = -this.wheelDeltaToPixels(event.deltaY, event.deltaMode) / 10;
|
|
1243
|
-
const rect = this.element.getBoundingClientRect();
|
|
1244
|
-
const point = getMouseClientPosition(event, rect);
|
|
1245
|
-
SCROLL_WHEEL_DELTA_PERCENTAGES.forEach((percentage, index) => {
|
|
1246
|
-
window.setTimeout(() => {
|
|
1247
|
-
if (this.interactionApi != null) {
|
|
1248
|
-
const zoomDelta = delta * percentage;
|
|
1249
|
-
this.zoomInteraction.zoomToPoint(point, zoomDelta, this.interactionApi);
|
|
1250
|
-
}
|
|
1251
|
-
}, index * 2);
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
wheelDeltaToPixels(deltaY, deltaMode) {
|
|
1256
|
-
if (this.computedBodyStyle == null) {
|
|
1257
|
-
this.computedBodyStyle = window.getComputedStyle(document.body);
|
|
1258
|
-
}
|
|
1259
|
-
const defaultLineHeight = this.computedBodyStyle.fontSize != null &&
|
|
1260
|
-
this.computedBodyStyle.fontSize !== '' &&
|
|
1261
|
-
!isNaN(parseFloat(this.computedBodyStyle.fontSize))
|
|
1262
|
-
? parseFloat(this.computedBodyStyle.fontSize) * DEFAULT_LINE_HEIGHT
|
|
1263
|
-
: DEFAULT_FONT_SIZE * DEFAULT_LINE_HEIGHT;
|
|
1264
|
-
if (deltaMode === 1) {
|
|
1265
|
-
// deltaMode 1 corresponds to DOM_DELTA_LINE, which computes deltas in lines
|
|
1266
|
-
return this.computedBodyStyle.lineHeight != null &&
|
|
1267
|
-
this.computedBodyStyle.lineHeight !== '' &&
|
|
1268
|
-
!isNaN(parseFloat(this.computedBodyStyle.lineHeight))
|
|
1269
|
-
? deltaY * parseFloat(this.computedBodyStyle.lineHeight)
|
|
1270
|
-
: deltaY * defaultLineHeight;
|
|
1271
|
-
}
|
|
1272
|
-
else if (deltaMode === 2) {
|
|
1273
|
-
// deltaMode 2 corresponds to DOM_DELTA_PAGE, which computes deltas in pages
|
|
1274
|
-
return this.computedBodyStyle.height != null &&
|
|
1275
|
-
this.computedBodyStyle.height !== '' &&
|
|
1276
|
-
!isNaN(parseFloat(this.computedBodyStyle.height))
|
|
1277
|
-
? deltaY * parseFloat(this.computedBodyStyle.height)
|
|
1278
|
-
: deltaY * window.innerHeight;
|
|
1279
|
-
}
|
|
1280
|
-
// deltaMode 0 corresponds to DOM_DELTA_PIXEL, which computes deltas in pixels
|
|
1281
|
-
return deltaY;
|
|
1282
|
-
}
|
|
1283
|
-
getCanvasPosition(event) {
|
|
1284
|
-
var _a;
|
|
1285
|
-
const canvasBounds = (_a = this.element) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
1286
|
-
const canvasOffset = canvasBounds != null
|
|
1287
|
-
? point.create(canvasBounds.left, canvasBounds.top)
|
|
1288
|
-
: undefined;
|
|
1289
|
-
return canvasOffset != null
|
|
1290
|
-
? point.subtract(point.create(event.clientX, event.clientY), canvasOffset)
|
|
1291
|
-
: undefined;
|
|
1292
|
-
}
|
|
1293
|
-
isTouch(event) {
|
|
1294
|
-
return window.PointerEvent != null && event instanceof PointerEvent
|
|
1295
|
-
? event.pointerType === 'touch'
|
|
1296
|
-
: false;
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
class MouseInteraction {
|
|
1301
|
-
setPosition(position) {
|
|
1302
|
-
this.currentPosition = position;
|
|
1303
|
-
}
|
|
1304
|
-
getPosition() {
|
|
1305
|
-
return this.currentPosition;
|
|
1306
|
-
}
|
|
1307
|
-
getType() {
|
|
1308
|
-
return this.type;
|
|
1309
|
-
}
|
|
1310
|
-
beginDrag(event, canvasPosition, api, element) {
|
|
1311
|
-
// noop
|
|
1312
|
-
}
|
|
1313
|
-
drag(event, api) {
|
|
1314
|
-
// noop
|
|
1315
|
-
}
|
|
1316
|
-
endDrag(event, api) {
|
|
1317
|
-
if (this.currentPosition != null) {
|
|
1318
|
-
api.endInteraction();
|
|
1319
|
-
this.currentPosition = undefined;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
zoom(delta, api) {
|
|
1323
|
-
// noop
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
class RotateInteraction extends MouseInteraction {
|
|
1327
|
-
constructor() {
|
|
1328
|
-
super(...arguments);
|
|
1329
|
-
this.type = 'rotate';
|
|
1330
|
-
}
|
|
1331
|
-
beginDrag(event, canvasPosition, api) {
|
|
1332
|
-
if (this.currentPosition == null) {
|
|
1333
|
-
this.currentPosition = point.create(event.screenX, event.screenY);
|
|
1334
|
-
api.beginInteraction();
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
drag(event, api) {
|
|
1338
|
-
if (this.currentPosition != null) {
|
|
1339
|
-
const position = point.create(event.screenX, event.screenY);
|
|
1340
|
-
const delta = point.subtract(position, this.currentPosition);
|
|
1341
|
-
api.rotateCamera(delta);
|
|
1342
|
-
this.currentPosition = position;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
endDrag(event, api) {
|
|
1346
|
-
super.endDrag(event, api);
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
class RotatePointInteraction extends MouseInteraction {
|
|
1350
|
-
constructor() {
|
|
1351
|
-
super(...arguments);
|
|
1352
|
-
this.type = 'rotate-point';
|
|
1353
|
-
}
|
|
1354
|
-
beginDrag(event, canvasPosition, api) {
|
|
1355
|
-
if (this.currentPosition == null) {
|
|
1356
|
-
this.currentPosition = point.create(event.screenX, event.screenY);
|
|
1357
|
-
this.startingPosition = canvasPosition;
|
|
1358
|
-
api.beginInteraction();
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
drag(event, api) {
|
|
1362
|
-
if (this.currentPosition != null && this.startingPosition != null) {
|
|
1363
|
-
const position = point.create(event.screenX, event.screenY);
|
|
1364
|
-
const delta = point.subtract(position, this.currentPosition);
|
|
1365
|
-
api.rotateCameraAtPoint(delta, this.startingPosition);
|
|
1366
|
-
this.currentPosition = position;
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
endDrag(event, api) {
|
|
1370
|
-
super.endDrag(event, api);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
class ZoomInteraction extends MouseInteraction {
|
|
1374
|
-
constructor(interactionTimeout = 1000) {
|
|
1375
|
-
super();
|
|
1376
|
-
this.interactionTimeout = interactionTimeout;
|
|
1377
|
-
this.type = 'zoom';
|
|
1378
|
-
this.didTransformBegin = false;
|
|
1379
|
-
}
|
|
1380
|
-
beginDrag(event, canvasPosition, api, element) {
|
|
1381
|
-
if (this.currentPosition == null) {
|
|
1382
|
-
this.currentPosition = point.create(event.clientX, event.clientY);
|
|
1383
|
-
const rect = element.getBoundingClientRect();
|
|
1384
|
-
const point$1 = getMouseClientPosition(event, rect);
|
|
1385
|
-
this.startPt = point$1;
|
|
1386
|
-
api.beginInteraction();
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
drag(event, api) {
|
|
1390
|
-
if (this.currentPosition != null) {
|
|
1391
|
-
const position = point.create(event.clientX, event.clientY);
|
|
1392
|
-
const delta = point.subtract(position, this.currentPosition);
|
|
1393
|
-
if (this.startPt != null) {
|
|
1394
|
-
api.zoomCameraToPoint(this.startPt, delta.y);
|
|
1395
|
-
this.currentPosition = position;
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
endDrag(event, api) {
|
|
1400
|
-
super.endDrag(event, api);
|
|
1401
|
-
this.stopInteractionTimer();
|
|
1402
|
-
this.didTransformBegin = false;
|
|
1403
|
-
this.startPt = undefined;
|
|
1404
|
-
}
|
|
1405
|
-
zoom(delta, api) {
|
|
1406
|
-
this.operateWithTimer(api, () => api.zoomCamera(delta));
|
|
1407
|
-
}
|
|
1408
|
-
zoomToPoint(pt, delta, api) {
|
|
1409
|
-
this.operateWithTimer(api, () => api.zoomCameraToPoint(pt, delta));
|
|
1410
|
-
}
|
|
1411
|
-
beginInteraction(api) {
|
|
1412
|
-
this.didTransformBegin = true;
|
|
1413
|
-
api.beginInteraction();
|
|
1414
|
-
}
|
|
1415
|
-
endInteraction(api) {
|
|
1416
|
-
this.didTransformBegin = false;
|
|
1417
|
-
api.endInteraction();
|
|
1418
|
-
}
|
|
1419
|
-
resetInteractionTimer(api) {
|
|
1420
|
-
this.stopInteractionTimer();
|
|
1421
|
-
this.startInteractionTimer(api);
|
|
1422
|
-
}
|
|
1423
|
-
startInteractionTimer(api) {
|
|
1424
|
-
this.interactionTimer = window.setTimeout(() => {
|
|
1425
|
-
this.interactionTimer = undefined;
|
|
1426
|
-
this.endInteraction(api);
|
|
1427
|
-
}, this.interactionTimeout);
|
|
1428
|
-
}
|
|
1429
|
-
stopInteractionTimer() {
|
|
1430
|
-
if (this.interactionTimer != null) {
|
|
1431
|
-
window.clearTimeout(this.interactionTimer);
|
|
1432
|
-
this.interactionTimer = undefined;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
operateWithTimer(api, f) {
|
|
1436
|
-
if (!this.didTransformBegin) {
|
|
1437
|
-
this.beginInteraction(api);
|
|
1438
|
-
}
|
|
1439
|
-
this.resetInteractionTimer(api);
|
|
1440
|
-
f();
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
class PanInteraction extends MouseInteraction {
|
|
1444
|
-
constructor() {
|
|
1445
|
-
super(...arguments);
|
|
1446
|
-
this.type = 'pan';
|
|
1447
|
-
}
|
|
1448
|
-
beginDrag(event, canvasPosition, api, element) {
|
|
1449
|
-
if (this.currentPosition == null) {
|
|
1450
|
-
this.currentPosition = point.create(event.screenX, event.screenY);
|
|
1451
|
-
this.canvasRect = element.getBoundingClientRect();
|
|
1452
|
-
api.beginInteraction();
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
drag(event, api) {
|
|
1456
|
-
if (this.currentPosition != null && this.canvasRect != null) {
|
|
1457
|
-
const position = getMouseClientPosition(event, this.canvasRect);
|
|
1458
|
-
api.panCameraToScreenPoint(position);
|
|
1459
|
-
this.currentPosition = position;
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
endDrag(event, api) {
|
|
1463
|
-
super.endDrag(event, api);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
class TwistInteraction extends MouseInteraction {
|
|
1467
|
-
constructor() {
|
|
1468
|
-
super(...arguments);
|
|
1469
|
-
this.type = 'twist';
|
|
1470
|
-
}
|
|
1471
|
-
beginDrag(event, canvasPosition, api, element) {
|
|
1472
|
-
this.currentPosition = point.create(event.offsetX, event.offsetY);
|
|
1473
|
-
this.canvasRect = element.getBoundingClientRect();
|
|
1474
|
-
api.beginInteraction();
|
|
1475
|
-
}
|
|
1476
|
-
drag(event, api) {
|
|
1477
|
-
const position = getMouseClientPosition(event, this.canvasRect);
|
|
1478
|
-
this.currentPosition = position;
|
|
1479
|
-
api.twistCamera(position);
|
|
1480
|
-
}
|
|
1481
|
-
endDrag(event, api) {
|
|
1482
|
-
super.endDrag(event, api);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
class PivotInteraction extends MouseInteraction {
|
|
1486
|
-
constructor() {
|
|
1487
|
-
super(...arguments);
|
|
1488
|
-
this.type = 'pivot';
|
|
1489
|
-
}
|
|
1490
|
-
beginDrag(event, canvasPosition, api) {
|
|
1491
|
-
if (this.currentPosition == null) {
|
|
1492
|
-
this.currentPosition = point.create(event.screenX, event.screenY);
|
|
1493
|
-
api.beginInteraction();
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
drag(event, api) {
|
|
1497
|
-
if (this.currentPosition != null) {
|
|
1498
|
-
const position = point.create(event.screenX, event.screenY);
|
|
1499
|
-
const delta = point.subtract(position, this.currentPosition);
|
|
1500
|
-
api.pivotCamera(-0.25 * delta.y, 0.25 * delta.x);
|
|
1501
|
-
this.currentPosition = position;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
endDrag(event, api) {
|
|
1505
|
-
super.endDrag(event, api);
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
class MouseInteractionHandler extends BaseInteractionHandler {
|
|
1510
|
-
constructor(getConfig, rotateInteraction = new RotateInteraction(), rotatePointInteraction = new RotatePointInteraction(), zoomInteraction = new ZoomInteraction(), panInteraction = new PanInteraction(), twistInteraction = new TwistInteraction(), pivotInteraction = new PivotInteraction()) {
|
|
1511
|
-
super('mousedown', 'mouseup', 'mousemove', rotateInteraction, rotatePointInteraction, zoomInteraction, panInteraction, twistInteraction, pivotInteraction, getConfig);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
class MultiTouchInteractionHandler {
|
|
1516
|
-
initialize(element, api) {
|
|
1517
|
-
this.element = element;
|
|
1518
|
-
this.interactionApi = api;
|
|
1519
|
-
}
|
|
1520
|
-
dispose() {
|
|
1521
|
-
this.element = undefined;
|
|
1522
|
-
}
|
|
1523
|
-
handleTwoPointTouchMove(point1, point2) {
|
|
1524
|
-
var _a, _b, _c, _d;
|
|
1525
|
-
if (this.currentPosition1 != null && this.currentPosition2 != null) {
|
|
1526
|
-
const delta = point.add(point.subtract(point1, this.currentPosition1), point.subtract(point2, this.currentPosition2));
|
|
1527
|
-
const distance = point.distance(point1, point2) -
|
|
1528
|
-
point.distance(this.currentPosition1, this.currentPosition2);
|
|
1529
|
-
const zoom = distance * 0.5;
|
|
1530
|
-
const previousToCurrent = matrix2.create(point.subtract(this.currentPosition1, this.currentPosition2), point.subtract(point1, point2));
|
|
1531
|
-
const angle$1 = angle.toDegrees(Math.atan2(matrix2.determinant(previousToCurrent), matrix2.dot(previousToCurrent)));
|
|
1532
|
-
const center = point.create((point1.x + point2.x) / 2, (point1.y + point2.y) / 2);
|
|
1533
|
-
(_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.beginInteraction();
|
|
1534
|
-
(_b = this.interactionApi) === null || _b === void 0 ? void 0 : _b.zoomCameraToPoint(center, zoom);
|
|
1535
|
-
(_c = this.interactionApi) === null || _c === void 0 ? void 0 : _c.panCameraByDelta(delta);
|
|
1536
|
-
// Setting a minimum angle to prevent wobbling
|
|
1537
|
-
if (Math.abs(angle$1) > 0.5) {
|
|
1538
|
-
(_d = this.interactionApi) === null || _d === void 0 ? void 0 : _d.twistCamera(angle$1);
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
this.currentPosition1 = point1;
|
|
1542
|
-
this.currentPosition2 = point2;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
class MultiPointerInteractionHandler extends MultiTouchInteractionHandler {
|
|
1547
|
-
constructor() {
|
|
1548
|
-
super();
|
|
1549
|
-
this.touchPoints = {};
|
|
1550
|
-
this.handlePointerDown = this.handlePointerDown.bind(this);
|
|
1551
|
-
this.handlePointerMove = this.handlePointerMove.bind(this);
|
|
1552
|
-
this.handlePointerUp = this.handlePointerUp.bind(this);
|
|
1553
|
-
}
|
|
1554
|
-
dispose() {
|
|
1555
|
-
var _a;
|
|
1556
|
-
(_a = this.element) === null || _a === void 0 ? void 0 : _a.removeEventListener('pointerdown', this.handlePointerDown);
|
|
1557
|
-
super.dispose();
|
|
1558
|
-
}
|
|
1559
|
-
initialize(element, api) {
|
|
1560
|
-
super.initialize(element, api);
|
|
1561
|
-
element.addEventListener('pointerdown', this.handlePointerDown);
|
|
1562
|
-
}
|
|
1563
|
-
handlePointerDown(event) {
|
|
1564
|
-
const point$1 = point.create(event.screenX, event.screenY);
|
|
1565
|
-
this.touchPoints = Object.assign(Object.assign({}, this.touchPoints), { [event.pointerId]: point$1 });
|
|
1566
|
-
const keys = Object.keys(this.touchPoints);
|
|
1567
|
-
if (keys.length === 1) {
|
|
1568
|
-
window.addEventListener('pointermove', this.handlePointerMove);
|
|
1569
|
-
window.addEventListener('pointerup', this.handlePointerUp);
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
handlePointerMove(event) {
|
|
1573
|
-
if (this.touchPoints[event.pointerId] != null) {
|
|
1574
|
-
this.touchPoints[event.pointerId] = point.create(event.screenX, event.screenY);
|
|
1575
|
-
}
|
|
1576
|
-
const keys = Object.keys(this.touchPoints);
|
|
1577
|
-
if (keys.length === 2) {
|
|
1578
|
-
const point1 = this.touchPoints[keys[0]];
|
|
1579
|
-
const point2 = this.touchPoints[keys[1]];
|
|
1580
|
-
this.handleTwoPointTouchMove(point1, point2);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
handlePointerUp(event) {
|
|
1584
|
-
var _a;
|
|
1585
|
-
delete this.touchPoints[event.pointerId];
|
|
1586
|
-
const keys = Object.keys(this.touchPoints);
|
|
1587
|
-
if (keys.length === 1) {
|
|
1588
|
-
(_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.endInteraction();
|
|
1589
|
-
this.currentPosition1 = undefined;
|
|
1590
|
-
this.currentPosition2 = undefined;
|
|
1591
|
-
}
|
|
1592
|
-
if (keys.length === 0) {
|
|
1593
|
-
window.removeEventListener('pointermove', this.handlePointerMove);
|
|
1594
|
-
window.removeEventListener('pointerup', this.handlePointerUp);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
class PointerInteractionHandler extends BaseInteractionHandler {
|
|
1600
|
-
constructor(getConfig) {
|
|
1601
|
-
super('pointerdown', 'pointerup', 'pointermove', new RotateInteraction(), new RotatePointInteraction(), new ZoomInteraction(), new PanInteraction(), new TwistInteraction(), new PivotInteraction(), getConfig);
|
|
1602
|
-
this.touchPoints = new Set();
|
|
1603
|
-
this.handlePointerDown = this.handlePointerDown.bind(this);
|
|
1604
|
-
this.handlePointerUp = this.handlePointerUp.bind(this);
|
|
1605
|
-
}
|
|
1606
|
-
initialize(element, api) {
|
|
1607
|
-
super.initialize(element, api);
|
|
1608
|
-
element.addEventListener('pointerdown', this.handlePointerDown);
|
|
1609
|
-
}
|
|
1610
|
-
handlePointerDown(event) {
|
|
1611
|
-
this.downPosition = point.create(event.screenX, event.screenY);
|
|
1612
|
-
this.touchPoints.add(event.pointerId);
|
|
1613
|
-
if (this.touchPoints.size === 1) {
|
|
1614
|
-
window.addEventListener('pointerup', this.handlePointerUp);
|
|
1615
|
-
}
|
|
1616
|
-
if (this.touchPoints.size === 2) {
|
|
1617
|
-
this.disableIndividualInteractions = true;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
handlePointerUp(event) {
|
|
1621
|
-
this.touchPoints.delete(event.pointerId);
|
|
1622
|
-
if (this.touchPoints.size < 2) {
|
|
1623
|
-
this.disableIndividualInteractions = false;
|
|
1624
|
-
}
|
|
1625
|
-
if (this.touchPoints.size === 0) {
|
|
1626
|
-
window.removeEventListener('pointerup', this.handlePointerUp);
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
class TapInteractionHandler {
|
|
1632
|
-
constructor(downEvent, upEvent, moveEvent, getConfig) {
|
|
1633
|
-
this.downEvent = downEvent;
|
|
1634
|
-
this.upEvent = upEvent;
|
|
1635
|
-
this.moveEvent = moveEvent;
|
|
1636
|
-
this.getConfig = getConfig;
|
|
1637
|
-
this.handleDown = this.handleDown.bind(this);
|
|
1638
|
-
this.handleUp = this.handleUp.bind(this);
|
|
1639
|
-
this.handleMove = this.handleMove.bind(this);
|
|
1640
|
-
this.handleTouchStart = this.handleTouchStart.bind(this);
|
|
1641
|
-
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
1642
|
-
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
|
1643
|
-
this.handlePointerMove = this.handlePointerMove.bind(this);
|
|
1644
|
-
this.handlePointerEnd = this.handlePointerEnd.bind(this);
|
|
1645
|
-
this.clearPositions = this.clearPositions.bind(this);
|
|
1646
|
-
this.restartDoubleTapTimer = this.restartDoubleTapTimer.bind(this);
|
|
1647
|
-
this.clearDoubleTapTimer = this.clearDoubleTapTimer.bind(this);
|
|
1648
|
-
this.restartLongPressTimer = this.restartLongPressTimer.bind(this);
|
|
1649
|
-
this.clearLongPressTimer = this.clearLongPressTimer.bind(this);
|
|
1650
|
-
this.setPointerPositions = this.setPointerPositions.bind(this);
|
|
1651
|
-
this.emit = this.emit.bind(this);
|
|
1652
|
-
}
|
|
1653
|
-
dispose() {
|
|
1654
|
-
var _a, _b;
|
|
1655
|
-
(_a = this.element) === null || _a === void 0 ? void 0 : _a.removeEventListener(this.downEvent, this.handleDown);
|
|
1656
|
-
(_b = this.element) === null || _b === void 0 ? void 0 : _b.removeEventListener('touchstart', this.handleTouchStart);
|
|
1657
|
-
this.element = undefined;
|
|
1658
|
-
this.clearDoubleTapTimer();
|
|
1659
|
-
this.clearLongPressTimer();
|
|
1660
|
-
}
|
|
1661
|
-
initialize(element, api) {
|
|
1662
|
-
this.element = element;
|
|
1663
|
-
this.interactionApi = api;
|
|
1664
|
-
element.addEventListener(this.downEvent, this.handleDown);
|
|
1665
|
-
element.addEventListener('touchstart', this.handleTouchStart);
|
|
1666
|
-
}
|
|
1667
|
-
handleTouchStart(event) {
|
|
1668
|
-
if (event.touches.length === 1) {
|
|
1669
|
-
this.setPointerPositions(point.create(event.touches[0].clientX, event.touches[0].clientY));
|
|
1670
|
-
this.restartLongPressTimer();
|
|
1671
|
-
window.addEventListener('touchend', this.handleTouchEnd);
|
|
1672
|
-
window.addEventListener('touchmove', this.handleTouchMove);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
handleTouchMove(event) {
|
|
1676
|
-
if (event.touches.length > 0) {
|
|
1677
|
-
this.handlePointerMove(point.create(event.touches[0].clientX, event.touches[0].clientY), true);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
handleTouchEnd(event) {
|
|
1681
|
-
if (this.pointerDownPosition != null) {
|
|
1682
|
-
window.removeEventListener('touchend', this.handleTouchEnd);
|
|
1683
|
-
window.removeEventListener('touchmove', this.handleTouchMove);
|
|
1684
|
-
}
|
|
1685
|
-
this.handlePointerEnd(this.pointerDownPosition);
|
|
1686
|
-
}
|
|
1687
|
-
handleDown(event) {
|
|
1688
|
-
this.setPointerPositions(point.create(event.clientX, event.clientY));
|
|
1689
|
-
this.buttons = event.buttons;
|
|
1690
|
-
const eventKeys = {
|
|
1691
|
-
altKey: event.altKey,
|
|
1692
|
-
ctrlKey: event.ctrlKey,
|
|
1693
|
-
metaKey: event.metaKey,
|
|
1694
|
-
shiftKey: event.shiftKey,
|
|
1695
|
-
};
|
|
1696
|
-
this.restartLongPressTimer(eventKeys);
|
|
1697
|
-
window.addEventListener(this.upEvent, this.handleUp);
|
|
1698
|
-
window.addEventListener(this.moveEvent, this.handleMove);
|
|
1699
|
-
}
|
|
1700
|
-
handleMove(event) {
|
|
1701
|
-
this.handlePointerMove(point.create(event.clientX, event.clientY), this.isTouch(event));
|
|
1702
|
-
}
|
|
1703
|
-
handleUp(event) {
|
|
1704
|
-
if (this.pointerDownPosition != null) {
|
|
1705
|
-
window.removeEventListener(this.upEvent, this.handleUp);
|
|
1706
|
-
window.removeEventListener(this.moveEvent, this.handleMove);
|
|
1707
|
-
}
|
|
1708
|
-
this.handlePointerEnd(point.create(event.clientX, event.clientY), {
|
|
1709
|
-
altKey: event.altKey,
|
|
1710
|
-
ctrlKey: event.ctrlKey,
|
|
1711
|
-
metaKey: event.metaKey,
|
|
1712
|
-
shiftKey: event.shiftKey,
|
|
1713
|
-
}, this.buttons, this.isTouch(event));
|
|
1714
|
-
this.buttons = undefined;
|
|
1715
|
-
}
|
|
1716
|
-
handlePointerMove(position, isTouch) {
|
|
1717
|
-
var _a;
|
|
1718
|
-
// Ignore pointer end events for this associated touch start
|
|
1719
|
-
// since the distance from the start is large enough
|
|
1720
|
-
const threshold = ((_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.pixelThreshold(isTouch)) || 2;
|
|
1721
|
-
if (this.pointerDownPosition != null &&
|
|
1722
|
-
point.distance(position, this.pointerDownPosition) >= threshold &&
|
|
1723
|
-
this.interactionTimer == null) {
|
|
1724
|
-
this.clearPositions();
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
handlePointerEnd(position, keyDetails = {}, buttons = 0, isTouch = false) {
|
|
1728
|
-
var _a, _b;
|
|
1729
|
-
if (position != null) {
|
|
1730
|
-
if (this.longPressTimer != null) {
|
|
1731
|
-
this.emit((_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.tap)(position, keyDetails, buttons);
|
|
1732
|
-
}
|
|
1733
|
-
if (this.doubleTapTimer != null &&
|
|
1734
|
-
this.secondPointerDownPosition != null) {
|
|
1735
|
-
this.emit((_b = this.interactionApi) === null || _b === void 0 ? void 0 : _b.doubleTap)(position, keyDetails, buttons, this.secondPointerDownPosition);
|
|
1736
|
-
this.clearDoubleTapTimer();
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
this.pointerDownPosition = undefined;
|
|
1740
|
-
this.clearLongPressTimer();
|
|
1741
|
-
}
|
|
1742
|
-
emit(emitter) {
|
|
1743
|
-
return (pointerUpPosition, keyDetails = {}, buttons = 0, pointerDownPosition, isTouch = false) => {
|
|
1744
|
-
var _a;
|
|
1745
|
-
const downPosition = pointerDownPosition || this.pointerDownPosition;
|
|
1746
|
-
const threshold = ((_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.pixelThreshold(isTouch)) || 1;
|
|
1747
|
-
let emittedPosition;
|
|
1748
|
-
if (this.interactionTimer != null) {
|
|
1749
|
-
emittedPosition = this.getCanvasPosition(downPosition || pointerUpPosition);
|
|
1750
|
-
}
|
|
1751
|
-
else if (downPosition != null &&
|
|
1752
|
-
point.distance(downPosition, pointerUpPosition) <= threshold) {
|
|
1753
|
-
emittedPosition = this.getCanvasPosition(pointerUpPosition);
|
|
1754
|
-
}
|
|
1755
|
-
if (emittedPosition != null && emitter != null) {
|
|
1756
|
-
emitter(emittedPosition, keyDetails, buttons);
|
|
1757
|
-
}
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
getCanvasPosition(point$1) {
|
|
1761
|
-
var _a;
|
|
1762
|
-
const canvasBounds = (_a = this.element) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
1763
|
-
const canvasOffset = canvasBounds != null
|
|
1764
|
-
? point.create(canvasBounds.left, canvasBounds.top)
|
|
1765
|
-
: undefined;
|
|
1766
|
-
return canvasOffset != null
|
|
1767
|
-
? point.subtract(point.create(point$1.x, point$1.y), canvasOffset)
|
|
1768
|
-
: undefined;
|
|
1769
|
-
}
|
|
1770
|
-
clearPositions() {
|
|
1771
|
-
this.pointerDownPosition = undefined;
|
|
1772
|
-
this.firstPointerDownPosition = undefined;
|
|
1773
|
-
this.secondPointerDownPosition = undefined;
|
|
1774
|
-
this.clearDoubleTapTimer();
|
|
1775
|
-
this.clearLongPressTimer();
|
|
1776
|
-
this.clearInteractionTimer();
|
|
1777
|
-
}
|
|
1778
|
-
clearDoubleTapTimer() {
|
|
1779
|
-
if (this.doubleTapTimer != null) {
|
|
1780
|
-
window.clearTimeout(this.doubleTapTimer);
|
|
1781
|
-
}
|
|
1782
|
-
this.doubleTapTimer = undefined;
|
|
1783
|
-
this.firstPointerDownPosition = undefined;
|
|
1784
|
-
this.secondPointerDownPosition = undefined;
|
|
1785
|
-
}
|
|
1786
|
-
restartDoubleTapTimer() {
|
|
1787
|
-
this.clearDoubleTapTimer();
|
|
1788
|
-
this.doubleTapTimer = window.setTimeout(() => this.clearDoubleTapTimer(), this.getConfig().events.doubleTapThreshold);
|
|
1789
|
-
}
|
|
1790
|
-
clearLongPressTimer() {
|
|
1791
|
-
if (this.longPressTimer != null) {
|
|
1792
|
-
window.clearTimeout(this.longPressTimer);
|
|
1793
|
-
}
|
|
1794
|
-
this.longPressTimer = undefined;
|
|
1795
|
-
}
|
|
1796
|
-
restartLongPressTimer(eventKeys = {}) {
|
|
1797
|
-
this.clearLongPressTimer();
|
|
1798
|
-
this.longPressTimer = window.setTimeout(() => {
|
|
1799
|
-
var _a;
|
|
1800
|
-
if (this.pointerDownPosition) {
|
|
1801
|
-
this.emit((_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.longPress)(this.pointerDownPosition, eventKeys, this.buttons);
|
|
1802
|
-
}
|
|
1803
|
-
this.clearLongPressTimer();
|
|
1804
|
-
}, this.getConfig().events.longPressThreshold);
|
|
1805
|
-
}
|
|
1806
|
-
restartInteractionTimer() {
|
|
1807
|
-
this.clearInteractionTimer();
|
|
1808
|
-
this.interactionTimer = window.setTimeout(() => {
|
|
1809
|
-
this.interactionTimer = undefined;
|
|
1810
|
-
}, this.getConfig().interactions.interactionDelay);
|
|
1811
|
-
}
|
|
1812
|
-
clearInteractionTimer() {
|
|
1813
|
-
if (this.interactionTimer != null) {
|
|
1814
|
-
window.clearTimeout(this.interactionTimer);
|
|
1815
|
-
this.interactionTimer = undefined;
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
setPointerPositions(point) {
|
|
1819
|
-
this.pointerDownPosition = point;
|
|
1820
|
-
this.restartInteractionTimer();
|
|
1821
|
-
if (this.firstPointerDownPosition == null) {
|
|
1822
|
-
this.restartDoubleTapTimer();
|
|
1823
|
-
this.firstPointerDownPosition = point;
|
|
1824
|
-
}
|
|
1825
|
-
else {
|
|
1826
|
-
this.secondPointerDownPosition = point;
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
isTouch(event) {
|
|
1830
|
-
return window.PointerEvent != null && event instanceof PointerEvent
|
|
1831
|
-
? event.pointerType === 'touch'
|
|
1832
|
-
: false;
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
class TouchInteractionHandler extends MultiTouchInteractionHandler {
|
|
1837
|
-
constructor() {
|
|
1838
|
-
super();
|
|
1839
|
-
this.handleTouchStart = this.handleTouchStart.bind(this);
|
|
1840
|
-
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
1841
|
-
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
|
1842
|
-
}
|
|
1843
|
-
dispose() {
|
|
1844
|
-
var _a;
|
|
1845
|
-
(_a = this.element) === null || _a === void 0 ? void 0 : _a.removeEventListener('touchstart', this.handleTouchStart);
|
|
1846
|
-
super.dispose();
|
|
1847
|
-
}
|
|
1848
|
-
initialize(element, api) {
|
|
1849
|
-
super.initialize(element, api);
|
|
1850
|
-
element.addEventListener('touchstart', this.handleTouchStart);
|
|
1851
|
-
}
|
|
1852
|
-
handleTouchStart(event) {
|
|
1853
|
-
if (event.touches.length >= 1) {
|
|
1854
|
-
const touch1 = event.touches[0];
|
|
1855
|
-
const touch2 = event.touches[1];
|
|
1856
|
-
this.currentPosition1 = point.create(touch1.screenX, touch1.screenY);
|
|
1857
|
-
this.currentPosition2 =
|
|
1858
|
-
touch2 != null
|
|
1859
|
-
? point.create(touch2.screenX, touch2.screenY)
|
|
1860
|
-
: undefined;
|
|
1861
|
-
window.addEventListener('touchmove', this.handleTouchMove, {
|
|
1862
|
-
passive: false,
|
|
1863
|
-
});
|
|
1864
|
-
window.addEventListener('touchend', this.handleTouchEnd);
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
handleTouchMove(event) {
|
|
1868
|
-
if (event.touches.length === 1) {
|
|
1869
|
-
this.handleOnePointTouchMove(event.touches[0]);
|
|
1870
|
-
}
|
|
1871
|
-
else if (event.touches.length === 2) {
|
|
1872
|
-
const point1 = point.create(event.touches[0].clientX, event.touches[0].clientY);
|
|
1873
|
-
const point2 = point.create(event.touches[1].clientX, event.touches[1].clientY);
|
|
1874
|
-
this.handleTwoPointTouchMove(point1, point2);
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
handleTouchEnd(event) {
|
|
1878
|
-
var _a;
|
|
1879
|
-
(_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.endInteraction();
|
|
1880
|
-
this.isInteracting = false;
|
|
1881
|
-
window.removeEventListener('touchmove', this.handleTouchMove);
|
|
1882
|
-
window.removeEventListener('touchend', this.handleTouchEnd);
|
|
1883
|
-
}
|
|
1884
|
-
handleOnePointTouchMove(touch) {
|
|
1885
|
-
var _a, _b, _c;
|
|
1886
|
-
const position = point.create(touch.screenX, touch.screenY);
|
|
1887
|
-
if (this.currentPosition1 != null) {
|
|
1888
|
-
const delta = point.subtract(position, this.currentPosition1);
|
|
1889
|
-
const threshold = ((_a = this.interactionApi) === null || _a === void 0 ? void 0 : _a.pixelThreshold(true)) || 2;
|
|
1890
|
-
if (point.distance(position, this.currentPosition1) >= threshold ||
|
|
1891
|
-
this.isInteracting) {
|
|
1892
|
-
(_b = this.interactionApi) === null || _b === void 0 ? void 0 : _b.beginInteraction();
|
|
1893
|
-
(_c = this.interactionApi) === null || _c === void 0 ? void 0 : _c.rotateCamera(delta);
|
|
1894
|
-
this.isInteracting = true;
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
this.currentPosition1 = position;
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
|
|
1901
|
-
/**
|
|
1902
|
-
* A meter for measuring timings. Measurements are stored using the browser's
|
|
1903
|
-
* performance APIs, which provide high-resolution timestamps, as well as
|
|
1904
|
-
* provides visibility in the browser's developer tooling.
|
|
1905
|
-
*/
|
|
1906
|
-
class TimingMeter {
|
|
1907
|
-
constructor(name, perf = window.performance) {
|
|
1908
|
-
this.name = name;
|
|
1909
|
-
this.perf = perf;
|
|
1910
|
-
this.measures = new Set();
|
|
1911
|
-
this.nextId = 0;
|
|
1912
|
-
}
|
|
1913
|
-
/**
|
|
1914
|
-
* Clears any measurements from the buffer.
|
|
1915
|
-
*/
|
|
1916
|
-
clearMeasurements() {
|
|
1917
|
-
this.perf.clearMeasures(this.name);
|
|
1918
|
-
}
|
|
1919
|
-
measure(target) {
|
|
1920
|
-
if (isPromise(target)) {
|
|
1921
|
-
const mark = this.begin();
|
|
1922
|
-
return target.finally(() => this.end(mark));
|
|
1923
|
-
}
|
|
1924
|
-
else if (typeof target === 'function') {
|
|
1925
|
-
const mark = this.begin();
|
|
1926
|
-
const result = target();
|
|
1927
|
-
this.end(mark);
|
|
1928
|
-
return result;
|
|
1929
|
-
}
|
|
1930
|
-
else {
|
|
1931
|
-
throw new Error('Input must be a function or Promise');
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Returns a list of measurements that have been recorded, and clears the
|
|
1936
|
-
* internal measurement buffer.
|
|
1937
|
-
*/
|
|
1938
|
-
takeMeasurements() {
|
|
1939
|
-
const timings = this.perf.getEntriesByName(this.name);
|
|
1940
|
-
this.clearMeasurements();
|
|
1941
|
-
return timings;
|
|
1942
|
-
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Returns the last measurement to have been recorded, and clears the internal
|
|
1945
|
-
* measurement buffer.
|
|
1946
|
-
*/
|
|
1947
|
-
takeLastMeasurement() {
|
|
1948
|
-
const measurements = this.takeMeasurements();
|
|
1949
|
-
return measurements[measurements.length - 1];
|
|
1950
|
-
}
|
|
1951
|
-
begin() {
|
|
1952
|
-
const mark = `${this.name}-${this.nextId++}`;
|
|
1953
|
-
this.measures.add(mark);
|
|
1954
|
-
this.perf.mark(mark);
|
|
1955
|
-
return mark;
|
|
1956
|
-
}
|
|
1957
|
-
end(mark) {
|
|
1958
|
-
this.perf.measure(this.name, mark);
|
|
1959
|
-
this.perf.clearMarks(mark);
|
|
1960
|
-
this.measures.delete(mark);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
const paintTime = new TimingMeter('paint_time');
|
|
1964
|
-
|
|
1965
|
-
function loadImageBytesAsImageElement(imageData) {
|
|
1966
|
-
return new Promise((resolve, reject) => {
|
|
1967
|
-
const blob = new Blob([imageData]);
|
|
1968
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
1969
|
-
const image = new Image();
|
|
1970
|
-
image.addEventListener('load', () => {
|
|
1971
|
-
resolve({ image, dispose: () => undefined });
|
|
1972
|
-
URL.revokeObjectURL(blobUrl);
|
|
1973
|
-
});
|
|
1974
|
-
image.addEventListener('error', () => {
|
|
1975
|
-
reject(new ImageLoadError('Failed to load image data'));
|
|
1976
|
-
URL.revokeObjectURL(blobUrl);
|
|
1977
|
-
});
|
|
1978
|
-
image.src = blobUrl;
|
|
1979
|
-
});
|
|
1980
|
-
}
|
|
1981
|
-
async function loadImageBytesAsImageBitmap(imageData) {
|
|
1982
|
-
const blob = new Blob([imageData]);
|
|
1983
|
-
const bitmap = await window.createImageBitmap(blob);
|
|
1984
|
-
return { image: bitmap, dispose: () => bitmap.close() };
|
|
1985
|
-
}
|
|
1986
|
-
function loadImageBytes(imageBytes) {
|
|
1987
|
-
if (window.createImageBitmap != null) {
|
|
1988
|
-
return loadImageBytesAsImageBitmap(imageBytes);
|
|
1989
|
-
}
|
|
1990
|
-
else {
|
|
1991
|
-
return loadImageBytesAsImageElement(imageBytes);
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
const REPORTING_INTERVAL_MS = 1000;
|
|
1996
|
-
function drawImage(image, data) {
|
|
1997
|
-
const rect = data.viewport.calculateDrawRect(data.frame.image);
|
|
1998
|
-
data.canvas.clearRect(0, 0, data.canvasDimensions.width, data.canvasDimensions.height);
|
|
1999
|
-
data.canvas.drawImage(image.image, rect.x, rect.y, rect.width, rect.height);
|
|
2000
|
-
}
|
|
2001
|
-
function reportTimings(meter, callback) {
|
|
2002
|
-
const timings = meter.takeMeasurements();
|
|
2003
|
-
if (timings.length > 0) {
|
|
2004
|
-
callback(timings);
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
function measureCanvasRenderer(meter, renderer, logFrameRate, callback, intervalMs = REPORTING_INTERVAL_MS) {
|
|
2008
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2009
|
-
let timer;
|
|
2010
|
-
let renderCount = 0;
|
|
2011
|
-
let fpsFrameCount;
|
|
2012
|
-
let fpsHistory = [];
|
|
2013
|
-
function start() {
|
|
2014
|
-
renderCount++;
|
|
2015
|
-
if (timer == null) {
|
|
2016
|
-
timer = setInterval(() => {
|
|
2017
|
-
reportTimings(meter, callback);
|
|
2018
|
-
if (renderCount === 0) {
|
|
2019
|
-
clearTimer();
|
|
2020
|
-
}
|
|
2021
|
-
}, intervalMs);
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
function end() {
|
|
2025
|
-
renderCount--;
|
|
2026
|
-
}
|
|
2027
|
-
function clearTimer() {
|
|
2028
|
-
if (timer != null) {
|
|
2029
|
-
clearInterval(timer);
|
|
2030
|
-
timer = undefined;
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
/* istanbul ignore next */
|
|
2034
|
-
if (logFrameRate) {
|
|
2035
|
-
setInterval(() => {
|
|
2036
|
-
if (fpsFrameCount != null) {
|
|
2037
|
-
if (fpsHistory.length === 5) {
|
|
2038
|
-
fpsHistory = [...fpsHistory.slice(1), fpsFrameCount];
|
|
2039
|
-
}
|
|
2040
|
-
else {
|
|
2041
|
-
fpsHistory.push(fpsFrameCount);
|
|
2042
|
-
}
|
|
2043
|
-
const avgFps = fpsHistory.reduce((res, num) => res + num) / fpsHistory.length;
|
|
2044
|
-
console.debug(`Paint rate: ${fpsFrameCount}fps`);
|
|
2045
|
-
console.debug(`Paint rate (avg): ${avgFps}`);
|
|
2046
|
-
fpsFrameCount = undefined;
|
|
2047
|
-
}
|
|
2048
|
-
}, 1000);
|
|
2049
|
-
}
|
|
2050
|
-
return (data) => {
|
|
2051
|
-
start();
|
|
2052
|
-
return meter
|
|
2053
|
-
.measure(async () => {
|
|
2054
|
-
const frame = await renderer(data);
|
|
2055
|
-
fpsFrameCount = fpsFrameCount == null ? 1 : fpsFrameCount + 1;
|
|
2056
|
-
return frame;
|
|
2057
|
-
})
|
|
2058
|
-
.finally(() => end());
|
|
2059
|
-
};
|
|
2060
|
-
}
|
|
2061
|
-
function createCanvasRenderer() {
|
|
2062
|
-
let accumulatedCorrelationIds = [];
|
|
2063
|
-
function addCorrelationIds(frame) {
|
|
2064
|
-
if (frame != null) {
|
|
2065
|
-
const frameWithCorrelationIds = frame.copy({
|
|
2066
|
-
correlationIds: [
|
|
2067
|
-
...frame.correlationIds,
|
|
2068
|
-
...accumulatedCorrelationIds.filter((id) => !frame.correlationIds.includes(id)),
|
|
2069
|
-
],
|
|
2070
|
-
});
|
|
2071
|
-
accumulatedCorrelationIds = [];
|
|
2072
|
-
return frameWithCorrelationIds;
|
|
2073
|
-
}
|
|
2074
|
-
return frame;
|
|
2075
|
-
}
|
|
2076
|
-
function loadFrame() {
|
|
2077
|
-
let lastLoadedFrameNumber = -1;
|
|
2078
|
-
return async (data) => {
|
|
2079
|
-
if (data.frame.sequenceNumber > lastLoadedFrameNumber) {
|
|
2080
|
-
const image = await loadImageBytes(data.frame.image.imageBytes);
|
|
2081
|
-
lastLoadedFrameNumber = data.frame.sequenceNumber;
|
|
2082
|
-
return image;
|
|
2083
|
-
}
|
|
2084
|
-
};
|
|
2085
|
-
}
|
|
2086
|
-
function drawFrame() {
|
|
2087
|
-
let lastDrawnFrameNumber = -1;
|
|
2088
|
-
return async (data, image) => {
|
|
2089
|
-
var _a;
|
|
2090
|
-
if (image != null && data.frame.sequenceNumber > lastDrawnFrameNumber) {
|
|
2091
|
-
lastDrawnFrameNumber = data.frame.sequenceNumber;
|
|
2092
|
-
(_a = data.beforeDraw) === null || _a === void 0 ? void 0 : _a.call(data);
|
|
2093
|
-
drawImage(image, data);
|
|
2094
|
-
image.dispose();
|
|
2095
|
-
return data.frame;
|
|
2096
|
-
}
|
|
2097
|
-
image === null || image === void 0 ? void 0 : image.dispose();
|
|
2098
|
-
};
|
|
2099
|
-
}
|
|
2100
|
-
const load = loadFrame();
|
|
2101
|
-
const draw = drawFrame();
|
|
2102
|
-
return async (data) => {
|
|
2103
|
-
var _a, _b;
|
|
2104
|
-
const predicatePassing = (_b = (_a = data.predicate) === null || _a === void 0 ? void 0 : _a.call(data)) !== null && _b !== void 0 ? _b : true;
|
|
2105
|
-
accumulatedCorrelationIds = [
|
|
2106
|
-
...accumulatedCorrelationIds,
|
|
2107
|
-
...data.frame.correlationIds,
|
|
2108
|
-
];
|
|
2109
|
-
if (predicatePassing) {
|
|
2110
|
-
return load(data).then((image) => draw(data, image).then(addCorrelationIds));
|
|
2111
|
-
}
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
function ifRequestId(f) {
|
|
2116
|
-
return (req) => {
|
|
2117
|
-
var _a;
|
|
2118
|
-
const reqId = (_a = req.request.requestId) === null || _a === void 0 ? void 0 : _a.value;
|
|
2119
|
-
if (reqId != null) {
|
|
2120
|
-
f(reqId)(req);
|
|
2121
|
-
}
|
|
2122
|
-
};
|
|
2123
|
-
}
|
|
2124
|
-
function ifDrawFrame(f) {
|
|
2125
|
-
return (req) => {
|
|
2126
|
-
const { drawFrame } = req.request;
|
|
2127
|
-
if (drawFrame != null) {
|
|
2128
|
-
f(drawFrame)(req);
|
|
2129
|
-
}
|
|
2130
|
-
};
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
const MUTE_WARNING_SECONDS = 1 * 60; // 1 minute
|
|
2134
|
-
function calculateSendToReceiveDuration(clockProvider) {
|
|
2135
|
-
let muteWarning = false;
|
|
2136
|
-
return (sentAt) => {
|
|
2137
|
-
const clock = clockProvider();
|
|
2138
|
-
if (clock != null) {
|
|
2139
|
-
const receivedAt = clock.remoteTime(new Date(Date.now()));
|
|
2140
|
-
const duration = toProtoDuration(sentAt, receivedAt);
|
|
2141
|
-
const durationInMs = protoToDate(duration).getTime();
|
|
2142
|
-
if (durationInMs >= 0) {
|
|
2143
|
-
return duration;
|
|
2144
|
-
}
|
|
2145
|
-
else {
|
|
2146
|
-
if (!muteWarning) {
|
|
2147
|
-
console.warn(`Possible erroneous send to receive timing. Muting for ${MUTE_WARNING_SECONDS}s. [sent-at=${sentAt.toISOString()}, received-at=${receivedAt.toISOString()}, remote-time=${clock.knownRemoteTime.toISOString()}]`);
|
|
2148
|
-
muteWarning = true;
|
|
2149
|
-
setTimeout(() => (muteWarning = false), MUTE_WARNING_SECONDS * 1000);
|
|
2150
|
-
}
|
|
2151
|
-
return undefined;
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
};
|
|
2155
|
-
}
|
|
2156
|
-
/**
|
|
2157
|
-
* Returns a request handler that will acknowledge any frame requests that it
|
|
2158
|
-
* receives.
|
|
2159
|
-
*
|
|
2160
|
-
* @param api The client to acknowledge frames from.
|
|
2161
|
-
* @param clock A function that returns a synchronized clock.
|
|
2162
|
-
*/
|
|
2163
|
-
function acknowledgeFrameRequests(api, clockProvider) {
|
|
2164
|
-
const sendToReceiveDuration = calculateSendToReceiveDuration(clockProvider);
|
|
2165
|
-
return ifRequestId((reqId) => ifDrawFrame((_) => (req) => {
|
|
2166
|
-
const sentAt = protoToDate(req.sentAtTime);
|
|
2167
|
-
if (sentAt != null) {
|
|
2168
|
-
api.replyResult(reqId, {
|
|
2169
|
-
drawFrame: {
|
|
2170
|
-
sendToReceiveDuration: sendToReceiveDuration(sentAt),
|
|
2171
|
-
},
|
|
2172
|
-
});
|
|
2173
|
-
}
|
|
2174
|
-
}));
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
var StorageKeys;
|
|
2178
|
-
(function (StorageKeys) {
|
|
2179
|
-
StorageKeys["DEVICE_ID"] = "vertexvis:device-id";
|
|
2180
|
-
})(StorageKeys || (StorageKeys = {}));
|
|
2181
|
-
function upsertStorageEntry(key, values, storage = window.localStorage) {
|
|
2182
|
-
const existing = storage.getItem(key);
|
|
2183
|
-
if (existing != null) {
|
|
2184
|
-
const updated = Object.assign(Object.assign({}, JSON.parse(existing)), values);
|
|
2185
|
-
storage.setItem(key, JSON.stringify(updated));
|
|
2186
|
-
}
|
|
2187
|
-
else {
|
|
2188
|
-
storage.setItem(key, JSON.stringify(values));
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
function getStorageEntry(key, f, storage = window.localStorage) {
|
|
2192
|
-
const item = storage.getItem(key);
|
|
2193
|
-
if (item != null) {
|
|
2194
|
-
return f(JSON.parse(item));
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
|
-
class ViewerStream extends StreamApi {
|
|
2199
|
-
constructor(ws, opts = {}) {
|
|
2200
|
-
var _a, _b, _c;
|
|
2201
|
-
super(ws, { loggingEnabled: opts.loggingEnabled });
|
|
2202
|
-
this.state = { type: 'disconnected' };
|
|
2203
|
-
this.stateChanged = new EventDispatcher();
|
|
2204
|
-
this.dimensions = dimensions.create(0, 0);
|
|
2205
|
-
this.streamAttributes = {};
|
|
2206
|
-
this.frameBgColor = color.create(255, 255, 255);
|
|
2207
|
-
this.config = parseConfig('platprod');
|
|
2208
|
-
this.options = {
|
|
2209
|
-
tokenRefreshOffsetInSeconds: (_a = opts.tokenRefreshOffsetInSeconds) !== null && _a !== void 0 ? _a : 30,
|
|
2210
|
-
offlineThresholdInSeconds: (_b = opts.offlineThresholdInSeconds) !== null && _b !== void 0 ? _b : 30,
|
|
2211
|
-
loadTimeoutInSeconds: (_c = opts.loadTimeoutInSeconds) !== null && _c !== void 0 ? _c : 15,
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
getState() {
|
|
2215
|
-
return this.state;
|
|
2216
|
-
}
|
|
2217
|
-
disconnect() {
|
|
2218
|
-
if (this.state.type !== 'disconnected' &&
|
|
2219
|
-
this.state.type !== 'connection-failed') {
|
|
2220
|
-
console.debug('Disconnecting websocket');
|
|
2221
|
-
this.state.connection.dispose();
|
|
2222
|
-
this.updateState({ type: 'disconnected' });
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
async load(urn, clientId, deviceId, config = parseConfig('platprod')) {
|
|
2226
|
-
this.clientId = clientId;
|
|
2227
|
-
this.deviceId = deviceId;
|
|
2228
|
-
this.config = config;
|
|
2229
|
-
if (this.state.type === 'disconnected') {
|
|
2230
|
-
return this.loadIfDisconnected(urn);
|
|
2231
|
-
}
|
|
2232
|
-
else if (this.state.type === 'connection-failed') {
|
|
2233
|
-
return this.loadIfDisconnected(urn);
|
|
2234
|
-
}
|
|
2235
|
-
else if (this.state.type === 'reconnecting') {
|
|
2236
|
-
return this.loadIfConnectingOrConnected(urn, this.state);
|
|
2237
|
-
}
|
|
2238
|
-
else if (this.state.type === 'connecting') {
|
|
2239
|
-
return this.loadIfConnectingOrConnected(urn, this.state);
|
|
2240
|
-
}
|
|
2241
|
-
else {
|
|
2242
|
-
return this.loadIfConnectingOrConnected(urn, this.state);
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
update(fields) {
|
|
2246
|
-
this.frameBgColor = fields.frameBgColor
|
|
2247
|
-
? fields.frameBgColor
|
|
2248
|
-
: this.frameBgColor;
|
|
2249
|
-
if (fields.dimensions != null && fields.dimensions !== this.dimensions) {
|
|
2250
|
-
this.dimensions = fields.dimensions;
|
|
2251
|
-
this.ifState('connected', () => this.updateDimensions({ dimensions: this.dimensions }));
|
|
2252
|
-
}
|
|
2253
|
-
if (fields.streamAttributes != null &&
|
|
2254
|
-
this.streamAttributes !== fields.streamAttributes) {
|
|
2255
|
-
this.streamAttributes = fields.streamAttributes;
|
|
2256
|
-
this.ifState('connected', () => this.updateStream({
|
|
2257
|
-
streamAttributes: toPbStreamAttributesOrThrow(this.streamAttributes),
|
|
2258
|
-
}));
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
async loadIfConnectingOrConnected(urn, state) {
|
|
2262
|
-
var _a;
|
|
2263
|
-
const { resource: pResource, subResource: pSubResource } = state.resource;
|
|
2264
|
-
const resource = fromUrn(urn);
|
|
2265
|
-
const hasResourceChanged = !objects.isEqual(pResource, resource.resource);
|
|
2266
|
-
const hasSubResourceChanged = !objects.isEqual(pSubResource, resource.subResource);
|
|
2267
|
-
const isConnecting = state.type === 'connecting' || state.type === 'reconnecting';
|
|
2268
|
-
const isConnected = state.type === 'connected';
|
|
2269
|
-
const suppliedIdQuery = resource.queries.find((q) => q.type === 'supplied-id');
|
|
2270
|
-
if (hasResourceChanged || (isConnecting && hasSubResourceChanged)) {
|
|
2271
|
-
this.disconnect();
|
|
2272
|
-
return this.loadIfDisconnected(urn);
|
|
2273
|
-
}
|
|
2274
|
-
else if (isConnected &&
|
|
2275
|
-
hasSubResourceChanged &&
|
|
2276
|
-
((_a = resource.subResource) === null || _a === void 0 ? void 0 : _a.type) === 'scene-view-state') {
|
|
2277
|
-
const payload = Object.assign(Object.assign({}, (resource.subResource.id != null
|
|
2278
|
-
? { sceneViewStateId: { hex: resource.subResource.id } }
|
|
2279
|
-
: {})), (suppliedIdQuery != null
|
|
2280
|
-
? { sceneViewStateSuppliedId: { value: suppliedIdQuery.id } }
|
|
2281
|
-
: {}));
|
|
2282
|
-
await this.loadSceneViewState(payload);
|
|
2283
|
-
this.updateState(Object.assign(Object.assign({}, state), { resource }));
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
async loadIfDisconnected(urn) {
|
|
2287
|
-
try {
|
|
2288
|
-
await this.connectWithNewStream(fromUrn(urn));
|
|
2289
|
-
}
|
|
2290
|
-
catch (e) {
|
|
2291
|
-
if (e instanceof CustomError) {
|
|
2292
|
-
this.updateState({
|
|
2293
|
-
type: 'connection-failed',
|
|
2294
|
-
message: `Cannot load scene. ${e.message}`,
|
|
2295
|
-
error: e,
|
|
2296
|
-
});
|
|
2297
|
-
}
|
|
2298
|
-
else if (e instanceof StreamRequestError) {
|
|
2299
|
-
this.updateState({
|
|
2300
|
-
type: 'connection-failed',
|
|
2301
|
-
message: `Cannot load scene. Stream request failed to start stream.`,
|
|
2302
|
-
error: e,
|
|
2303
|
-
});
|
|
2304
|
-
}
|
|
2305
|
-
else {
|
|
2306
|
-
this.updateState({
|
|
2307
|
-
type: 'connection-failed',
|
|
2308
|
-
message: `Cannot load scene for unknown reason. See console logs.`,
|
|
2309
|
-
error: e,
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
throw e;
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
connectWithNewStream(resource) {
|
|
2316
|
-
return this.openWebsocketStream(resource, 'connecting', () => this.requestNewStream(resource));
|
|
2317
|
-
}
|
|
2318
|
-
connectToExistingStream(state) {
|
|
2319
|
-
return this.openWebsocketStream(state.resource, 'reconnecting', () => this.requestReconnectStream(state), { maxRetries: Number.POSITIVE_INFINITY });
|
|
2320
|
-
}
|
|
2321
|
-
async openWebsocketStream(resource, type, requestStream, { maxRetries = 3 } = {}) {
|
|
2322
|
-
const descriptor = getWebsocketDescriptor(getWebsocketUri(this.config, resource.resource, this.clientId, this.deviceId));
|
|
2323
|
-
console.debug(`Initiating WS connection [uri=${descriptor.url}]`);
|
|
2324
|
-
const controller = new AbortController();
|
|
2325
|
-
const settings = getStreamSettings(this.config);
|
|
2326
|
-
this.updateState({
|
|
2327
|
-
type,
|
|
2328
|
-
resource,
|
|
2329
|
-
connection: {
|
|
2330
|
-
dispose: () => {
|
|
2331
|
-
this.dispose();
|
|
2332
|
-
controller.abort();
|
|
2333
|
-
},
|
|
2334
|
-
},
|
|
2335
|
-
});
|
|
2336
|
-
const connection = await async.abort(controller.signal, async.retry(() => this.connect(descriptor, settings), {
|
|
2337
|
-
maxRetries,
|
|
2338
|
-
delaysInMs: ViewerStream.WS_RECONNECT_DELAYS,
|
|
2339
|
-
})).catch((e) => {
|
|
2340
|
-
throw new WebsocketConnectionError('Websocket connection failed.', e instanceof Error ? e : undefined);
|
|
2341
|
-
});
|
|
2342
|
-
if (!connection.aborted) {
|
|
2343
|
-
return this.requestNewOrExistingStream(resource, connection.result, requestStream);
|
|
2344
|
-
}
|
|
2345
|
-
else {
|
|
2346
|
-
this.updateState({ type: 'disconnected' });
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
async requestNewOrExistingStream(resource, connection, requestStream) {
|
|
2350
|
-
const pendingClock = this.requestClock();
|
|
2351
|
-
const stream = await requestStream();
|
|
2352
|
-
console.debug(`Stream connected [stream-id=${stream.streamId}, scene-id=${stream.sceneId}, scene-view-id=${stream.sceneViewId}]`);
|
|
2353
|
-
const onRequest = this.onRequest((msg) => {
|
|
2354
|
-
const req = msg.request.drawFrame;
|
|
2355
|
-
if (req != null) {
|
|
2356
|
-
const frame = fromPbFrameOrThrow(stream.worldOrientation)(req);
|
|
2357
|
-
if (this.state.type === 'connected') {
|
|
2358
|
-
this.updateState(Object.assign(Object.assign({}, this.state), { frame }));
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
});
|
|
2362
|
-
const reconnect = this.reconnectWhenNeeded();
|
|
2363
|
-
const refreshToken = this.refreshTokenWhenExpired(stream.token);
|
|
2364
|
-
const acknowledgeFrameRequests = this.acknowledgeFrameRequests();
|
|
2365
|
-
const frame = stream.frame == null
|
|
2366
|
-
? await this.waitForFrame(stream.worldOrientation, this.options.loadTimeoutInSeconds)
|
|
2367
|
-
: stream.frame;
|
|
2368
|
-
const clock = await pendingClock;
|
|
2369
|
-
console.debug(`Synchronized clocks [local-time=${clock.knownLocalTime.toISOString()}, remote-time=${clock.knownRemoteTime.toISOString()}]`);
|
|
2370
|
-
this.updateState({
|
|
2371
|
-
type: 'connected',
|
|
2372
|
-
connection: {
|
|
2373
|
-
dispose: () => {
|
|
2374
|
-
reconnect.dispose();
|
|
2375
|
-
onRequest.dispose();
|
|
2376
|
-
refreshToken.dispose();
|
|
2377
|
-
acknowledgeFrameRequests.dispose();
|
|
2378
|
-
connection.dispose();
|
|
2379
|
-
},
|
|
2380
|
-
},
|
|
2381
|
-
resource,
|
|
2382
|
-
streamId: stream.streamId,
|
|
2383
|
-
deviceId: stream.deviceId,
|
|
2384
|
-
sceneId: stream.sceneId,
|
|
2385
|
-
sceneViewId: stream.sceneViewId,
|
|
2386
|
-
worldOrientation: stream.worldOrientation,
|
|
2387
|
-
token: stream.token,
|
|
2388
|
-
frame,
|
|
2389
|
-
clock,
|
|
2390
|
-
});
|
|
2391
|
-
}
|
|
2392
|
-
async requestNewStream(resource) {
|
|
2393
|
-
var _a, _b;
|
|
2394
|
-
const suppliedIdQuery = resource.queries.find((q) => q.type === 'supplied-id');
|
|
2395
|
-
const res = fromPbStartStreamResponseOrThrow(await this.startStream({
|
|
2396
|
-
streamKey: { value: resource.resource.id },
|
|
2397
|
-
dimensions: this.dimensions,
|
|
2398
|
-
frameBackgroundColor: toPbColorOrThrow(this.frameBgColor),
|
|
2399
|
-
streamAttributes: toPbStreamAttributesOrThrow(this.streamAttributes),
|
|
2400
|
-
sceneViewStateId: ((_a = resource.subResource) === null || _a === void 0 ? void 0 : _a.type) === 'scene-view-state' &&
|
|
2401
|
-
resource.subResource.id != null
|
|
2402
|
-
? { hex: resource.subResource.id }
|
|
2403
|
-
: undefined,
|
|
2404
|
-
sceneViewStateSuppliedId: ((_b = resource.subResource) === null || _b === void 0 ? void 0 : _b.type) === 'scene-view-state' &&
|
|
2405
|
-
suppliedIdQuery != null
|
|
2406
|
-
? { value: suppliedIdQuery.id }
|
|
2407
|
-
: undefined,
|
|
2408
|
-
}));
|
|
2409
|
-
return {
|
|
2410
|
-
resource: resource,
|
|
2411
|
-
streamId: res.streamId,
|
|
2412
|
-
sceneId: res.sceneId,
|
|
2413
|
-
sceneViewId: res.sceneViewId,
|
|
2414
|
-
deviceId: res.sessionId,
|
|
2415
|
-
token: res.token,
|
|
2416
|
-
worldOrientation: res.worldOrientation,
|
|
2417
|
-
frame: undefined,
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
|
-
async requestReconnectStream(state) {
|
|
2421
|
-
const res = fromPbReconnectResponseOrThrow(await this.reconnect({
|
|
2422
|
-
streamId: { hex: state.streamId },
|
|
2423
|
-
dimensions: this.dimensions,
|
|
2424
|
-
frameBackgroundColor: toPbColorOrThrow(this.frameBgColor),
|
|
2425
|
-
streamAttributes: toPbStreamAttributesOrThrow(this.streamAttributes),
|
|
2426
|
-
}));
|
|
2427
|
-
return Object.assign(Object.assign({}, state), { token: res.token });
|
|
2428
|
-
}
|
|
2429
|
-
async requestClock() {
|
|
2430
|
-
const remoteTime = fromPbSyncTimeResponseOrThrow(await this.syncTime({
|
|
2431
|
-
requestTime: currentDateAsProtoTimestamp(),
|
|
2432
|
-
}));
|
|
2433
|
-
return new SynchronizedClock(remoteTime);
|
|
2434
|
-
}
|
|
2435
|
-
reconnectWhenNeeded() {
|
|
2436
|
-
const whenDisconnected = this.onClose(() => {
|
|
2437
|
-
if (this.state.type === 'connected') {
|
|
2438
|
-
this.closeAndReconnect(this.state);
|
|
2439
|
-
}
|
|
2440
|
-
});
|
|
2441
|
-
const whenRequested = this.onRequest((msg) => {
|
|
2442
|
-
const isReconnectMsg = msg.request.gracefulReconnection != null;
|
|
2443
|
-
if (isReconnectMsg && this.state.type === 'connected') {
|
|
2444
|
-
console.debug('Received request for graceful reconnect. Closing connection and attempting reconnect.');
|
|
2445
|
-
this.closeAndReconnect(this.state);
|
|
2446
|
-
}
|
|
2447
|
-
});
|
|
2448
|
-
const whenOffline = this.reconnectWhenOffline();
|
|
2449
|
-
return {
|
|
2450
|
-
dispose: () => {
|
|
2451
|
-
whenDisconnected.dispose();
|
|
2452
|
-
whenRequested.dispose();
|
|
2453
|
-
whenOffline.dispose();
|
|
2454
|
-
},
|
|
2455
|
-
};
|
|
2456
|
-
}
|
|
2457
|
-
refreshTokenWhenExpired(token) {
|
|
2458
|
-
let timer;
|
|
2459
|
-
const startTimer = (token) => {
|
|
2460
|
-
const { tokenRefreshOffsetInSeconds } = this.options;
|
|
2461
|
-
const ms = token.remainingTimeInMs(tokenRefreshOffsetInSeconds);
|
|
2462
|
-
timer = window.setTimeout(async () => {
|
|
2463
|
-
const res = await this.refreshToken();
|
|
2464
|
-
const newToken = fromPbRefreshTokenResponseOrThrow(res);
|
|
2465
|
-
startTimer(newToken);
|
|
2466
|
-
if (this.state.type === 'connected') {
|
|
2467
|
-
this.updateState(Object.assign(Object.assign({}, this.state), { token: newToken }));
|
|
2468
|
-
}
|
|
2469
|
-
}, ms);
|
|
2470
|
-
};
|
|
2471
|
-
startTimer(token);
|
|
2472
|
-
return { dispose: () => clearTimeout(timer) };
|
|
2473
|
-
}
|
|
2474
|
-
reconnectWhenOffline() {
|
|
2475
|
-
let timer;
|
|
2476
|
-
const clearTimer = () => window.clearTimeout(timer);
|
|
2477
|
-
const restartTimer = () => {
|
|
2478
|
-
clearTimer();
|
|
2479
|
-
const delayInSec = this.options.offlineThresholdInSeconds;
|
|
2480
|
-
console.debug(`Detected that host is offline. Will attempt reconnect in ${delayInSec}s.`);
|
|
2481
|
-
timer = window.setTimeout(() => {
|
|
2482
|
-
if (this.state.type === 'connected') {
|
|
2483
|
-
this.closeAndReconnect(this.state);
|
|
2484
|
-
}
|
|
2485
|
-
}, delayInSec * 1000);
|
|
2486
|
-
};
|
|
2487
|
-
const handleOnline = () => {
|
|
2488
|
-
console.debug('Detected that host is online.');
|
|
2489
|
-
clearTimer();
|
|
2490
|
-
};
|
|
2491
|
-
const handleOffline = () => restartTimer();
|
|
2492
|
-
window.addEventListener('offline', handleOffline);
|
|
2493
|
-
window.addEventListener('online', handleOnline);
|
|
2494
|
-
return {
|
|
2495
|
-
dispose: () => {
|
|
2496
|
-
clearTimer();
|
|
2497
|
-
window.removeEventListener('offline', restartTimer);
|
|
2498
|
-
window.removeEventListener('online', clearTimer);
|
|
2499
|
-
},
|
|
2500
|
-
};
|
|
2501
|
-
}
|
|
2502
|
-
closeAndReconnect(state) {
|
|
2503
|
-
state.connection.dispose();
|
|
2504
|
-
return this.connectToExistingStream(state);
|
|
2505
|
-
}
|
|
2506
|
-
async waitForFrame(worldOrientation, timeoutInSeconds) {
|
|
2507
|
-
let disposable;
|
|
2508
|
-
try {
|
|
2509
|
-
return await async.timeout(timeoutInSeconds * 1000, new Promise((resolve) => {
|
|
2510
|
-
disposable = this.onRequest((msg) => {
|
|
2511
|
-
try {
|
|
2512
|
-
const req = msg.request.drawFrame;
|
|
2513
|
-
if (req != null) {
|
|
2514
|
-
const frame = fromPbFrameOrThrow(worldOrientation)(req);
|
|
2515
|
-
resolve(frame);
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
finally {
|
|
2519
|
-
disposable === null || disposable === void 0 ? void 0 : disposable.dispose();
|
|
2520
|
-
}
|
|
2521
|
-
});
|
|
2522
|
-
}));
|
|
2523
|
-
}
|
|
2524
|
-
catch (e) {
|
|
2525
|
-
throw new SceneRenderError(`Frame timed out after ${timeoutInSeconds / 1000}s`, e instanceof Error ? e : undefined);
|
|
2526
|
-
}
|
|
2527
|
-
finally {
|
|
2528
|
-
disposable === null || disposable === void 0 ? void 0 : disposable.dispose();
|
|
2529
|
-
}
|
|
2530
|
-
}
|
|
2531
|
-
acknowledgeFrameRequests() {
|
|
2532
|
-
return this.onRequest(acknowledgeFrameRequests(this, () => this.state.type === 'connected' ? this.state.clock : undefined));
|
|
2533
|
-
}
|
|
2534
|
-
updateState(state) {
|
|
2535
|
-
if (this.state !== state) {
|
|
2536
|
-
this.state = state;
|
|
2537
|
-
this.stateChanged.emit(this.state);
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
ifState(state, f) {
|
|
2541
|
-
if (this.state.type === state) {
|
|
2542
|
-
return f();
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
onStateChanged(listener) {
|
|
2546
|
-
return this.stateChanged.on(listener);
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
ViewerStream.WS_RECONNECT_DELAYS = [0, 1000, 1000, 5000];
|
|
2550
|
-
const toPbStreamAttributesOrThrow = mapper.ifInvalidThrow(toPbStreamAttributes);
|
|
2551
|
-
const toPbColorOrThrow = mapper.ifInvalidThrow(toPbRGBi);
|
|
2552
|
-
function getStreamSettings(config) {
|
|
2553
|
-
return {
|
|
2554
|
-
EXPERIMENTAL_frameDelivery: Object.assign(Object.assign({}, config.EXPERIMENTAL_frameDelivery), { rateLimitingEnabled: config.flags.throttleFrameDelivery }),
|
|
2555
|
-
EXPERIMENTAL_adaptiveRendering: Object.assign(Object.assign({}, config.EXPERIMENTAL_adaptiveRendering), { enabled: config.flags.adaptiveRendering }),
|
|
2556
|
-
EXPERIMENTAL_qualityOfService: Object.assign({}, config.EXPERIMENTAL_qualityOfService),
|
|
2557
|
-
};
|
|
2558
|
-
}
|
|
2559
|
-
function getWebsocketDescriptor(uri$1) {
|
|
2560
|
-
return {
|
|
2561
|
-
url: uri.toString(uri$1),
|
|
2562
|
-
protocols: ['ws.vertexvis.com'],
|
|
2563
|
-
};
|
|
2564
|
-
}
|
|
2565
|
-
function getWebsocketUri(config, resource, clientId, deviceId) {
|
|
2566
|
-
if (clientId != null) {
|
|
2567
|
-
return uri.appendPath(uri.toString(uri.parseAndAddParams('/ws', {
|
|
2568
|
-
clientId,
|
|
2569
|
-
deviceId,
|
|
2570
|
-
})), uri.parse(config.network.renderingHost));
|
|
2571
|
-
}
|
|
2572
|
-
else {
|
|
2573
|
-
return uri.appendPath(`/stream-keys/${resource.id}/session`, uri.parse(config.network.renderingHost));
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
const DEFAULT_VIEWER_SCENE_WAIT_MS = 15000;
|
|
2578
|
-
function getElementBackgroundColor(element) {
|
|
2579
|
-
const styles = window.getComputedStyle(element);
|
|
2580
|
-
return color.fromCss(styles.backgroundColor);
|
|
2581
|
-
}
|
|
2582
|
-
function getElementBoundingClientRect(element) {
|
|
2583
|
-
return element.getBoundingClientRect();
|
|
2584
|
-
}
|
|
2585
|
-
|
|
2586
|
-
const viewerCss = ":host{--image-background:var(--image-background);--viewer-background:var(--viewer-background);display:block;position:relative;width:300px;height:300px;min-width:1px;min-height:1px}.canvas-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;position:relative;background:var(--image-background, var(--viewer-background, #ffffff))}.enable-pointer-events{touch-action:none}.viewer-container{width:100%;height:100%;overflow:hidden;background:var(--viewer-background, #ffffff)}.error-message{position:absolute;top:50%;width:100%;transform:translateY(-50%);text-align:center}";
|
|
2587
|
-
|
|
2588
|
-
const Viewer = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
|
|
2589
|
-
constructor() {
|
|
2590
|
-
super();
|
|
2591
|
-
this.__registerHost();
|
|
2592
|
-
this.__attachShadow();
|
|
2593
|
-
this.tap = createEvent(this, "tap", 7);
|
|
2594
|
-
this.doubletap = createEvent(this, "doubletap", 7);
|
|
2595
|
-
this.longpress = createEvent(this, "longpress", 7);
|
|
2596
|
-
this.frameReceived = createEvent(this, "frameReceived", 7);
|
|
2597
|
-
this.frameDrawn = createEvent(this, "frameDrawn", 7);
|
|
2598
|
-
this.tokenExpired = createEvent(this, "tokenExpired", 7);
|
|
2599
|
-
this.connectionChange = createEvent(this, "connectionChange", 7);
|
|
2600
|
-
this.sceneReady = createEvent(this, "sceneReady", 7);
|
|
2601
|
-
this.sceneChanged = createEvent(this, "sceneChanged", 7);
|
|
2602
|
-
this.interactionStarted = createEvent(this, "interactionStarted", 7);
|
|
2603
|
-
this.interactionFinished = createEvent(this, "interactionFinished", 7);
|
|
2604
|
-
this.cameraTypeChanged = createEvent(this, "cameraTypeChanged", 7);
|
|
2605
|
-
this.deviceIdChange = createEvent(this, "deviceIdChange", 7);
|
|
2606
|
-
this.dimensionschange = createEvent(this, "dimensionschange", 7);
|
|
2607
|
-
/**
|
|
2608
|
-
* Sets the default environment for the viewer. This setting is used for
|
|
2609
|
-
* auto-configuring network hosts.
|
|
2610
|
-
*
|
|
2611
|
-
* Use the `config` property for manually setting hosts.
|
|
2612
|
-
*
|
|
2613
|
-
* @see Viewer.config
|
|
2614
|
-
*/
|
|
2615
|
-
this.configEnv = 'platprod';
|
|
2616
|
-
/**
|
|
2617
|
-
* Enables or disables the default mouse and touch interactions provided by
|
|
2618
|
-
* the viewer. Enabled by default.
|
|
2619
|
-
*/
|
|
2620
|
-
this.cameraControls = true;
|
|
2621
|
-
/**
|
|
2622
|
-
* The type of camera model to represent the scene with. Can be either
|
|
2623
|
-
* `perspective` or `orthographic`, and defaults to `perspective`.
|
|
2624
|
-
*/
|
|
2625
|
-
this.cameraType = 'perspective';
|
|
2626
|
-
/**
|
|
2627
|
-
* Enables or disables the default keyboard shortcut interactions provided by
|
|
2628
|
-
* the viewer. Enabled by default, requires `cameraControls` being enabled.
|
|
2629
|
-
*
|
|
2630
|
-
*/
|
|
2631
|
-
this.keyboardControls = true;
|
|
2632
|
-
/**
|
|
2633
|
-
* Enables or disables the default rotation interaction being changed to
|
|
2634
|
-
* rotate around the pointer down location.
|
|
2635
|
-
*/
|
|
2636
|
-
this.rotateAroundTapPoint = true;
|
|
2637
|
-
/**
|
|
2638
|
-
* Specifies how phantom parts should appear.
|
|
2639
|
-
* The opacity must be between 0 and 1, where 0 is completely hidden and 1 is completely visible.
|
|
2640
|
-
*/
|
|
2641
|
-
this.phantom = { opacity: 0.1 };
|
|
2642
|
-
/**
|
|
2643
|
-
* Specifies whether to use the default lights for the scene. When false, default
|
|
2644
|
-
* lights are used. When true, no default lights are used, and the lights must
|
|
2645
|
-
* be specified separately.
|
|
2646
|
-
*/
|
|
2647
|
-
this.noDefaultLights = false;
|
|
2648
|
-
/**
|
|
2649
|
-
* @private
|
|
2650
|
-
* @internal
|
|
2651
|
-
* Specifies experimental rendering options. For Vertex use only.
|
|
2652
|
-
*/
|
|
2653
|
-
this.experimentalRenderingOptions = '';
|
|
2654
|
-
/**
|
|
2655
|
-
* An optional value that will debounce frame updates when resizing
|
|
2656
|
-
* this viewer element.
|
|
2657
|
-
*/
|
|
2658
|
-
this.resizeDebounce = 100;
|
|
2659
|
-
/**
|
|
2660
|
-
* @internal
|
|
2661
|
-
*/
|
|
2662
|
-
this.stencilBuffer = new StencilBufferManager(this.hostElement);
|
|
2663
|
-
/**
|
|
2664
|
-
* Represents the current viewport of the viewer. The viewport represents the
|
|
2665
|
-
* dimensions of the canvas where a frame is rendered. It contains methods for
|
|
2666
|
-
* translating between viewport coordinates, frame coordinates and world
|
|
2667
|
-
* coordinates.
|
|
2668
|
-
*/
|
|
2669
|
-
this.viewport = Viewport.fromDimensions(dimensions.create(0, 0));
|
|
2670
|
-
/**
|
|
2671
|
-
* This stores internal state that you want to preserve across live-reloads,
|
|
2672
|
-
* but shouldn't trigger a refresh if the data changes. Marking this with
|
|
2673
|
-
* @State to allow to preserve state across live-reloads.
|
|
2674
|
-
*/
|
|
2675
|
-
this.stateMap = {
|
|
2676
|
-
cursorManager: new CursorManager(),
|
|
2677
|
-
streamState: { type: 'disconnected' },
|
|
2678
|
-
};
|
|
2679
|
-
this.interactionHandlers = [];
|
|
2680
|
-
this.defaultInteractionHandlerDisposables = [];
|
|
2681
|
-
this.tapKeyInteractions = [];
|
|
2682
|
-
this.defaultTapKeyInteractions = [];
|
|
2683
|
-
this.internalFrameDrawnDispatcher = new EventDispatcher();
|
|
2684
|
-
this.handleElementResize = this.handleElementResize.bind(this);
|
|
2685
|
-
}
|
|
2686
|
-
/**
|
|
2687
|
-
* @ignore
|
|
2688
|
-
*/
|
|
2689
|
-
componentWillLoad() {
|
|
2690
|
-
var _a;
|
|
2691
|
-
this.updateResolvedConfig();
|
|
2692
|
-
this.calculateComponentDimensions();
|
|
2693
|
-
this.resizeObserver = new ResizeObserver(this.handleElementResize);
|
|
2694
|
-
this.registerSlotChangeListeners();
|
|
2695
|
-
this.stream =
|
|
2696
|
-
(_a = this.stream) !== null && _a !== void 0 ? _a : new ViewerStream(new WebSocketClientImpl(), {
|
|
2697
|
-
loggingEnabled: this.getResolvedConfig().flags.logWsMessages,
|
|
2698
|
-
});
|
|
2699
|
-
this.addStreamListeners();
|
|
2700
|
-
this.updateStreamAttributes();
|
|
2701
|
-
this.stateMap.cursorManager.onChanged.on(() => this.handleCursorChanged());
|
|
2702
|
-
}
|
|
2703
|
-
/**
|
|
2704
|
-
* @ignore
|
|
2705
|
-
*/
|
|
2706
|
-
componentDidLoad() {
|
|
2707
|
-
var _a;
|
|
2708
|
-
this.interactionApi = this.createInteractionApi();
|
|
2709
|
-
if (this.containerElement != null) {
|
|
2710
|
-
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(this.containerElement);
|
|
2711
|
-
}
|
|
2712
|
-
if (this.src != null) {
|
|
2713
|
-
this.load(this.src).catch((e) => {
|
|
2714
|
-
console.error('Error loading scene', e);
|
|
2715
|
-
});
|
|
2716
|
-
}
|
|
2717
|
-
this.initializeDefaultInteractionHandlers();
|
|
2718
|
-
this.injectViewerApi();
|
|
2719
|
-
}
|
|
2720
|
-
/**
|
|
2721
|
-
* @ignore
|
|
2722
|
-
*/
|
|
2723
|
-
render() {
|
|
2724
|
-
var _a;
|
|
2725
|
-
return (h(Host, null, h("div", { class: "viewer-container", style: { cursor: cssCursor((_a = this.cursor) !== null && _a !== void 0 ? _a : '') }, onContextMenu: (event) => event.preventDefault() }, h("div", { ref: (ref) => (this.containerElement = ref), class: classnames('canvas-container', {
|
|
2726
|
-
'enable-pointer-events ': window.PointerEvent != null,
|
|
2727
|
-
}) }, h("canvas", { ref: (ref) => {
|
|
2728
|
-
this.canvasElement = ref;
|
|
2729
|
-
this.stateMap.interactionTarget = ref;
|
|
2730
|
-
}, class: "canvas" }), this.errorMessage != null ? (h("div", { class: "error-message" }, this.errorMessage)) : null), h("slot", null))));
|
|
2731
|
-
}
|
|
2732
|
-
/**
|
|
2733
|
-
* @internal
|
|
2734
|
-
*/
|
|
2735
|
-
async dispatchFrameDrawn(frame) {
|
|
2736
|
-
this.frame = frame;
|
|
2737
|
-
this.internalFrameDrawnDispatcher.emit(frame);
|
|
2738
|
-
this.frameDrawn.emit(frame);
|
|
2739
|
-
}
|
|
2740
|
-
/**
|
|
2741
|
-
* Registers and initializes an interaction handler with the viewer. Returns a
|
|
2742
|
-
* `Disposable` that should be used to deregister the interaction handler.
|
|
2743
|
-
*
|
|
2744
|
-
* `InteractionHandler`s are used to build custom mouse and touch interactions
|
|
2745
|
-
* for the viewer. Use `<vertex-viewer camera-controls="false" />` to disable
|
|
2746
|
-
* the default camera controls provided by the viewer.
|
|
2747
|
-
*
|
|
2748
|
-
* @example
|
|
2749
|
-
* ```
|
|
2750
|
-
* class CustomInteractionHandler extends InteractionHandler {
|
|
2751
|
-
* private element: HTMLElement;
|
|
2752
|
-
* private api: InteractionApi;
|
|
2753
|
-
*
|
|
2754
|
-
* public dispose(): void {
|
|
2755
|
-
* this.element.removeEventListener('click', this.handleElementClick);
|
|
2756
|
-
* }
|
|
2757
|
-
*
|
|
2758
|
-
* public initialize(element: HTMLElement, api: InteractionApi): void {
|
|
2759
|
-
* this.api = api;
|
|
2760
|
-
* this.element = element;
|
|
2761
|
-
* this.element.addEventListener('click', this.handleElementClick);
|
|
2762
|
-
* }
|
|
2763
|
-
*
|
|
2764
|
-
* private handleElementClick = (event: MouseEvent) => {
|
|
2765
|
-
* api.tap({ x: event.clientX, y: event.clientY });
|
|
2766
|
-
* }
|
|
2767
|
-
* }
|
|
2768
|
-
*
|
|
2769
|
-
* const viewer = document.querySelector("vertex-viewer");
|
|
2770
|
-
* viewer.registerInteractionHandler(new CustomInteractionHandler);
|
|
2771
|
-
* ```
|
|
2772
|
-
*
|
|
2773
|
-
* @param interactionHandler The interaction handler to register.
|
|
2774
|
-
* @returns {Promise<void>} A promise containing the disposable to use to
|
|
2775
|
-
* deregister the handler.
|
|
2776
|
-
*/
|
|
2777
|
-
async registerInteractionHandler(interactionHandler) {
|
|
2778
|
-
this.interactionHandlers.push(interactionHandler);
|
|
2779
|
-
this.initializeInteractionHandler(interactionHandler);
|
|
2780
|
-
return {
|
|
2781
|
-
dispose: () => {
|
|
2782
|
-
const index = this.interactionHandlers.indexOf(interactionHandler);
|
|
2783
|
-
if (index !== -1) {
|
|
2784
|
-
this.interactionHandlers[index].dispose();
|
|
2785
|
-
this.interactionHandlers.splice(index, 1);
|
|
2786
|
-
}
|
|
2787
|
-
},
|
|
2788
|
-
};
|
|
2789
|
-
}
|
|
2790
|
-
/**
|
|
2791
|
-
* Registers a key interaction to be invoked when a specific set of
|
|
2792
|
-
* keys are pressed during a `tap` event.
|
|
2793
|
-
*
|
|
2794
|
-
* `KeyInteraction`s are used to build custom keyboard shortcuts for the
|
|
2795
|
-
* viewer using the current state of they keyboard to determine whether
|
|
2796
|
-
* the `fn` should be invoked. Use `<vertex-viewer keyboard-controls="false" />`
|
|
2797
|
-
* to disable the default keyboard shortcuts provided by the viewer.
|
|
2798
|
-
*
|
|
2799
|
-
* @example
|
|
2800
|
-
* ```
|
|
2801
|
-
* class CustomKeyboardInteraction extends KeyInteraction<TapEventDetails> {
|
|
2802
|
-
* constructor(private viewer: HTMLVertexViewerElement) {}
|
|
2803
|
-
*
|
|
2804
|
-
* public predicate(keyState: KeyState): boolean {
|
|
2805
|
-
* return keyState['Alt'];
|
|
2806
|
-
* }
|
|
2807
|
-
*
|
|
2808
|
-
* public async fn(event: TapEventDetails) {
|
|
2809
|
-
* const scene = await this.viewer.scene();
|
|
2810
|
-
* const result = await scene.raycaster().hitItems(event.position);
|
|
2811
|
-
*
|
|
2812
|
-
* if (result.hits.length > 0) {
|
|
2813
|
-
* await scene
|
|
2814
|
-
* .camera()
|
|
2815
|
-
* .fitTo(q => q.withItemId(result.hits[0].itemId))
|
|
2816
|
-
* .render();
|
|
2817
|
-
* }
|
|
2818
|
-
* }
|
|
2819
|
-
* }
|
|
2820
|
-
* ```
|
|
2821
|
-
*
|
|
2822
|
-
* @param keyInteraction - The `KeyInteraction` to register.
|
|
2823
|
-
*/
|
|
2824
|
-
async registerTapKeyInteraction(keyInteraction) {
|
|
2825
|
-
this.tapKeyInteractions = [...this.tapKeyInteractions, keyInteraction];
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* The HTML element that will handle interaction events from the user. Used by
|
|
2829
|
-
* components to listen for interaction events from the same element as the
|
|
2830
|
-
* viewer. Note, this property maybe removed in the future when refactoring
|
|
2831
|
-
* our interaction handling.
|
|
2832
|
-
*
|
|
2833
|
-
* @internal
|
|
2834
|
-
* @deprecated Use `InteractionHandler`.
|
|
2835
|
-
*/
|
|
2836
|
-
async getInteractionTarget_DEPRECATED() {
|
|
2837
|
-
if (this.stateMap.interactionTarget != null) {
|
|
2838
|
-
return this.stateMap.interactionTarget;
|
|
2839
|
-
}
|
|
2840
|
-
else
|
|
2841
|
-
throw new Error('Interaction target is undefined.');
|
|
2842
|
-
}
|
|
2843
|
-
/**
|
|
2844
|
-
* Adds a cursor to the viewer, and displays it if the cursor has the highest
|
|
2845
|
-
* priority.
|
|
2846
|
-
*
|
|
2847
|
-
* Cursors are managed as a prioritized list. A cursor is displayed if it has
|
|
2848
|
-
* the highest priority or if the cursor is the most recently added cursor in
|
|
2849
|
-
* the set of cursors with the same priority.
|
|
2850
|
-
*
|
|
2851
|
-
* To remove a cursor, call `dispose()` on the returned disposable.
|
|
2852
|
-
*
|
|
2853
|
-
* @param cursor The cursor to add.
|
|
2854
|
-
* @param priority The priority of the cursor.
|
|
2855
|
-
* @returns A disposable that can be used to remove the cursor.
|
|
2856
|
-
* @see See {@link CursorManager} for constants to pass to `priority`.
|
|
2857
|
-
*/
|
|
2858
|
-
async addCursor(cursor, priority) {
|
|
2859
|
-
return this.stateMap.cursorManager.add(cursor, priority);
|
|
2860
|
-
}
|
|
2861
|
-
async getInteractionHandlers() {
|
|
2862
|
-
return this.interactionHandlers;
|
|
2863
|
-
}
|
|
2864
|
-
/**
|
|
2865
|
-
* @internal
|
|
2866
|
-
* @ignore
|
|
2867
|
-
*/
|
|
2868
|
-
async getKeyInteractions() {
|
|
2869
|
-
return this.tapKeyInteractions;
|
|
2870
|
-
}
|
|
2871
|
-
async getBaseInteractionHandler() {
|
|
2872
|
-
return this.baseInteractionHandler;
|
|
2873
|
-
}
|
|
2874
|
-
/**
|
|
2875
|
-
* @deprecated Use `token`.
|
|
2876
|
-
*/
|
|
2877
|
-
async getJwt() {
|
|
2878
|
-
return this.token;
|
|
2879
|
-
}
|
|
2880
|
-
handleSrcChanged(src) {
|
|
2881
|
-
if (src != null) {
|
|
2882
|
-
this.load(src);
|
|
2883
|
-
}
|
|
2884
|
-
else {
|
|
2885
|
-
this.unload();
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
/**
|
|
2889
|
-
* @ignore
|
|
2890
|
-
*/
|
|
2891
|
-
handleCameraControlsChanged() {
|
|
2892
|
-
this.initializeDefaultCameraInteractionHandlers();
|
|
2893
|
-
}
|
|
2894
|
-
/**
|
|
2895
|
-
* @ignore
|
|
2896
|
-
*/
|
|
2897
|
-
handleKeyboardControlsChanged() {
|
|
2898
|
-
this.initializeDefaultKeyboardInteractionHandlers();
|
|
2899
|
-
}
|
|
2900
|
-
/**
|
|
2901
|
-
* @ignore
|
|
2902
|
-
*/
|
|
2903
|
-
handleRotateAboutTapPointChanged() {
|
|
2904
|
-
var _a, _b;
|
|
2905
|
-
this.updateStreamAttributes();
|
|
2906
|
-
if (this.rotateAroundTapPoint) {
|
|
2907
|
-
(_a = this.baseInteractionHandler) === null || _a === void 0 ? void 0 : _a.setPrimaryInteractionType('rotate-point');
|
|
2908
|
-
}
|
|
2909
|
-
else {
|
|
2910
|
-
(_b = this.baseInteractionHandler) === null || _b === void 0 ? void 0 : _b.setPrimaryInteractionType('rotate');
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
handleCameraTypeChanged(updatedCameraType, previousCameraType) {
|
|
2914
|
-
if (updatedCameraType !== previousCameraType) {
|
|
2915
|
-
this.updateCameraType();
|
|
2916
|
-
}
|
|
2917
|
-
}
|
|
2918
|
-
/**
|
|
2919
|
-
* @ignore
|
|
2920
|
-
*/
|
|
2921
|
-
handleDepthBuffersChanged() {
|
|
2922
|
-
this.updateStreamAttributes();
|
|
2923
|
-
}
|
|
2924
|
-
handlePhantomChanged() {
|
|
2925
|
-
this.updateStreamAttributes();
|
|
2926
|
-
}
|
|
2927
|
-
handleNoDefaultLightsChanged() {
|
|
2928
|
-
this.updateStreamAttributes();
|
|
2929
|
-
}
|
|
2930
|
-
/**
|
|
2931
|
-
* @ignore
|
|
2932
|
-
*/
|
|
2933
|
-
handleExperimentalRenderingOptionsChanged() {
|
|
2934
|
-
this.updateStreamAttributes();
|
|
2935
|
-
}
|
|
2936
|
-
/**
|
|
2937
|
-
* @ignore
|
|
2938
|
-
*/
|
|
2939
|
-
handleFeatureLinesChanged() {
|
|
2940
|
-
this.updateStreamAttributes();
|
|
2941
|
-
}
|
|
2942
|
-
/**
|
|
2943
|
-
* @ignore
|
|
2944
|
-
*/
|
|
2945
|
-
handleSelectionHighlightingChanged() {
|
|
2946
|
-
this.updateStreamAttributes();
|
|
2947
|
-
}
|
|
2948
|
-
/**
|
|
2949
|
-
* @ignore
|
|
2950
|
-
*/
|
|
2951
|
-
handleFeatureHighlightingChanged() {
|
|
2952
|
-
this.updateStreamAttributes();
|
|
2953
|
-
}
|
|
2954
|
-
/**
|
|
2955
|
-
* @ignore
|
|
2956
|
-
*/
|
|
2957
|
-
handleFeatureMapsChanged() {
|
|
2958
|
-
this.updateStreamAttributes();
|
|
2959
|
-
}
|
|
2960
|
-
/**
|
|
2961
|
-
* @ignore
|
|
2962
|
-
*/
|
|
2963
|
-
handleConfigChanged() {
|
|
2964
|
-
this.updateResolvedConfig();
|
|
2965
|
-
}
|
|
2966
|
-
/**
|
|
2967
|
-
* @ignore
|
|
2968
|
-
*/
|
|
2969
|
-
handleConfigEnvChanged() {
|
|
2970
|
-
this.updateResolvedConfig();
|
|
2971
|
-
}
|
|
2972
|
-
/**
|
|
2973
|
-
* Loads the given scene into the viewer and return a `Promise` that
|
|
2974
|
-
* resolves when the scene has been loaded. The specified scene is
|
|
2975
|
-
* provided as a URN in the following format:
|
|
2976
|
-
*
|
|
2977
|
-
* * `urn:vertex:scene:<sceneid>`
|
|
2978
|
-
*
|
|
2979
|
-
* @param urn The URN of the resource to load.
|
|
2980
|
-
*/
|
|
2981
|
-
async load(urn) {
|
|
2982
|
-
var _a;
|
|
2983
|
-
if (this.stream != null && this.dimensions != null) {
|
|
2984
|
-
this.calculateComponentDimensions();
|
|
2985
|
-
this.stream.update({
|
|
2986
|
-
streamAttributes: this.getStreamAttributes(),
|
|
2987
|
-
config: parseConfig(this.configEnv, this.config),
|
|
2988
|
-
dimensions: this.dimensions,
|
|
2989
|
-
frameBgColor: this.getBackgroundColor(),
|
|
2990
|
-
});
|
|
2991
|
-
await ((_a = this.stream) === null || _a === void 0 ? void 0 : _a.load(urn, this.clientId, this.getDeviceId(), this.getResolvedConfig()));
|
|
2992
|
-
this.sceneReady.emit();
|
|
2993
|
-
}
|
|
2994
|
-
else {
|
|
2995
|
-
throw new ViewerInitializationError('Cannot load scene. Viewer has not been initialized.');
|
|
2996
|
-
}
|
|
2997
|
-
}
|
|
2998
|
-
/**
|
|
2999
|
-
* Disconnects the websocket and removes any internal state associated with
|
|
3000
|
-
* the scene.
|
|
3001
|
-
*/
|
|
3002
|
-
async unload() {
|
|
3003
|
-
if (this.stream != null) {
|
|
3004
|
-
this.stream.disconnect();
|
|
3005
|
-
this.frame = undefined;
|
|
3006
|
-
this.errorMessage = undefined;
|
|
3007
|
-
}
|
|
3008
|
-
if (this.canvasElement != null) {
|
|
3009
|
-
const context = this.canvasElement.getContext('2d');
|
|
3010
|
-
if (context != null) {
|
|
3011
|
-
context.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
/**
|
|
3016
|
-
* Returns an object that is used to perform operations on the `Scene` that's
|
|
3017
|
-
* currently being viewed. These operations include updating items,
|
|
3018
|
-
* positioning the camera and performing hit tests.
|
|
3019
|
-
*/
|
|
3020
|
-
async scene() {
|
|
3021
|
-
return this.createScene();
|
|
3022
|
-
}
|
|
3023
|
-
/**
|
|
3024
|
-
* Returns `true` indicating that the scene is ready to be interacted with.
|
|
3025
|
-
*/
|
|
3026
|
-
async isSceneReady() {
|
|
3027
|
-
return this.stateMap.streamState.type === 'connected';
|
|
3028
|
-
}
|
|
3029
|
-
async handleTapEvent(event) {
|
|
3030
|
-
this.tapKeyInteractions
|
|
3031
|
-
.filter((i) => i.predicate(event.detail))
|
|
3032
|
-
.forEach((i) => i.fn(event.detail));
|
|
3033
|
-
}
|
|
3034
|
-
emitConnectionChange(status) {
|
|
3035
|
-
this.connectionChange.emit(status);
|
|
3036
|
-
}
|
|
3037
|
-
handleElementResize(entries) {
|
|
3038
|
-
const dimensionsHaveChanged = entries.length > 0 &&
|
|
3039
|
-
this.dimensions != null &&
|
|
3040
|
-
!dimensions.isEqual(entries[0].contentRect, this.viewport);
|
|
3041
|
-
if (dimensionsHaveChanged) {
|
|
3042
|
-
if (this.resizeTimer != null) {
|
|
3043
|
-
clearTimeout(this.resizeTimer);
|
|
3044
|
-
this.resizeTimer = undefined;
|
|
3045
|
-
}
|
|
3046
|
-
if (!this.isResizing) {
|
|
3047
|
-
this.resizeTimer = setTimeout(() => {
|
|
3048
|
-
this.isResizing = true;
|
|
3049
|
-
this.isResizeUpdate = true;
|
|
3050
|
-
this.recalculateComponentDimensions();
|
|
3051
|
-
}, this.resizeDebounce);
|
|
3052
|
-
}
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
registerSlotChangeListeners() {
|
|
3056
|
-
this.mutationObserver = new MutationObserver((_) => this.injectViewerApi());
|
|
3057
|
-
this.mutationObserver.observe(this.hostElement, {
|
|
3058
|
-
childList: true,
|
|
3059
|
-
subtree: true,
|
|
3060
|
-
});
|
|
3061
|
-
}
|
|
3062
|
-
injectViewerApi() {
|
|
3063
|
-
function queryChildren(el) {
|
|
3064
|
-
return Array.from(el.querySelectorAll('*'));
|
|
3065
|
-
}
|
|
3066
|
-
const children = queryChildren(this.hostElement);
|
|
3067
|
-
children
|
|
3068
|
-
.filter((node) => node.nodeName.startsWith('VERTEX-'))
|
|
3069
|
-
.reduce((elements, element) => [
|
|
3070
|
-
...elements,
|
|
3071
|
-
element,
|
|
3072
|
-
...queryChildren(element),
|
|
3073
|
-
], [])
|
|
3074
|
-
.forEach((node) => {
|
|
3075
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3076
|
-
node.viewer = this.hostElement;
|
|
3077
|
-
});
|
|
3078
|
-
}
|
|
3079
|
-
calculateComponentDimensions() {
|
|
3080
|
-
var _a;
|
|
3081
|
-
const maxPixelCount = 2073600;
|
|
3082
|
-
const bounds = this.getBounds();
|
|
3083
|
-
if ((bounds === null || bounds === void 0 ? void 0 : bounds.width) != null && (bounds === null || bounds === void 0 ? void 0 : bounds.height) != null) {
|
|
3084
|
-
const measuredViewport = dimensions.create(bounds.width, bounds.height);
|
|
3085
|
-
const trimmedViewport = dimensions.scaleFit(maxPixelCount, measuredViewport);
|
|
3086
|
-
this.hostDimensions = measuredViewport;
|
|
3087
|
-
this.dimensions =
|
|
3088
|
-
trimmedViewport != null
|
|
3089
|
-
? dimensions.create(trimmedViewport.width, trimmedViewport.height)
|
|
3090
|
-
: undefined;
|
|
3091
|
-
this.viewport = Viewport.fromDimensions((_a = this.getCanvasDimensions()) !== null && _a !== void 0 ? _a : dimensions.create(0, 0));
|
|
3092
|
-
}
|
|
3093
|
-
}
|
|
3094
|
-
recalculateComponentDimensions() {
|
|
3095
|
-
var _a;
|
|
3096
|
-
if (this.isResizing) {
|
|
3097
|
-
this.calculateComponentDimensions();
|
|
3098
|
-
this.isResizing = false;
|
|
3099
|
-
if (((_a = this.stream) === null || _a === void 0 ? void 0 : _a.getState().type) === 'connected') {
|
|
3100
|
-
this.updateDimensions(this.dimensions);
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
reportPerformance(timings) {
|
|
3105
|
-
if (this.stateMap.streamState.type === 'connected') {
|
|
3106
|
-
const payload = {
|
|
3107
|
-
timings: timings.map((t) => ({
|
|
3108
|
-
receiveToPaintDuration: toProtoDuration(t.duration),
|
|
3109
|
-
})),
|
|
3110
|
-
};
|
|
3111
|
-
this.getStream().recordPerformance(payload, false);
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
addStreamListeners() {
|
|
3115
|
-
this.stateMap.streamListeners = this.getStream().stateChanged.on((s) => {
|
|
3116
|
-
this.handleStreamStateChanged(this.stateMap.streamState, s);
|
|
3117
|
-
});
|
|
3118
|
-
}
|
|
3119
|
-
handleStreamStateChanged(previous, state) {
|
|
3120
|
-
this.stateMap.streamState = state;
|
|
3121
|
-
if (state.type === 'connecting') {
|
|
3122
|
-
this.handleConnecting(previous, state);
|
|
3123
|
-
}
|
|
3124
|
-
else if (state.type === 'connected') {
|
|
3125
|
-
this.handleConnected(previous, state);
|
|
3126
|
-
}
|
|
3127
|
-
else if (state.type === 'connection-failed') {
|
|
3128
|
-
this.handleConnectionFailed(previous, state);
|
|
3129
|
-
}
|
|
3130
|
-
else if (state.type === 'disconnected') {
|
|
3131
|
-
this.handleDisconnected(previous, state);
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
handleConnecting(previous, state) {
|
|
3135
|
-
if (previous.type !== 'connecting') {
|
|
3136
|
-
this.token = undefined;
|
|
3137
|
-
this.errorMessage = undefined;
|
|
3138
|
-
this.emitConnectionChange({ status: 'connecting' });
|
|
3139
|
-
}
|
|
3140
|
-
}
|
|
3141
|
-
handleConnected(previous, state) {
|
|
3142
|
-
var _a;
|
|
3143
|
-
this.token = state.token.token;
|
|
3144
|
-
if (previous.type !== 'connected') {
|
|
3145
|
-
this.errorMessage = undefined;
|
|
3146
|
-
this.canvasRenderer = measureCanvasRenderer(paintTime, createCanvasRenderer(), this.getResolvedConfig().flags.logFrameRate, (timings) => this.reportPerformance(timings));
|
|
3147
|
-
this.emitConnectionChange({
|
|
3148
|
-
status: 'connected',
|
|
3149
|
-
jwt: state.token.token,
|
|
3150
|
-
});
|
|
3151
|
-
this.deviceIdChange.emit(state.deviceId);
|
|
3152
|
-
}
|
|
3153
|
-
if (((_a = this.frame) === null || _a === void 0 ? void 0 : _a.getId()) !== state.frame.getId()) {
|
|
3154
|
-
this.updateFrame(state.frame);
|
|
3155
|
-
}
|
|
3156
|
-
this.updateDimensions(this.dimensions);
|
|
3157
|
-
}
|
|
3158
|
-
handleConnectionFailed(previous, state) {
|
|
3159
|
-
if (previous.type !== 'connection-failed') {
|
|
3160
|
-
this.token = undefined;
|
|
3161
|
-
this.errorMessage = state.message;
|
|
3162
|
-
this.emitConnectionChange({
|
|
3163
|
-
status: 'connection-failed',
|
|
3164
|
-
errorMessage: state.message,
|
|
3165
|
-
});
|
|
3166
|
-
}
|
|
3167
|
-
}
|
|
3168
|
-
handleDisconnected(previous, state) {
|
|
3169
|
-
if (previous.type !== 'disconnected') {
|
|
3170
|
-
this.token = undefined;
|
|
3171
|
-
this.errorMessage = undefined;
|
|
3172
|
-
this.emitConnectionChange({ status: 'disconnected' });
|
|
3173
|
-
}
|
|
3174
|
-
}
|
|
3175
|
-
updateDimensions(dimensions) {
|
|
3176
|
-
var _a;
|
|
3177
|
-
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.update({ dimensions });
|
|
3178
|
-
this.dimensionschange.emit(dimensions);
|
|
3179
|
-
}
|
|
3180
|
-
async updateFrame(frame) {
|
|
3181
|
-
var _a;
|
|
3182
|
-
const canvasDimensions = this.getCanvasDimensions();
|
|
3183
|
-
if (this.canvasElement != null &&
|
|
3184
|
-
canvasDimensions != null &&
|
|
3185
|
-
((_a = this.frame) === null || _a === void 0 ? void 0 : _a.getId()) !== frame.getId()) {
|
|
3186
|
-
const canvas = this.canvasElement.getContext('2d');
|
|
3187
|
-
if (canvas != null) {
|
|
3188
|
-
const previousFrame = this.frame;
|
|
3189
|
-
this.frame = copySummaryIfInvalid(frame, previousFrame);
|
|
3190
|
-
this.updateInteractionApi(previousFrame);
|
|
3191
|
-
const data = {
|
|
3192
|
-
canvas,
|
|
3193
|
-
canvasDimensions,
|
|
3194
|
-
dimensions: this.dimensions,
|
|
3195
|
-
frame: this.frame,
|
|
3196
|
-
viewport: this.viewport,
|
|
3197
|
-
beforeDraw: () => {
|
|
3198
|
-
this.updateCanvasDimensions(canvasDimensions);
|
|
3199
|
-
this.isResizeUpdate = false;
|
|
3200
|
-
},
|
|
3201
|
-
predicate: () => {
|
|
3202
|
-
if (this.isResizeUpdate) {
|
|
3203
|
-
return (this.dimensions == null ||
|
|
3204
|
-
dimensions.isEqual(this.dimensions, data.frame.image.imageAttr.frameDimensions));
|
|
3205
|
-
}
|
|
3206
|
-
return true;
|
|
3207
|
-
},
|
|
3208
|
-
};
|
|
3209
|
-
this.frameReceived.emit(this.frame);
|
|
3210
|
-
if (this.frame.scene.hasChanged) {
|
|
3211
|
-
this.sceneChanged.emit();
|
|
3212
|
-
}
|
|
3213
|
-
const drawnFrame = await this.canvasRenderer(data);
|
|
3214
|
-
if (drawnFrame != null) {
|
|
3215
|
-
this.dispatchFrameDrawn(drawnFrame);
|
|
3216
|
-
}
|
|
3217
|
-
}
|
|
3218
|
-
}
|
|
3219
|
-
}
|
|
3220
|
-
async initializeDefaultInteractionHandlers() {
|
|
3221
|
-
var _a;
|
|
3222
|
-
await this.initializeDefaultCameraInteractionHandlers();
|
|
3223
|
-
await this.initializeDefaultTapInteractionHandler();
|
|
3224
|
-
this.initializeDefaultKeyboardInteractionHandlers();
|
|
3225
|
-
if (this.rotateAroundTapPoint) {
|
|
3226
|
-
(_a = this.baseInteractionHandler) === null || _a === void 0 ? void 0 : _a.setPrimaryInteractionType('rotate-point');
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
clearDefaultCameraInteractionHandlers() {
|
|
3230
|
-
this.defaultInteractionHandlerDisposables.forEach((disposable) => disposable.dispose());
|
|
3231
|
-
this.defaultInteractionHandlerDisposables = [];
|
|
3232
|
-
}
|
|
3233
|
-
clearDefaultKeyboardInteractions() {
|
|
3234
|
-
this.defaultTapKeyInteractions.forEach((interaction) => {
|
|
3235
|
-
const index = this.tapKeyInteractions.indexOf(interaction);
|
|
3236
|
-
if (index !== -1) {
|
|
3237
|
-
this.tapKeyInteractions.splice(index, 1);
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
this.tapKeyInteractions = [];
|
|
3241
|
-
}
|
|
3242
|
-
async initializeDefaultCameraInteractionHandlers() {
|
|
3243
|
-
var _a, _b;
|
|
3244
|
-
this.clearDefaultCameraInteractionHandlers();
|
|
3245
|
-
if (this.cameraControls) {
|
|
3246
|
-
if (window.PointerEvent != null) {
|
|
3247
|
-
this.baseInteractionHandler =
|
|
3248
|
-
(_a = this.baseInteractionHandler) !== null && _a !== void 0 ? _a : new PointerInteractionHandler(() => this.getResolvedConfig());
|
|
3249
|
-
const baseDisposable = await this.registerInteractionHandler(this.baseInteractionHandler);
|
|
3250
|
-
const multiPointerDisposable = await this.registerInteractionHandler(new MultiPointerInteractionHandler());
|
|
3251
|
-
this.defaultInteractionHandlerDisposables = [
|
|
3252
|
-
baseDisposable,
|
|
3253
|
-
multiPointerDisposable,
|
|
3254
|
-
];
|
|
3255
|
-
}
|
|
3256
|
-
else {
|
|
3257
|
-
// fallback to touch events and mouse events as a default
|
|
3258
|
-
this.baseInteractionHandler =
|
|
3259
|
-
(_b = this.baseInteractionHandler) !== null && _b !== void 0 ? _b : new MouseInteractionHandler(() => this.getResolvedConfig());
|
|
3260
|
-
const baseDisposable = await this.registerInteractionHandler(this.baseInteractionHandler);
|
|
3261
|
-
const touchDisposable = await this.registerInteractionHandler(new TouchInteractionHandler());
|
|
3262
|
-
this.defaultInteractionHandlerDisposables = [
|
|
3263
|
-
baseDisposable,
|
|
3264
|
-
touchDisposable,
|
|
3265
|
-
];
|
|
3266
|
-
}
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3269
|
-
initializeDefaultKeyboardInteractionHandlers() {
|
|
3270
|
-
var _a;
|
|
3271
|
-
this.clearDefaultKeyboardInteractions();
|
|
3272
|
-
if (this.keyboardControls && this.stream != null) {
|
|
3273
|
-
(_a = this.baseInteractionHandler) === null || _a === void 0 ? void 0 : _a.setDefaultKeyboardControls(this.keyboardControls);
|
|
3274
|
-
const flyToPart = new FlyToPartKeyInteraction(this.stream, () => this.getResolvedConfig(), () => this.getImageScale());
|
|
3275
|
-
const flyToPosition = new FlyToPositionKeyInteraction(this.stream, () => this.getResolvedConfig(), () => this.getImageScale(), () => this.createScene());
|
|
3276
|
-
this.registerTapKeyInteraction(flyToPart);
|
|
3277
|
-
this.registerTapKeyInteraction(flyToPosition);
|
|
3278
|
-
this.defaultTapKeyInteractions = [flyToPart, flyToPosition];
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
async initializeDefaultTapInteractionHandler() {
|
|
3282
|
-
if (this.tapHandlerDisposable == null) {
|
|
3283
|
-
if (window.PointerEvent != null) {
|
|
3284
|
-
const tapInteractionHandler = new TapInteractionHandler('pointerdown', 'pointerup', 'pointermove', () => this.getResolvedConfig());
|
|
3285
|
-
this.tapHandlerDisposable = await this.registerInteractionHandler(tapInteractionHandler);
|
|
3286
|
-
}
|
|
3287
|
-
else {
|
|
3288
|
-
const tapInteractionHandler = new TapInteractionHandler('mousedown', 'mouseup', 'mousemove', () => this.getResolvedConfig());
|
|
3289
|
-
this.tapHandlerDisposable = await this.registerInteractionHandler(tapInteractionHandler);
|
|
3290
|
-
}
|
|
3291
|
-
}
|
|
3292
|
-
}
|
|
3293
|
-
initializeInteractionHandler(handler) {
|
|
3294
|
-
if (this.stateMap.interactionTarget == null) {
|
|
3295
|
-
throw new InteractionHandlerError('Cannot initialize interaction handler. Interaction target is undefined.');
|
|
3296
|
-
}
|
|
3297
|
-
if (this.interactionApi == null) {
|
|
3298
|
-
throw new InteractionHandlerError('Cannot initialize interaction handler. Interaction APi is undefined.');
|
|
3299
|
-
}
|
|
3300
|
-
handler.initialize(this.stateMap.interactionTarget, this.interactionApi);
|
|
3301
|
-
}
|
|
3302
|
-
createInteractionApi() {
|
|
3303
|
-
if (this.stream == null) {
|
|
3304
|
-
throw new ComponentInitializationError('Cannot create interaction API. Component has not been initialized.');
|
|
3305
|
-
}
|
|
3306
|
-
return this.frame == null || this.frame.scene.camera.isPerspective()
|
|
3307
|
-
? new InteractionApiPerspective(this.stream, this.stateMap.cursorManager, () => this.getResolvedConfig().interactions, () => this.createScene(), () => this.frame, () => this.viewport, this.tap, this.doubletap, this.longpress, this.interactionStarted, this.interactionFinished)
|
|
3308
|
-
: new InteractionApiOrthographic(this.stream, this.stateMap.cursorManager, () => this.getResolvedConfig().interactions, () => this.createScene(), () => this.frame, () => this.viewport, this.tap, this.doubletap, this.longpress, this.interactionStarted, this.interactionFinished);
|
|
3309
|
-
}
|
|
3310
|
-
handleCursorChanged() {
|
|
3311
|
-
window.requestAnimationFrame(() => {
|
|
3312
|
-
this.cursor = this.stateMap.cursorManager.getActiveCursor();
|
|
3313
|
-
});
|
|
3314
|
-
}
|
|
3315
|
-
async createScene() {
|
|
3316
|
-
const state = await this.waitForConnectedState();
|
|
3317
|
-
const { frame, sceneId, sceneViewId, worldOrientation } = state;
|
|
3318
|
-
return new Scene(this.getStream(), frame, fromPbFrameOrThrow(worldOrientation), () => this.getImageScale(), this.viewport, sceneId, sceneViewId);
|
|
3319
|
-
}
|
|
3320
|
-
/**
|
|
3321
|
-
* This function is currently not in use, but will required
|
|
3322
|
-
* when we want to automatically configure the background color of
|
|
3323
|
-
* JPEG images.
|
|
3324
|
-
*/
|
|
3325
|
-
getBackgroundColor() {
|
|
3326
|
-
if (this.containerElement != null) {
|
|
3327
|
-
return getElementBackgroundColor(this.containerElement);
|
|
3328
|
-
}
|
|
3329
|
-
}
|
|
3330
|
-
getBounds() {
|
|
3331
|
-
return getElementBoundingClientRect(this.hostElement);
|
|
3332
|
-
}
|
|
3333
|
-
getCanvasDimensions() {
|
|
3334
|
-
return this.getResolvedConfig().flags.letterboxFrames
|
|
3335
|
-
? this.dimensions
|
|
3336
|
-
: this.hostDimensions;
|
|
3337
|
-
}
|
|
3338
|
-
getImageScale() {
|
|
3339
|
-
const canvasDimensions = this.getCanvasDimensions();
|
|
3340
|
-
if (this.dimensions != null && canvasDimensions != null) {
|
|
3341
|
-
return point.create(this.dimensions.width / canvasDimensions.width, this.dimensions.height / canvasDimensions.height);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
getStreamAttributes() {
|
|
3345
|
-
return {
|
|
3346
|
-
depthBuffers: this.getDepthBufferStreamAttributesValue(),
|
|
3347
|
-
phantom: this.phantom,
|
|
3348
|
-
noDefaultLights: this.noDefaultLights,
|
|
3349
|
-
featureLines: this.featureLines,
|
|
3350
|
-
featureHighlighting: this.featureHighlighting,
|
|
3351
|
-
featureMaps: this.featureMaps,
|
|
3352
|
-
experimentalRenderingOptions: this.experimentalRenderingOptions,
|
|
3353
|
-
selectionHighlighting: this.selectionHighlighting,
|
|
3354
|
-
};
|
|
3355
|
-
}
|
|
3356
|
-
updateCanvasDimensions(dimensions) {
|
|
3357
|
-
if (this.canvasElement != null) {
|
|
3358
|
-
this.canvasElement.width = dimensions.width;
|
|
3359
|
-
this.canvasElement.height = dimensions.height;
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
updateStreamAttributes() {
|
|
3363
|
-
var _a;
|
|
3364
|
-
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.update({ streamAttributes: this.getStreamAttributes() });
|
|
3365
|
-
}
|
|
3366
|
-
updateInteractionApi(previousFrame) {
|
|
3367
|
-
if (this.frame != null) {
|
|
3368
|
-
const hasChangedFromPerspective = (previousFrame == null || previousFrame.scene.camera.isPerspective()) &&
|
|
3369
|
-
this.frame.scene.camera.isOrthographic();
|
|
3370
|
-
const hasChangedFromOrthographic = (previousFrame == null ||
|
|
3371
|
-
previousFrame.scene.camera.isOrthographic()) &&
|
|
3372
|
-
this.frame.scene.camera.isPerspective();
|
|
3373
|
-
if (hasChangedFromPerspective || hasChangedFromOrthographic) {
|
|
3374
|
-
this.interactionApi = this.createInteractionApi();
|
|
3375
|
-
this.cameraType = this.frame.scene.camera.isPerspective()
|
|
3376
|
-
? 'perspective'
|
|
3377
|
-
: 'orthographic';
|
|
3378
|
-
this.cameraTypeChanged.emit(this.cameraType);
|
|
3379
|
-
this.interactionHandlers.forEach((handler) => this.initializeInteractionHandler(handler));
|
|
3380
|
-
}
|
|
3381
|
-
}
|
|
3382
|
-
}
|
|
3383
|
-
updateCameraType() {
|
|
3384
|
-
var _a, _b;
|
|
3385
|
-
if (this.frame != null) {
|
|
3386
|
-
if (this.cameraType === 'orthographic' &&
|
|
3387
|
-
this.frame.scene.camera.isPerspective()) {
|
|
3388
|
-
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.replaceCamera({
|
|
3389
|
-
camera: toProtobuf(toOrthographic(this.frame.scene.camera, this.frame.scene.boundingBox)),
|
|
3390
|
-
});
|
|
3391
|
-
}
|
|
3392
|
-
else if (this.cameraType === 'perspective' &&
|
|
3393
|
-
this.frame.scene.camera.isOrthographic()) {
|
|
3394
|
-
(_b = this.stream) === null || _b === void 0 ? void 0 : _b.replaceCamera({
|
|
3395
|
-
camera: toProtobuf(toPerspective(this.frame.scene.camera)),
|
|
3396
|
-
});
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3399
|
-
}
|
|
3400
|
-
getDepthBufferStreamAttributesValue() {
|
|
3401
|
-
var _a;
|
|
3402
|
-
const depthBuffer = (_a = this.depthBuffers) !== null && _a !== void 0 ? _a : (this.rotateAroundTapPoint ? 'final' : undefined);
|
|
3403
|
-
return depthBuffer;
|
|
3404
|
-
}
|
|
3405
|
-
updateResolvedConfig() {
|
|
3406
|
-
this.resolvedConfig = parseConfig(this.configEnv, this.config);
|
|
3407
|
-
}
|
|
3408
|
-
getResolvedConfig() {
|
|
3409
|
-
return getRequiredProp('Resolved config is undefined', () => this.resolvedConfig);
|
|
3410
|
-
}
|
|
3411
|
-
getStream() {
|
|
3412
|
-
return getRequiredProp('Stream is undefined', () => this.stream);
|
|
3413
|
-
}
|
|
3414
|
-
getDeviceId() {
|
|
3415
|
-
if (this.deviceId == null) {
|
|
3416
|
-
try {
|
|
3417
|
-
this.deviceId = getStorageEntry(StorageKeys.DEVICE_ID, (entry) => entry['device-id']);
|
|
3418
|
-
}
|
|
3419
|
-
catch (e) {
|
|
3420
|
-
console.warn('Cannot read device ID. Local storage is not supported.');
|
|
3421
|
-
}
|
|
3422
|
-
if (this.deviceId == null) {
|
|
3423
|
-
this.deviceId = uuid.create();
|
|
3424
|
-
try {
|
|
3425
|
-
upsertStorageEntry(StorageKeys.DEVICE_ID, {
|
|
3426
|
-
['device-id']: this.deviceId,
|
|
3427
|
-
});
|
|
3428
|
-
}
|
|
3429
|
-
catch (e) {
|
|
3430
|
-
console.warn('Cannot write device ID. Local storage is not supported.');
|
|
3431
|
-
}
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
return this.deviceId;
|
|
3435
|
-
}
|
|
3436
|
-
async waitForConnectedState() {
|
|
3437
|
-
if (this.stateMap.streamState.type !== 'connected') {
|
|
3438
|
-
console.debug('Stream was not in a connected state. Waiting for successful connection.');
|
|
3439
|
-
return new Promise((resolve, reject) => {
|
|
3440
|
-
const disposable = this.getStream().onStateChanged((state) => {
|
|
3441
|
-
if (state.type === 'connected') {
|
|
3442
|
-
resolve(state);
|
|
3443
|
-
}
|
|
3444
|
-
});
|
|
3445
|
-
setTimeout(() => {
|
|
3446
|
-
disposable.dispose();
|
|
3447
|
-
reject(new Error('Timed out waiting for connected state.'));
|
|
3448
|
-
}, DEFAULT_VIEWER_SCENE_WAIT_MS);
|
|
3449
|
-
});
|
|
3450
|
-
}
|
|
3451
|
-
return this.stateMap.streamState;
|
|
3452
|
-
}
|
|
3453
|
-
get hostElement() { return this; }
|
|
3454
|
-
static get watchers() { return {
|
|
3455
|
-
"src": ["handleSrcChanged"],
|
|
3456
|
-
"cameraControls": ["handleCameraControlsChanged"],
|
|
3457
|
-
"keyboardControls": ["handleKeyboardControlsChanged"],
|
|
3458
|
-
"rotateAroundTapPoint": ["handleRotateAboutTapPointChanged"],
|
|
3459
|
-
"cameraType": ["handleCameraTypeChanged"],
|
|
3460
|
-
"depthBuffers": ["handleDepthBuffersChanged"],
|
|
3461
|
-
"phantom": ["handlePhantomChanged"],
|
|
3462
|
-
"noDefaultLights": ["handleNoDefaultLightsChanged"],
|
|
3463
|
-
"experimentalRenderingOptions": ["handleExperimentalRenderingOptionsChanged"],
|
|
3464
|
-
"featureLines": ["handleFeatureLinesChanged"],
|
|
3465
|
-
"selectionHighlighting": ["handleSelectionHighlightingChanged"],
|
|
3466
|
-
"featureHighlighting": ["handleFeatureHighlightingChanged"],
|
|
3467
|
-
"featureMaps": ["handleFeatureMapsChanged"],
|
|
3468
|
-
"config": ["handleConfigChanged"],
|
|
3469
|
-
"configEnv": ["handleConfigEnvChanged"]
|
|
3470
|
-
}; }
|
|
3471
|
-
static get style() { return viewerCss; }
|
|
3472
|
-
}, [1, "vertex-viewer", {
|
|
3473
|
-
"src": [1],
|
|
3474
|
-
"clientId": [1, "client-id"],
|
|
3475
|
-
"deviceId": [1025, "device-id"],
|
|
3476
|
-
"config": [1],
|
|
3477
|
-
"configEnv": [1, "config-env"],
|
|
3478
|
-
"resolvedConfig": [1040],
|
|
3479
|
-
"cameraControls": [4, "camera-controls"],
|
|
3480
|
-
"cameraType": [1537, "camera-type"],
|
|
3481
|
-
"keyboardControls": [4, "keyboard-controls"],
|
|
3482
|
-
"rotateAroundTapPoint": [4, "rotate-around-tap-point"],
|
|
3483
|
-
"token": [1025],
|
|
3484
|
-
"depthBuffers": [1, "depth-buffers"],
|
|
3485
|
-
"phantom": [16],
|
|
3486
|
-
"noDefaultLights": [4, "no-default-lights"],
|
|
3487
|
-
"experimentalRenderingOptions": [1, "experimental-rendering-options"],
|
|
3488
|
-
"featureLines": [16],
|
|
3489
|
-
"selectionHighlighting": [16],
|
|
3490
|
-
"featureHighlighting": [16],
|
|
3491
|
-
"featureMaps": [1, "feature-maps"],
|
|
3492
|
-
"resizeDebounce": [2, "resize-debounce"],
|
|
3493
|
-
"frame": [1040],
|
|
3494
|
-
"stream": [1040],
|
|
3495
|
-
"stencilBuffer": [1040],
|
|
3496
|
-
"viewport": [1040],
|
|
3497
|
-
"dimensions": [32],
|
|
3498
|
-
"hostDimensions": [32],
|
|
3499
|
-
"errorMessage": [32],
|
|
3500
|
-
"cursor": [32],
|
|
3501
|
-
"stateMap": [32],
|
|
3502
|
-
"dispatchFrameDrawn": [64],
|
|
3503
|
-
"registerInteractionHandler": [64],
|
|
3504
|
-
"registerTapKeyInteraction": [64],
|
|
3505
|
-
"getInteractionTarget_DEPRECATED": [64],
|
|
3506
|
-
"addCursor": [64],
|
|
3507
|
-
"getInteractionHandlers": [64],
|
|
3508
|
-
"getKeyInteractions": [64],
|
|
3509
|
-
"getBaseInteractionHandler": [64],
|
|
3510
|
-
"getJwt": [64],
|
|
3511
|
-
"load": [64],
|
|
3512
|
-
"unload": [64],
|
|
3513
|
-
"scene": [64],
|
|
3514
|
-
"isSceneReady": [64]
|
|
3515
|
-
}, [[0, "tap", "handleTapEvent"]]]);
|
|
3516
|
-
function getRequiredProp(errorMsg, getter) {
|
|
3517
|
-
const value = getter();
|
|
3518
|
-
if (value != null) {
|
|
3519
|
-
return value;
|
|
3520
|
-
}
|
|
3521
|
-
else
|
|
3522
|
-
throw new Error(errorMsg);
|
|
3523
|
-
}
|
|
3524
|
-
function defineCustomElement$1() {
|
|
3525
|
-
if (typeof customElements === "undefined") {
|
|
3526
|
-
return;
|
|
3527
|
-
}
|
|
3528
|
-
const components = ["vertex-viewer"];
|
|
3529
|
-
components.forEach(tagName => { switch (tagName) {
|
|
3530
|
-
case "vertex-viewer":
|
|
3531
|
-
if (!customElements.get(tagName)) {
|
|
3532
|
-
customElements.define(tagName, Viewer);
|
|
3533
|
-
}
|
|
3534
|
-
break;
|
|
3535
|
-
} });
|
|
3536
|
-
}
|
|
3537
|
-
|
|
3538
|
-
const VertexViewer = Viewer;
|
|
3539
|
-
const defineCustomElement = defineCustomElement$1;
|
|
3540
|
-
|
|
3541
|
-
export { VertexViewer, defineCustomElement };
|
|
3542
|
-
|
|
3543
|
-
//# sourceMappingURL=vertex-viewer.js.map
|