@vertexvis/viewer 0.18.0 → 0.18.1-testing.0

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.
Files changed (59) hide show
  1. package/dist/cjs/{config-ee5af2fe.js → config-ace9f5d5.js} +2 -1
  2. package/dist/cjs/config-ace9f5d5.js.map +1 -0
  3. package/dist/cjs/{controller-6914028a.js → controller-ee852f01.js} +12 -3
  4. package/dist/cjs/controller-ee852f01.js.map +1 -0
  5. package/dist/cjs/index.cjs.js +1 -1
  6. package/dist/cjs/vertex-scene-tree.cjs.entry.js +2 -2
  7. package/dist/cjs/vertex-viewer-measurement-precise.cjs.entry.js +1 -1
  8. package/dist/cjs/vertex-viewer-pin-group.cjs.entry.js +1 -1
  9. package/dist/cjs/vertex-viewer.cjs.entry.js +103 -35
  10. package/dist/cjs/vertex-viewer.cjs.entry.js.map +1 -1
  11. package/dist/collection/components/scene-tree/lib/controller.js +11 -2
  12. package/dist/collection/components/scene-tree/lib/controller.js.map +1 -1
  13. package/dist/collection/lib/interactions/interactionApi.js +0 -34
  14. package/dist/collection/lib/interactions/interactionApi.js.map +1 -1
  15. package/dist/collection/lib/interactions/interactionApiPerspective.js +103 -1
  16. package/dist/collection/lib/interactions/interactionApiPerspective.js.map +1 -1
  17. package/dist/collection/lib/types/interactions.js +1 -0
  18. package/dist/collection/lib/types/interactions.js.map +1 -1
  19. package/dist/custom-elements/index.js +114 -36
  20. package/dist/custom-elements/index.js.map +1 -1
  21. package/dist/esm/{config-65fd1bba.js → config-2b2e3f66.js} +2 -1
  22. package/dist/esm/config-2b2e3f66.js.map +1 -0
  23. package/dist/esm/{controller-1f6c87ab.js → controller-ea698f9b.js} +12 -3
  24. package/dist/esm/controller-ea698f9b.js.map +1 -0
  25. package/dist/esm/index.js +1 -1
  26. package/dist/esm/index.mjs +1 -1
  27. package/dist/esm/vertex-scene-tree.entry.js +2 -2
  28. package/dist/esm/vertex-viewer-measurement-precise.entry.js +1 -1
  29. package/dist/esm/vertex-viewer-pin-group.entry.js +1 -1
  30. package/dist/esm/vertex-viewer.entry.js +103 -35
  31. package/dist/esm/vertex-viewer.entry.js.map +1 -1
  32. package/dist/types/components/scene-tree/lib/controller.d.ts +1 -0
  33. package/dist/types/lib/interactions/interactionApi.d.ts +1 -1
  34. package/dist/types/lib/interactions/interactionApiPerspective.d.ts +3 -0
  35. package/dist/types/lib/types/interactions.d.ts +7 -0
  36. package/dist/viewer/index.esm.js +1 -1
  37. package/dist/viewer/p-0de95c4d.js +5 -0
  38. package/dist/viewer/p-0de95c4d.js.map +1 -0
  39. package/dist/viewer/{p-e2e7d01b.entry.js → p-2cca6daa.entry.js} +2 -2
  40. package/dist/viewer/{p-d8c70701.entry.js → p-2ce1d5a7.entry.js} +2 -2
  41. package/dist/viewer/{p-31c3b8d6.entry.js → p-4225576d.entry.js} +2 -2
  42. package/dist/viewer/p-5145f091.entry.js +5 -0
  43. package/dist/viewer/p-5145f091.entry.js.map +1 -0
  44. package/dist/viewer/{p-931247b6.js → p-bb131c4a.js} +2 -2
  45. package/dist/viewer/p-bb131c4a.js.map +1 -0
  46. package/dist/viewer/viewer.esm.js +1 -1
  47. package/package.json +7 -7
  48. package/dist/cjs/config-ee5af2fe.js.map +0 -1
  49. package/dist/cjs/controller-6914028a.js.map +0 -1
  50. package/dist/esm/config-65fd1bba.js.map +0 -1
  51. package/dist/esm/controller-1f6c87ab.js.map +0 -1
  52. package/dist/viewer/p-089d11bf.js +0 -5
  53. package/dist/viewer/p-089d11bf.js.map +0 -1
  54. package/dist/viewer/p-931247b6.js.map +0 -1
  55. package/dist/viewer/p-f1e718b4.entry.js +0 -5
  56. package/dist/viewer/p-f1e718b4.entry.js.map +0 -1
  57. /package/dist/viewer/{p-e2e7d01b.entry.js.map → p-2cca6daa.entry.js.map} +0 -0
  58. /package/dist/viewer/{p-d8c70701.entry.js.map → p-2ce1d5a7.entry.js.map} +0 -0
  59. /package/dist/viewer/{p-31c3b8d6.entry.js.map → p-4225576d.entry.js.map} +0 -0
