@shapediver/viewer.rendering-engine.camera-engine 2.11.0 → 2.12.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 (51) hide show
  1. package/dist/implementation/camera/AbstractCamera.d.ts +5 -3
  2. package/dist/implementation/camera/AbstractCamera.d.ts.map +1 -1
  3. package/dist/implementation/camera/AbstractCamera.js +26 -18
  4. package/dist/implementation/camera/AbstractCamera.js.map +1 -1
  5. package/dist/implementation/controls/AbstractCameraControls.d.ts +13 -7
  6. package/dist/implementation/controls/AbstractCameraControls.d.ts.map +1 -1
  7. package/dist/implementation/controls/AbstractCameraControls.js +71 -38
  8. package/dist/implementation/controls/AbstractCameraControls.js.map +1 -1
  9. package/dist/implementation/controls/OrthographicCameraControls.d.ts.map +1 -1
  10. package/dist/implementation/controls/OrthographicCameraControls.js +2 -2
  11. package/dist/implementation/controls/OrthographicCameraControls.js.map +1 -1
  12. package/dist/implementation/controls/PerspectiveCameraControls.d.ts +15 -3
  13. package/dist/implementation/controls/PerspectiveCameraControls.d.ts.map +1 -1
  14. package/dist/implementation/controls/PerspectiveCameraControls.js +108 -78
  15. package/dist/implementation/controls/PerspectiveCameraControls.js.map +1 -1
  16. package/dist/implementation/controls/orthographic/CameraControlsEventDistribution.d.ts +3 -3
  17. package/dist/implementation/controls/orthographic/CameraControlsEventDistribution.d.ts.map +1 -1
  18. package/dist/implementation/controls/orthographic/CameraControlsEventDistribution.js +31 -23
  19. package/dist/implementation/controls/orthographic/CameraControlsEventDistribution.js.map +1 -1
  20. package/dist/implementation/controls/orthographic/CameraControlsLogic.d.ts.map +1 -1
  21. package/dist/implementation/controls/orthographic/CameraControlsLogic.js +36 -38
  22. package/dist/implementation/controls/orthographic/CameraControlsLogic.js.map +1 -1
  23. package/dist/implementation/controls/perspective/CameraControlsEventDistribution.d.ts +4 -4
  24. package/dist/implementation/controls/perspective/CameraControlsEventDistribution.d.ts.map +1 -1
  25. package/dist/implementation/controls/perspective/CameraControlsEventDistribution.js +39 -32
  26. package/dist/implementation/controls/perspective/CameraControlsEventDistribution.js.map +1 -1
  27. package/dist/implementation/controls/perspective/CameraControlsLogic.d.ts +3 -2
  28. package/dist/implementation/controls/perspective/CameraControlsLogic.d.ts.map +1 -1
  29. package/dist/implementation/controls/perspective/CameraControlsLogic.js +88 -66
  30. package/dist/implementation/controls/perspective/CameraControlsLogic.js.map +1 -1
  31. package/dist/interfaces/camera/ICamera.d.ts +9 -8
  32. package/dist/interfaces/camera/ICamera.d.ts.map +1 -1
  33. package/dist/interfaces/controls/ICameraControls.d.ts +4 -3
  34. package/dist/interfaces/controls/ICameraControls.d.ts.map +1 -1
  35. package/dist/interfaces/controls/ICameraControlsLogic.d.ts +3 -2
  36. package/dist/interfaces/controls/ICameraControlsLogic.d.ts.map +1 -1
  37. package/dist/interfaces/controls/IPerspectiveCameraControls.d.ts +4 -0
  38. package/dist/interfaces/controls/IPerspectiveCameraControls.d.ts.map +1 -1
  39. package/package.json +7 -7
  40. package/src/implementation/camera/AbstractCamera.ts +44 -37
  41. package/src/implementation/controls/AbstractCameraControls.ts +113 -70
  42. package/src/implementation/controls/OrthographicCameraControls.ts +9 -9
  43. package/src/implementation/controls/PerspectiveCameraControls.ts +114 -73
  44. package/src/implementation/controls/orthographic/CameraControlsEventDistribution.ts +61 -56
  45. package/src/implementation/controls/orthographic/CameraControlsLogic.ts +45 -53
  46. package/src/implementation/controls/perspective/CameraControlsEventDistribution.ts +83 -80
  47. package/src/implementation/controls/perspective/CameraControlsLogic.ts +100 -81
  48. package/src/interfaces/camera/ICamera.ts +31 -26
  49. package/src/interfaces/controls/ICameraControls.ts +5 -5
  50. package/src/interfaces/controls/ICameraControlsLogic.ts +6 -2
  51. package/src/interfaces/controls/IPerspectiveCameraControls.ts +10 -2
@@ -1,9 +1,9 @@
1
- import { mat4, quat, vec2, vec3 } from 'gl-matrix'
2
- import { Box, Sphere, Spherical } from '@shapediver/viewer.shared.math'
1
+ import { mat4, quat, vec2, vec3 } from 'gl-matrix';
2
+ import { Box, Sphere, Spherical } from '@shapediver/viewer.shared.math';
3
3
 
