@shapediver/viewer.rendering-engine.camera-engine 1.15.7 → 2.0.2

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 (81) hide show
  1. package/dist/implementation/CameraEngine.d.ts +15 -12
  2. package/dist/implementation/CameraEngine.d.ts.map +1 -1
  3. package/dist/implementation/CameraEngine.js +53 -40
  4. package/dist/implementation/CameraEngine.js.map +1 -1
  5. package/dist/implementation/camera/AbstractCamera.d.ts +23 -46
  6. package/dist/implementation/camera/AbstractCamera.d.ts.map +1 -1
  7. package/dist/implementation/camera/AbstractCamera.js +11 -6
  8. package/dist/implementation/camera/AbstractCamera.js.map +1 -1
  9. package/dist/implementation/camera/OrthographicCamera.d.ts +10 -5
  10. package/dist/implementation/camera/OrthographicCamera.d.ts.map +1 -1
  11. package/dist/implementation/camera/OrthographicCamera.js +29 -15
  12. package/dist/implementation/camera/OrthographicCamera.js.map +1 -1
  13. package/dist/implementation/camera/PerspectiveCamera.d.ts +10 -4
  14. package/dist/implementation/camera/PerspectiveCamera.d.ts.map +1 -1
  15. package/dist/implementation/camera/PerspectiveCamera.js +27 -15
  16. package/dist/implementation/camera/PerspectiveCamera.js.map +1 -1
  17. package/dist/implementation/controls/AbstractCameraControls.d.ts +6 -12
  18. package/dist/implementation/controls/AbstractCameraControls.d.ts.map +1 -1
  19. package/dist/implementation/controls/AbstractCameraControls.js +5 -5
  20. package/dist/implementation/controls/AbstractCameraControls.js.map +1 -1
  21. package/dist/implementation/controls/OrthographicCameraControls.d.ts +2 -2
  22. package/dist/implementation/controls/OrthographicCameraControls.d.ts.map +1 -1
  23. package/dist/implementation/controls/OrthographicCameraControls.js +3 -4
  24. package/dist/implementation/controls/OrthographicCameraControls.js.map +1 -1
  25. package/dist/implementation/controls/PerspectiveCameraControls.d.ts +2 -2
  26. package/dist/implementation/controls/PerspectiveCameraControls.d.ts.map +1 -1
  27. package/dist/implementation/controls/PerspectiveCameraControls.js +3 -4
  28. package/dist/implementation/controls/PerspectiveCameraControls.js.map +1 -1
  29. package/dist/implementation/interpolation/CameraInterpolationManager.d.ts +2 -8
  30. package/dist/implementation/interpolation/CameraInterpolationManager.d.ts.map +1 -1
  31. package/dist/implementation/interpolation/CameraInterpolationManager.js +0 -1
  32. package/dist/implementation/interpolation/CameraInterpolationManager.js.map +1 -1
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/interfaces/ICameraEngine.d.ts +4 -3
  38. package/dist/interfaces/ICameraEngine.d.ts.map +1 -1
  39. package/dist/interfaces/ICameraEngine.js +6 -6
  40. package/dist/interfaces/ICameraEngine.js.map +1 -1
  41. package/dist/interfaces/camera/ICamera.d.ts +37 -33
  42. package/dist/interfaces/camera/ICamera.d.ts.map +1 -1
  43. package/dist/interfaces/camera/IOrthographicCamera.d.ts +1 -0
  44. package/dist/interfaces/camera/IOrthographicCamera.d.ts.map +1 -1
  45. package/dist/interfaces/camera/IPerspectiveCamera.d.ts +1 -0
  46. package/dist/interfaces/camera/IPerspectiveCamera.d.ts.map +1 -1
  47. package/dist/interfaces/controls/ICameraControls.d.ts +29 -0
  48. package/dist/interfaces/controls/ICameraControls.d.ts.map +1 -1
  49. package/dist/interfaces/controls/ICameraControlsUsage.d.ts +2 -9
  50. package/dist/interfaces/controls/ICameraControlsUsage.d.ts.map +1 -1
  51. package/package.json +15 -12
  52. package/src/implementation/CameraEngine.ts +324 -0
  53. package/src/implementation/camera/AbstractCamera.ts +314 -0
  54. package/src/implementation/camera/OrthographicCamera.ts +276 -0
  55. package/src/implementation/camera/PerspectiveCamera.ts +250 -0
  56. package/src/implementation/controls/AbstractCameraControls.ts +329 -0
  57. package/src/implementation/controls/OrthographicCameraControls.ts +131 -0
  58. package/src/implementation/controls/PerspectiveCameraControls.ts +263 -0
  59. package/src/implementation/controls/orthographic/CameraControlsEventDistribution.ts +205 -0
  60. package/src/implementation/controls/orthographic/CameraControlsLogic.ts +279 -0
  61. package/src/implementation/controls/perspective/CameraControlsEventDistribution.ts +221 -0
  62. package/src/implementation/controls/perspective/CameraControlsLogic.ts +478 -0
  63. package/src/implementation/interpolation/CameraInterpolationManager.ts +154 -0
  64. package/src/implementation/interpolation/interpolationMethods/CameraCylindricalInterpolation.ts +86 -0
  65. package/src/implementation/interpolation/interpolationMethods/CameraLinearInterpolation.ts +42 -0
  66. package/src/implementation/interpolation/interpolationMethods/CameraMultipleInterpolation.ts +63 -0
  67. package/src/implementation/interpolation/interpolationMethods/CameraOrthographicInterpolation.ts +43 -0
  68. package/src/implementation/interpolation/interpolationMethods/CameraSphericalInterpolation.ts +67 -0
  69. package/src/index.ts +31 -0
  70. package/src/interfaces/ICameraEngine.ts +19 -0
  71. package/src/interfaces/camera/ICamera.ts +77 -0
  72. package/src/interfaces/camera/IOrthographicCamera.ts +18 -0
  73. package/src/interfaces/camera/IPerspectiveCamera.ts +10 -0
  74. package/src/interfaces/controls/ICameraControls.ts +37 -0
  75. package/src/interfaces/controls/ICameraControlsEventDistribution.ts +8 -0
  76. package/src/interfaces/controls/ICameraControlsLogic.ts +8 -0
  77. package/src/interfaces/controls/ICameraControlsUsage.ts +30 -0
  78. package/src/interfaces/controls/IOrthographicCameraControls.ts +13 -0
  79. package/src/interfaces/controls/IPerspectiveCameraControls.ts +25 -0
  80. package/src/interfaces/interpolation/ICameraInterpolation.ts +5 -0
  81. package/tsconfig.json +17 -0