@@ -1,8 +1,9 @@
1
1
  /*!
2
2
  * Copyright (c) 2023 Vertex Software LLC. All rights reserved.
3
3
  */
4
- import { Point, Vector3 } from '@vertexvis/geometry';
4
+ import { Plane, Point, Ray, Vector3 } from '@vertexvis/geometry';
5
5
  import { InteractionApi, } from './interactionApi';
6
+ const CAMERA_MIN_ZOOM_SCALAR = 0.2;
6
7
  export class InteractionApiPerspective extends InteractionApi {
7
8
  constructor(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter) {
8
9
  super(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter);
@@ -30,6 +31,48 @@ export class InteractionApiPerspective extends InteractionApi {
30
31
  return camera.moveBy(offset);
31
32
  });
32
33
  }
34
+ async zoomCameraToPoint(point, delta) {
35
+ return this.transformCamera(({ camera, viewport, frame, depthBuffer, boundingBox }) => {
36
+ const cam = frame.scene.camera;
37
+ const dir = cam.direction;
38
+ const frameCam = camera.toFrameCamera();
39
+ const ray = viewport.transformPointToRay(point, frame.image, frameCam);
40
+ if (this.zoomData == null) {
41
+ const fallbackPlane = Plane.fromNormalAndCoplanarPoint(dir, cam.lookAt);
42
+ const fallbackPt = Ray.intersectPlane(ray, fallbackPlane);
43
+ if (fallbackPt == null) {
44
+ console.warn('Cannot determine fallback point for zoom. Ray does not intersect plane.');
45
+ return camera;
46
+ }
47
+ const hitPt = depthBuffer != null
48
+ ? this.getWorldPoint(point, depthBuffer, fallbackPt)
49
+ : fallbackPt;
50
+ const hitPlane = Plane.fromNormalAndCoplanarPoint(dir, hitPt);
51
+ this.zoomData = { hitPt, hitPlane };
52
+ }
53
+ if (this.zoomData != null) {
54
+ const { hitPlane } = this.zoomData;
55
+ const { position, distance, isPastHitPlane, keepCurrent } = this.computeZoomDistances(delta, camera, viewport, boundingBox, ray, this.zoomData);
56
+ if (isPastHitPlane && !keepCurrent) {
57
+ const viewVectorRay = Ray.create({
58
+ origin: position,
59
+ direction: Vector3.normalize(camera.viewVector),
60
+ });
61
+ return camera.update({
62
+ position,
63
+ lookAt: Ray.at(viewVectorRay, distance),
64
+ });
65
+ }
66
+ else if (!keepCurrent) {
67
+ return camera.update({
68
+ position,
69
+ lookAt: Plane.projectPoint(hitPlane, position),
70
+ });
71
+ }
72
+ }
73
+ return camera;
74
+ });
75
+ }
33
76
  async transformCamera(t) {
34
77
  var _a;
35
78
  if (this.isInteracting()) {
@@ -51,5 +94,64 @@ export class InteractionApiPerspective extends InteractionApi {
51
94
  await ((_a = this.currentCamera) === null || _a === void 0 ? void 0 : _a.render());
52
95
  }
53
96
  }
97
+ computeZoomDistances(delta, camera, viewport, boundingBox, pointRay, zoomData) {
98
+ const config = this.getConfig();
99
+ const { hitPt, hitPlane } = zoomData;
100
+ const minDistance = config.useMinimumPerspectiveZoomDistance
101
+ ? this.computeZoomMinimumDistance(camera, boundingBox)
102
+ : -1;
103
+ const expectedDistance = Vector3.distance(camera.position, hitPt);
104
+ const actualDistance = Math.max(minDistance, expectedDistance);
105
+ const epsilon = (6 * actualDistance * delta) / viewport.height;
106
+ const expectedPosition = Ray.at(pointRay, epsilon);
107
+ const expectedViewVector = Ray.create({
108
+ origin: expectedPosition,
109
+ direction: Vector3.normalize(camera.viewVector),
110
+ });
111
+ const expectedIntersection = Ray.intersectPlane(expectedViewVector, hitPlane);
112
+ if (expectedIntersection == null &&
113
+ config.useMinimumPerspectiveZoomDistance) {
114
+ const minDistanceEpsilon = (6 * minDistance * delta) / viewport.height;
115
+ const position = Ray.at(pointRay, minDistanceEpsilon);
116
+ return {
117
+ position,
118
+ distance: minDistance,
119
+ isPastHitPlane: true,
120
+ keepCurrent: false,
121
+ };
122
+ }
123
+ else if (expectedIntersection == null) {
124
+ return {
125
+ position: camera.position,
126
+ distance: actualDistance,
127
+ isPastHitPlane: true,
128
+ keepCurrent: true,
129
+ };
130
+ }
131
+ return {
132
+ position: expectedPosition,
133
+ distance: actualDistance,
134
+ isPastHitPlane: false,
135
+ keepCurrent: false,
136
+ };
137
+ }
138
+ computeZoomMinimumDistance(camera, boundingBox) {
139
+ const xLength = Math.abs(boundingBox.min.x) + Math.abs(boundingBox.max.x);
140
+ const yLength = Math.abs(boundingBox.min.y) + Math.abs(boundingBox.max.y);
141
+ const zLength = Math.abs(boundingBox.min.z) + Math.abs(boundingBox.max.z);
142
+ const maxLength = Math.max(xLength, yLength, zLength);
143
+ const absDotX = Math.abs(Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.right()));
144
+ const absDotY = Math.abs(Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.up()));
145
+ const absDotZ = Math.abs(Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.back()));
146
+ const scaledLengthX = xLength * absDotX;
147
+ const scaledLengthY = yLength * absDotY;
148
+ const scaledLengthZ = zLength * absDotZ;
149
+ const relevanceLengthX = maxLength / xLength;
150
+ const relevanceLengthY = maxLength / yLength;
151
+ const relevanceLengthZ = maxLength / zLength;
152
+ return (((scaledLengthX + scaledLengthY + scaledLengthZ) /
153
+ (relevanceLengthX + relevanceLengthY + relevanceLengthZ)) *
154
+ CAMERA_MIN_ZOOM_SCALAR);
155
+ }
54
156
  }