4
- import { PerspectiveCameraControls } from '../PerspectiveCameraControls'
5
- import { ICameraControlsLogic } from '../../../interfaces/controls/ICameraControlsLogic'
6
- import { PerspectiveCamera } from '../../camera/PerspectiveCamera'
4
+ import { PerspectiveCameraControls } from '../PerspectiveCameraControls';
5
+ import { ICameraControlsLogic } from '../../../interfaces/controls/ICameraControlsLogic';
6
+ import { PerspectiveCamera } from '../../camera/PerspectiveCamera';
7
7
 
8
8
  export class CameraControlsLogic implements ICameraControlsLogic {
9
9
  // #region Properties (15)
@@ -49,7 +49,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
49
49
  autoRotationSpeed: 2 * Math.PI / 60 / 60,
50
50
  damping: 1.0,
51
51
  movementSmoothness: 1.0,
52
- panSpeed: 2.0,
52
+ panSpeed: 1.75,
53
53
  rotationSpeed: Math.PI,
54
54
  zoomSpeed: 0.025,
55
55
  };
@@ -57,8 +57,8 @@ export class CameraControlsLogic implements ICameraControlsLogic {
57
57
  autoRotationSpeed: 1.0,
58
58
  damping: 1.0,
59
59
  movementSmoothness: 1.0,
60
- panSpeed: 1.5,
61
- rotationSpeed: 2.0,
60
+ panSpeed: 1.0 / 1.75,
61
+ rotationSpeed: 1.5,
62
62
  zoomSpeed: 100.0,
63
63
  };
64
64
 
@@ -76,7 +76,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
76
76
  // #region Public Methods (7)
77
77
 