@@ -0,0 +1,478 @@
1
+ import { mat4, quat, vec2, vec3 } from 'gl-matrix'
2
+ import { Box, Sphere, Spherical } from '@shapediver/viewer.shared.math'
3
+
4
+ import { PerspectiveCameraControls } from '../PerspectiveCameraControls'
5
+ import { ICameraControlsLogic } from '../../../interfaces/controls/ICameraControlsLogic'
6
+ import { PerspectiveCamera } from '../../camera/PerspectiveCamera'
7
+
8
+ export class CameraControlsLogic implements ICameraControlsLogic {
9
+ // #region Properties (15)
10
+
11
+ private _adjustedSettings = {
12
+ autoRotationSpeed: () => this._controls.autoRotationSpeed * this._settingsAdjustments.autoRotationSpeed,
13
+ damping: () => this._controls.damping * this._settingsAdjustments.damping,
14
+ movementSmoothness: () => this._controls.movementSmoothness * this._settingsAdjustments.movementSmoothness,
15
+ panSpeed: () => this._controls.panSpeed * this._settingsAdjustments.panSpeed,
16
+ rotationSpeed: () => this._controls.rotationSpeed * this._settingsAdjustments.rotationSpeed,
17
+ zoomSpeed: () => this._controls.zoomSpeed * this._settingsAdjustments.zoomSpeed,
18
+ };
19
+ private _damping = {
20
+ rotation: {
21
+ time: 0,
22
+ duration: 0,
23
+ theta: 0,
24
+ phi: 0
25
+ },
26
+ zoom: {
27
+ time: 0,
28
+ duration: 0,
29
+ delta: 0
30
+ },
31
+ pan: {
32
+ time: 0,
33
+ duration: 0,
34
+ offset: vec3.create()
35
+ },
36
+ };
37
+ private _dollyDelta = 0;
38
+ private _dollyEnd = 0;
39
+ private _dollyStart = 0;
40
+ private _panDelta = vec2.create();
41
+ private _panEnd = vec2.create();
42
+ private _panStart = vec2.create();
43
+ private _quat: quat;
44
+ private _quatInverse: quat;
45
+ private _rotateDelta = vec2.create();
46
+ private _rotateEnd = vec2.create();
47
+ private _rotateStart = vec2.create();
48
+ private _settingsAdjustments = {
49
+ autoRotationSpeed: 2 * Math.PI / 60 / 60,
50
+ damping: 1.0,
51
+ movementSmoothness: 1.0,
52
+ panSpeed: 2.0,
53
+ rotationSpeed: Math.PI,
54
+ zoomSpeed: 0.025,
55
+ };
56
+ private _touchAdjustments = {
57
+ autoRotationSpeed: 1.0,
58
+ damping: 1.0,
59
+ movementSmoothness: 1.0,
60
+ panSpeed: 1.5,
61
+ rotationSpeed: 2.0,
62
+ zoomSpeed: 100.0,
63
+ };
64
+
65
+ // #endregion Properties (15)
66
+
67
+ // #region Constructors (1)
68
+
69
+ constructor(private readonly _controls: PerspectiveCameraControls) {
70
+ this._quat = quat.fromValues(-Math.sin(Math.PI / 4), 0, 0, Math.sin(Math.PI / 4));
71
+ this._quatInverse = quat.fromValues(Math.sin(Math.PI / 4), 0, 0, Math.sin(Math.PI / 4));
72
+ }
73
+
74
+ // #endregion Constructors (1)
75
+
76
+ // #region Public Methods (7)
77
+
78
+ public isWithinRestrictions(position: vec3, target: vec3): boolean {
79
+ let pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
80
+ pSphere = new Sphere(this._controls.spherePositionRestriction.center, this._controls.spherePositionRestriction.radius),
81
+ tBox = new Box(this._controls.cubeTargetRestriction.min, this._controls.cubeTargetRestriction.max),
82
+ tSphere = new Sphere(this._controls.sphereTargetRestriction.center, this._controls.sphereTargetRestriction.radius);
83
+
84
+ if (!(pBox.containsPoint(position) && pSphere.containsPoint(position))) return false;
85
+ if (!(tBox.containsPoint(target) && tSphere.containsPoint(target))) return false;
86
+
87
+ let currentDistance = vec3.distance(position, target);
88
+ if (currentDistance > this._controls.zoomRestriction.maxDistance || currentDistance < this._controls.zoomRestriction.minDistance) return false;
89
+
90
+ let minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
91
+ maxPolarAngle = this._controls.rotationRestriction.maxPolarAngle * (Math.PI / 180),
92
+ minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
93
+ maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
94
+
95
+ if (minAzimuthAngle !== -Infinity ||
96
+ maxAzimuthAngle !== Infinity ||
97
+ minPolarAngle !== 0 ||
98
+ maxPolarAngle !== 180) {
99
+ let offset = vec3.sub(vec3.create(), position, target);
100
+ vec3.transformQuat(offset, offset, this._quat);
101
+ const spherical = new Spherical().fromVec3(offset);
102
+
103
+ if (spherical.theta < minAzimuthAngle ||
104
+ spherical.theta > maxAzimuthAngle ||
105
+ spherical.phi < minPolarAngle ||
106
+ spherical.phi > maxPolarAngle) {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ return true;
112
+ }
113
+
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
+
120
+ if (!active) {
121
+ this._panStart = vec2.fromValues(x, y);
122
+ } else {
123
+ this._panEnd = vec2.fromValues(x, y);
124
+ vec2.sub(this._panDelta, this._panEnd, this._panStart);
125
+ if (this._panDelta[0] === 0 && this._panDelta[1] === 0) return;
126
+
127
+ vec2.copy(this._panStart, this._panEnd);
128
+
129
+ 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)));
131
+
132
+ if (this._damping.pan.duration > 0) {
133
+ if (offset[0] < 0) {
134
+ offset[0] = Math.min(offset[0], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[0]);
135
+ } else {
136
+ offset[0] = Math.max(offset[0], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[0]);
137
+ }
138
+ if (offset[1] < 0) {
139
+ offset[1] = Math.min(offset[1], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[1]);
140
+ } else {
141
+ offset[1] = Math.max(offset[1], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[1]);
142
+ }
143
+ if (offset[2] < 0) {
144
+ offset[2] = Math.min(offset[2], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[2]);
145
+ } else {
146
+ offset[2] = Math.max(offset[2], this._adjustedSettings.movementSmoothness() * this._damping.pan.offset[2]);
147
+ }
148
+ }
149
+
150
+ let damping = 1 - Math.max(0.01, Math.min(0.99, this._adjustedSettings.damping()));
151
+
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));
155
+ this._damping.pan.time = 0;
156
+ this._damping.pan.duration = Math.max(framesOffsetX, Math.max(framesOffsetY, framesOffsetZ)) * 16.6666;
157
+ this._damping.pan.offset = vec3.clone(offset);
158
+
159
+ this._damping.rotation.duration = 0;
160
+ this._damping.zoom.duration = 0;
161
+
162
+ this._controls.applyTargetVector(offset, true);
163
+ this._controls.applyPositionVector(offset, true);
164
+ }
165
+ }
166
+
167
+ public reset() {
168
+ this._damping = {
169
+ rotation: {
170
+ time: 0,
171
+ duration: 0,
172
+ theta: 0,
173
+ phi: 0
174
+ },
175
+ zoom: {
176
+ time: 0,
177
+ duration: 0,
178
+ delta: 0
179
+ },
180
+ pan: {
181
+ time: 0,
182
+ duration: 0,
183
+ offset: vec3.create()
184
+ },
185
+ };
186
+ this._dollyDelta = 0;
187
+ this._dollyEnd = 0;
188
+ this._dollyStart = 0;
189
+ this._panDelta = vec2.create();
190
+ this._panEnd = vec2.create();
191
+ this._panStart = vec2.create();
192
+ this._rotateDelta = vec2.create();
193
+ this._rotateEnd = vec2.create();
194
+ this._rotateStart = vec2.create();
195
+ }
196
+
197
+ public restrict(position: vec3, target: vec3): { position: vec3, target: vec3 } {
198
+ let pBox = new Box(this._controls.cubePositionRestriction.min, this._controls.cubePositionRestriction.max),
199
+ pSphere = new Sphere(this._controls.spherePositionRestriction.center, this._controls.spherePositionRestriction.radius),
200
+ tBox = new Box(this._controls.cubeTargetRestriction.min, this._controls.cubeTargetRestriction.max),
201
+ tSphere = new Sphere(this._controls.sphereTargetRestriction.center, this._controls.sphereTargetRestriction.radius);
202
+
203
+ if (!pBox.containsPoint(position))
204
+ position = pBox.clampPoint(position);
205
+
206
+ if (!pSphere.containsPoint(position))
207
+ position = pSphere.clampPoint(position);
208
+
209
+ if (!tBox.containsPoint(target))
210
+ target = tBox.clampPoint(target);
211
+
212
+ if (!tSphere.containsPoint(target))
213
+ target = tSphere.clampPoint(target);
214
+
215
+ // zoom restrictions
216
+ let currentDistance = vec3.distance(position, target);
217
+ 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));
220
+ vec3.add(position, vec3.multiply(position, direction, vec3.fromValues(distance, distance, distance)), target);
221
+ }
222
+
223
+ // angle restrictions
224
+ let minPolarAngle = this._controls.rotationRestriction.minPolarAngle * (Math.PI / 180),
225
+ maxPolarAngle = this._controls.rotationRestriction.maxPolarAngle * (Math.PI / 180),
226
+ minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
227
+ maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
228
+
229
+ if (minAzimuthAngle !== -Infinity ||
230
+ maxAzimuthAngle !== Infinity ||
231
+ minPolarAngle !== 0 ||
232
+ maxPolarAngle !== 180) {
233
+ let offset = vec3.subtract(vec3.create(), position, target);
234
+ vec3.transformQuat(offset, offset, this._quat);
235
+
236
+ const spherical = new Spherical().fromVec3(offset);
237
+
238
+ if (spherical.theta < minAzimuthAngle ||
239
+ spherical.theta > maxAzimuthAngle ||
240
+ spherical.phi < minPolarAngle ||
241
+ spherical.phi > maxPolarAngle) {
242
+ spherical.theta = Math.max(minAzimuthAngle, Math.min(maxAzimuthAngle, spherical.theta));
243
+ spherical.phi = Math.max(minPolarAngle, Math.min(maxPolarAngle, spherical.phi));
244
+ spherical.makeSafe();
245
+ offset = spherical.toVec3();
246
+ vec3.transformQuat(offset, offset, this._quatInverse);
247
+ vec3.add(position, offset, target);
248
+ }
249
+ }
250
+
251
+ return { position, target };
252
+ }
253
+
254
+ 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
+
260
+ if (!active) {
261
+ this._rotateStart = vec2.fromValues(x, y)
262
+ } else {
263
+ this._rotateEnd = vec2.fromValues(x, y)
264
+ vec2.subtract(this._rotateDelta, this._rotateEnd, this._rotateStart)
265
+ vec2.copy(this._rotateStart, this._rotateEnd)
266
+
267
+ if (!this._controls.canvas) return;
268
+ if (this._controls.canvas.clientWidth == 0 || this._controls.canvas.clientHeight == 0) return;
269
+
270
+ 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];
274
+
275
+ if (this._damping.rotation.duration > 0) {
276
+ let thetaDelta = this._damping.rotation.theta - spherical.theta;
277
+ spherical.theta += thetaDelta * this._adjustedSettings.movementSmoothness();
278
+
279
+ let phiDelta = this._damping.rotation.phi - spherical.phi;
280
+ spherical.phi += phiDelta * this._adjustedSettings.movementSmoothness();
281
+ }
282
+
283
+ let offset = this.rotationSphericalToOffset(spherical);
284
+
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));
288
+
289
+ this._damping.rotation.time = 0;
290
+ this._damping.rotation.duration = Math.max(framesTheta, framesPhi) * 16.6666;
291
+ this._damping.rotation.theta = spherical.theta;
292
+ this._damping.rotation.phi = spherical.phi;
293
+
294
+ this._damping.pan.duration = 0;
295
+ this._damping.zoom.duration = 0;
296
+
297
+ this._controls.applyPositionVector(offset, true);
298
+ }
299
+ }
300
+
301
+ public update(time: number, manualInteraction: boolean): void {
302
+ if (manualInteraction === true) {
303
+ this._damping.zoom.duration = 0;
304
+ this._damping.pan.duration = 0;
305
+ this._damping.rotation.duration = 0;
306
+ }
307
+
308
+ let damping = 1 - Math.max(0.01, Math.min(1, this._adjustedSettings.damping()));
309
+
310
+ if (this._damping.pan.duration > 0) {
311
+ if (this._damping.pan.time + time > this._damping.pan.duration) {
312
+ this._damping.pan.time = this._damping.pan.duration;
313
+ this._damping.pan.duration = 0;
314
+ } else {
315
+ this._damping.pan.time += time;
316
+
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));
320
+ this._controls.applyTargetVector(offset);
321
+ this._controls.applyPositionVector(offset);
322
+ }
323
+ } else {
324
+ this._damping.pan.time = 0;
325
+ }
326
+
327
+ if (this._damping.rotation.duration > 0) {
328
+ if (this._damping.rotation.time + time > this._damping.rotation.duration) {
329
+ this._damping.rotation.time = this._damping.rotation.duration;
330
+ this._damping.rotation.duration = 0;
331
+ } else {
332
+ this._damping.rotation.time += time;
333
+
334
+ let frameSinceStart = this._damping.rotation.time / 16.6666;
335
+ let spherical = new Spherical();
336
+ spherical.theta = this._damping.rotation.theta * Math.pow(damping, frameSinceStart);
337
+ spherical.phi = this._damping.rotation.phi * Math.pow(damping, frameSinceStart);
338
+ let offset = this.rotationSphericalToOffset(spherical);
339
+ this._controls.applyPositionVector(offset);
340
+ }
341
+ } else {
342
+ this._damping.rotation.time = 0;
343
+ }
344
+
345
+ if (this._damping.zoom.duration > 0) {
346
+ if (this._damping.zoom.time + time > this._damping.zoom.duration) {
347
+ this._damping.zoom.time = this._damping.zoom.duration;
348
+ this._damping.zoom.duration = 0;
349
+ } else {
350
+ this._damping.zoom.time += time;
351
+
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);
355
+ this._controls.applyPositionVector(offset);
356
+ }
357
+ } else {
358
+ this._damping.zoom.time = 0;
359
+ }
360
+
361
+ if (this._controls.enableAutoRotation) {
362
+ let spherical = new Spherical(1.0, 0.0, -this._adjustedSettings.autoRotationSpeed());
363
+ let offset = this.rotationSphericalToOffset(spherical);
364
+ this._controls.applyPositionVector(offset);
365
+ }
366
+ }
367
+
368
+ 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;
373
+
374
+ if (!active) {
375
+ this._dollyStart = distance;
376
+ } else {
377
+ this._dollyEnd = distance;
378
+ this._dollyDelta = this._dollyEnd - this._dollyStart;
379
+ this._dollyStart = this._dollyEnd;
380
+
381
+ if (this._damping.zoom.duration > 0) {
382
+ if (this._dollyDelta < 0) {
383
+ this._dollyDelta = Math.min(this._dollyDelta, this._adjustedSettings.movementSmoothness() * this._damping.zoom.delta);
384
+ } else {
385
+ this._dollyDelta = Math.max(this._dollyDelta, this._adjustedSettings.movementSmoothness() * this._damping.zoom.delta);
386
+ }
387
+ }
388
+
389
+ let delta = - this._dollyDelta * this._adjustedSettings.zoomSpeed() * (touch ? this._touchAdjustments.zoomSpeed : 1.0);
390
+
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));
393
+ this._damping.zoom.time = 0;
394
+ this._damping.zoom.duration = framesDelta * 16.6666;
395
+ this._damping.zoom.delta = delta;
396
+
397
+ this._damping.rotation.duration = 0;
398
+ this._damping.pan.duration = 0;
399
+
400
+ let offset = this.zoomDistanceToOffset(delta);
401
+ this._controls.applyPositionVector(offset, true);
402
+ }
403
+ }
404
+
405
+ // #endregion Public Methods (7)
406
+
407
+ // #region Private Methods (3)
408
+
409
+ private panDeltaToOffset(panDelta: vec2): vec3 {
410
+ let offset = vec3.create();
411
+ let panOffset = vec3.create();
412
+
413
+ // perspective
414
+ vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
415
+ var targetDistance = vec3.length(offset);
416
+
417
+ // half of the fov is center to top of screen
418
+ targetDistance *= Math.tan((((<PerspectiveCamera>this._controls.camera).fov / 2) * Math.PI) / 180.0);
419
+
420
+ // we use only clientHeight here so aspect ratio does not distort speed
421
+ // left
422
+ const mat = mat4.targetTo(mat4.create(), this._controls.camera.position, this._controls.camera.target, vec3.fromValues(0, 0, 1));
423
+
424
+ const v1 = vec3.fromValues(mat[0], mat[1], mat[2]);
425
+ const scalar1 = -(2 * panDelta[0] * targetDistance);
426
+ vec3.multiply(v1, v1, vec3.fromValues(scalar1, scalar1, scalar1));
427
+ vec3.add(panOffset, panOffset, v1);
428
+
429
+ // // up
430
+ const v2 = vec3.fromValues(mat[4], mat[5], mat[6])
431
+ const scalar2 = 2 * panDelta[1] * targetDistance;
432
+ vec3.multiply(v2, v2, vec3.fromValues(scalar2, scalar2, scalar2));
433
+ vec3.add(panOffset, panOffset, v2);
434
+
435
+ return vec3.clone(panOffset);
436
+ }
437
+
438
+ private rotationSphericalToOffset(s: Spherical): vec3 {
439
+ let offset = vec3.create();
440
+ vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
441
+ vec3.transformQuat(offset, offset, this._quat);
442
+ let spherical = new Spherical().fromVec3(offset);
443
+
444
+ spherical.theta += s.theta;
445
+ spherical.phi += s.phi;
446
+
447
+ let minAzimuthAngle = this._controls.rotationRestriction.minAzimuthAngle * (Math.PI / 180),
448
+ maxAzimuthAngle = this._controls.rotationRestriction.maxAzimuthAngle * (Math.PI / 180);
449
+
450
+ if (spherical.theta > Math.PI) {
451
+ spherical.theta -= 2 * Math.PI;
452
+ if (minAzimuthAngle > spherical.theta) {
453
+ spherical.theta += 2 * Math.PI;
454
+ }
455
+ } else if (spherical.theta < -Math.PI) {
456
+ spherical.theta += 2 * Math.PI;
457
+ if (maxAzimuthAngle < spherical.theta) {
458
+ spherical.theta -= 2 * Math.PI;
459
+ }
460
+ }
461
+
462
+ spherical.makeSafe();
463
+ offset = spherical.toVec3();
464
+ offset = vec3.transformQuat(vec3.create(), offset, this._quatInverse);
465
+ offset = vec3.add(vec3.create(), offset, this._controls.getTargetWithManualUpdates())
466
+ offset = vec3.subtract(vec3.create(), offset, this._controls.getPositionWithManualUpdates());
467
+ return vec3.clone(offset);
468
+
469
+ }
470
+
471
+ private zoomDistanceToOffset(distance: number): vec3 {
472
+ let offset = vec3.create();
473
+ vec3.subtract(offset, this._controls.getPositionWithManualUpdates(), this._controls.getTargetWithManualUpdates());
474
+ return vec3.multiply(vec3.create(), offset, vec3.fromValues(distance, distance, distance));
475
+ }
476
+
477
+ // #endregion Private Methods (3)
478
+ };
@@ -0,0 +1,154 @@
1
+ import * as TWEEN from '@tweenjs/tween.js'
2
+ import { vec3 } from 'gl-matrix'
3
+
4
+ import { CameraMultipleInterpolation } from './interpolationMethods/CameraMultipleInterpolation'
5
+ import { CameraSphericalInterpolation } from './interpolationMethods/CameraSphericalInterpolation'
6
+ import { ICameraControlsUsage } from '../../interfaces/controls/ICameraControlsUsage'
7
+ import { ICamera, ICameraOptions } from '../../interfaces/camera/ICamera'
8
+ import { CameraLinearInterpolation } from './interpolationMethods/CameraLinearInterpolation'
9
+ import { CameraCylindricalInterpolation } from './interpolationMethods/CameraCylindricalInterpolation'
10
+ import { ICameraInterpolation } from '../../interfaces/interpolation/ICameraInterpolation'
11
+
12
+ export class CameraInterpolationManager {
13
+ // #region Properties (3)
14
+
15
+
16
+ private TweenWrapper = class {
17
+ private _properties: { delta: 0 } = { delta: 0 };
18
+ private _tween!: TWEEN.Tween<{ delta: number }>;
19
+ private _resolve!: Function;
20
+
21
+ constructor(options: {duration: number, easing: (amount: number) => number, coordinates: string, interpolation: Function }, cb: ICameraInterpolation, onComplete: Function) {
22
+ this._tween = new TWEEN.Tween(this._properties);
23
+ this._tween.easing(options.easing);
24
+ this._tween.to({ delta: 1.0 }, options.duration);
25
+
26
+ this._tween.onUpdate((v) => {
27
+ cb.onUpdate(v);
28
+ });
29
+
30
+ this._tween.onStop((v) => {
31
+ if(cb.onStop) cb.onStop(v);
32
+ this._resolve(true);
33
+ });
34
+ this._tween.onComplete((v) => {
35
+ if(cb.onComplete) cb.onComplete(v);
36
+ onComplete();
37
+ this._resolve(true);
38
+ });
39
+ }
40
+
41
+ public start(): Promise<boolean> {
42
+ return new Promise((resolve) => {
43
+ this._resolve = resolve;
44
+ this._tween.start();
45
+ });
46
+ }
47
+
48
+ public stop(): void {
49
+ this._tween.stop();
50
+ }
51
+ };
52
+ private _tween: any;
53
+
54
+ // #endregion Properties (3)
55
+
56
+ // #region Constructors (1)
57
+
58
+ constructor(
59
+ private readonly _camera: ICamera,
60
+ private readonly _cameraControls: ICameraControlsUsage
61
+ ) {
62
+ }
63
+
64
+ // #endregion Constructors (1)
65
+
66
+ // #region Public Methods (3)
67
+
68
+ public active(): boolean {
69
+ return this._tween ? true : false;
70
+ }
71
+ /**
72
+ * cameraTween
73
+ */
74
+ public interpolate(path: { position: vec3, target: vec3 }[], options: ICameraOptions = {}) : Promise<boolean>
75
+ {
76
+
77
+ const newPath: { position: vec3, target: vec3 }[] = [];
78
+ for(let i = 0; i < path.length; i++)
79
+ newPath.push({
80
+ position: path[i].position,
81
+ target: path[i].target,
82
+ });
83
+
84
+
85
+ if(this._tween) {
86
+ this._tween.stop();
87
+ this._tween = null;
88
+ }
89
+ let parsedOptions = this.optionsParser(options);
90
+
91
+ this._tween = new this.TweenWrapper(
92
+ parsedOptions,
93
+ newPath.length === 2 ?
94
+ this.getCameraInterpolation(newPath[0], newPath[1], parsedOptions.coordinates) :
95
+ new CameraMultipleInterpolation(this._camera, this._cameraControls, newPath, parsedOptions.interpolation),
96
+ () => { this._tween = null; }
97
+ );
98
+ return this._tween.start();
99
+ }
100
+
101
+ public stop(): void {
102
+ if(this._tween) this._tween.stop();
103
+ this._tween = null;
104
+ }
105
+
106
+ // #endregion Public Methods (3)
107
+
108
+ // #region Private Methods (2)
109
+
110
+ private getCameraInterpolation(from: { position: vec3, target: vec3 }, to: { position: vec3, target: vec3 }, type: string) {
111
+ switch(type) {
112
+ case 'linear':
113
+ return new CameraLinearInterpolation(this._camera, this._cameraControls, from, to);
114
+ case 'spherical':
115
+ return new CameraSphericalInterpolation(this._camera, this._cameraControls, from, to);
116
+ case 'cylindrical':
117
+ return new CameraCylindricalInterpolation(this._camera, this._cameraControls, from, to);
118
+ default:
119
+ return new CameraMultipleInterpolation(this._camera, this._cameraControls, [from, to], TWEEN.Interpolation.CatmullRom);
120
+ }
121
+ }
122
+
123
+ private optionsParser(options: ICameraOptions): {duration: number, easing: (amount: number) => number, coordinates: string, interpolation: (v: number[], k: number) => number }
124
+ {
125
+ let easing = TWEEN.Easing.Quartic.InOut;
126
+ if(typeof options.easing === 'string') {
127
+ const keys = options.easing.split('.');
128
+ const easingFamily = TWEEN.Easing[<keyof typeof TWEEN.Easing>keys[0]];
129
+ if(easingFamily) {
130
+ const easingFunction = easingFamily[<keyof typeof easingFamily>keys[1]];
131
+ if(easingFunction) easing = easingFunction;
132
+ }
133
+ } else if(typeof options.easing === 'function') {
134
+ easing = <(amount: number) => number>options.easing;
135
+ }
136
+
137
+ let interpolation = TWEEN.Interpolation.CatmullRom;
138
+ if(typeof options.interpolation === 'string') {
139
+ const interpolationFunction = TWEEN.Interpolation[<keyof typeof TWEEN.Interpolation>options.interpolation];
140
+ if(interpolationFunction && interpolationFunction !== TWEEN.Interpolation.Utils) interpolation = <(v: number[], k: number) => number>interpolationFunction;
141
+ } else if(typeof options.interpolation === 'function') {
142
+ interpolation = <(v: number[], k: number) => number>options.interpolation;
143
+ }
144
+
145
+ return {
146
+ duration: options.duration && options.duration >= 0 ? options.duration : 0,
147
+ easing,
148
+ coordinates: options.coordinates !== 'spherical' && options.coordinates !== 'linear' && options.coordinates !== 'cylindrical' ? 'cylindrical' : options.coordinates,
149
+ interpolation
150
+ };
151
+ }
152
+
153
+ // #endregion Private Methods (2)
154
+ }