55
157
  //# sourceMappingURL=interactionApiPerspective.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactionApiPerspective.js","sourceRoot":"","sources":["../../../src/lib/interactions/interactionApiPerspective.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from '@stencil/core';\nimport { Point, Vector3 } from '@vertexvis/geometry';\nimport { StreamApi } from '@vertexvis/stream-api';\n\nimport { ReceivedFrame } from '../..';\nimport { CursorManager } from '../cursors';\nimport { PerspectiveCamera } from '../scenes';\nimport { Viewport } from '../types';\nimport {\n CameraTransform,\n InteractionApi,\n InteractionConfigProvider,\n SceneProvider,\n} from './interactionApi';\nimport { TapEventDetails } from './tapEventDetails';\n\nexport class InteractionApiPerspective extends InteractionApi {\n public constructor(\n stream: StreamApi,\n cursors: CursorManager,\n getConfig: InteractionConfigProvider,\n getScene: SceneProvider,\n getFrame: () => ReceivedFrame | undefined,\n getViewport: () => Viewport,\n tapEmitter: EventEmitter<TapEventDetails>,\n doubleTapEmitter: EventEmitter<TapEventDetails>,\n longPressEmitter: EventEmitter<TapEventDetails>,\n interactionStartedEmitter: EventEmitter<void>,\n interactionFinishedEmitter: EventEmitter<void>\n ) {\n super(\n stream,\n cursors,\n getConfig,\n getScene,\n getFrame,\n getViewport,\n tapEmitter,\n doubleTapEmitter,\n longPressEmitter,\n interactionStartedEmitter,\n interactionFinishedEmitter\n );\n }\n\n /**\n * Performs a pan operation of the scene's camera, and requests a new image\n * for the updated scene.\n *\n * @param delta A position delta `{x, y}` in the 2D coordinate space of the\n * viewer.\n */\n public async panCameraByDelta(delta: Point.Point): Promise<void> {\n return this.transformCamera(({ camera, viewport }) => {\n const vv = camera.viewVector;\n\n const u = Vector3.normalize(camera.up);\n const v = Vector3.normalize(vv);\n\n const throttledDelta = Point.scale(delta, 0.25, 0.25);\n const d = Vector3.magnitude(vv) * Math.tan(camera.fovY ?? 45);\n const epsilonX = (throttledDelta.x * d) / viewport.width;\n const epsilonY = (throttledDelta.y / viewport.width) * d;\n\n const xvec = Vector3.cross(u, v);\n const yvec = Vector3.cross(v, xvec);\n\n const offset = Vector3.add(\n Vector3.scale(epsilonX, xvec),\n Vector3.scale(epsilonY, yvec)\n );\n\n return camera.moveBy(offset);\n });\n }\n\n public async transformCamera(\n t: CameraTransform<PerspectiveCamera>\n ): Promise<void> {\n if (this.isInteracting()) {\n const scene = await this.getScene();\n const viewport = this.getViewport();\n const frame = this.getFrame();\n const depthBuffer = await frame?.depthBuffer();\n\n this.currentCamera =\n this.currentCamera != null && viewport != null && frame != null\n ? t({\n camera: this.currentCamera as PerspectiveCamera,\n viewport,\n scale: scene.scale(),\n boundingBox: scene.boundingBox(),\n frame,\n depthBuffer,\n })\n : undefined;\n\n await this.currentCamera?.render();\n }\n }\n}\n"]}