78
78
  public isWithinRestrictions(position: vec3, target: vec3): boolean {
79
- let pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
79
+ const pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
80
80
  pSphere = new Sphere(this._controls.spherePositionRestriction.center, this._controls.spherePositionRestriction.radius),
81
81
  tBox = new Box(this._controls.cubeTargetRestriction.min, this._controls.cubeTargetRestriction.max),
82
82
  tSphere = new Sphere(this._controls.sphereTargetRestriction.center, this._controls.sphereTargetRestriction.radius);
@@ -84,10 +84,10 @@ export class CameraControlsLogic implements ICameraControlsLogic {
84
84
  if (!(pBox.containsPoint(position) && pSphere.containsPoint(position))) return false;
85
85
  if (!(tBox.containsPoint(target) && tSphere.containsPoint(target))) return false;
86
86
 
87
- let currentDistance = vec3.distance(position, target);
87
+ const currentDistance = vec3.distance(position, target);
88
88
  if (currentDistance > this._controls.zoomRestriction.maxDistance || currentDistance < this._controls.zoomRestriction.minDistance) return false;
89
89
 
90
- let minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
90
+ const minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
91
91
  maxPolarAngle = this._controls.rotationRestriction.maxPolarAngle * (Math.PI / 180),
92
92
  minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
93
93
  maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
@@ -96,7 +96,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
96
96
  maxAzimuthAngle !== Infinity ||
97
97
  minPolarAngle !== 0 ||
98
98
  maxPolarAngle !== 180) {
99
- let offset = vec3.sub(vec3.create(), position, target);
99
+ const offset = vec3.sub(vec3.create(), position, target);
100
100
  vec3.transformQuat(offset, offset, this._quat);
101
101
  const spherical = new Spherical().fromVec3(offset);
102
102
 
@@ -112,10 +112,6 @@ export class CameraControlsLogic implements ICameraControlsLogic {
112
112
  }
113
113
 
114
114
  public pan(x: number, y: number, active: boolean, touch: boolean): void {
115
- if (touch) {
116
- x = x / window.devicePixelRatio;
117
- y = y / window.devicePixelRatio;
118
- }
119
115
 
120
116
  if (!active) {
121
117
  this._panStart = vec2.fromValues(x, y);
@@ -123,11 +119,10 @@ export class CameraControlsLogic implements ICameraControlsLogic {
123
119
  this._panEnd = vec2.fromValues(x, y);
124
120
  vec2.sub(this._panDelta, this._panEnd, this._panStart);
125
121
  if (this._panDelta[0] === 0 && this._panDelta[1] === 0) return;
126
-
127
122
  vec2.copy(this._panStart, this._panEnd);
128
123
 
129
124
  const adjustedPanSpeed = this._adjustedSettings.panSpeed() * (touch ? this._touchAdjustments.panSpeed : 1.0);
130
- let offset = this.panDeltaToOffset(vec2.mul(vec2.create(), this._panDelta, vec2.fromValues(adjustedPanSpeed, adjustedPanSpeed)));
125
+ const offset = this.panDeltaToOffset(vec2.mul(vec2.create(), this._panDelta, vec2.fromValues(adjustedPanSpeed, adjustedPanSpeed)));
131
126
 
132
127
  if (this._damping.pan.duration > 0) {
133
128
  if (offset[0] < 0) {
@@ -147,11 +142,11 @@ export class CameraControlsLogic implements ICameraControlsLogic {
147
142
  }
148
143
  }
149
144
 
150
- let damping = 1 - Math.max(0.01, Math.min(0.99, this._adjustedSettings.damping()));
145
+ const damping = 1 - Math.max(0.01, Math.min(0.99, this._adjustedSettings.damping()));
151
146
 
152
- let framesOffsetX = (Math.log(1 / Math.abs(offset[0])) - 5 * Math.log(10)) / (Math.log(damping));
153
- let framesOffsetY = (Math.log(1 / Math.abs(offset[1])) - 5 * Math.log(10)) / (Math.log(damping));
154
- let framesOffsetZ = (Math.log(1 / Math.abs(offset[2])) - 5 * Math.log(10)) / (Math.log(damping));
147
+ const framesOffsetX = (Math.log(1 / Math.abs(offset[0])) - 5 * Math.log(10)) / (Math.log(damping));
148
+ const framesOffsetY = (Math.log(1 / Math.abs(offset[1])) - 5 * Math.log(10)) / (Math.log(damping));
149
+ const framesOffsetZ = (Math.log(1 / Math.abs(offset[2])) - 5 * Math.log(10)) / (Math.log(damping));
155
150
  this._damping.pan.time = 0;
156
151
  this._damping.pan.duration = Math.max(framesOffsetX, Math.max(framesOffsetY, framesOffsetZ)) * 16.6666;
157
152
  this._damping.pan.offset = vec3.clone(offset);
@@ -194,8 +189,8 @@ export class CameraControlsLogic implements ICameraControlsLogic {
194
189
  this._rotateStart = vec2.create();
195
190
  }
196
191
 
197
- public restrict(position: vec3, target: vec3): { position: vec3, target: vec3 } {
198
- let pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
192
+ public restrict(position: vec3, target: vec3, sceneRotation: vec2): { position: vec3, target: vec3, sceneRotation: vec2 } {
193
+ const pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
199
194
  pSphere = new Sphere(this._controls.spherePositionRestriction.center, this._controls.spherePositionRestriction.radius),
200
195
  tBox = new Box(this._controls.cubeTargetRestriction.min, this._controls.cubeTargetRestriction.max),
201
196
  tSphere = new Sphere(this._controls.sphereTargetRestriction.center, this._controls.sphereTargetRestriction.radius);
@@ -213,15 +208,15 @@ export class CameraControlsLogic implements ICameraControlsLogic {
213
208
  target = tSphere.clampPoint(target);
214
209
 
215
210
  // zoom restrictions
216
- let currentDistance = vec3.distance(position, target);
211
+ const currentDistance = vec3.distance(position, target);
217
212
  if (currentDistance > this._controls.zoomRestriction.maxDistance || currentDistance < this._controls.zoomRestriction.minDistance) {
218
- let direction = vec3.normalize(vec3.create(), vec3.subtract(vec3.create(), position, target))
219
- let distance = Math.max(this._controls.zoomRestriction.minDistance, Math.min(this._controls.zoomRestriction.maxDistance, currentDistance));
213
+ const direction = vec3.normalize(vec3.create(), vec3.subtract(vec3.create(), position, target));
214
+ const distance = Math.max(this._controls.zoomRestriction.minDistance, Math.min(this._controls.zoomRestriction.maxDistance, currentDistance));
220
215
  vec3.add(position, vec3.multiply(position, direction, vec3.fromValues(distance, distance, distance)), target);
221
216
  }
222
217
 
223
218
  // angle restrictions
224
- let minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
219
+ const minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
225
220
  maxPolarAngle = this._controls.rotationRestriction.maxPolarAngle * (Math.PI / 180),
226
221
  minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
227
222
  maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
@@ -229,7 +224,10 @@ export class CameraControlsLogic implements ICameraControlsLogic {
229
224
  if (minAzimuthAngle !== -Infinity ||
230
225
  maxAzimuthAngle !== Infinity ||
231
226
  minPolarAngle !== 0 ||
232
- maxPolarAngle !== 180) {
227
+ maxPolarAngle !== 180 ||
228
+ this._controls.enableAzimuthRotation === false ||
229
+ this._controls.enablePolarRotation === false
230
+ ) {
233
231
  let offset = vec3.subtract(vec3.create(), position, target);
234
232
  vec3.transformQuat(offset, offset, this._quat);
235
233
 
@@ -238,53 +236,72 @@ export class CameraControlsLogic implements ICameraControlsLogic {
238
236
  if (spherical.theta < minAzimuthAngle ||
239
237
  spherical.theta > maxAzimuthAngle ||
240
238
  spherical.phi < minPolarAngle ||
241
- spherical.phi > maxPolarAngle) {
239
+ spherical.phi > maxPolarAngle ||
240
+ this._controls.enableAzimuthRotation === false ||
241
+ this._controls.enablePolarRotation === false
242
+ ) {
242
243
  spherical.theta = Math.max(minAzimuthAngle, Math.min(maxAzimuthAngle, spherical.theta));
243
244
  spherical.phi = Math.max(minPolarAngle, Math.min(maxPolarAngle, spherical.phi));
245
+
246
+ if (this._controls.enableAzimuthRotation === false || this._controls.enablePolarRotation === false) {
247
+ const defaultOffset = vec3.subtract(vec3.create(), this._controls.camera.defaultPosition, this._controls.camera.defaultTarget);
248
+ vec3.transformQuat(defaultOffset, defaultOffset, this._quat);
249
+
250
+ const defaultSpherical = new Spherical().fromVec3(defaultOffset);
251
+ if (this._controls.enableAzimuthRotation === false) spherical.theta = defaultSpherical.theta;
252
+ if (this._controls.enablePolarRotation === false) spherical.phi = defaultSpherical.phi;
253
+ }
254
+
244
255
  spherical.makeSafe();
245
256
  offset = spherical.toVec3();
246
257
  vec3.transformQuat(offset, offset, this._quatInverse);
247
258
  vec3.add(position, offset, target);
248
259
  }
260
+
261
+ if (sceneRotation[1] < minAzimuthAngle ||
262
+ sceneRotation[1] > maxAzimuthAngle ||
263
+ sceneRotation[0] < minPolarAngle ||
264
+ sceneRotation[0] > maxPolarAngle ||
265
+ this._controls.enableAzimuthRotation === false ||
266
+ this._controls.enablePolarRotation === false) {
267
+ sceneRotation[1] = this._controls.enableAzimuthRotation === false ? 0 : Math.max(minAzimuthAngle, Math.min(maxAzimuthAngle, sceneRotation[1]));
268
+ sceneRotation[0] = this._controls.enablePolarRotation === false ? 0 : Math.max(minPolarAngle, Math.min(maxPolarAngle, sceneRotation[0]));
269
+ }
249
270
  }
250
271
 
251
- return { position, target };
272
+ return { position, target, sceneRotation };
252
273
  }
253
274
 
254
275
  public rotate(x: number, y: number, active: boolean, touch: boolean): void {
255
- if (touch) {
256
- x = x / window.devicePixelRatio;
257
- y = y / window.devicePixelRatio;
258
- }
259
276
 
260
277
  if (!active) {
261
- this._rotateStart = vec2.fromValues(x, y)
278
+ this._rotateStart = vec2.fromValues(x, y);
262
279
  } else {
263
- this._rotateEnd = vec2.fromValues(x, y)
264
- vec2.subtract(this._rotateDelta, this._rotateEnd, this._rotateStart)
265
- vec2.copy(this._rotateStart, this._rotateEnd)
280
+ this._rotateEnd = vec2.fromValues(x, y);
281
+ vec2.subtract(this._rotateDelta, this._rotateEnd, this._rotateStart);
282
+ vec2.copy(this._rotateStart, this._rotateEnd);
266
283
 
267
284
  if (!this._controls.canvas) return;
268
285
  if (this._controls.canvas.clientWidth == 0 || this._controls.canvas.clientHeight == 0) return;
269
286
 
270
287
  const spherical = new Spherical();
271
- let rotationSpeed = this._adjustedSettings.rotationSpeed() * (touch ? this._touchAdjustments.rotationSpeed : 1.0);
272
- spherical.theta -= rotationSpeed * this._rotateDelta[0];
273
- spherical.phi -= rotationSpeed * this._rotateDelta[1];
288
+ const rotationSpeed = this._adjustedSettings.rotationSpeed() * (touch ? this._touchAdjustments.rotationSpeed : 1.0);
289
+ spherical.theta -= rotationSpeed * this._rotateDelta[0] / this._controls.canvas.clientHeight;
290
+ spherical.phi -= rotationSpeed * this._rotateDelta[1] / this._controls.canvas.clientHeight;
274
291
 
275
292
  if (this._damping.rotation.duration > 0) {
276
- let thetaDelta = this._damping.rotation.theta - spherical.theta;
293
+ const thetaDelta = this._damping.rotation.theta - spherical.theta;
277
294
  spherical.theta += thetaDelta * this._adjustedSettings.movementSmoothness();
278
295
 
279
- let phiDelta = this._damping.rotation.phi - spherical.phi;
296
+ const phiDelta = this._damping.rotation.phi - spherical.phi;
280
297
  spherical.phi += phiDelta * this._adjustedSettings.movementSmoothness();
281
298
  }
282
299
 
283
- let offset = this.rotationSphericalToOffset(spherical);
300
+ const offset = this.rotationSphericalToOffset(this._controls.enableTurntableControls ? new Spherical(1.0, spherical.phi, 0) : spherical);
284
301
 
285
- let damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
286
- let framesTheta = (Math.log(1 / Math.abs(spherical.theta)) - 5 * Math.log(10)) / (Math.log(damping));
287
- let framesPhi = (Math.log(1 / Math.abs(spherical.phi)) - 5 * Math.log(10)) / (Math.log(damping));
302
+ const damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
303
+ const framesTheta = (Math.log(1 / Math.abs(spherical.theta)) - 5 * Math.log(10)) / (Math.log(damping));
304
+ const framesPhi = (Math.log(1 / Math.abs(spherical.phi)) - 5 * Math.log(10)) / (Math.log(damping));
288
305
 
289
306
  this._damping.rotation.time = 0;
290
307
  this._damping.rotation.duration = Math.max(framesTheta, framesPhi) * 16.6666;
@@ -295,6 +312,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
295
312
  this._damping.zoom.duration = 0;
296
313
 
297
314
  this._controls.applyPositionVector(offset, true);
315
+ if (this._controls.enableTurntableControls) this._controls.applyRotation([0, spherical.theta], true);
298
316
  }
299
317
  }
300
318
 
@@ -305,7 +323,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
305
323
  this._damping.rotation.duration = 0;
306
324
  }
307
325
 
308
- let damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
326
+ const damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
309
327
 
310
328
  if (this._damping.pan.duration > 0) {
311
329
  if (this._damping.pan.time + time > this._damping.pan.duration) {
@@ -314,9 +332,9 @@ export class CameraControlsLogic implements ICameraControlsLogic {
314
332
  } else {
315
333
  this._damping.pan.time += time;
316
334
 
317
- let frameSinceStart = this._damping.pan.time / 16.6666;
318
- let dampingFrames = Math.pow(damping, frameSinceStart);
319
- let offset = vec3.multiply(vec3.create(), this._damping.pan.offset, vec3.fromValues(dampingFrames, dampingFrames, dampingFrames));
335
+ const frameSinceStart = this._damping.pan.time / 16.6666;
336
+ const dampingFrames = Math.pow(damping, frameSinceStart);
337
+ const offset = vec3.multiply(vec3.create(), this._damping.pan.offset, vec3.fromValues(dampingFrames, dampingFrames, dampingFrames));
320
338
  this._controls.applyTargetVector(offset);
321
339
  this._controls.applyPositionVector(offset);
322
340
  }
@@ -331,12 +349,13 @@ export class CameraControlsLogic implements ICameraControlsLogic {
331
349
  } else {
332
350
  this._damping.rotation.time += time;
333
351
 
334
- let frameSinceStart = this._damping.rotation.time / 16.6666;
335
- let spherical = new Spherical();
352
+ const frameSinceStart = this._damping.rotation.time / 16.6666;
353
+ const spherical = new Spherical();
336
354
  spherical.theta = this._damping.rotation.theta * Math.pow(damping, frameSinceStart);
337
355
  spherical.phi = this._damping.rotation.phi * Math.pow(damping, frameSinceStart);
338
- let offset = this.rotationSphericalToOffset(spherical);
356
+ const offset = this.rotationSphericalToOffset(this._controls.enableTurntableControls ? new Spherical(1.0, spherical.phi, 0) : spherical);
339
357
  this._controls.applyPositionVector(offset);
358
+ if (this._controls.enableTurntableControls) this._controls.applyRotation([0, spherical.theta]);
340
359
  }
341
360
  } else {
342
361
  this._damping.rotation.time = 0;
@@ -349,9 +368,9 @@ export class CameraControlsLogic implements ICameraControlsLogic {
349
368
  } else {
350
369
  this._damping.zoom.time += time;
351
370
 
352
- let frameSinceStart = this._damping.zoom.time / 16.6666;
353
- let delta = this._damping.zoom.delta * Math.pow(damping, frameSinceStart);
354
- let offset = this.zoomDistanceToOffset(delta);
371
+ const frameSinceStart = this._damping.zoom.time / 16.6666;
372
+ const delta = this._damping.zoom.delta * Math.pow(damping, frameSinceStart);
373
+ const offset = this.zoomDistanceToOffset(delta);
355
374
  this._controls.applyPositionVector(offset);
356
375
  }
357
376
  } else {
@@ -359,17 +378,15 @@ export class CameraControlsLogic implements ICameraControlsLogic {
359
378
  }
360
379
 
361
380
  if (this._controls.enableAutoRotation) {
362
- let spherical = new Spherical(1.0, 0.0, -this._adjustedSettings.autoRotationSpeed());
363
- let offset = this.rotationSphericalToOffset(spherical);
381
+ const spherical = new Spherical(1.0, 0.0, -this._adjustedSettings.autoRotationSpeed());
382
+ const offset = this.rotationSphericalToOffset(this._controls.enableTurntableControls ? new Spherical(1.0, spherical.phi, 0) : spherical);
364
383
  this._controls.applyPositionVector(offset);
384
+ if (this._controls.enableTurntableControls) this._controls.applyRotation([0, spherical.theta]);
365
385
  }
366
386
  }
367
387
 
368
388
  public zoom(x: number, y: number, active: boolean, touch: boolean): void {
369
- var distance = Math.sqrt(x * x + y * y);
370
-
371
- if (touch)
372
- distance = distance / window.devicePixelRatio;
389
+ const distance = Math.sqrt(x * x + y * y);
373
390
 
374
391
  if (!active) {
375
392
  this._dollyStart = distance;
@@ -386,10 +403,10 @@ export class CameraControlsLogic implements ICameraControlsLogic {
386
403
  }
387
404
  }
388
405
 
389
- let delta = - this._dollyDelta * this._adjustedSettings.zoomSpeed() * (touch ? this._touchAdjustments.zoomSpeed : 1.0);
406
+ const delta = - this._dollyDelta * this._adjustedSettings.zoomSpeed() * (touch ? this._touchAdjustments.zoomSpeed : 1.0);
390
407
 
391
- let damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
392
- let framesDelta = (Math.log(1 / Math.abs(this._dollyDelta)) - 5 * Math.log(10)) / (Math.log(damping));
408
+ const damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
409
+ const framesDelta = (Math.log(1 / Math.abs(this._dollyDelta)) - 5 * Math.log(10)) / (Math.log(damping));
393
410
  this._damping.zoom.time = 0;
394
411
  this._damping.zoom.duration = framesDelta * 16.6666;
395
412
  this._damping.zoom.delta = delta;
@@ -397,7 +414,7 @@ export class CameraControlsLogic implements ICameraControlsLogic {
397
414
  this._damping.rotation.duration = 0;
398
415
  this._damping.pan.duration = 0;
399
416
 
400
- let offset = this.zoomDistanceToOffset(delta);
417
+ const offset = this.zoomDistanceToOffset(delta);
401
418
  this._controls.applyPositionVector(offset, true);
402
419
  }
403
420
  }
@@ -407,12 +424,15 @@ export class CameraControlsLogic implements ICameraControlsLogic {
407
424
  // #region Private Methods (3)
408
425
 
409
426
  private panDeltaToOffset(panDelta: vec2): vec3 {
410
- let offset = vec3.create();
411
- let panOffset = vec3.create();
427
+ const offset = vec3.create();
428
+ const panOffset = vec3.create();
429
+
430
+ if (!this._controls.canvas) return offset;
431
+ if (this._controls.canvas.clientWidth == 0 || this._controls.canvas.clientHeight == 0) return offset;
412
432
 
413
433
  // perspective
414
434
  vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
415
- var targetDistance = vec3.length(offset);
435
+ let targetDistance = vec3.length(offset);
416
436
 
417
437
  // half of the fov is center to top of screen
418
438
  targetDistance *= Math.tan((((<PerspectiveCamera>this._controls.camera).fov / 2) * Math.PI) / 180.0);
@@ -422,13 +442,13 @@ export class CameraControlsLogic implements ICameraControlsLogic {
422
442
  const mat = mat4.targetTo(mat4.create(), this._controls.camera.position, this._controls.camera.target, vec3.fromValues(0, 0, 1));
423
443
 
424
444
  const v1 = vec3.fromValues(mat[0], mat[1], mat[2]);
425
- const scalar1 = -(2 * panDelta[0] * targetDistance);
445
+ const scalar1 = -(2 * panDelta[0] * targetDistance / this._controls.canvas?.clientHeight);
426
446
  vec3.multiply(v1, v1, vec3.fromValues(scalar1, scalar1, scalar1));
427
447
  vec3.add(panOffset, panOffset, v1);
428
448
 
429
449
  // // up
430
- const v2 = vec3.fromValues(mat[4], mat[5], mat[6])
431
- const scalar2 = 2 * panDelta[1] * targetDistance;
450
+ const v2 = vec3.fromValues(mat[4], mat[5], mat[6]);
451
+ const scalar2 = 2 * panDelta[1] * targetDistance / this._controls.canvas?.clientHeight;
432
452
  vec3.multiply(v2, v2, vec3.fromValues(scalar2, scalar2, scalar2));
433
453
  vec3.add(panOffset, panOffset, v2);
434
454
 
@@ -439,13 +459,13 @@ export class CameraControlsLogic implements ICameraControlsLogic {
439
459
  let offset = vec3.create();
440
460
  vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
441
461
  vec3.transformQuat(offset, offset, this._quat);
442
- let spherical = new Spherical().fromVec3(offset);
462
+ const spherical = new Spherical().fromVec3(offset);
443
463
 
444
464
  spherical.theta += s.theta;
445
465
  spherical.phi += s.phi;
446
466
 
447
- let minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
448
- maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
467
+ const minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
468
+ maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
449
469
 
450
470
  if (spherical.theta > Math.PI) {
451
471
  spherical.theta -= 2 * Math.PI;
@@ -462,17 +482,16 @@ export class CameraControlsLogic implements ICameraControlsLogic {
462
482
  spherical.makeSafe();
463
483
  offset = spherical.toVec3();
464
484
  offset = vec3.transformQuat(vec3.create(), offset, this._quatInverse);
465
- offset = vec3.add(vec3.create(), offset, this._controls.getTargetWithManualUpdates())
485
+ offset = vec3.add(vec3.create(), offset, this._controls.getTargetWithManualUpdates());
466
486
  offset = vec3.subtract(vec3.create(), offset, this._controls.getPositionWithManualUpdates());
467
487
  return vec3.clone(offset);
468
-
469
488
  }
470
489
 
471
490
  private zoomDistanceToOffset(distance: number): vec3 {
472
- let offset = vec3.create();
491
+ const offset = vec3.create();
473
492
  vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
474
493
  return vec3.multiply(vec3.create(), offset, vec3.fromValues(distance, distance, distance));
475
494
  }
476
495
 
477
496
  // #endregion Private Methods (3)
478
- };
497
+ }
@@ -1,16 +1,28 @@
1
- import { mat4, vec2, vec3 } from 'gl-matrix'
2
- import { ITreeNode, ITreeNodeData } from '@shapediver/viewer.shared.node-tree'
1
+ /* eslint-disable @typescript-eslint/ban-types */
2
+ import { vec2, vec3 } from 'gl-matrix';
3
+ import { ITreeNode, ITreeNodeData } from '@shapediver/viewer.shared.node-tree';
3
4
 
4
- import { ICameraControls } from '../controls/ICameraControls'
5
- import { CAMERA_TYPE } from '../ICameraEngine'
6
- import { IBox } from '@shapediver/viewer.shared.math'
7
- import { SettingsEngine } from '@shapediver/viewer.shared.services'
5
+ import { ICameraControls } from '../controls/ICameraControls';
6
+ import { CAMERA_TYPE } from '../ICameraEngine';
7
+ import { IBox } from '@shapediver/viewer.shared.math';
8
+ import { SettingsEngine } from '@shapediver/viewer.shared.services';
8
9
 
9
10
  export interface ICameraOptions {
11
+ // #region Properties (4)
12
+
13
+ /**
14
+ * The coordinate type of the camera interpolation. (default: 'cylindrical')
15
+ */
16
+ coordinates?: 'spherical' | 'linear' | 'cylindrical';
17
+ /**
18
+ * The duration of the camera movement. (default: cameraMovementDuration set in the settings)
19
+ * When set to 0, the camera is immediately updated to the specified position and target.
20
+ */
21
+ duration?: number;
10
22
  /**
11
23
  * The easing type of the camera interpolation. (default: 'Quadratic.InOut')
12
24
  */
13
- easing?: 'Linear.None' |
25
+ easing?: 'Linear.None' |
14
26
  'Quadratic.In' | 'Quadratic.Out' | 'Quadratic.InOut' |
15
27
  'Cubic.In' | 'Cubic.Out' | 'Cubic.InOut' |
16
28
  'Quartic.In' | 'Quartic.Out' | 'Quartic.InOut' |
@@ -20,25 +32,17 @@ export interface ICameraOptions {
20
32
  'Circular.In' | 'Circular.Out' | 'Circular.InOut' |
21
33
  'Elastic.In' | 'Elastic.Out' | 'Elastic.InOut' |
22
34
  'Back.In' | 'Back.Out' | 'Back.InOut' |
23
- 'Bounce.In' | 'Bounce.Out' | 'Bounce.InOut' |
24
- Function;
25
- /**
26
- * The duration of the camera movement. (default: cameraMovementDuration set in the settings)
27
- * When set to 0, the camera is immediately updated to the specified position and target.
28
- */
29
- duration?: number;
30
- /**
31
- * The coordinate type of the camera interpolation. (default: 'cylindrical')
32
- */
33
- coordinates?: 'spherical' | 'linear' | 'cylindrical';
35
+ 'Bounce.In' | 'Bounce.Out' | 'Bounce.InOut' | Function;
34
36
  /**
35
37
  * The interpolation type of the camera interpolation. (default: 'CatmullRom')
36
38
  */
37
39
  interpolation?: 'Linear' | 'Bezier' | 'CatmullRom' | Function
40
+
41
+ // #endregion Properties (4)
38
42
  }
39
43
 
40
44
  export interface ICamera extends ITreeNodeData {
41
- // #region Properties (13)
45
+ // #region Properties (20)
42
46
 
43
47
  readonly controls: ICameraControls;
44
48
  readonly id: string;
@@ -51,28 +55,29 @@ export interface ICamera extends ITreeNodeData {
51
55
  defaultPosition: vec3;
52
56
  defaultTarget: vec3;
53
57
  enableCameraControls: boolean;
54
- position: vec3;
55
58
  name?: string;
56
59
  node?: ITreeNode;
57
60
  order?: number;
61
+ position: vec3;
58
62
  revertAtMouseUp: boolean;
59
63
  revertAtMouseUpDuration: number;
64
+ sceneRotation: vec2;
60
65
  target: vec3;
61
66
  useNodeData: boolean;
62
67
  zoomExtentsFactor: number;
63
68
 
64
- // #endregion Properties (13)
69
+ // #endregion Properties (20)
65
70
 
66
- // #region Public Methods (6)
71
+ // #region Public Methods (8)
67
72
 
68
73
  animate(path: { position: vec3, target: vec3 }[], options?: ICameraOptions): Promise<boolean>;
69
74
  applySettings(settingsEngine: SettingsEngine): void;
70
- reset(options?: ICameraOptions): Promise<boolean>;
71
- set(position: vec3, target: vec3, options?: ICameraOptions): Promise<boolean>;
72
- zoomTo(zoomTarget?: IBox, options?: ICameraOptions): Promise<boolean>;
73
75
  calculateZoomTo(zoomTarget?: IBox, startingPosition?: vec3, startingTarget?: vec3): { position: vec3; target: vec3; };
74
76
  project(p: vec3): vec2;
77
+ reset(options?: ICameraOptions): Promise<boolean>;
78
+ set(position: vec3, target: vec3, options?: ICameraOptions): Promise<boolean>;
75
79
  unproject(p: vec3): vec3;
80
+ zoomTo(zoomTarget?: IBox, options?: ICameraOptions): Promise<boolean>;
76
81
 
77
- // #endregion Public Methods (6)
82
+ // #endregion Public Methods (8)
78
83
  }
@@ -1,6 +1,6 @@
1
- import { mat4, vec3 } from "gl-matrix";
2
- import { ICamera, ICameraOptions } from "../camera/ICamera";
3
- import { ICameraControlsEventDistribution } from "./ICameraControlsEventDistribution";
1
+ import { mat4, vec2, vec3 } from 'gl-matrix';
2
+ import { ICamera, ICameraOptions } from '../camera/ICamera';
3
+ import { ICameraControlsEventDistribution } from './ICameraControlsEventDistribution';
4
4
 
5
5
  export interface ICameraControls {
6
6
  // #region Properties (6)
@@ -23,7 +23,7 @@ export interface ICameraControls {
23
23
  applyTargetMatrix(matrix: mat4, manualInteraction?: boolean | undefined): void;
24
24
  applyTargetVector(vector: vec3, manualInteraction?: boolean | undefined): void;
25
25
  applyUpMatrix(matrix: mat4, manualInteraction?: boolean | undefined): void;
26
- assignViewer(viewportId: string, canvas: HTMLCanvasElement):void;
26
+ assignViewer(viewportId: string, canvas: HTMLCanvasElement): void;
27
27
  getPositionWithManualUpdates(): vec3;
28
28
  getPositionWithUpdates(): vec3;
29
29
  getTargetWithManualUpdates(): vec3;
@@ -31,7 +31,7 @@ export interface ICameraControls {
31
31
  isMoving(): boolean;
32
32
  isWithinRestrictions(position: vec3, target: vec3): boolean;
33
33
  reset(): void;
34
- update(time: number): { position: vec3, target: vec3 };
34
+ update(time: number): { position: vec3, target: vec3, sceneRotation: vec2 };
35
35
 
36
36
  // #endregion Public Methods (15)
37
37
  }
@@ -1,8 +1,12 @@
1
- import { vec3 } from 'gl-matrix'
1
+ import { vec2, vec3 } from 'gl-matrix';
2
2
 
3
3
  export interface ICameraControlsLogic {
4
+ // #region Public Methods (4)
5
+
4
6
  isWithinRestrictions(position: any, target: any): boolean;
5
7
  reset(): void;
6
- restrict(p: vec3, t: vec3): { position: vec3, target: vec3 };
8
+ restrict(p: vec3, t: vec3, s?: vec2): { position: vec3, target: vec3, sceneRotation?: vec2 };
7
9
  update(time: number, manualInteraction: boolean): void
10
+
11
+ // #endregion Public Methods (4)
8
12
  }
@@ -1,16 +1,21 @@
1
- import { vec3 } from 'gl-matrix'
1
+ import { vec3 } from 'gl-matrix';
2
2
 
3
- import { ICameraControls } from './ICameraControls'
3
+ import { ICameraControls } from './ICameraControls';
4
4
 
5
5
  export interface IPerspectiveCameraControls extends ICameraControls {
6
+ // #region Properties (23)
7
+
6
8
  autoRotationSpeed: number;
7
9
  cubePositionRestriction: { min: vec3, max: vec3 };
8
10
  cubeTargetRestriction: { min: vec3, max: vec3 };
9
11
  damping: number;
10
12
  enableAutoRotation: boolean;
13
+ enableAzimuthRotation: boolean;
11
14
  enableKeyPan: boolean;
12
15
  enablePan: boolean;
16
+ enablePolarRotation: boolean;
13
17
  enableRotation: boolean;
18
+ enableTurntableControls: boolean;
14
19
  enableZoom: boolean;
15
20
  input: { keys: { up: number, down: number, left: number, right: number }, mouse: { rotate: number, zoom: number, pan: number }, touch: { rotate: number, zoom: number, pan: number } };
16
21
  keyPanSpeed: number;
@@ -20,6 +25,9 @@ export interface IPerspectiveCameraControls extends ICameraControls {
20
25
  rotationSpeed: number;
21
26
  spherePositionRestriction: { center: vec3, radius: number };
22
27
  sphereTargetRestriction: { center: vec3, radius: number };
28
+ turntableCenter: vec3;
23
29
  zoomRestriction: { minDistance: number, maxDistance: number };
24
30
  zoomSpeed: number;
31
+
32
+ // #endregion Properties (23)
25
33
  }