1
+ {"version":3,"file":"interactionApiPerspective.js","sourceRoot":"","sources":["../../../src/lib/interactions/interactionApiPerspective.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from '@stencil/core';\nimport { BoundingBox, Plane, Point, Ray, Vector3 } from '@vertexvis/geometry';\nimport { StreamApi } from '@vertexvis/stream-api';\n\nimport { ReceivedFrame } from '../..';\nimport { CursorManager } from '../cursors';\nimport { PerspectiveCamera } from '../scenes';\nimport { Viewport } from '../types';\nimport {\n CameraTransform,\n InteractionApi,\n InteractionConfigProvider,\n SceneProvider,\n ZoomData,\n} from './interactionApi';\nimport { TapEventDetails } from './tapEventDetails';\n\ninterface ZoomPositionData {\n position: Vector3.Vector3;\n distance: number;\n isPastHitPlane: boolean;\n keepCurrent: boolean;\n}\n\nconst CAMERA_MIN_ZOOM_SCALAR = 0.2;\n\nexport class InteractionApiPerspective extends InteractionApi {\n public constructor(\n stream: StreamApi,\n cursors: CursorManager,\n getConfig: InteractionConfigProvider,\n getScene: SceneProvider,\n getFrame: () => ReceivedFrame | undefined,\n getViewport: () => Viewport,\n tapEmitter: EventEmitter<TapEventDetails>,\n doubleTapEmitter: EventEmitter<TapEventDetails>,\n longPressEmitter: EventEmitter<TapEventDetails>,\n interactionStartedEmitter: EventEmitter<void>,\n interactionFinishedEmitter: EventEmitter<void>\n ) {\n super(\n stream,\n cursors,\n getConfig,\n getScene,\n getFrame,\n getViewport,\n tapEmitter,\n doubleTapEmitter,\n longPressEmitter,\n interactionStartedEmitter,\n interactionFinishedEmitter\n );\n }\n\n /**\n * Performs a pan operation of the scene's camera, and requests a new image\n * for the updated scene.\n *\n * @param delta A position delta `{x, y}` in the 2D coordinate space of the\n * viewer.\n */\n public async panCameraByDelta(delta: Point.Point): Promise<void> {\n return this.transformCamera(({ camera, viewport }) => {\n const vv = camera.viewVector;\n\n const u = Vector3.normalize(camera.up);\n const v = Vector3.normalize(vv);\n\n const throttledDelta = Point.scale(delta, 0.25, 0.25);\n const d = Vector3.magnitude(vv) * Math.tan(camera.fovY ?? 45);\n const epsilonX = (throttledDelta.x * d) / viewport.width;\n const epsilonY = (throttledDelta.y / viewport.width) * d;\n\n const xvec = Vector3.cross(u, v);\n const yvec = Vector3.cross(v, xvec);\n\n const offset = Vector3.add(\n Vector3.scale(epsilonX, xvec),\n Vector3.scale(epsilonY, yvec)\n );\n\n return camera.moveBy(offset);\n });\n }\n\n public async zoomCameraToPoint(\n point: Point.Point,\n delta: number\n ): Promise<void> {\n return this.transformCamera(\n ({ camera, viewport, frame, depthBuffer, boundingBox }) => {\n const cam = frame.scene.camera;\n const dir = cam.direction;\n\n const frameCam = camera.toFrameCamera();\n const ray = viewport.transformPointToRay(point, frame.image, frameCam);\n\n if (this.zoomData == null) {\n const fallbackPlane = Plane.fromNormalAndCoplanarPoint(\n dir,\n cam.lookAt\n );\n const fallbackPt = Ray.intersectPlane(ray, fallbackPlane);\n if (fallbackPt == null) {\n console.warn(\n 'Cannot determine fallback point for zoom. Ray does not intersect plane.'\n );\n return camera;\n }\n\n const hitPt =\n depthBuffer != null\n ? this.getWorldPoint(point, depthBuffer, fallbackPt)\n : fallbackPt;\n const hitPlane = Plane.fromNormalAndCoplanarPoint(dir, hitPt);\n this.zoomData = { hitPt, hitPlane };\n }\n\n if (this.zoomData != null) {\n const { hitPlane } = this.zoomData;\n const { position, distance, isPastHitPlane, keepCurrent } =\n this.computeZoomDistances(\n delta,\n camera,\n viewport,\n boundingBox,\n ray,\n this.zoomData\n );\n\n if (isPastHitPlane && !keepCurrent) {\n const viewVectorRay = Ray.create({\n origin: position,\n direction: Vector3.normalize(camera.viewVector),\n });\n\n return camera.update({\n position,\n lookAt: Ray.at(viewVectorRay, distance),\n });\n } else if (!keepCurrent) {\n return camera.update({\n position,\n lookAt: Plane.projectPoint(hitPlane, position),\n });\n }\n }\n return camera;\n }\n );\n }\n\n public async transformCamera(\n t: CameraTransform<PerspectiveCamera>\n ): Promise<void> {\n if (this.isInteracting()) {\n const scene = await this.getScene();\n const viewport = this.getViewport();\n const frame = this.getFrame();\n const depthBuffer = await frame?.depthBuffer();\n\n this.currentCamera =\n this.currentCamera != null && viewport != null && frame != null\n ? t({\n camera: this.currentCamera as PerspectiveCamera,\n viewport,\n scale: scene.scale(),\n boundingBox: scene.boundingBox(),\n frame,\n depthBuffer,\n })\n : undefined;\n\n await this.currentCamera?.render();\n }\n }\n\n private computeZoomDistances(\n delta: number,\n camera: PerspectiveCamera,\n viewport: Viewport,\n boundingBox: BoundingBox.BoundingBox,\n pointRay: Ray.Ray,\n zoomData: ZoomData\n ): ZoomPositionData {\n const config = this.getConfig();\n const { hitPt, hitPlane } = zoomData;\n const minDistance = config.useMinimumPerspectiveZoomDistance\n ? this.computeZoomMinimumDistance(camera, boundingBox)\n : -1;\n const expectedDistance = Vector3.distance(camera.position, hitPt);\n const actualDistance = Math.max(minDistance, expectedDistance);\n const epsilon = (6 * actualDistance * delta) / viewport.height;\n\n const expectedPosition = Ray.at(pointRay, epsilon);\n const expectedViewVector = Ray.create({\n origin: expectedPosition,\n direction: Vector3.normalize(camera.viewVector),\n });\n const expectedIntersection = Ray.intersectPlane(\n expectedViewVector,\n hitPlane\n );\n\n if (\n expectedIntersection == null &&\n config.useMinimumPerspectiveZoomDistance\n ) {\n const minDistanceEpsilon = (6 * minDistance * delta) / viewport.height;\n const position = Ray.at(pointRay, minDistanceEpsilon);\n\n return {\n position,\n distance: minDistance,\n isPastHitPlane: true,\n keepCurrent: false,\n };\n } else if (expectedIntersection == null) {\n return {\n position: camera.position,\n distance: actualDistance,\n isPastHitPlane: true,\n keepCurrent: true,\n };\n }\n\n return {\n position: expectedPosition,\n distance: actualDistance,\n isPastHitPlane: false,\n keepCurrent: false,\n };\n }\n\n private computeZoomMinimumDistance(\n camera: PerspectiveCamera,\n boundingBox: BoundingBox.BoundingBox\n ): number {\n const xLength = Math.abs(boundingBox.min.x) + Math.abs(boundingBox.max.x);\n const yLength = Math.abs(boundingBox.min.y) + Math.abs(boundingBox.max.y);\n const zLength = Math.abs(boundingBox.min.z) + Math.abs(boundingBox.max.z);\n const maxLength = Math.max(xLength, yLength, zLength);\n\n const absDotX = Math.abs(\n Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.right())\n );\n const absDotY = Math.abs(\n Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.up())\n );\n const absDotZ = Math.abs(\n Vector3.dot(Vector3.normalize(camera.viewVector), Vector3.back())\n );\n\n const scaledLengthX = xLength * absDotX;\n const scaledLengthY = yLength * absDotY;\n const scaledLengthZ = zLength * absDotZ;\n\n const relevanceLengthX = maxLength / xLength;\n const relevanceLengthY = maxLength / yLength;\n const relevanceLengthZ = maxLength / zLength;\n\n return (\n ((scaledLengthX + scaledLengthY + scaledLengthZ) /\n (relevanceLengthX + relevanceLengthY + relevanceLengthZ)) *\n CAMERA_MIN_ZOOM_SCALAR\n );\n }\n}\n"]}
@@ -5,5 +5,6 @@ export const defaultInteractionConfig = {
5
5
  finePointerThreshold: 1,
6
6
  coarsePointerThreshold: 3,
7
7
  interactionDelay: 75,
8
+ useMinimumPerspectiveZoomDistance: true,
8
9
  };
9
10
  //# sourceMappingURL=interactions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../../src/lib/types/interactions.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,wBAAwB,GAAsB;EACzD,oBAAoB,EAAE,CAAC;EACvB,sBAAsB,EAAE,CAAC;EACzB,gBAAgB,EAAE,EAAE;CACrB,CAAC","sourcesContent":["export interface InteractionConfig {\n /**\n * The amount of movement required with a fine (mouse, trackpad, etc) pointer\n * input before an interaction is considered a movement that will update the\n * camera. This threshold is always scaled up based on the device's pixel ratio.\n */\n finePointerThreshold: number;\n\n /**\n * The amount of movement required with a coarse (touch, stylus, etc) pointer\n * input before an interaction is considered a movement that will update the\n * camera. This threshold is always scaled up based on the device's pixel ratio.\n */\n coarsePointerThreshold: number;\n\n /**\n * The amount of time before a movement is considered an interaction and will\n * update the camera.\n */\n interactionDelay: number;\n}\n\nexport const defaultInteractionConfig: InteractionConfig = {\n finePointerThreshold: 1,\n coarsePointerThreshold: 3,\n interactionDelay: 75,\n};\n"]}
1
+ {"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../../src/lib/types/interactions.ts"],"names":[],"mappings":"AA8BA,MAAM,CAAC,MAAM,wBAAwB,GAAsB;EACzD,oBAAoB,EAAE,CAAC;EACvB,sBAAsB,EAAE,CAAC;EACzB,gBAAgB,EAAE,EAAE;EACpB,iCAAiC,EAAE,IAAI;CACxC,CAAC","sourcesContent":["export interface InteractionConfig {\n /**\n * The amount of movement required with a fine (mouse, trackpad, etc) pointer\n * input before an interaction is considered a movement that will update the\n * camera. This threshold is always scaled up based on the device's pixel ratio.\n */\n finePointerThreshold: number;\n\n /**\n * The amount of movement required with a coarse (touch, stylus, etc) pointer\n * input before an interaction is considered a movement that will update the\n * camera. This threshold is always scaled up based on the device's pixel ratio.\n */\n coarsePointerThreshold: number;\n\n /**\n * The amount of time before a movement is considered an interaction and will\n * update the camera.\n */\n interactionDelay: number;\n\n /**\n * Whether the `zoomCameraToPoint` interaction through the `InteractionApiPerspective`\n * will enforce a minimum distance when moving the camera. When set, the camera will be\n * able to move through geometry under the cursor, rather than being restricted by the\n * point under the cursor. Defaults to `true`.\n */\n useMinimumPerspectiveZoomDistance: boolean;\n}\n\nexport const defaultInteractionConfig: InteractionConfig = {\n finePointerThreshold: 1,\n coarsePointerThreshold: 3,\n interactionDelay: 75,\n useMinimumPerspectiveZoomDistance: true,\n};\n"]}
@@ -12218,7 +12218,7 @@ class SceneTreeController {
12218
12218
  this.pages = new Map();
12219
12219
  this.activeRowRange = [0, 0];
12220
12220
  this.metadataKeys = [];
12221
- this.debugLogs = false;
12221
+ this.debugLogs = true;
12222
12222
  /**
12223
12223
  * A dispatcher that emits an event whenever the internal state has changed.
12224
12224
  */
@@ -12432,6 +12432,7 @@ class SceneTreeController {
12432
12432
  ? undefined
12433
12433
  : this.state.shouldShowEmptyResults,
12434
12434
  handshakeReceived: reset ? undefined : this.state.handshakeReceived,
12435
+ firstFetchComplete: reset ? undefined : this.state.firstFetchComplete,
12435
12436
  });
12436
12437
  }
12437
12438
  cancel() {
@@ -12724,6 +12725,14 @@ class SceneTreeController {
12724
12725
  if (msg.hasHandshake()) {
12725
12726
  this.clearHandshakeTimer();
12726
12727
  this.updateState(Object.assign(Object.assign({}, this.state), { handshakeReceived: true }));
12728
+ // Verify that we've loaded the first page of the tree when we receive
12729
+ // a handshake in the case that the listener was not fully registered
12730
+ // when a `ListChange` was sent.
12731
+ if (this.state.firstFetchComplete) {
12732
+ this.log('Scene tree did not have the first page before the handshake completed');
12733
+ this.invalidateAfterOffset(0);
12734
+ this.fetchUnloadedPagesInActiveRows();
12735
+ }
12727
12736
  }
12728
12737
  this.startIdleReconnectTimer();
12729
12738
  const { change } = msg.toObject();
@@ -12819,7 +12828,7 @@ class SceneTreeController {
12819
12828
  }
12820
12829
  else {
12821
12830
  this.updateState(Object.assign(Object.assign({}, this.state), { totalRows: totalRows, rows: rows, shouldShowLoading: rows.length === 0 && this.state.shouldShowLoading, shouldShowEmptyResults: this.state.filterTerm != null &&
12822
- (rows.length === 0 || this.state.totalFilteredRows === 0) }));
12831
+ (rows.length === 0 || this.state.totalFilteredRows === 0), firstFetchComplete: true }));
12823
12832
  }
12824
12833
  }
12825
12834
  }
@@ -65474,6 +65483,7 @@ const defaultInteractionConfig = {
65474
65483
  finePointerThreshold: 1,
65475
65484
  coarsePointerThreshold: 3,
65476
65485
  interactionDelay: 75,
65486
+ useMinimumPerspectiveZoomDistance: true,
65477
65487
  };
65478
65488
 
65479
65489
  function fromUrn(urn) {
@@ -110951,40 +110961,6 @@ class InteractionApi {
110951
110961
  return newCamera;
110952
110962
  });
110953
110963
  }
110954
- async zoomCameraToPoint(point, delta) {
110955
- return this.transformCamera(({ camera, viewport, frame, depthBuffer }) => {
110956
- const cam = frame.scene.camera;
110957
- const dir = cam.direction;
110958
- const frameCam = camera.toFrameCamera();
110959
- const ray$1 = viewport.transformPointToRay(point, frame.image, frameCam);
110960
- if (this.zoomData == null) {
110961
- const fallbackPlane = plane.fromNormalAndCoplanarPoint(dir, cam.lookAt);
110962
- const fallbackPt = ray.intersectPlane(ray$1, fallbackPlane);
110963
- if (fallbackPt == null) {
110964
- console.warn('Cannot determine fallback point for zoom. Ray does not intersect plane.');
110965
- return camera;
110966
- }
110967
- const hitPt = depthBuffer != null
110968
- ? this.getWorldPoint(point, depthBuffer, fallbackPt)
110969
- : fallbackPt;
110970
- const hitPlane = plane.fromNormalAndCoplanarPoint(dir, hitPt);
110971
- this.zoomData = { hitPt, hitPlane };
110972
- }
110973
- if (this.zoomData != null) {
110974
- const { hitPt, hitPlane } = this.zoomData;
110975
- const distance = vector3.distance(camera.position, hitPt);
110976
- const epsilon = (6 * distance * delta) / viewport.height;
110977
- const position = ray.at(ray$1, epsilon);
110978
- const lookAt = plane.projectPoint(hitPlane, position);
110979
- const newCamera = camera.update({ position, lookAt });
110980
- const newDistance = vector3.distance(position, lookAt);
110981
- if (newDistance >= newCamera.near) {
110982
- return newCamera;
110983
- }
110984
- }
110985
- return camera;
110986
- });
110987
- }
110988
110964
  /**
110989
110965
  * Performs a pivot operation of the scene's camera, updating the lookAt
110990
110966
  * while maintaining the position, and requests a new image for the
@@ -111236,6 +111212,7 @@ class InteractionApiOrthographic extends InteractionApi {
111236
111212
  }
111237
111213
  }
111238
111214
 
111215
+ const CAMERA_MIN_ZOOM_SCALAR = 0.2;
111239
111216
  class InteractionApiPerspective extends InteractionApi {
111240
111217
  constructor(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter) {
111241
111218
  super(stream, cursors, getConfig, getScene, getFrame, getViewport, tapEmitter, doubleTapEmitter, longPressEmitter, interactionStartedEmitter, interactionFinishedEmitter);
@@ -111263,6 +111240,48 @@ class InteractionApiPerspective extends InteractionApi {
111263
111240
  return camera.moveBy(offset);
111264
111241
  });
111265
111242
  }
111243
+ async zoomCameraToPoint(point, delta) {
111244
+ return this.transformCamera(({ camera, viewport, frame, depthBuffer, boundingBox }) => {
111245
+ const cam = frame.scene.camera;
111246
+ const dir = cam.direction;
111247
+ const frameCam = camera.toFrameCamera();
111248
+ const ray$1 = viewport.transformPointToRay(point, frame.image, frameCam);
111249
+ if (this.zoomData == null) {
111250
+ const fallbackPlane = plane.fromNormalAndCoplanarPoint(dir, cam.lookAt);
111251
+ const fallbackPt = ray.intersectPlane(ray$1, fallbackPlane);
111252
+ if (fallbackPt == null) {
111253
+ console.warn('Cannot determine fallback point for zoom. Ray does not intersect plane.');
111254
+ return camera;
111255
+ }
111256
+ const hitPt = depthBuffer != null
111257
+ ? this.getWorldPoint(point, depthBuffer, fallbackPt)
111258
+ : fallbackPt;
111259
+ const hitPlane = plane.fromNormalAndCoplanarPoint(dir, hitPt);
111260
+ this.zoomData = { hitPt, hitPlane };
111261
+ }
111262
+ if (this.zoomData != null) {
111263
+ const { hitPlane } = this.zoomData;
111264
+ const { position, distance, isPastHitPlane, keepCurrent } = this.computeZoomDistances(delta, camera, viewport, boundingBox, ray$1, this.zoomData);
111265
+ if (isPastHitPlane && !keepCurrent) {
111266
+ const viewVectorRay = ray.create({
111267
+ origin: position,
111268
+ direction: vector3.normalize(camera.viewVector),
111269
+ });
111270
+ return camera.update({
111271
+ position,
111272
+ lookAt: ray.at(viewVectorRay, distance),
111273
+ });
111274
+ }
111275
+ else if (!keepCurrent) {
111276
+ return camera.update({
111277
+ position,
111278
+ lookAt: plane.projectPoint(hitPlane, position),
111279
+ });
111280
+ }
111281
+ }
111282
+ return camera;
111283
+ });
111284
+ }
111266
111285
  async transformCamera(t) {
111267
111286
  var _a;
111268
111287
  if (this.isInteracting()) {
@@ -111284,6 +111303,65 @@ class InteractionApiPerspective extends InteractionApi {
111284
111303
  await ((_a = this.currentCamera) === null || _a === void 0 ? void 0 : _a.render());
111285
111304
  }
111286
111305
  }
111306
+ computeZoomDistances(delta, camera, viewport, boundingBox, pointRay, zoomData) {
111307
+ const config = this.getConfig();
111308
+ const { hitPt, hitPlane } = zoomData;
111309
+ const minDistance = config.useMinimumPerspectiveZoomDistance
111310
+ ? this.computeZoomMinimumDistance(camera, boundingBox)
111311
+ : -1;
111312
+ const expectedDistance = vector3.distance(camera.position, hitPt);
111313
+ const actualDistance = Math.max(minDistance, expectedDistance);
111314
+ const epsilon = (6 * actualDistance * delta) / viewport.height;
111315
+ const expectedPosition = ray.at(pointRay, epsilon);
111316
+ const expectedViewVector = ray.create({
111317
+ origin: expectedPosition,
111318
+ direction: vector3.normalize(camera.viewVector),
111319
+ });
111320
+ const expectedIntersection = ray.intersectPlane(expectedViewVector, hitPlane);
111321
+ if (expectedIntersection == null &&
111322
+ config.useMinimumPerspectiveZoomDistance) {
111323
+ const minDistanceEpsilon = (6 * minDistance * delta) / viewport.height;
111324
+ const position = ray.at(pointRay, minDistanceEpsilon);
111325
+ return {
111326
+ position,
111327
+ distance: minDistance,
111328
+ isPastHitPlane: true,
111329
+ keepCurrent: false,
111330
+ };
111331
+ }
111332
+ else if (expectedIntersection == null) {
111333
+ return {
111334
+ position: camera.position,
111335
+ distance: actualDistance,
111336
+ isPastHitPlane: true,
111337
+ keepCurrent: true,
111338
+ };
111339
+ }
111340
+ return {
111341
+ position: expectedPosition,
111342
+ distance: actualDistance,
111343
+ isPastHitPlane: false,
111344
+ keepCurrent: false,
111345
+ };
111346
+ }
111347
+ computeZoomMinimumDistance(camera, boundingBox) {
111348
+ const xLength = Math.abs(boundingBox.min.x) + Math.abs(boundingBox.max.x);
111349
+ const yLength = Math.abs(boundingBox.min.y) + Math.abs(boundingBox.max.y);
111350
+ const zLength = Math.abs(boundingBox.min.z) + Math.abs(boundingBox.max.z);
111351
+ const maxLength = Math.max(xLength, yLength, zLength);
111352
+ const absDotX = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.right()));
111353
+ const absDotY = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.up()));
111354
+ const absDotZ = Math.abs(vector3.dot(vector3.normalize(camera.viewVector), vector3.back()));
111355
+ const scaledLengthX = xLength * absDotX;
111356
+ const scaledLengthY = yLength * absDotY;
111357
+ const scaledLengthZ = zLength * absDotZ;
111358
+ const relevanceLengthX = maxLength / xLength;
111359
+ const relevanceLengthY = maxLength / yLength;
111360
+ const relevanceLengthZ = maxLength / zLength;
111361
+ return (((scaledLengthX + scaledLengthY + scaledLengthZ) /
111362
+ (relevanceLengthX + relevanceLengthY + relevanceLengthZ)) *
111363
+ CAMERA_MIN_ZOOM_SCALAR);
111364
+ }
111287
111365
  }
111288
111366
 
111289
111367
  class FlyToPositionKeyInteraction {