@expofp/renderer 1.3.2 → 1.4.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 (3) hide show
  1. package/dist/index.d.ts +38 -407
  2. package/dist/index.js +2618 -68
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ var _a;
4
5
  import { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Vector4, DoubleSide, MeshBasicMaterial, Texture, Quaternion, Group, PlaneGeometry, SRGBColorSpace, Vector2, BufferGeometry, LinearSRGBColorSpace, Mesh, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera, Scene, Camera, MathUtils, Clock, WebGLRenderer } from "three";
5
6
  import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
6
7
  import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
7
8
  import { LineMaterial, LineSegmentsGeometry } from "three/examples/jsm/Addons.js";
8
9
  import { MaxRectsPacker, Rectangle } from "maxrects-packer";
9
10
  import { converter, parse } from "culori";
10
- import { RAD2DEG, DEG2RAD, MathUtils as MathUtils$1 } from "three/src/math/MathUtils.js";
11
- import CameraControls from "camera-controls";
11
+ import { RAD2DEG, DEG2RAD as DEG2RAD$1 } from "three/src/math/MathUtils.js";
12
12
  import { EventManager, Rotate, Pan } from "mjolnir.js";
13
13
  function isObject(item) {
14
14
  return !!item && typeof item === "object" && !Array.isArray(item);
@@ -330,9 +330,9 @@ class BatchedMesh extends BatchedMesh$1 {
330
330
  return instanceId;
331
331
  }
332
332
  onBeforeRender(renderer, scene, camera, geometry, material, group) {
333
- var _a;
333
+ var _a2;
334
334
  if (!this.isMaterialPatched) this.patchMaterial(material);
335
- (_a = this.uniformsTexture) == null ? void 0 : _a.update();
335
+ (_a2 = this.uniformsTexture) == null ? void 0 : _a2.update();
336
336
  if (this.useMultiDraw) return super.onBeforeRender(renderer, scene, camera, geometry, material, group);
337
337
  if (!this.indexBuffer) {
338
338
  const vertexCount = geometry.getAttribute("position").count;
@@ -343,11 +343,11 @@ class BatchedMesh extends BatchedMesh$1 {
343
343
  this._multiDrawCount = 0;
344
344
  }
345
345
  onAfterRender(renderer, scene, camera, geometry) {
346
- var _a;
346
+ var _a2;
347
347
  if (this.useMultiDraw) return;
348
348
  const batchCount = this.batchCount;
349
349
  const gl = renderer.getContext();
350
- if (geometry.index == null) return console.warn("No index buffer", (_a = this.parent) == null ? void 0 : _a.name);
350
+ if (geometry.index == null) return console.warn("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
351
351
  const type = this.getIndexType(gl, geometry.index);
352
352
  gl.drawElements(gl.TRIANGLES, batchCount, type, 0);
353
353
  renderer.info.update(batchCount, gl.TRIANGLES, 1);
@@ -394,11 +394,11 @@ class BatchedMesh extends BatchedMesh$1 {
394
394
  return this;
395
395
  }
396
396
  dispose() {
397
- var _a;
397
+ var _a2;
398
398
  this.geometry.setIndex(this.indexBuffer ?? null);
399
399
  super.dispose();
400
400
  this.uniformSchema = {};
401
- (_a = this.uniformsTexture) == null ? void 0 : _a.dispose();
401
+ (_a2 = this.uniformsTexture) == null ? void 0 : _a2.dispose();
402
402
  this.uniformsTexture = void 0;
403
403
  this.indexBuffer = void 0;
404
404
  this.geometryById.forEach((geometry) => geometry.dispose());
@@ -407,9 +407,9 @@ class BatchedMesh extends BatchedMesh$1 {
407
407
  return this;
408
408
  }
409
409
  resizeToFitGeometry(geometry) {
410
- var _a;
410
+ var _a2;
411
411
  const vertexCount = geometry.attributes["position"].count;
412
- const indexCount = ((_a = geometry.index) == null ? void 0 : _a.count) ?? 0;
412
+ const indexCount = ((_a2 = geometry.index) == null ? void 0 : _a2.count) ?? 0;
413
413
  this._maxVertexCount += vertexCount;
414
414
  this._maxIndexCount += indexCount;
415
415
  this.setGeometrySize(this._maxVertexCount, this._maxIndexCount);
@@ -451,12 +451,12 @@ class BatchedMesh extends BatchedMesh$1 {
451
451
  const onBeforeCompile = material.onBeforeCompile.bind(material);
452
452
  const customProgramCacheKey = material.customProgramCacheKey.bind(material);
453
453
  material.onBeforeCompile = (shader, renderer) => {
454
- var _a;
454
+ var _a2;
455
455
  onBeforeCompile(shader, renderer);
456
456
  shader.defines ?? (shader.defines = {});
457
457
  shader.defines["USE_BATCH_UNIFORMS"] = "";
458
458
  shader.uniforms["uniformsTexture"] = { value: this.uniformsTexture };
459
- const { vertex, fragment } = ((_a = this.uniformsTexture) == null ? void 0 : _a.getUniformsGLSL("uniformsTexture", "instanceId", "int")) ?? {
459
+ const { vertex, fragment } = ((_a2 = this.uniformsTexture) == null ? void 0 : _a2.getUniformsGLSL("uniformsTexture", "instanceId", "int")) ?? {
460
460
  vertex: "",
461
461
  fragment: ""
462
462
  };
@@ -607,7 +607,7 @@ class BatchedText extends BatchedText$1 {
607
607
  this.dispatchEvent({ type: "dispose" });
608
608
  }
609
609
  _prepareForRender(material) {
610
- var _a;
610
+ var _a2;
611
611
  const isOutline = material.isTextOutlineMaterial;
612
612
  material.uniforms.uTroikaIsOutline.value = isOutline;
613
613
  let texture = this._dataTextures[isOutline ? "outline" : "main"];
@@ -626,7 +626,7 @@ class BatchedText extends BatchedText$1 {
626
626
  const texData = texture.image.data;
627
627
  this.textureNeedsUpdate = false;
628
628
  for (const text of this.textArray) {
629
- const index = ((_a = this._members.get(text)) == null ? void 0 : _a.index) ?? -1;
629
+ const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
630
630
  const textRenderInfo = text.textRenderInfo;
631
631
  if (index < 0 || !textRenderInfo) continue;
632
632
  const startIndex = index * floatsPerMember;
@@ -1313,7 +1313,7 @@ class ImageSystem extends RenderableSystem {
1313
1313
  if (this.memoryLimitMb) this.resizeTextures();
1314
1314
  }
1315
1315
  buildLayer(layer) {
1316
- var _a;
1316
+ var _a2;
1317
1317
  const group = new Group();
1318
1318
  const images = layer.children;
1319
1319
  const bins = this.packImages(images);
@@ -1331,7 +1331,7 @@ class ImageSystem extends RenderableSystem {
1331
1331
  const instanceMaterial = this.materialSystem.createTextureMaterial(texture, true);
1332
1332
  const instanceGeometry = new PlaneGeometry();
1333
1333
  const vertexCount = instanceGeometry.attributes["position"].count;
1334
- const indexCount = ((_a = instanceGeometry.index) == null ? void 0 : _a.count) ?? 0;
1334
+ const indexCount = ((_a2 = instanceGeometry.index) == null ? void 0 : _a2.count) ?? 0;
1335
1335
  const batchedMesh = new BatchedMesh(instanceCount, vertexCount, indexCount, instanceMaterial);
1336
1336
  batchedMesh.sortObjects = false;
1337
1337
  batchedMesh.addPerInstanceUniforms({ vertex: { uvOffset: "vec4" } });
@@ -1352,7 +1352,7 @@ class ImageSystem extends RenderableSystem {
1352
1352
  * Resize textures to fit the memory limit.
1353
1353
  */
1354
1354
  resizeTextures() {
1355
- var _a;
1355
+ var _a2;
1356
1356
  if (!this.memoryLimitMb) {
1357
1357
  console.warn("Memory limit is not set, unable to resize textures.");
1358
1358
  return;
@@ -1390,7 +1390,7 @@ class ImageSystem extends RenderableSystem {
1390
1390
  const resizedTexture = resizeTexture(texture, resizeFactor);
1391
1391
  const textureDim = `${texture.image.width}x${texture.image.height}`;
1392
1392
  const resizedDim = `${resizedTexture.image.width}x${resizedTexture.image.height}`;
1393
- console.log(`Resized atlas for ${mesh.name || ((_a = mesh.parent) == null ? void 0 : _a.name)}, from ${textureDim} to ${resizedDim}`);
1393
+ console.log(`Resized atlas for ${mesh.name || ((_a2 = mesh.parent) == null ? void 0 : _a2.name)}, from ${textureDim} to ${resizedDim}`);
1394
1394
  newTotal += getTextureSizeBytes(resizedTexture);
1395
1395
  material.map = resizedTexture;
1396
1396
  material.needsUpdate = true;
@@ -1490,7 +1490,7 @@ class LineSystem extends RenderableSystem {
1490
1490
  this.materialSystem = materialSystem;
1491
1491
  }
1492
1492
  buildLayer(layer) {
1493
- var _a;
1493
+ var _a2;
1494
1494
  const group = new Group();
1495
1495
  let vertexCount = 0;
1496
1496
  let indexCount = 0;
@@ -1501,7 +1501,7 @@ class LineSystem extends RenderableSystem {
1501
1501
  lineGeometry.setPositions(line.points.flatMap((pt) => [pt.x, pt.y, 0]));
1502
1502
  geometries.set(line, this.deinterleaveGeometry(lineGeometry));
1503
1503
  vertexCount += lineGeometry.attributes["position"].count;
1504
- indexCount += ((_a = lineGeometry.index) == null ? void 0 : _a.count) ?? 0;
1504
+ indexCount += ((_a2 = lineGeometry.index) == null ? void 0 : _a2.count) ?? 0;
1505
1505
  }
1506
1506
  const material = this.materialSystem.createLineMaterial({ color: "white" });
1507
1507
  const batchedMesh = new BatchedMesh(lines.length, vertexCount, indexCount, material);
@@ -1709,8 +1709,8 @@ class MeshSystem extends RenderableSystem {
1709
1709
  const [opaqueShapes, transparentShapes] = partition(
1710
1710
  shapes,
1711
1711
  (shapeDef) => {
1712
- var _a;
1713
- return (((_a = mapShapeToNormColor.get(shapeDef)) == null ? void 0 : _a.alpha) ?? 1) === 1;
1712
+ var _a2;
1713
+ return (((_a2 = mapShapeToNormColor.get(shapeDef)) == null ? void 0 : _a2.alpha) ?? 1) === 1;
1714
1714
  }
1715
1715
  );
1716
1716
  const transparentShapesGrouped = groupBy(transparentShapes, (shapeDef) => mapShapeToNormColor.get(shapeDef).alpha);
@@ -1735,7 +1735,7 @@ class MeshSystem extends RenderableSystem {
1735
1735
  }
1736
1736
  }
1737
1737
  buildBatchedMesh(shapes, opacity = 1) {
1738
- var _a, _b;
1738
+ var _a2, _b;
1739
1739
  let vertexCount = 0;
1740
1740
  let indexCount = 0;
1741
1741
  let rectGeometry = void 0;
@@ -1748,7 +1748,7 @@ class MeshSystem extends RenderableSystem {
1748
1748
  rectGeometry.deleteAttribute("uv");
1749
1749
  }
1750
1750
  vertexCount += rectGeometry.getAttribute("position").count;
1751
- indexCount += ((_a = rectGeometry.index) == null ? void 0 : _a.count) ?? 0;
1751
+ indexCount += ((_a2 = rectGeometry.index) == null ? void 0 : _a2.count) ?? 0;
1752
1752
  } else if (shapeDef.shape instanceof Polygon) {
1753
1753
  const geometry = this.buildPolygonGeometry(shapeDef.shape);
1754
1754
  shapeDefToGeometry.set(shapeDef, geometry);
@@ -2181,6 +2181,2521 @@ class LayerSystem {
2181
2181
  return fullName;
2182
2182
  }
2183
2183
  }
2184
+ /*!
2185
+ * camera-controls
2186
+ * https://github.com/yomotsu/camera-controls
2187
+ * (c) 2017 @yomotsu
2188
+ * Released under the MIT License.
2189
+ */
2190
+ const MOUSE_BUTTON = {
2191
+ LEFT: 1,
2192
+ RIGHT: 2,
2193
+ MIDDLE: 4
2194
+ };
2195
+ const ACTION = Object.freeze({
2196
+ NONE: 0,
2197
+ ROTATE: 1,
2198
+ ROTATE_POLAR: 8388608,
2199
+ ROTATE_AZIMUTH: 16777216,
2200
+ TRUCK: 2,
2201
+ SCREEN_PAN: 4,
2202
+ OFFSET: 8,
2203
+ DOLLY: 16,
2204
+ ZOOM: 32,
2205
+ TOUCH_ROTATE: 64,
2206
+ TOUCH_TRUCK: 128,
2207
+ TOUCH_SCREEN_PAN: 256,
2208
+ TOUCH_OFFSET: 512,
2209
+ TOUCH_DOLLY: 1024,
2210
+ TOUCH_ZOOM: 2048,
2211
+ TOUCH_DOLLY_TRUCK: 4096,
2212
+ TOUCH_DOLLY_SCREEN_PAN: 8192,
2213
+ TOUCH_DOLLY_OFFSET: 16384,
2214
+ TOUCH_DOLLY_ROTATE: 32768,
2215
+ TOUCH_ZOOM_TRUCK: 65536,
2216
+ TOUCH_ZOOM_OFFSET: 131072,
2217
+ TOUCH_ZOOM_SCREEN_PAN: 262144,
2218
+ TOUCH_ZOOM_ROTATE: 524288
2219
+ });
2220
+ const DOLLY_DIRECTION = {
2221
+ NONE: 0,
2222
+ IN: 1,
2223
+ OUT: -1
2224
+ };
2225
+ function isPerspectiveCamera(camera) {
2226
+ return camera.isPerspectiveCamera;
2227
+ }
2228
+ function isOrthographicCamera(camera) {
2229
+ return camera.isOrthographicCamera;
2230
+ }
2231
+ const PI_2 = Math.PI * 2;
2232
+ const PI_HALF = Math.PI / 2;
2233
+ const EPSILON = 1e-5;
2234
+ const DEG2RAD = Math.PI / 180;
2235
+ function clamp(value, min, max) {
2236
+ return Math.max(min, Math.min(max, value));
2237
+ }
2238
+ function approxZero(number, error = EPSILON) {
2239
+ return Math.abs(number) < error;
2240
+ }
2241
+ function approxEquals(a, b, error = EPSILON) {
2242
+ return approxZero(a - b, error);
2243
+ }
2244
+ function roundToStep(value, step) {
2245
+ return Math.round(value / step) * step;
2246
+ }
2247
+ function infinityToMaxNumber(value) {
2248
+ if (isFinite(value))
2249
+ return value;
2250
+ if (value < 0)
2251
+ return -Number.MAX_VALUE;
2252
+ return Number.MAX_VALUE;
2253
+ }
2254
+ function maxNumberToInfinity(value) {
2255
+ if (Math.abs(value) < Number.MAX_VALUE)
2256
+ return value;
2257
+ return value * Infinity;
2258
+ }
2259
+ function smoothDamp(current, target, currentVelocityRef, smoothTime, maxSpeed = Infinity, deltaTime) {
2260
+ smoothTime = Math.max(1e-4, smoothTime);
2261
+ const omega = 2 / smoothTime;
2262
+ const x = omega * deltaTime;
2263
+ const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
2264
+ let change = current - target;
2265
+ const originalTo = target;
2266
+ const maxChange = maxSpeed * smoothTime;
2267
+ change = clamp(change, -maxChange, maxChange);
2268
+ target = current - change;
2269
+ const temp = (currentVelocityRef.value + omega * change) * deltaTime;
2270
+ currentVelocityRef.value = (currentVelocityRef.value - omega * temp) * exp;
2271
+ let output = target + (change + temp) * exp;
2272
+ if (originalTo - current > 0 === output > originalTo) {
2273
+ output = originalTo;
2274
+ currentVelocityRef.value = (output - originalTo) / deltaTime;
2275
+ }
2276
+ return output;
2277
+ }
2278
+ function smoothDampVec3(current, target, currentVelocityRef, smoothTime, maxSpeed = Infinity, deltaTime, out) {
2279
+ smoothTime = Math.max(1e-4, smoothTime);
2280
+ const omega = 2 / smoothTime;
2281
+ const x = omega * deltaTime;
2282
+ const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
2283
+ let targetX = target.x;
2284
+ let targetY = target.y;
2285
+ let targetZ = target.z;
2286
+ let changeX = current.x - targetX;
2287
+ let changeY = current.y - targetY;
2288
+ let changeZ = current.z - targetZ;
2289
+ const originalToX = targetX;
2290
+ const originalToY = targetY;
2291
+ const originalToZ = targetZ;
2292
+ const maxChange = maxSpeed * smoothTime;
2293
+ const maxChangeSq = maxChange * maxChange;
2294
+ const magnitudeSq = changeX * changeX + changeY * changeY + changeZ * changeZ;
2295
+ if (magnitudeSq > maxChangeSq) {
2296
+ const magnitude = Math.sqrt(magnitudeSq);
2297
+ changeX = changeX / magnitude * maxChange;
2298
+ changeY = changeY / magnitude * maxChange;
2299
+ changeZ = changeZ / magnitude * maxChange;
2300
+ }
2301
+ targetX = current.x - changeX;
2302
+ targetY = current.y - changeY;
2303
+ targetZ = current.z - changeZ;
2304
+ const tempX = (currentVelocityRef.x + omega * changeX) * deltaTime;
2305
+ const tempY = (currentVelocityRef.y + omega * changeY) * deltaTime;
2306
+ const tempZ = (currentVelocityRef.z + omega * changeZ) * deltaTime;
2307
+ currentVelocityRef.x = (currentVelocityRef.x - omega * tempX) * exp;
2308
+ currentVelocityRef.y = (currentVelocityRef.y - omega * tempY) * exp;
2309
+ currentVelocityRef.z = (currentVelocityRef.z - omega * tempZ) * exp;
2310
+ out.x = targetX + (changeX + tempX) * exp;
2311
+ out.y = targetY + (changeY + tempY) * exp;
2312
+ out.z = targetZ + (changeZ + tempZ) * exp;
2313
+ const origMinusCurrentX = originalToX - current.x;
2314
+ const origMinusCurrentY = originalToY - current.y;
2315
+ const origMinusCurrentZ = originalToZ - current.z;
2316
+ const outMinusOrigX = out.x - originalToX;
2317
+ const outMinusOrigY = out.y - originalToY;
2318
+ const outMinusOrigZ = out.z - originalToZ;
2319
+ if (origMinusCurrentX * outMinusOrigX + origMinusCurrentY * outMinusOrigY + origMinusCurrentZ * outMinusOrigZ > 0) {
2320
+ out.x = originalToX;
2321
+ out.y = originalToY;
2322
+ out.z = originalToZ;
2323
+ currentVelocityRef.x = (out.x - originalToX) / deltaTime;
2324
+ currentVelocityRef.y = (out.y - originalToY) / deltaTime;
2325
+ currentVelocityRef.z = (out.z - originalToZ) / deltaTime;
2326
+ }
2327
+ return out;
2328
+ }
2329
+ function extractClientCoordFromEvent(pointers, out) {
2330
+ out.set(0, 0);
2331
+ pointers.forEach((pointer) => {
2332
+ out.x += pointer.clientX;
2333
+ out.y += pointer.clientY;
2334
+ });
2335
+ out.x /= pointers.length;
2336
+ out.y /= pointers.length;
2337
+ }
2338
+ function notSupportedInOrthographicCamera(camera, message) {
2339
+ if (isOrthographicCamera(camera)) {
2340
+ console.warn(`${message} is not supported in OrthographicCamera`);
2341
+ return true;
2342
+ }
2343
+ return false;
2344
+ }
2345
+ class EventDispatcher {
2346
+ constructor() {
2347
+ __publicField(this, "_listeners", {});
2348
+ }
2349
+ /**
2350
+ * Adds the specified event listener.
2351
+ * @param type event name
2352
+ * @param listener handler function
2353
+ * @category Methods
2354
+ */
2355
+ addEventListener(type, listener) {
2356
+ const listeners = this._listeners;
2357
+ if (listeners[type] === void 0)
2358
+ listeners[type] = [];
2359
+ if (listeners[type].indexOf(listener) === -1)
2360
+ listeners[type].push(listener);
2361
+ }
2362
+ /**
2363
+ * Presence of the specified event listener.
2364
+ * @param type event name
2365
+ * @param listener handler function
2366
+ * @category Methods
2367
+ */
2368
+ hasEventListener(type, listener) {
2369
+ const listeners = this._listeners;
2370
+ return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
2371
+ }
2372
+ /**
2373
+ * Removes the specified event listener
2374
+ * @param type event name
2375
+ * @param listener handler function
2376
+ * @category Methods
2377
+ */
2378
+ removeEventListener(type, listener) {
2379
+ const listeners = this._listeners;
2380
+ const listenerArray = listeners[type];
2381
+ if (listenerArray !== void 0) {
2382
+ const index = listenerArray.indexOf(listener);
2383
+ if (index !== -1)
2384
+ listenerArray.splice(index, 1);
2385
+ }
2386
+ }
2387
+ /**
2388
+ * Removes all event listeners
2389
+ * @param type event name
2390
+ * @category Methods
2391
+ */
2392
+ removeAllEventListeners(type) {
2393
+ if (!type) {
2394
+ this._listeners = {};
2395
+ return;
2396
+ }
2397
+ if (Array.isArray(this._listeners[type]))
2398
+ this._listeners[type].length = 0;
2399
+ }
2400
+ /**
2401
+ * Fire an event type.
2402
+ * @param event DispatcherEvent
2403
+ * @category Methods
2404
+ */
2405
+ dispatchEvent(event) {
2406
+ const listeners = this._listeners;
2407
+ const listenerArray = listeners[event.type];
2408
+ if (listenerArray !== void 0) {
2409
+ event.target = this;
2410
+ const array = listenerArray.slice(0);
2411
+ for (let i = 0, l = array.length; i < l; i++) {
2412
+ array[i].call(this, event);
2413
+ }
2414
+ }
2415
+ }
2416
+ }
2417
+ const VERSION = "3.1.1";
2418
+ const TOUCH_DOLLY_FACTOR = 1 / 8;
2419
+ const isMac = /Mac/.test((_a = globalThis == null ? void 0 : globalThis.navigator) == null ? void 0 : _a.platform);
2420
+ let THREE;
2421
+ let _ORIGIN;
2422
+ let _AXIS_Y;
2423
+ let _AXIS_Z;
2424
+ let _PLANE_XY;
2425
+ let _v2;
2426
+ let _v3A;
2427
+ let _v3B;
2428
+ let _v3C;
2429
+ let _cameraDirection;
2430
+ let _xColumn;
2431
+ let _yColumn;
2432
+ let _zColumn;
2433
+ let _deltaTarget;
2434
+ let _deltaOffset;
2435
+ let _sphericalA;
2436
+ let _sphericalB;
2437
+ let _box3A;
2438
+ let _box3B;
2439
+ let _sphere;
2440
+ let _quaternionA;
2441
+ let _quaternionB;
2442
+ let _rotationMatrix;
2443
+ let _raycaster;
2444
+ class CameraControls extends EventDispatcher {
2445
+ /**
2446
+ * Creates a `CameraControls` instance.
2447
+ *
2448
+ * Note:
2449
+ * You **must install** three.js before using camera-controls. see [#install](#install)
2450
+ * Not doing so will lead to runtime errors (`undefined` references to THREE).
2451
+ *
2452
+ * e.g.
2453
+ * ```
2454
+ * CameraControls.install( { THREE } );
2455
+ * const cameraControls = new CameraControls( camera, domElement );
2456
+ * ```
2457
+ *
2458
+ * @param camera A `THREE.PerspectiveCamera` or `THREE.OrthographicCamera` to be controlled.
2459
+ * @param domElement A `HTMLElement` for the draggable area, usually `renderer.domElement`.
2460
+ * @category Constructor
2461
+ */
2462
+ constructor(camera, domElement) {
2463
+ super();
2464
+ /**
2465
+ * Minimum vertical angle in radians.
2466
+ * The angle has to be between `0` and `.maxPolarAngle` inclusive.
2467
+ * The default value is `0`.
2468
+ *
2469
+ * e.g.
2470
+ * ```
2471
+ * cameraControls.maxPolarAngle = 0;
2472
+ * ```
2473
+ * @category Properties
2474
+ */
2475
+ __publicField(this, "minPolarAngle", 0);
2476
+ // radians
2477
+ /**
2478
+ * Maximum vertical angle in radians.
2479
+ * The angle has to be between `.maxPolarAngle` and `Math.PI` inclusive.
2480
+ * The default value is `Math.PI`.
2481
+ *
2482
+ * e.g.
2483
+ * ```
2484
+ * cameraControls.maxPolarAngle = Math.PI;
2485
+ * ```
2486
+ * @category Properties
2487
+ */
2488
+ __publicField(this, "maxPolarAngle", Math.PI);
2489
+ // radians
2490
+ /**
2491
+ * Minimum horizontal angle in radians.
2492
+ * The angle has to be less than `.maxAzimuthAngle`.
2493
+ * The default value is `- Infinity`.
2494
+ *
2495
+ * e.g.
2496
+ * ```
2497
+ * cameraControls.minAzimuthAngle = - Infinity;
2498
+ * ```
2499
+ * @category Properties
2500
+ */
2501
+ __publicField(this, "minAzimuthAngle", -Infinity);
2502
+ // radians
2503
+ /**
2504
+ * Maximum horizontal angle in radians.
2505
+ * The angle has to be greater than `.minAzimuthAngle`.
2506
+ * The default value is `Infinity`.
2507
+ *
2508
+ * e.g.
2509
+ * ```
2510
+ * cameraControls.maxAzimuthAngle = Infinity;
2511
+ * ```
2512
+ * @category Properties
2513
+ */
2514
+ __publicField(this, "maxAzimuthAngle", Infinity);
2515
+ // radians
2516
+ // How far you can dolly in and out ( PerspectiveCamera only )
2517
+ /**
2518
+ * Minimum distance for dolly. The value must be higher than `0`. Default is `Number.EPSILON`.
2519
+ * PerspectiveCamera only.
2520
+ * @category Properties
2521
+ */
2522
+ __publicField(this, "minDistance", Number.EPSILON);
2523
+ /**
2524
+ * Maximum distance for dolly. The value must be higher than `minDistance`. Default is `Infinity`.
2525
+ * PerspectiveCamera only.
2526
+ * @category Properties
2527
+ */
2528
+ __publicField(this, "maxDistance", Infinity);
2529
+ /**
2530
+ * `true` to enable Infinity Dolly for wheel and pinch. Use this with `minDistance` and `maxDistance`
2531
+ * If the Dolly distance is less (or over) than the `minDistance` (or `maxDistance`), `infinityDolly` will keep the distance and pushes the target position instead.
2532
+ * @category Properties
2533
+ */
2534
+ __publicField(this, "infinityDolly", false);
2535
+ /**
2536
+ * Minimum camera zoom.
2537
+ * @category Properties
2538
+ */
2539
+ __publicField(this, "minZoom", 0.01);
2540
+ /**
2541
+ * Maximum camera zoom.
2542
+ * @category Properties
2543
+ */
2544
+ __publicField(this, "maxZoom", Infinity);
2545
+ /**
2546
+ * Approximate time in seconds to reach the target. A smaller value will reach the target faster.
2547
+ * @category Properties
2548
+ */
2549
+ __publicField(this, "smoothTime", 0.25);
2550
+ /**
2551
+ * the smoothTime while dragging
2552
+ * @category Properties
2553
+ */
2554
+ __publicField(this, "draggingSmoothTime", 0.125);
2555
+ /**
2556
+ * the smoothTime while using mouse wheel for dolly or zoom.
2557
+ * Defaults to `draggingSmoothTime` (0.125).
2558
+ * @category Properties
2559
+ */
2560
+ __publicField(this, "wheelSmoothTime", 0.125);
2561
+ /**
2562
+ * Approximate time in seconds to reach the target for azimuth (horizontal) rotation when not user-controlled.
2563
+ * Defaults to `smoothTime` (0.25).
2564
+ * @category Properties
2565
+ */
2566
+ __publicField(this, "azimuthTime", 0.25);
2567
+ /**
2568
+ * Approximate time in seconds to reach the target for polar (vertical) rotation when not user-controlled.
2569
+ * Defaults to `smoothTime` (0.25).
2570
+ * @category Properties
2571
+ */
2572
+ __publicField(this, "polarTime", 0.25);
2573
+ /**
2574
+ * Approximate time in seconds to reach the target for dolly when not user-controlled.
2575
+ * Defaults to `smoothTime` (0.25).
2576
+ * @category Properties
2577
+ */
2578
+ __publicField(this, "dollyTime", 0.25);
2579
+ /**
2580
+ * Approximate time in seconds to reach the target for truck when not user-controlled.
2581
+ * Defaults to `smoothTime` (0.25).
2582
+ * @category Properties
2583
+ */
2584
+ __publicField(this, "truckTime", 0.25);
2585
+ /**
2586
+ * Max transition speed in unit-per-seconds
2587
+ * @category Properties
2588
+ */
2589
+ __publicField(this, "maxSpeed", Infinity);
2590
+ /**
2591
+ * Speed of azimuth (horizontal) rotation.
2592
+ * @category Properties
2593
+ */
2594
+ __publicField(this, "azimuthRotateSpeed", 1);
2595
+ /**
2596
+ * Speed of polar (vertical) rotation.
2597
+ * @category Properties
2598
+ */
2599
+ __publicField(this, "polarRotateSpeed", 1);
2600
+ /**
2601
+ * Speed of mouse-wheel dollying.
2602
+ * @category Properties
2603
+ */
2604
+ __publicField(this, "dollySpeed", 1);
2605
+ /**
2606
+ * `true` to invert direction when dollying or zooming via drag
2607
+ * @category Properties
2608
+ */
2609
+ __publicField(this, "dollyDragInverted", false);
2610
+ /**
2611
+ * Speed of drag for truck and pedestal.
2612
+ * @category Properties
2613
+ */
2614
+ __publicField(this, "truckSpeed", 2);
2615
+ /**
2616
+ * `true` to enable Dolly-in to the mouse cursor coords.
2617
+ * @category Properties
2618
+ */
2619
+ __publicField(this, "dollyToCursor", false);
2620
+ /**
2621
+ * @category Properties
2622
+ */
2623
+ __publicField(this, "dragToOffset", false);
2624
+ /**
2625
+ * Friction ratio of the boundary.
2626
+ * @category Properties
2627
+ */
2628
+ __publicField(this, "boundaryFriction", 0);
2629
+ /**
2630
+ * Controls how soon the `rest` event fires as the camera slows.
2631
+ * @category Properties
2632
+ */
2633
+ __publicField(this, "restThreshold", 0.01);
2634
+ /**
2635
+ * An array of Meshes to collide with camera.
2636
+ * Be aware colliderMeshes may decrease performance. The collision test uses 4 raycasters from the camera since the near plane has 4 corners.
2637
+ * @category Properties
2638
+ */
2639
+ __publicField(this, "colliderMeshes", []);
2640
+ // button configs
2641
+ /**
2642
+ * User's mouse input config.
2643
+ *
2644
+ * | button to assign | behavior |
2645
+ * | --------------------- | -------- |
2646
+ * | `mouseButtons.left` | `CameraControls.ACTION.ROTATE`* \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` |
2647
+ * | `mouseButtons.right` | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.TRUCK`* \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` |
2648
+ * | `mouseButtons.wheel` ¹ | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` |
2649
+ * | `mouseButtons.middle` ² | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY`* \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` |
2650
+ *
2651
+ * 1. Mouse wheel event for scroll "up/down" on mac "up/down/left/right"
2652
+ * 2. Mouse click on wheel event "button"
2653
+ * - \* is the default.
2654
+ * - The default of `mouseButtons.wheel` is:
2655
+ * - `DOLLY` for Perspective camera.
2656
+ * - `ZOOM` for Orthographic camera, and can't set `DOLLY`.
2657
+ * @category Properties
2658
+ */
2659
+ __publicField(this, "mouseButtons");
2660
+ /**
2661
+ * User's touch input config.
2662
+ *
2663
+ * | fingers to assign | behavior |
2664
+ * | --------------------- | -------- |
2665
+ * | `touches.one` | `CameraControls.ACTION.TOUCH_ROTATE`* \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.DOLLY` | `CameraControls.ACTION.ZOOM` | `CameraControls.ACTION.NONE` |
2666
+ * | `touches.two` | `ACTION.TOUCH_DOLLY_TRUCK` \| `ACTION.TOUCH_DOLLY_OFFSET` \| `ACTION.TOUCH_DOLLY_ROTATE` \| `ACTION.TOUCH_ZOOM_TRUCK` \| `ACTION.TOUCH_ZOOM_OFFSET` \| `ACTION.TOUCH_ZOOM_ROTATE` \| `ACTION.TOUCH_DOLLY` \| `ACTION.TOUCH_ZOOM` \| `CameraControls.ACTION.TOUCH_ROTATE` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.NONE` |
2667
+ * | `touches.three` | `ACTION.TOUCH_DOLLY_TRUCK` \| `ACTION.TOUCH_DOLLY_OFFSET` \| `ACTION.TOUCH_DOLLY_ROTATE` \| `ACTION.TOUCH_ZOOM_TRUCK` \| `ACTION.TOUCH_ZOOM_OFFSET` \| `ACTION.TOUCH_ZOOM_ROTATE` \| `CameraControls.ACTION.TOUCH_ROTATE` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.NONE` |
2668
+ *
2669
+ * - \* is the default.
2670
+ * - The default of `touches.two` and `touches.three` is:
2671
+ * - `TOUCH_DOLLY_TRUCK` for Perspective camera.
2672
+ * - `TOUCH_ZOOM_TRUCK` for Orthographic camera, and can't set `TOUCH_DOLLY_TRUCK` and `TOUCH_DOLLY`.
2673
+ * @category Properties
2674
+ */
2675
+ __publicField(this, "touches");
2676
+ /**
2677
+ * Force cancel user dragging.
2678
+ * @category Methods
2679
+ */
2680
+ // cancel will be overwritten in the constructor.
2681
+ __publicField(this, "cancel", () => {
2682
+ });
2683
+ /**
2684
+ * Still an experimental feature.
2685
+ * This could change at any time.
2686
+ * @category Methods
2687
+ */
2688
+ __publicField(this, "lockPointer");
2689
+ /**
2690
+ * Still an experimental feature.
2691
+ * This could change at any time.
2692
+ * @category Methods
2693
+ */
2694
+ __publicField(this, "unlockPointer");
2695
+ __publicField(this, "_enabled", true);
2696
+ __publicField(this, "_camera");
2697
+ __publicField(this, "_yAxisUpSpace");
2698
+ __publicField(this, "_yAxisUpSpaceInverse");
2699
+ __publicField(this, "_state", ACTION.NONE);
2700
+ __publicField(this, "_domElement");
2701
+ __publicField(this, "_viewport", null);
2702
+ // the location of focus, where the object orbits around
2703
+ __publicField(this, "_target");
2704
+ __publicField(this, "_targetEnd");
2705
+ __publicField(this, "_focalOffset");
2706
+ __publicField(this, "_focalOffsetEnd");
2707
+ // rotation and dolly distance
2708
+ __publicField(this, "_spherical");
2709
+ __publicField(this, "_sphericalEnd");
2710
+ __publicField(this, "_lastDistance");
2711
+ __publicField(this, "_zoom");
2712
+ __publicField(this, "_zoomEnd");
2713
+ __publicField(this, "_lastZoom");
2714
+ // reset
2715
+ __publicField(this, "_cameraUp0");
2716
+ __publicField(this, "_target0");
2717
+ __publicField(this, "_position0");
2718
+ __publicField(this, "_zoom0");
2719
+ __publicField(this, "_focalOffset0");
2720
+ __publicField(this, "_dollyControlCoord");
2721
+ __publicField(this, "_dollyToCursorTarget");
2722
+ __publicField(this, "_changedDolly", 0);
2723
+ __publicField(this, "_changedZoom", 0);
2724
+ // collisionTest uses nearPlane. ( PerspectiveCamera only )
2725
+ __publicField(this, "_nearPlaneCorners");
2726
+ __publicField(this, "_hasRested", true);
2727
+ __publicField(this, "_boundary");
2728
+ __publicField(this, "_boundaryEnclosesCamera", false);
2729
+ __publicField(this, "_needsUpdate", true);
2730
+ __publicField(this, "_updatedLastTime", false);
2731
+ __publicField(this, "_elementRect", new DOMRect());
2732
+ __publicField(this, "_isDragging", false);
2733
+ __publicField(this, "_dragNeedsUpdate", true);
2734
+ __publicField(this, "_activePointers", []);
2735
+ __publicField(this, "_lockedPointer", null);
2736
+ __publicField(this, "_interactiveArea", new DOMRect(0, 0, 1, 1));
2737
+ // Use draggingSmoothTime over smoothTime while true.
2738
+ // set automatically true on user-dragging start.
2739
+ // set automatically false on programmable methods call.
2740
+ __publicField(this, "_isUserControllingRotate", false);
2741
+ __publicField(this, "_isUserControllingDolly", false);
2742
+ __publicField(this, "_isUserControllingTruck", false);
2743
+ __publicField(this, "_isUserControllingOffset", false);
2744
+ __publicField(this, "_isUserControllingZoom", false);
2745
+ __publicField(this, "_isWheelControllingDolly", false);
2746
+ __publicField(this, "_isWheelControllingZoom", false);
2747
+ __publicField(this, "_lastDollyDirection", DOLLY_DIRECTION.NONE);
2748
+ // velocities for smoothDamp
2749
+ __publicField(this, "_thetaVelocity", { value: 0 });
2750
+ __publicField(this, "_phiVelocity", { value: 0 });
2751
+ __publicField(this, "_radiusVelocity", { value: 0 });
2752
+ __publicField(this, "_targetVelocity", new THREE.Vector3());
2753
+ __publicField(this, "_focalOffsetVelocity", new THREE.Vector3());
2754
+ __publicField(this, "_zoomVelocity", { value: 0 });
2755
+ __publicField(this, "_truckInternal", (deltaX, deltaY, dragToOffset, screenSpacePanning) => {
2756
+ let truckX;
2757
+ let pedestalY;
2758
+ if (isPerspectiveCamera(this._camera)) {
2759
+ const offset = _v3A.copy(this._camera.position).sub(this._target);
2760
+ const fov = this._camera.getEffectiveFOV() * DEG2RAD;
2761
+ const targetDistance = offset.length() * Math.tan(fov * 0.5);
2762
+ truckX = this.truckSpeed * deltaX * targetDistance / this._elementRect.height;
2763
+ pedestalY = this.truckSpeed * deltaY * targetDistance / this._elementRect.height;
2764
+ } else if (isOrthographicCamera(this._camera)) {
2765
+ const camera = this._camera;
2766
+ truckX = this.truckSpeed * deltaX * (camera.right - camera.left) / camera.zoom / this._elementRect.width;
2767
+ pedestalY = this.truckSpeed * deltaY * (camera.top - camera.bottom) / camera.zoom / this._elementRect.height;
2768
+ } else {
2769
+ return;
2770
+ }
2771
+ if (screenSpacePanning) {
2772
+ dragToOffset ? this.setFocalOffset(this._focalOffsetEnd.x + truckX, this._focalOffsetEnd.y, this._focalOffsetEnd.z, true) : this.truck(truckX, 0, true);
2773
+ this.forward(-pedestalY, true);
2774
+ } else {
2775
+ dragToOffset ? this.setFocalOffset(this._focalOffsetEnd.x + truckX, this._focalOffsetEnd.y + pedestalY, this._focalOffsetEnd.z, true) : this.truck(truckX, pedestalY, true);
2776
+ }
2777
+ });
2778
+ __publicField(this, "_rotateInternal", (deltaX, deltaY) => {
2779
+ const hasRotate = (this._state & ACTION.ROTATE) === ACTION.ROTATE;
2780
+ const hasRotatePolar = (this._state & ACTION.ROTATE_POLAR) === ACTION.ROTATE_POLAR;
2781
+ const hasRotateAzimuth = (this._state & ACTION.ROTATE_AZIMUTH) === ACTION.ROTATE_AZIMUTH;
2782
+ let theta = 0;
2783
+ let phi = 0;
2784
+ if (hasRotate || hasRotatePolar && hasRotateAzimuth) {
2785
+ theta = PI_2 * this.azimuthRotateSpeed * deltaX / this._elementRect.height;
2786
+ phi = PI_2 * this.polarRotateSpeed * deltaY / this._elementRect.height;
2787
+ } else {
2788
+ if (hasRotateAzimuth) {
2789
+ theta = PI_2 * this.azimuthRotateSpeed * deltaX / this._elementRect.height;
2790
+ }
2791
+ if (hasRotatePolar) {
2792
+ phi = PI_2 * this.polarRotateSpeed * deltaY / this._elementRect.height;
2793
+ }
2794
+ }
2795
+ if (theta !== 0 || phi !== 0) {
2796
+ this.rotate(theta, phi, true);
2797
+ }
2798
+ });
2799
+ __publicField(this, "_cursorToWorld", (x, y) => {
2800
+ const camera = this._camera;
2801
+ const cameraDirection = this._getCameraDirection(_cameraDirection);
2802
+ const planeX = _v3A.copy(cameraDirection).cross(camera.up).normalize();
2803
+ if (planeX.lengthSq() === 0) planeX.x = 1;
2804
+ const planeY = _v3B.crossVectors(planeX, cameraDirection);
2805
+ const worldToScreen = this._sphericalEnd.radius * Math.tan(camera.getEffectiveFOV() * DEG2RAD * 0.5);
2806
+ const cursor = _v3C.copy(this._targetEnd).add(planeX.multiplyScalar(x * worldToScreen * camera.aspect)).add(planeY.multiplyScalar(y * worldToScreen));
2807
+ return cursor;
2808
+ });
2809
+ __publicField(this, "_cursorToPlane", (x, y) => {
2810
+ const cursor = this._cursorToWorld(x, y);
2811
+ const origin2 = _v3A.setFromSpherical(this._sphericalEnd).applyQuaternion(this._yAxisUpSpaceInverse).add(this._targetEnd);
2812
+ const rayDirection = _v3B.copy(cursor).sub(origin2).normalize();
2813
+ _raycaster.set(origin2, rayDirection);
2814
+ const intersection = _raycaster.ray.intersectPlane(_PLANE_XY, _v3C);
2815
+ return intersection;
2816
+ });
2817
+ __publicField(this, "_dollyInternal", (delta, x, y) => {
2818
+ const dollyScale = Math.pow(0.95, -delta * this.dollySpeed);
2819
+ const lastDistance = this._sphericalEnd.radius;
2820
+ const distance = this._sphericalEnd.radius * dollyScale;
2821
+ const clampedDistance = clamp(distance, this.minDistance, this.maxDistance);
2822
+ const overflowedDistance = clampedDistance - distance;
2823
+ let truckOverride = false;
2824
+ if (this.infinityDolly) {
2825
+ if (this.dollyToCursor) {
2826
+ this._dollyToNoClamp(distance, true);
2827
+ this._changedDolly += distance - lastDistance;
2828
+ } else {
2829
+ this.dollyInFixed(overflowedDistance, true);
2830
+ this._dollyToNoClamp(clampedDistance, true);
2831
+ }
2832
+ } else {
2833
+ if (this.dollyToCursor) {
2834
+ const deltaDistance = clampedDistance - lastDistance;
2835
+ this._dollyControlCoord.set(x, y);
2836
+ this._dollyToNoClamp(clampedDistance, true);
2837
+ const intersection = this._cursorToPlane(x, y);
2838
+ if (intersection) {
2839
+ this._changedDolly += deltaDistance;
2840
+ this._dollyToCursorTarget.copy(intersection);
2841
+ } else {
2842
+ this._dollyToNoClamp(lastDistance, true);
2843
+ this._changedDolly = 0;
2844
+ truckOverride = true;
2845
+ const bottomNDC = new THREE.Vector2(0, -1);
2846
+ const ndcToClient = new THREE.Vector2(-1, 1);
2847
+ this._dollyControlCoord.sub(bottomNDC).multiplyScalar(deltaDistance / (this.truckSpeed * this.truckSpeed)).multiply(ndcToClient);
2848
+ this._truckInternal(this._dollyControlCoord.x, this._dollyControlCoord.y, false, true);
2849
+ }
2850
+ } else {
2851
+ this._dollyToNoClamp(clampedDistance, true);
2852
+ }
2853
+ }
2854
+ if (!truckOverride) this._lastDollyDirection = Math.sign(-delta);
2855
+ return truckOverride;
2856
+ });
2857
+ __publicField(this, "_zoomInternal", (delta, x, y) => {
2858
+ const zoomScale = Math.pow(0.95, delta * this.dollySpeed);
2859
+ const lastZoom = this._zoom;
2860
+ const zoom = this._zoom * zoomScale;
2861
+ this.zoomTo(zoom, true);
2862
+ if (this.dollyToCursor) {
2863
+ this._changedZoom += zoom - lastZoom;
2864
+ this._dollyControlCoord.set(x, y);
2865
+ }
2866
+ });
2867
+ if (typeof THREE === "undefined") {
2868
+ console.error("camera-controls: `THREE` is undefined. You must first run `CameraControls.install( { THREE: THREE } )`. Check the docs for further information.");
2869
+ }
2870
+ this._camera = camera;
2871
+ this._yAxisUpSpace = new THREE.Quaternion().setFromUnitVectors(this._camera.up, _AXIS_Y);
2872
+ this._yAxisUpSpaceInverse = this._yAxisUpSpace.clone().invert();
2873
+ this._state = ACTION.NONE;
2874
+ this._target = new THREE.Vector3();
2875
+ this._targetEnd = this._target.clone();
2876
+ this._focalOffset = new THREE.Vector3();
2877
+ this._focalOffsetEnd = this._focalOffset.clone();
2878
+ this._spherical = new THREE.Spherical().setFromVector3(_v3A.copy(this._camera.position).applyQuaternion(this._yAxisUpSpace));
2879
+ this._sphericalEnd = this._spherical.clone();
2880
+ this._lastDistance = this._spherical.radius;
2881
+ this._zoom = this._camera.zoom;
2882
+ this._zoomEnd = this._zoom;
2883
+ this._lastZoom = this._zoom;
2884
+ this._nearPlaneCorners = [
2885
+ new THREE.Vector3(),
2886
+ new THREE.Vector3(),
2887
+ new THREE.Vector3(),
2888
+ new THREE.Vector3()
2889
+ ];
2890
+ this._updateNearPlaneCorners();
2891
+ this._boundary = new THREE.Box3(new THREE.Vector3(-Infinity, -Infinity, -Infinity), new THREE.Vector3(Infinity, Infinity, Infinity));
2892
+ this._cameraUp0 = this._camera.up.clone();
2893
+ this._target0 = this._target.clone();
2894
+ this._position0 = this._camera.position.clone();
2895
+ this._zoom0 = this._zoom;
2896
+ this._focalOffset0 = this._focalOffset.clone();
2897
+ this._dollyControlCoord = new THREE.Vector2();
2898
+ this._dollyToCursorTarget = new THREE.Vector3();
2899
+ this.mouseButtons = {
2900
+ left: ACTION.ROTATE,
2901
+ middle: ACTION.DOLLY,
2902
+ right: ACTION.TRUCK,
2903
+ wheel: isPerspectiveCamera(this._camera) ? ACTION.DOLLY : isOrthographicCamera(this._camera) ? ACTION.ZOOM : ACTION.NONE
2904
+ };
2905
+ this.touches = {
2906
+ one: ACTION.TOUCH_ROTATE,
2907
+ two: isPerspectiveCamera(this._camera) ? ACTION.TOUCH_DOLLY_TRUCK : isOrthographicCamera(this._camera) ? ACTION.TOUCH_ZOOM_TRUCK : ACTION.NONE,
2908
+ three: ACTION.TOUCH_TRUCK
2909
+ };
2910
+ const dragStartPosition = new THREE.Vector2();
2911
+ const lastDragPosition = new THREE.Vector2();
2912
+ const dollyStart = new THREE.Vector2();
2913
+ const onPointerDown = (event) => {
2914
+ if (!this._enabled || !this._domElement)
2915
+ return;
2916
+ if (this._interactiveArea.left !== 0 || this._interactiveArea.top !== 0 || this._interactiveArea.width !== 1 || this._interactiveArea.height !== 1) {
2917
+ const elRect = this._domElement.getBoundingClientRect();
2918
+ const left = event.clientX / elRect.width;
2919
+ const top = event.clientY / elRect.height;
2920
+ if (left < this._interactiveArea.left || left > this._interactiveArea.right || top < this._interactiveArea.top || top > this._interactiveArea.bottom)
2921
+ return;
2922
+ }
2923
+ const mouseButton = event.pointerType !== "mouse" ? null : (event.buttons & MOUSE_BUTTON.LEFT) === MOUSE_BUTTON.LEFT ? MOUSE_BUTTON.LEFT : (event.buttons & MOUSE_BUTTON.MIDDLE) === MOUSE_BUTTON.MIDDLE ? MOUSE_BUTTON.MIDDLE : (event.buttons & MOUSE_BUTTON.RIGHT) === MOUSE_BUTTON.RIGHT ? MOUSE_BUTTON.RIGHT : null;
2924
+ if (mouseButton !== null) {
2925
+ const zombiePointer = this._findPointerByMouseButton(mouseButton);
2926
+ zombiePointer && this._disposePointer(zombiePointer);
2927
+ }
2928
+ if ((event.buttons & MOUSE_BUTTON.LEFT) === MOUSE_BUTTON.LEFT && this._lockedPointer)
2929
+ return;
2930
+ const pointer = {
2931
+ pointerId: event.pointerId,
2932
+ clientX: event.clientX,
2933
+ clientY: event.clientY,
2934
+ deltaX: 0,
2935
+ deltaY: 0,
2936
+ mouseButton
2937
+ };
2938
+ this._activePointers.push(pointer);
2939
+ this._domElement.ownerDocument.removeEventListener("pointermove", onPointerMove, { passive: false });
2940
+ this._domElement.ownerDocument.removeEventListener("pointerup", onPointerUp);
2941
+ this._domElement.ownerDocument.addEventListener("pointermove", onPointerMove, { passive: false });
2942
+ this._domElement.ownerDocument.addEventListener("pointerup", onPointerUp);
2943
+ this._isDragging = true;
2944
+ startDragging(event);
2945
+ };
2946
+ const onPointerMove = (event) => {
2947
+ if (event.cancelable)
2948
+ event.preventDefault();
2949
+ const pointerId = event.pointerId;
2950
+ const pointer = this._lockedPointer || this._findPointerById(pointerId);
2951
+ if (!pointer)
2952
+ return;
2953
+ pointer.clientX = event.clientX;
2954
+ pointer.clientY = event.clientY;
2955
+ pointer.deltaX = event.movementX;
2956
+ pointer.deltaY = event.movementY;
2957
+ this._state = 0;
2958
+ if (event.pointerType === "touch") {
2959
+ switch (this._activePointers.length) {
2960
+ case 1:
2961
+ this._state = this.touches.one;
2962
+ break;
2963
+ case 2:
2964
+ this._state = this.touches.two;
2965
+ break;
2966
+ case 3:
2967
+ this._state = this.touches.three;
2968
+ break;
2969
+ }
2970
+ } else {
2971
+ if (!this._isDragging && this._lockedPointer || this._isDragging && (event.buttons & MOUSE_BUTTON.LEFT) === MOUSE_BUTTON.LEFT) {
2972
+ this._state = this._state | this.mouseButtons.left;
2973
+ }
2974
+ if (this._isDragging && (event.buttons & MOUSE_BUTTON.MIDDLE) === MOUSE_BUTTON.MIDDLE) {
2975
+ this._state = this._state | this.mouseButtons.middle;
2976
+ }
2977
+ if (this._isDragging && (event.buttons & MOUSE_BUTTON.RIGHT) === MOUSE_BUTTON.RIGHT) {
2978
+ this._state = this._state | this.mouseButtons.right;
2979
+ }
2980
+ }
2981
+ dragging();
2982
+ };
2983
+ const onPointerUp = (event) => {
2984
+ const pointer = this._findPointerById(event.pointerId);
2985
+ if (pointer && pointer === this._lockedPointer)
2986
+ return;
2987
+ pointer && this._disposePointer(pointer);
2988
+ if (event.pointerType === "touch") {
2989
+ switch (this._activePointers.length) {
2990
+ case 0:
2991
+ this._state = ACTION.NONE;
2992
+ break;
2993
+ case 1:
2994
+ this._state = this.touches.one;
2995
+ break;
2996
+ case 2:
2997
+ this._state = this.touches.two;
2998
+ break;
2999
+ case 3:
3000
+ this._state = this.touches.three;
3001
+ break;
3002
+ }
3003
+ } else {
3004
+ this._state = ACTION.NONE;
3005
+ }
3006
+ endDragging();
3007
+ };
3008
+ let lastScrollTimeStamp = -1;
3009
+ const onMouseWheel = (event) => {
3010
+ if (!this._domElement)
3011
+ return;
3012
+ if (!this._enabled || this.mouseButtons.wheel === ACTION.NONE)
3013
+ return;
3014
+ if (this._interactiveArea.left !== 0 || this._interactiveArea.top !== 0 || this._interactiveArea.width !== 1 || this._interactiveArea.height !== 1) {
3015
+ const elRect = this._domElement.getBoundingClientRect();
3016
+ const left = event.clientX / elRect.width;
3017
+ const top = event.clientY / elRect.height;
3018
+ if (left < this._interactiveArea.left || left > this._interactiveArea.right || top < this._interactiveArea.top || top > this._interactiveArea.bottom)
3019
+ return;
3020
+ }
3021
+ event.preventDefault();
3022
+ if (this.dollyToCursor || this.mouseButtons.wheel === ACTION.ROTATE || this.mouseButtons.wheel === ACTION.TRUCK) {
3023
+ const now = performance.now();
3024
+ if (lastScrollTimeStamp - now < 1e3)
3025
+ this._getClientRect(this._elementRect);
3026
+ lastScrollTimeStamp = now;
3027
+ }
3028
+ const deltaYFactor = isMac ? -1 : -3;
3029
+ const delta = event.deltaMode === 1 || event.ctrlKey ? event.deltaY / deltaYFactor : event.deltaY / (deltaYFactor * 10);
3030
+ const x = this.dollyToCursor ? (event.clientX - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
3031
+ const y = this.dollyToCursor ? (event.clientY - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
3032
+ const wheelAction = this.mouseButtons.wheel;
3033
+ if (wheelAction === ACTION.ROTATE || wheelAction === ACTION.ROTATE_POLAR || wheelAction === ACTION.ROTATE_AZIMUTH || wheelAction === (ACTION.ROTATE_POLAR | ACTION.ROTATE_AZIMUTH)) {
3034
+ this._rotateInternal(event.deltaX, event.deltaY);
3035
+ this._isUserControllingRotate = true;
3036
+ } else if (wheelAction === ACTION.TRUCK) {
3037
+ this._truckInternal(event.deltaX, event.deltaY, false, false);
3038
+ this._isUserControllingTruck = true;
3039
+ } else if (wheelAction === ACTION.SCREEN_PAN) {
3040
+ this._truckInternal(event.deltaX, event.deltaY, false, true);
3041
+ this._isUserControllingTruck = true;
3042
+ } else if (wheelAction === ACTION.OFFSET) {
3043
+ this._truckInternal(event.deltaX, event.deltaY, true, false);
3044
+ this._isUserControllingOffset = true;
3045
+ } else if (wheelAction === ACTION.DOLLY) {
3046
+ const truckOverride = this._dollyInternal(-delta, x, y);
3047
+ if (!truckOverride) {
3048
+ this._isUserControllingDolly = true;
3049
+ this._isWheelControllingDolly = true;
3050
+ } else {
3051
+ this._isUserControllingTruck = true;
3052
+ }
3053
+ } else if (wheelAction === ACTION.ZOOM) {
3054
+ this._zoomInternal(-delta, x, y);
3055
+ this._isUserControllingZoom = true;
3056
+ this._isWheelControllingZoom = true;
3057
+ }
3058
+ this.dispatchEvent({ type: "control" });
3059
+ };
3060
+ const onContextMenu = (event) => {
3061
+ if (!this._domElement || !this._enabled)
3062
+ return;
3063
+ if (this.mouseButtons.right === CameraControls.ACTION.NONE) {
3064
+ const pointerId = event instanceof PointerEvent ? event.pointerId : 0;
3065
+ const pointer = this._findPointerById(pointerId);
3066
+ pointer && this._disposePointer(pointer);
3067
+ this._domElement.ownerDocument.removeEventListener("pointermove", onPointerMove, { passive: false });
3068
+ this._domElement.ownerDocument.removeEventListener("pointerup", onPointerUp);
3069
+ return;
3070
+ }
3071
+ event.preventDefault();
3072
+ };
3073
+ const startDragging = (event) => {
3074
+ if (!this._enabled)
3075
+ return;
3076
+ extractClientCoordFromEvent(this._activePointers, _v2);
3077
+ this._getClientRect(this._elementRect);
3078
+ dragStartPosition.copy(_v2);
3079
+ lastDragPosition.copy(_v2);
3080
+ const isMultiTouch = this._activePointers.length >= 2;
3081
+ if (isMultiTouch) {
3082
+ const dx = _v2.x - this._activePointers[1].clientX;
3083
+ const dy = _v2.y - this._activePointers[1].clientY;
3084
+ const distance = Math.sqrt(dx * dx + dy * dy);
3085
+ dollyStart.set(0, distance);
3086
+ const x = (this._activePointers[0].clientX + this._activePointers[1].clientX) * 0.5;
3087
+ const y = (this._activePointers[0].clientY + this._activePointers[1].clientY) * 0.5;
3088
+ lastDragPosition.set(x, y);
3089
+ }
3090
+ this._state = 0;
3091
+ if (!event) {
3092
+ if (this._lockedPointer)
3093
+ this._state = this._state | this.mouseButtons.left;
3094
+ } else if ("pointerType" in event && event.pointerType === "touch") {
3095
+ switch (this._activePointers.length) {
3096
+ case 1:
3097
+ this._state = this.touches.one;
3098
+ break;
3099
+ case 2:
3100
+ this._state = this.touches.two;
3101
+ break;
3102
+ case 3:
3103
+ this._state = this.touches.three;
3104
+ break;
3105
+ }
3106
+ } else {
3107
+ if (!this._lockedPointer && (event.buttons & MOUSE_BUTTON.LEFT) === MOUSE_BUTTON.LEFT) {
3108
+ this._state = this._state | this.mouseButtons.left;
3109
+ }
3110
+ if ((event.buttons & MOUSE_BUTTON.MIDDLE) === MOUSE_BUTTON.MIDDLE) {
3111
+ this._state = this._state | this.mouseButtons.middle;
3112
+ }
3113
+ if ((event.buttons & MOUSE_BUTTON.RIGHT) === MOUSE_BUTTON.RIGHT) {
3114
+ this._state = this._state | this.mouseButtons.right;
3115
+ }
3116
+ }
3117
+ if ((this._state & (ACTION.ROTATE | ACTION.ROTATE_POLAR | ACTION.ROTATE_AZIMUTH)) !== ACTION.NONE || (this._state & ACTION.TOUCH_ROTATE) === ACTION.TOUCH_ROTATE || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE || (this._state & ACTION.TOUCH_ZOOM_ROTATE) === ACTION.TOUCH_ZOOM_ROTATE) {
3118
+ const hasRotate = (this._state & ACTION.ROTATE) === ACTION.ROTATE;
3119
+ const hasRotatePolar = (this._state & ACTION.ROTATE_POLAR) === ACTION.ROTATE_POLAR;
3120
+ const hasRotateAzimuth = (this._state & ACTION.ROTATE_AZIMUTH) === ACTION.ROTATE_AZIMUTH;
3121
+ const hasTouchRotate = (this._state & ACTION.TOUCH_ROTATE) === ACTION.TOUCH_ROTATE || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE || (this._state & ACTION.TOUCH_ZOOM_ROTATE) === ACTION.TOUCH_ZOOM_ROTATE;
3122
+ if (hasRotate || hasRotateAzimuth || hasTouchRotate) {
3123
+ this._sphericalEnd.theta = this._spherical.theta;
3124
+ this._thetaVelocity.value = 0;
3125
+ }
3126
+ if (hasRotate || hasRotatePolar || hasTouchRotate) {
3127
+ this._sphericalEnd.phi = this._spherical.phi;
3128
+ this._phiVelocity.value = 0;
3129
+ }
3130
+ }
3131
+ if ((this._state & ACTION.TRUCK) === ACTION.TRUCK || (this._state & ACTION.SCREEN_PAN) === ACTION.SCREEN_PAN || (this._state & ACTION.TOUCH_TRUCK) === ACTION.TOUCH_TRUCK || (this._state & ACTION.TOUCH_SCREEN_PAN) === ACTION.TOUCH_SCREEN_PAN || (this._state & ACTION.TOUCH_DOLLY_TRUCK) === ACTION.TOUCH_DOLLY_TRUCK || (this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN || (this._state & ACTION.TOUCH_ZOOM_TRUCK) === ACTION.TOUCH_ZOOM_TRUCK || (this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN) {
3132
+ this._targetEnd.copy(this._target);
3133
+ this._targetVelocity.set(0, 0, 0);
3134
+ }
3135
+ if ((this._state & ACTION.DOLLY) === ACTION.DOLLY || (this._state & ACTION.TOUCH_DOLLY) === ACTION.TOUCH_DOLLY || (this._state & ACTION.TOUCH_DOLLY_TRUCK) === ACTION.TOUCH_DOLLY_TRUCK || (this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN || (this._state & ACTION.TOUCH_DOLLY_OFFSET) === ACTION.TOUCH_DOLLY_OFFSET || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE) {
3136
+ this._sphericalEnd.radius = this._spherical.radius;
3137
+ this._radiusVelocity.value = 0;
3138
+ }
3139
+ if ((this._state & ACTION.ZOOM) === ACTION.ZOOM || (this._state & ACTION.TOUCH_ZOOM) === ACTION.TOUCH_ZOOM || (this._state & ACTION.TOUCH_ZOOM_TRUCK) === ACTION.TOUCH_ZOOM_TRUCK || (this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN) === ACTION.TOUCH_ZOOM_SCREEN_PAN || (this._state & ACTION.TOUCH_ZOOM_OFFSET) === ACTION.TOUCH_ZOOM_OFFSET || (this._state & ACTION.TOUCH_ZOOM_ROTATE) === ACTION.TOUCH_ZOOM_ROTATE) {
3140
+ this._zoomEnd = this._zoom;
3141
+ this._zoomVelocity.value = 0;
3142
+ }
3143
+ if ((this._state & ACTION.OFFSET) === ACTION.OFFSET || (this._state & ACTION.TOUCH_OFFSET) === ACTION.TOUCH_OFFSET || (this._state & ACTION.TOUCH_DOLLY_OFFSET) === ACTION.TOUCH_DOLLY_OFFSET || (this._state & ACTION.TOUCH_ZOOM_OFFSET) === ACTION.TOUCH_ZOOM_OFFSET) {
3144
+ this._focalOffsetEnd.copy(this._focalOffset);
3145
+ this._focalOffsetVelocity.set(0, 0, 0);
3146
+ }
3147
+ this.dispatchEvent({ type: "controlstart" });
3148
+ };
3149
+ const dragging = () => {
3150
+ if (!this._enabled || !this._dragNeedsUpdate)
3151
+ return;
3152
+ this._dragNeedsUpdate = false;
3153
+ extractClientCoordFromEvent(this._activePointers, _v2);
3154
+ const isPointerLockActive = this._domElement && this._domElement.ownerDocument.pointerLockElement === this._domElement;
3155
+ const lockedPointer = isPointerLockActive ? this._lockedPointer || this._activePointers[0] : null;
3156
+ const deltaX = lockedPointer ? -lockedPointer.deltaX : lastDragPosition.x - _v2.x;
3157
+ const deltaY = lockedPointer ? -lockedPointer.deltaY : lastDragPosition.y - _v2.y;
3158
+ lastDragPosition.copy(_v2);
3159
+ if ((this._state & (ACTION.ROTATE | ACTION.ROTATE_POLAR | ACTION.ROTATE_AZIMUTH)) !== ACTION.NONE || (this._state & ACTION.TOUCH_ROTATE) === ACTION.TOUCH_ROTATE || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE || (this._state & ACTION.TOUCH_ZOOM_ROTATE) === ACTION.TOUCH_ZOOM_ROTATE) {
3160
+ this._rotateInternal(deltaX, deltaY);
3161
+ this._isUserControllingRotate = true;
3162
+ }
3163
+ if ((this._state & ACTION.DOLLY) === ACTION.DOLLY || (this._state & ACTION.ZOOM) === ACTION.ZOOM) {
3164
+ const dollyX = this.dollyToCursor ? (dragStartPosition.x - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
3165
+ const dollyY = this.dollyToCursor ? (dragStartPosition.y - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
3166
+ const dollyDirection = this.dollyDragInverted ? -1 : 1;
3167
+ if ((this._state & ACTION.DOLLY) === ACTION.DOLLY) {
3168
+ const truckOverride = this._dollyInternal(dollyDirection * deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
3169
+ if (!truckOverride) {
3170
+ this._isUserControllingDolly = true;
3171
+ } else {
3172
+ this._isUserControllingTruck = true;
3173
+ }
3174
+ } else {
3175
+ this._zoomInternal(dollyDirection * deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
3176
+ this._isUserControllingZoom = true;
3177
+ }
3178
+ }
3179
+ if ((this._state & ACTION.TOUCH_DOLLY) === ACTION.TOUCH_DOLLY || (this._state & ACTION.TOUCH_ZOOM) === ACTION.TOUCH_ZOOM || (this._state & ACTION.TOUCH_DOLLY_TRUCK) === ACTION.TOUCH_DOLLY_TRUCK || (this._state & ACTION.TOUCH_ZOOM_TRUCK) === ACTION.TOUCH_ZOOM_TRUCK || (this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN || (this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN) === ACTION.TOUCH_ZOOM_SCREEN_PAN || (this._state & ACTION.TOUCH_DOLLY_OFFSET) === ACTION.TOUCH_DOLLY_OFFSET || (this._state & ACTION.TOUCH_ZOOM_OFFSET) === ACTION.TOUCH_ZOOM_OFFSET || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE || (this._state & ACTION.TOUCH_ZOOM_ROTATE) === ACTION.TOUCH_ZOOM_ROTATE) {
3180
+ const dx = _v2.x - this._activePointers[1].clientX;
3181
+ const dy = _v2.y - this._activePointers[1].clientY;
3182
+ const distance = Math.sqrt(dx * dx + dy * dy);
3183
+ const dollyDelta = dollyStart.y - distance;
3184
+ dollyStart.set(0, distance);
3185
+ const dollyX = this.dollyToCursor ? (lastDragPosition.x - this._elementRect.x) / this._elementRect.width * 2 - 1 : 0;
3186
+ const dollyY = this.dollyToCursor ? (lastDragPosition.y - this._elementRect.y) / this._elementRect.height * -2 + 1 : 0;
3187
+ if ((this._state & ACTION.TOUCH_DOLLY) === ACTION.TOUCH_DOLLY || (this._state & ACTION.TOUCH_DOLLY_ROTATE) === ACTION.TOUCH_DOLLY_ROTATE || (this._state & ACTION.TOUCH_DOLLY_TRUCK) === ACTION.TOUCH_DOLLY_TRUCK || (this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN || (this._state & ACTION.TOUCH_DOLLY_OFFSET) === ACTION.TOUCH_DOLLY_OFFSET) {
3188
+ const truckOverride = this._dollyInternal(dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
3189
+ if (!truckOverride) {
3190
+ this._isUserControllingDolly = true;
3191
+ } else {
3192
+ this._isUserControllingTruck = true;
3193
+ }
3194
+ } else {
3195
+ this._zoomInternal(dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY);
3196
+ this._isUserControllingZoom = true;
3197
+ }
3198
+ }
3199
+ if ((this._state & ACTION.TRUCK) === ACTION.TRUCK || (this._state & ACTION.TOUCH_TRUCK) === ACTION.TOUCH_TRUCK || (this._state & ACTION.TOUCH_DOLLY_TRUCK) === ACTION.TOUCH_DOLLY_TRUCK || (this._state & ACTION.TOUCH_ZOOM_TRUCK) === ACTION.TOUCH_ZOOM_TRUCK) {
3200
+ this._truckInternal(deltaX, deltaY, false, false);
3201
+ this._isUserControllingTruck = true;
3202
+ }
3203
+ if ((this._state & ACTION.SCREEN_PAN) === ACTION.SCREEN_PAN || (this._state & ACTION.TOUCH_SCREEN_PAN) === ACTION.TOUCH_SCREEN_PAN || (this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN) === ACTION.TOUCH_DOLLY_SCREEN_PAN || (this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN) === ACTION.TOUCH_ZOOM_SCREEN_PAN) {
3204
+ this._truckInternal(deltaX, deltaY, false, true);
3205
+ this._isUserControllingTruck = true;
3206
+ }
3207
+ if ((this._state & ACTION.OFFSET) === ACTION.OFFSET || (this._state & ACTION.TOUCH_OFFSET) === ACTION.TOUCH_OFFSET || (this._state & ACTION.TOUCH_DOLLY_OFFSET) === ACTION.TOUCH_DOLLY_OFFSET || (this._state & ACTION.TOUCH_ZOOM_OFFSET) === ACTION.TOUCH_ZOOM_OFFSET) {
3208
+ this._truckInternal(deltaX, deltaY, true, false);
3209
+ this._isUserControllingOffset = true;
3210
+ }
3211
+ this.dispatchEvent({ type: "control" });
3212
+ };
3213
+ const endDragging = () => {
3214
+ extractClientCoordFromEvent(this._activePointers, _v2);
3215
+ lastDragPosition.copy(_v2);
3216
+ this._dragNeedsUpdate = false;
3217
+ if (this._activePointers.length === 0 || this._activePointers.length === 1 && this._activePointers[0] === this._lockedPointer) {
3218
+ this._isDragging = false;
3219
+ }
3220
+ if (this._activePointers.length === 0 && this._domElement) {
3221
+ this._domElement.ownerDocument.removeEventListener("pointermove", onPointerMove, { passive: false });
3222
+ this._domElement.ownerDocument.removeEventListener("pointerup", onPointerUp);
3223
+ this.dispatchEvent({ type: "controlend" });
3224
+ }
3225
+ };
3226
+ this.lockPointer = () => {
3227
+ if (!this._enabled || !this._domElement)
3228
+ return;
3229
+ this.cancel();
3230
+ this._lockedPointer = {
3231
+ pointerId: -1,
3232
+ clientX: 0,
3233
+ clientY: 0,
3234
+ deltaX: 0,
3235
+ deltaY: 0,
3236
+ mouseButton: null
3237
+ };
3238
+ this._activePointers.push(this._lockedPointer);
3239
+ this._domElement.ownerDocument.removeEventListener("pointermove", onPointerMove, { passive: false });
3240
+ this._domElement.ownerDocument.removeEventListener("pointerup", onPointerUp);
3241
+ this._domElement.requestPointerLock();
3242
+ this._domElement.ownerDocument.addEventListener("pointerlockchange", onPointerLockChange);
3243
+ this._domElement.ownerDocument.addEventListener("pointerlockerror", onPointerLockError);
3244
+ this._domElement.ownerDocument.addEventListener("pointermove", onPointerMove, { passive: false });
3245
+ this._domElement.ownerDocument.addEventListener("pointerup", onPointerUp);
3246
+ startDragging();
3247
+ };
3248
+ this.unlockPointer = () => {
3249
+ var _a2, _b, _c;
3250
+ if (this._lockedPointer !== null) {
3251
+ this._disposePointer(this._lockedPointer);
3252
+ this._lockedPointer = null;
3253
+ }
3254
+ (_a2 = this._domElement) == null ? void 0 : _a2.ownerDocument.exitPointerLock();
3255
+ (_b = this._domElement) == null ? void 0 : _b.ownerDocument.removeEventListener("pointerlockchange", onPointerLockChange);
3256
+ (_c = this._domElement) == null ? void 0 : _c.ownerDocument.removeEventListener("pointerlockerror", onPointerLockError);
3257
+ this.cancel();
3258
+ };
3259
+ const onPointerLockChange = () => {
3260
+ const isPointerLockActive = this._domElement && this._domElement.ownerDocument.pointerLockElement === this._domElement;
3261
+ if (!isPointerLockActive)
3262
+ this.unlockPointer();
3263
+ };
3264
+ const onPointerLockError = () => {
3265
+ this.unlockPointer();
3266
+ };
3267
+ this._addAllEventListeners = (domElement2) => {
3268
+ this._domElement = domElement2;
3269
+ this._domElement.style.touchAction = "none";
3270
+ this._domElement.style.userSelect = "none";
3271
+ this._domElement.style.webkitUserSelect = "none";
3272
+ this._domElement.addEventListener("pointerdown", onPointerDown);
3273
+ this._domElement.addEventListener("pointercancel", onPointerUp);
3274
+ this._domElement.addEventListener("wheel", onMouseWheel, { passive: false });
3275
+ this._domElement.addEventListener("contextmenu", onContextMenu);
3276
+ };
3277
+ this._removeAllEventListeners = () => {
3278
+ if (!this._domElement)
3279
+ return;
3280
+ this._domElement.style.touchAction = "";
3281
+ this._domElement.style.userSelect = "";
3282
+ this._domElement.style.webkitUserSelect = "";
3283
+ this._domElement.removeEventListener("pointerdown", onPointerDown);
3284
+ this._domElement.removeEventListener("pointercancel", onPointerUp);
3285
+ this._domElement.removeEventListener("wheel", onMouseWheel, { passive: false });
3286
+ this._domElement.removeEventListener("contextmenu", onContextMenu);
3287
+ this._domElement.ownerDocument.removeEventListener("pointermove", onPointerMove, { passive: false });
3288
+ this._domElement.ownerDocument.removeEventListener("pointerup", onPointerUp);
3289
+ this._domElement.ownerDocument.removeEventListener("pointerlockchange", onPointerLockChange);
3290
+ this._domElement.ownerDocument.removeEventListener("pointerlockerror", onPointerLockError);
3291
+ };
3292
+ this.cancel = () => {
3293
+ if (this._state === ACTION.NONE)
3294
+ return;
3295
+ this._state = ACTION.NONE;
3296
+ this._activePointers.length = 0;
3297
+ endDragging();
3298
+ };
3299
+ if (domElement)
3300
+ this.connect(domElement);
3301
+ this.update(0);
3302
+ }
3303
+ /**
3304
+ * Injects THREE as the dependency. You can then proceed to use CameraControls.
3305
+ *
3306
+ * e.g
3307
+ * ```javascript
3308
+ * CameraControls.install( { THREE: THREE } );
3309
+ * ```
3310
+ *
3311
+ * Note: If you do not wish to use enter three.js to reduce file size(tree-shaking for example), make a subset to install.
3312
+ *
3313
+ * ```js
3314
+ * import {
3315
+ * Vector2,
3316
+ * Vector3,
3317
+ * Vector4,
3318
+ * Quaternion,
3319
+ * Matrix4,
3320
+ * Spherical,
3321
+ * Box3,
3322
+ * Sphere,
3323
+ * Raycaster,
3324
+ * MathUtils,
3325
+ * } from 'three';
3326
+ *
3327
+ * const subsetOfTHREE = {
3328
+ * Vector2 : Vector2,
3329
+ * Vector3 : Vector3,
3330
+ * Vector4 : Vector4,
3331
+ * Quaternion: Quaternion,
3332
+ * Matrix4 : Matrix4,
3333
+ * Spherical : Spherical,
3334
+ * Box3 : Box3,
3335
+ * Sphere : Sphere,
3336
+ * Raycaster : Raycaster,
3337
+ * };
3338
+
3339
+ * CameraControls.install( { THREE: subsetOfTHREE } );
3340
+ * ```
3341
+ * @category Statics
3342
+ */
3343
+ static install(libs) {
3344
+ THREE = libs.THREE;
3345
+ _ORIGIN = Object.freeze(new THREE.Vector3(0, 0, 0));
3346
+ _AXIS_Y = Object.freeze(new THREE.Vector3(0, 1, 0));
3347
+ _AXIS_Z = Object.freeze(new THREE.Vector3(0, 0, 1));
3348
+ _PLANE_XY = Object.freeze(new THREE.Plane(_AXIS_Z, 0));
3349
+ _v2 = new THREE.Vector2();
3350
+ _v3A = new THREE.Vector3();
3351
+ _v3B = new THREE.Vector3();
3352
+ _v3C = new THREE.Vector3();
3353
+ _cameraDirection = new THREE.Vector3();
3354
+ _xColumn = new THREE.Vector3();
3355
+ _yColumn = new THREE.Vector3();
3356
+ _zColumn = new THREE.Vector3();
3357
+ _deltaTarget = new THREE.Vector3();
3358
+ _deltaOffset = new THREE.Vector3();
3359
+ _sphericalA = new THREE.Spherical();
3360
+ _sphericalB = new THREE.Spherical();
3361
+ _box3A = new THREE.Box3();
3362
+ _box3B = new THREE.Box3();
3363
+ _sphere = new THREE.Sphere();
3364
+ _quaternionA = new THREE.Quaternion();
3365
+ _quaternionB = new THREE.Quaternion();
3366
+ _rotationMatrix = new THREE.Matrix4();
3367
+ _raycaster = new THREE.Raycaster();
3368
+ }
3369
+ /**
3370
+ * list all ACTIONs
3371
+ * @category Statics
3372
+ */
3373
+ static get ACTION() {
3374
+ return ACTION;
3375
+ }
3376
+ /**
3377
+ * @deprecated Use `cameraControls.mouseButtons.left = CameraControls.ACTION.SCREEN_PAN` instead.
3378
+ */
3379
+ set verticalDragToForward(_) {
3380
+ console.warn("camera-controls: `verticalDragToForward` was removed. Use `mouseButtons.left = CameraControls.ACTION.SCREEN_PAN` instead.");
3381
+ }
3382
+ /**
3383
+ * The camera to be controlled
3384
+ * @category Properties
3385
+ */
3386
+ get camera() {
3387
+ return this._camera;
3388
+ }
3389
+ set camera(camera) {
3390
+ this._camera = camera;
3391
+ this.updateCameraUp();
3392
+ this._camera.updateProjectionMatrix();
3393
+ this._updateNearPlaneCorners();
3394
+ this._needsUpdate = true;
3395
+ }
3396
+ /**
3397
+ * Whether or not the controls are enabled.
3398
+ * `false` to disable user dragging/touch-move, but all methods works.
3399
+ * @category Properties
3400
+ */
3401
+ get enabled() {
3402
+ return this._enabled;
3403
+ }
3404
+ set enabled(enabled) {
3405
+ this._enabled = enabled;
3406
+ if (!this._domElement)
3407
+ return;
3408
+ if (enabled) {
3409
+ this._domElement.style.touchAction = "none";
3410
+ this._domElement.style.userSelect = "none";
3411
+ this._domElement.style.webkitUserSelect = "none";
3412
+ } else {
3413
+ this.cancel();
3414
+ this._domElement.style.touchAction = "";
3415
+ this._domElement.style.userSelect = "";
3416
+ this._domElement.style.webkitUserSelect = "";
3417
+ }
3418
+ }
3419
+ /**
3420
+ * Returns `true` if the controls are active updating.
3421
+ * readonly value.
3422
+ * @category Properties
3423
+ */
3424
+ get active() {
3425
+ return !this._hasRested;
3426
+ }
3427
+ /**
3428
+ * Getter for the current `ACTION`.
3429
+ * readonly value.
3430
+ * @category Properties
3431
+ */
3432
+ get currentAction() {
3433
+ return this._state;
3434
+ }
3435
+ /**
3436
+ * get/set Current distance.
3437
+ * @category Properties
3438
+ */
3439
+ get distance() {
3440
+ return this._spherical.radius;
3441
+ }
3442
+ set distance(distance) {
3443
+ if (this._spherical.radius === distance && this._sphericalEnd.radius === distance)
3444
+ return;
3445
+ this._spherical.radius = distance;
3446
+ this._sphericalEnd.radius = distance;
3447
+ this._needsUpdate = true;
3448
+ }
3449
+ // horizontal angle
3450
+ /**
3451
+ * get/set the azimuth angle (horizontal) in radians.
3452
+ * Every 360 degrees turn is added to `.azimuthAngle` value, which is accumulative.
3453
+ * @category Properties
3454
+ */
3455
+ get azimuthAngle() {
3456
+ return this._spherical.theta;
3457
+ }
3458
+ set azimuthAngle(azimuthAngle) {
3459
+ if (this._spherical.theta === azimuthAngle && this._sphericalEnd.theta === azimuthAngle)
3460
+ return;
3461
+ this._spherical.theta = azimuthAngle;
3462
+ this._sphericalEnd.theta = azimuthAngle;
3463
+ this._needsUpdate = true;
3464
+ }
3465
+ // vertical angle
3466
+ /**
3467
+ * get/set the polar angle (vertical) in radians.
3468
+ * @category Properties
3469
+ */
3470
+ get polarAngle() {
3471
+ return this._spherical.phi;
3472
+ }
3473
+ set polarAngle(polarAngle) {
3474
+ if (this._spherical.phi === polarAngle && this._sphericalEnd.phi === polarAngle)
3475
+ return;
3476
+ this._spherical.phi = polarAngle;
3477
+ this._sphericalEnd.phi = polarAngle;
3478
+ this._needsUpdate = true;
3479
+ }
3480
+ /**
3481
+ * Whether camera position should be enclosed in the boundary or not.
3482
+ * @category Properties
3483
+ */
3484
+ get boundaryEnclosesCamera() {
3485
+ return this._boundaryEnclosesCamera;
3486
+ }
3487
+ set boundaryEnclosesCamera(boundaryEnclosesCamera) {
3488
+ this._boundaryEnclosesCamera = boundaryEnclosesCamera;
3489
+ this._needsUpdate = true;
3490
+ }
3491
+ /**
3492
+ * Set drag-start, touches and wheel enable area in the domElement.
3493
+ * each values are between `0` and `1` inclusive, where `0` is left/top and `1` is right/bottom of the screen.
3494
+ * e.g. `{ x: 0, y: 0, width: 1, height: 1 }` for entire area.
3495
+ * @category Properties
3496
+ */
3497
+ set interactiveArea(interactiveArea) {
3498
+ this._interactiveArea.width = clamp(interactiveArea.width, 0, 1);
3499
+ this._interactiveArea.height = clamp(interactiveArea.height, 0, 1);
3500
+ this._interactiveArea.x = clamp(interactiveArea.x, 0, 1 - this._interactiveArea.width);
3501
+ this._interactiveArea.y = clamp(interactiveArea.y, 0, 1 - this._interactiveArea.height);
3502
+ }
3503
+ /**
3504
+ * Adds the specified event listener.
3505
+ * Applicable event types (which is `K`) are:
3506
+ * | Event name | Timing |
3507
+ * | ------------------- | ------ |
3508
+ * | `'controlstart'` | When the user starts to control the camera via mouse / touches. ¹ |
3509
+ * | `'control'` | When the user controls the camera (dragging). |
3510
+ * | `'controlend'` | When the user ends to control the camera. ¹ |
3511
+ * | `'transitionstart'` | When any kind of transition starts, either user control or using a method with `enableTransition = true` |
3512
+ * | `'update'` | When the camera position is updated. |
3513
+ * | `'wake'` | When the camera starts moving. |
3514
+ * | `'rest'` | When the camera movement is below `.restThreshold` ². |
3515
+ * | `'sleep'` | When the camera end moving. |
3516
+ *
3517
+ * 1. `mouseButtons.wheel` (Mouse wheel control) does not emit `'controlstart'` and `'controlend'`. `mouseButtons.wheel` uses scroll-event internally, and scroll-event happens intermittently. That means "start" and "end" cannot be detected.
3518
+ * 2. Due to damping, `sleep` will usually fire a few seconds after the camera _appears_ to have stopped moving. If you want to do something (e.g. enable UI, perform another transition) at the point when the camera has stopped, you probably want the `rest` event. This can be fine tuned using the `.restThreshold` parameter. See the [Rest and Sleep Example](https://yomotsu.github.io/camera-controls/examples/rest-and-sleep.html).
3519
+ *
3520
+ * e.g.
3521
+ * ```
3522
+ * cameraControl.addEventListener( 'controlstart', myCallbackFunction );
3523
+ * ```
3524
+ * @param type event name
3525
+ * @param listener handler function
3526
+ * @category Methods
3527
+ */
3528
+ addEventListener(type, listener) {
3529
+ super.addEventListener(type, listener);
3530
+ }
3531
+ /**
3532
+ * Removes the specified event listener
3533
+ * e.g.
3534
+ * ```
3535
+ * cameraControl.addEventListener( 'controlstart', myCallbackFunction );
3536
+ * ```
3537
+ * @param type event name
3538
+ * @param listener handler function
3539
+ * @category Methods
3540
+ */
3541
+ removeEventListener(type, listener) {
3542
+ super.removeEventListener(type, listener);
3543
+ }
3544
+ /**
3545
+ * Rotate azimuthal angle(horizontal) and polar angle(vertical).
3546
+ * Every value is added to the current value.
3547
+ * @param azimuthAngle Azimuth rotate angle. In radian.
3548
+ * @param polarAngle Polar rotate angle. In radian.
3549
+ * @param enableTransition Whether to move smoothly or immediately
3550
+ * @category Methods
3551
+ */
3552
+ rotate(azimuthAngle, polarAngle, enableTransition = false) {
3553
+ return this.rotateTo(this._sphericalEnd.theta + azimuthAngle, this._sphericalEnd.phi + polarAngle, enableTransition);
3554
+ }
3555
+ /**
3556
+ * Rotate azimuthal angle(horizontal) to the given angle and keep the same polar angle(vertical) target.
3557
+ *
3558
+ * e.g.
3559
+ * ```
3560
+ * cameraControls.rotateAzimuthTo( 30 * THREE.MathUtils.DEG2RAD, true );
3561
+ * ```
3562
+ * @param azimuthAngle Azimuth rotate angle. In radian.
3563
+ * @param enableTransition Whether to move smoothly or immediately
3564
+ * @category Methods
3565
+ */
3566
+ rotateAzimuthTo(azimuthAngle, enableTransition = false) {
3567
+ return this.rotateTo(azimuthAngle, this._sphericalEnd.phi, enableTransition);
3568
+ }
3569
+ /**
3570
+ * Rotate polar angle(vertical) to the given angle and keep the same azimuthal angle(horizontal) target.
3571
+ *
3572
+ * e.g.
3573
+ * ```
3574
+ * cameraControls.rotatePolarTo( 30 * THREE.MathUtils.DEG2RAD, true );
3575
+ * ```
3576
+ * @param polarAngle Polar rotate angle. In radian.
3577
+ * @param enableTransition Whether to move smoothly or immediately
3578
+ * @category Methods
3579
+ */
3580
+ rotatePolarTo(polarAngle, enableTransition = false) {
3581
+ return this.rotateTo(this._sphericalEnd.theta, polarAngle, enableTransition);
3582
+ }
3583
+ /**
3584
+ * Rotate azimuthal angle(horizontal) and polar angle(vertical) to the given angle.
3585
+ * Camera view will rotate over the orbit pivot absolutely:
3586
+ *
3587
+ * azimuthAngle
3588
+ * ```
3589
+ * 0º
3590
+ * \
3591
+ * 90º -----+----- -90º
3592
+ * \
3593
+ * 180º
3594
+ * ```
3595
+ * | direction | angle |
3596
+ * | --------- | ---------------------- |
3597
+ * | front | 0º |
3598
+ * | left | 90º (`Math.PI / 2`) |
3599
+ * | right | -90º (`- Math.PI / 2`) |
3600
+ * | back | 180º (`Math.PI`) |
3601
+ *
3602
+ * polarAngle
3603
+ * ```
3604
+ * 180º
3605
+ * |
3606
+ * 90º
3607
+ * |
3608
+ * 0º
3609
+ * ```
3610
+ * | direction | angle |
3611
+ * | -------------------- | ---------------------- |
3612
+ * | top/sky | 180º (`Math.PI`) |
3613
+ * | horizontal from view | 90º (`Math.PI / 2`) |
3614
+ * | bottom/floor | 0º |
3615
+ *
3616
+ * @param azimuthAngle Azimuth rotate angle to. In radian.
3617
+ * @param polarAngle Polar rotate angle to. In radian.
3618
+ * @param enableTransition Whether to move smoothly or immediately
3619
+ * @category Methods
3620
+ */
3621
+ rotateTo(azimuthAngle, polarAngle, enableTransition = false) {
3622
+ this._isUserControllingRotate = false;
3623
+ const theta = clamp(azimuthAngle, this.minAzimuthAngle, this.maxAzimuthAngle);
3624
+ const phi = clamp(polarAngle, this.minPolarAngle, this.maxPolarAngle);
3625
+ this._sphericalEnd.theta = theta;
3626
+ this._sphericalEnd.phi = phi;
3627
+ this._sphericalEnd.makeSafe();
3628
+ this._needsUpdate = true;
3629
+ if (!enableTransition) {
3630
+ this._spherical.theta = this._sphericalEnd.theta;
3631
+ this._spherical.phi = this._sphericalEnd.phi;
3632
+ }
3633
+ const resolveImmediately = !enableTransition || approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) && approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold);
3634
+ return this._createOnRestPromise(resolveImmediately);
3635
+ }
3636
+ /**
3637
+ * Dolly in/out camera position.
3638
+ * @param distance Distance of dollyIn. Negative number for dollyOut.
3639
+ * @param enableTransition Whether to move smoothly or immediately.
3640
+ * @category Methods
3641
+ */
3642
+ dolly(distance, enableTransition = false) {
3643
+ return this.dollyTo(this._sphericalEnd.radius - distance, enableTransition);
3644
+ }
3645
+ /**
3646
+ * Dolly in/out camera position to given distance.
3647
+ * @param distance Distance of dolly.
3648
+ * @param enableTransition Whether to move smoothly or immediately.
3649
+ * @category Methods
3650
+ */
3651
+ dollyTo(distance, enableTransition = false) {
3652
+ this._isUserControllingDolly = false;
3653
+ this._isWheelControllingDolly = false;
3654
+ this._lastDollyDirection = DOLLY_DIRECTION.NONE;
3655
+ this._changedDolly = 0;
3656
+ return this._dollyToNoClamp(clamp(distance, this.minDistance, this.maxDistance), enableTransition);
3657
+ }
3658
+ _dollyToNoClamp(distance, enableTransition = false) {
3659
+ const lastRadius = this._sphericalEnd.radius;
3660
+ const hasCollider = this.colliderMeshes.length >= 1;
3661
+ if (hasCollider) {
3662
+ const maxDistanceByCollisionTest = this._collisionTest();
3663
+ const isCollided = approxEquals(maxDistanceByCollisionTest, this._spherical.radius);
3664
+ const isDollyIn = lastRadius > distance;
3665
+ if (!isDollyIn && isCollided)
3666
+ return Promise.resolve();
3667
+ this._sphericalEnd.radius = Math.min(distance, maxDistanceByCollisionTest);
3668
+ } else {
3669
+ this._sphericalEnd.radius = distance;
3670
+ }
3671
+ this._needsUpdate = true;
3672
+ if (!enableTransition) {
3673
+ this._spherical.radius = this._sphericalEnd.radius;
3674
+ }
3675
+ const resolveImmediately = !enableTransition || approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
3676
+ return this._createOnRestPromise(resolveImmediately);
3677
+ }
3678
+ /**
3679
+ * Dolly in, but does not change the distance between the target and the camera, and moves the target position instead.
3680
+ * Specify a negative value for dolly out.
3681
+ * @param distance Distance of dolly.
3682
+ * @param enableTransition Whether to move smoothly or immediately.
3683
+ * @category Methods
3684
+ */
3685
+ dollyInFixed(distance, enableTransition = false) {
3686
+ this._targetEnd.add(this._getCameraDirection(_cameraDirection).multiplyScalar(distance));
3687
+ if (!enableTransition) {
3688
+ this._target.copy(this._targetEnd);
3689
+ }
3690
+ const resolveImmediately = !enableTransition || approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) && approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) && approxEquals(this._target.z, this._targetEnd.z, this.restThreshold);
3691
+ return this._createOnRestPromise(resolveImmediately);
3692
+ }
3693
+ /**
3694
+ * Zoom in/out camera. The value is added to camera zoom.
3695
+ * Limits set with `.minZoom` and `.maxZoom`
3696
+ * @param zoomStep zoom scale
3697
+ * @param enableTransition Whether to move smoothly or immediately
3698
+ * @category Methods
3699
+ */
3700
+ zoom(zoomStep, enableTransition = false) {
3701
+ return this.zoomTo(this._zoomEnd + zoomStep, enableTransition);
3702
+ }
3703
+ /**
3704
+ * Zoom in/out camera to given scale. The value overwrites camera zoom.
3705
+ * Limits set with .minZoom and .maxZoom
3706
+ * @param zoom
3707
+ * @param enableTransition
3708
+ * @category Methods
3709
+ */
3710
+ zoomTo(zoom, enableTransition = false) {
3711
+ this._isUserControllingZoom = false;
3712
+ this._isWheelControllingZoom = false;
3713
+ this._zoomEnd = clamp(zoom, this.minZoom, this.maxZoom);
3714
+ this._needsUpdate = true;
3715
+ if (!enableTransition) {
3716
+ this._zoom = this._zoomEnd;
3717
+ }
3718
+ const resolveImmediately = !enableTransition || approxEquals(this._zoom, this._zoomEnd, this.restThreshold);
3719
+ this._changedZoom = 0;
3720
+ return this._createOnRestPromise(resolveImmediately);
3721
+ }
3722
+ /**
3723
+ * @deprecated `pan()` has been renamed to `truck()`
3724
+ * @category Methods
3725
+ */
3726
+ pan(x, y, enableTransition = false) {
3727
+ console.warn("`pan` has been renamed to `truck`");
3728
+ return this.truck(x, y, enableTransition);
3729
+ }
3730
+ /**
3731
+ * Truck and pedestal camera using current azimuthal angle
3732
+ * @param x Horizontal translate amount
3733
+ * @param y Vertical translate amount
3734
+ * @param enableTransition Whether to move smoothly or immediately
3735
+ * @category Methods
3736
+ */
3737
+ truck(x, y, enableTransition = false) {
3738
+ this._camera.updateMatrix();
3739
+ _xColumn.setFromMatrixColumn(this._camera.matrix, 0);
3740
+ _yColumn.setFromMatrixColumn(this._camera.matrix, 1);
3741
+ _xColumn.multiplyScalar(x);
3742
+ _yColumn.multiplyScalar(-y);
3743
+ const offset = _v3A.copy(_xColumn).add(_yColumn);
3744
+ const to = _v3B.copy(this._targetEnd).add(offset);
3745
+ return this.moveTo(to.x, to.y, to.z, enableTransition);
3746
+ }
3747
+ /**
3748
+ * Move forward / backward.
3749
+ * @param distance Amount to move forward / backward. Negative value to move backward
3750
+ * @param enableTransition Whether to move smoothly or immediately
3751
+ * @category Methods
3752
+ */
3753
+ forward(distance, enableTransition = false) {
3754
+ _v3A.setFromMatrixColumn(this._camera.matrix, 0);
3755
+ _v3A.crossVectors(this._camera.up, _v3A);
3756
+ _v3A.multiplyScalar(distance);
3757
+ const to = _v3B.copy(this._targetEnd).add(_v3A);
3758
+ return this.moveTo(to.x, to.y, to.z, enableTransition);
3759
+ }
3760
+ /**
3761
+ * Move up / down.
3762
+ * @param height Amount to move up / down. Negative value to move down
3763
+ * @param enableTransition Whether to move smoothly or immediately
3764
+ * @category Methods
3765
+ */
3766
+ elevate(height, enableTransition = false) {
3767
+ _v3A.copy(this._camera.up).multiplyScalar(height);
3768
+ return this.moveTo(this._targetEnd.x + _v3A.x, this._targetEnd.y + _v3A.y, this._targetEnd.z + _v3A.z, enableTransition);
3769
+ }
3770
+ /**
3771
+ * Move target position to given point.
3772
+ * @param x x coord to move center position
3773
+ * @param y y coord to move center position
3774
+ * @param z z coord to move center position
3775
+ * @param enableTransition Whether to move smoothly or immediately
3776
+ * @category Methods
3777
+ */
3778
+ moveTo(x, y, z, enableTransition = false) {
3779
+ this._isUserControllingTruck = false;
3780
+ const offset = _v3A.set(x, y, z).sub(this._targetEnd);
3781
+ this._encloseToBoundary(this._targetEnd, offset, this.boundaryFriction);
3782
+ this._needsUpdate = true;
3783
+ if (!enableTransition) {
3784
+ this._target.copy(this._targetEnd);
3785
+ }
3786
+ const resolveImmediately = !enableTransition || approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) && approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) && approxEquals(this._target.z, this._targetEnd.z, this.restThreshold);
3787
+ return this._createOnRestPromise(resolveImmediately);
3788
+ }
3789
+ /**
3790
+ * Look in the given point direction.
3791
+ * @param x point x.
3792
+ * @param y point y.
3793
+ * @param z point z.
3794
+ * @param enableTransition Whether to move smoothly or immediately.
3795
+ * @returns Transition end promise
3796
+ * @category Methods
3797
+ */
3798
+ lookInDirectionOf(x, y, z, enableTransition = false) {
3799
+ const point = _v3A.set(x, y, z);
3800
+ const direction = point.sub(this._targetEnd).normalize();
3801
+ const position = direction.multiplyScalar(-this._sphericalEnd.radius).add(this._targetEnd);
3802
+ return this.setPosition(position.x, position.y, position.z, enableTransition);
3803
+ }
3804
+ /**
3805
+ * Fit the viewport to the box or the bounding box of the object, using the nearest axis. paddings are in unit.
3806
+ * set `cover: true` to fill enter screen.
3807
+ * e.g.
3808
+ * ```
3809
+ * cameraControls.fitToBox( myMesh );
3810
+ * ```
3811
+ * @param box3OrObject Axis aligned bounding box to fit the view.
3812
+ * @param enableTransition Whether to move smoothly or immediately.
3813
+ * @param options | `<object>` { cover: boolean, paddingTop: number, paddingLeft: number, paddingBottom: number, paddingRight: number }
3814
+ * @returns Transition end promise
3815
+ * @category Methods
3816
+ */
3817
+ fitToBox(box3OrObject, enableTransition, { cover = false, paddingLeft = 0, paddingRight = 0, paddingBottom = 0, paddingTop = 0 } = {}) {
3818
+ const promises = [];
3819
+ const aabb = box3OrObject.isBox3 ? _box3A.copy(box3OrObject) : _box3A.setFromObject(box3OrObject);
3820
+ if (aabb.isEmpty()) {
3821
+ console.warn("camera-controls: fitTo() cannot be used with an empty box. Aborting");
3822
+ Promise.resolve();
3823
+ }
3824
+ const theta = roundToStep(this._sphericalEnd.theta, PI_HALF);
3825
+ const phi = roundToStep(this._sphericalEnd.phi, PI_HALF);
3826
+ promises.push(this.rotateTo(theta, phi, enableTransition));
3827
+ const normal = _v3A.setFromSpherical(this._sphericalEnd).normalize();
3828
+ const rotation = _quaternionA.setFromUnitVectors(normal, _AXIS_Z);
3829
+ const viewFromPolar = approxEquals(Math.abs(normal.y), 1);
3830
+ if (viewFromPolar) {
3831
+ rotation.multiply(_quaternionB.setFromAxisAngle(_AXIS_Y, theta));
3832
+ }
3833
+ rotation.multiply(this._yAxisUpSpaceInverse);
3834
+ const bb = _box3B.makeEmpty();
3835
+ _v3B.copy(aabb.min).applyQuaternion(rotation);
3836
+ bb.expandByPoint(_v3B);
3837
+ _v3B.copy(aabb.min).setX(aabb.max.x).applyQuaternion(rotation);
3838
+ bb.expandByPoint(_v3B);
3839
+ _v3B.copy(aabb.min).setY(aabb.max.y).applyQuaternion(rotation);
3840
+ bb.expandByPoint(_v3B);
3841
+ _v3B.copy(aabb.max).setZ(aabb.min.z).applyQuaternion(rotation);
3842
+ bb.expandByPoint(_v3B);
3843
+ _v3B.copy(aabb.min).setZ(aabb.max.z).applyQuaternion(rotation);
3844
+ bb.expandByPoint(_v3B);
3845
+ _v3B.copy(aabb.max).setY(aabb.min.y).applyQuaternion(rotation);
3846
+ bb.expandByPoint(_v3B);
3847
+ _v3B.copy(aabb.max).setX(aabb.min.x).applyQuaternion(rotation);
3848
+ bb.expandByPoint(_v3B);
3849
+ _v3B.copy(aabb.max).applyQuaternion(rotation);
3850
+ bb.expandByPoint(_v3B);
3851
+ bb.min.x -= paddingLeft;
3852
+ bb.min.y -= paddingBottom;
3853
+ bb.max.x += paddingRight;
3854
+ bb.max.y += paddingTop;
3855
+ rotation.setFromUnitVectors(_AXIS_Z, normal);
3856
+ if (viewFromPolar) {
3857
+ rotation.premultiply(_quaternionB.invert());
3858
+ }
3859
+ rotation.premultiply(this._yAxisUpSpace);
3860
+ const bbSize = bb.getSize(_v3A);
3861
+ const center = bb.getCenter(_v3B).applyQuaternion(rotation);
3862
+ if (isPerspectiveCamera(this._camera)) {
3863
+ const distance = this.getDistanceToFitBox(bbSize.x, bbSize.y, bbSize.z, cover);
3864
+ promises.push(this.moveTo(center.x, center.y, center.z, enableTransition));
3865
+ promises.push(this.dollyTo(distance, enableTransition));
3866
+ promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
3867
+ } else if (isOrthographicCamera(this._camera)) {
3868
+ const camera = this._camera;
3869
+ const width = camera.right - camera.left;
3870
+ const height = camera.top - camera.bottom;
3871
+ const zoom = cover ? Math.max(width / bbSize.x, height / bbSize.y) : Math.min(width / bbSize.x, height / bbSize.y);
3872
+ promises.push(this.moveTo(center.x, center.y, center.z, enableTransition));
3873
+ promises.push(this.zoomTo(zoom, enableTransition));
3874
+ promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
3875
+ }
3876
+ return Promise.all(promises);
3877
+ }
3878
+ /**
3879
+ * Fit the viewport to the sphere or the bounding sphere of the object.
3880
+ * @param sphereOrMesh
3881
+ * @param enableTransition
3882
+ * @category Methods
3883
+ */
3884
+ fitToSphere(sphereOrMesh, enableTransition) {
3885
+ const promises = [];
3886
+ const isObject3D = "isObject3D" in sphereOrMesh;
3887
+ const boundingSphere = isObject3D ? CameraControls.createBoundingSphere(sphereOrMesh, _sphere) : _sphere.copy(sphereOrMesh);
3888
+ promises.push(this.moveTo(boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z, enableTransition));
3889
+ if (isPerspectiveCamera(this._camera)) {
3890
+ const distanceToFit = this.getDistanceToFitSphere(boundingSphere.radius);
3891
+ promises.push(this.dollyTo(distanceToFit, enableTransition));
3892
+ } else if (isOrthographicCamera(this._camera)) {
3893
+ const width = this._camera.right - this._camera.left;
3894
+ const height = this._camera.top - this._camera.bottom;
3895
+ const diameter = 2 * boundingSphere.radius;
3896
+ const zoom = Math.min(width / diameter, height / diameter);
3897
+ promises.push(this.zoomTo(zoom, enableTransition));
3898
+ }
3899
+ promises.push(this.setFocalOffset(0, 0, 0, enableTransition));
3900
+ return Promise.all(promises);
3901
+ }
3902
+ /**
3903
+ * Look at the `target` from the `position`.
3904
+ * @param positionX
3905
+ * @param positionY
3906
+ * @param positionZ
3907
+ * @param targetX
3908
+ * @param targetY
3909
+ * @param targetZ
3910
+ * @param enableTransition
3911
+ * @category Methods
3912
+ */
3913
+ setLookAt(positionX, positionY, positionZ, targetX, targetY, targetZ, enableTransition = false) {
3914
+ this._isUserControllingRotate = false;
3915
+ this._isUserControllingDolly = false;
3916
+ this._isWheelControllingDolly = false;
3917
+ this._isUserControllingTruck = false;
3918
+ this._lastDollyDirection = DOLLY_DIRECTION.NONE;
3919
+ this._changedDolly = 0;
3920
+ const target = _v3B.set(targetX, targetY, targetZ);
3921
+ const position = _v3A.set(positionX, positionY, positionZ);
3922
+ this._targetEnd.copy(target);
3923
+ this._sphericalEnd.setFromVector3(position.sub(target).applyQuaternion(this._yAxisUpSpace));
3924
+ this._needsUpdate = true;
3925
+ if (!enableTransition) {
3926
+ this._target.copy(this._targetEnd);
3927
+ this._spherical.copy(this._sphericalEnd);
3928
+ }
3929
+ const resolveImmediately = !enableTransition || approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) && approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) && approxEquals(this._target.z, this._targetEnd.z, this.restThreshold) && approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) && approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold) && approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
3930
+ return this._createOnRestPromise(resolveImmediately);
3931
+ }
3932
+ /**
3933
+ * Interpolates between two states.
3934
+ * @param stateA
3935
+ * @param stateB
3936
+ * @param t
3937
+ * @param enableTransition
3938
+ * @category Methods
3939
+ */
3940
+ lerp(stateA, stateB, t, enableTransition = false) {
3941
+ this._isUserControllingRotate = false;
3942
+ this._isUserControllingDolly = false;
3943
+ this._isWheelControllingDolly = false;
3944
+ this._isUserControllingTruck = false;
3945
+ this._lastDollyDirection = DOLLY_DIRECTION.NONE;
3946
+ this._changedDolly = 0;
3947
+ const targetA = _v3A.set(...stateA.target);
3948
+ if ("spherical" in stateA) {
3949
+ _sphericalA.set(...stateA.spherical);
3950
+ } else {
3951
+ const positionA = _v3B.set(...stateA.position);
3952
+ _sphericalA.setFromVector3(positionA.sub(targetA).applyQuaternion(this._yAxisUpSpace));
3953
+ }
3954
+ const targetB = _v3C.set(...stateB.target);
3955
+ if ("spherical" in stateB) {
3956
+ _sphericalB.set(...stateB.spherical);
3957
+ } else {
3958
+ const positionB = _v3B.set(...stateB.position);
3959
+ _sphericalB.setFromVector3(positionB.sub(targetB).applyQuaternion(this._yAxisUpSpace));
3960
+ }
3961
+ this._targetEnd.copy(targetA.lerp(targetB, t));
3962
+ const deltaTheta = _sphericalB.theta - _sphericalA.theta;
3963
+ const deltaPhi = _sphericalB.phi - _sphericalA.phi;
3964
+ const deltaRadius = _sphericalB.radius - _sphericalA.radius;
3965
+ this._sphericalEnd.set(_sphericalA.radius + deltaRadius * t, _sphericalA.phi + deltaPhi * t, _sphericalA.theta + deltaTheta * t);
3966
+ this._needsUpdate = true;
3967
+ if (!enableTransition) {
3968
+ this._target.copy(this._targetEnd);
3969
+ this._spherical.copy(this._sphericalEnd);
3970
+ }
3971
+ const resolveImmediately = !enableTransition || approxEquals(this._target.x, this._targetEnd.x, this.restThreshold) && approxEquals(this._target.y, this._targetEnd.y, this.restThreshold) && approxEquals(this._target.z, this._targetEnd.z, this.restThreshold) && approxEquals(this._spherical.theta, this._sphericalEnd.theta, this.restThreshold) && approxEquals(this._spherical.phi, this._sphericalEnd.phi, this.restThreshold) && approxEquals(this._spherical.radius, this._sphericalEnd.radius, this.restThreshold);
3972
+ return this._createOnRestPromise(resolveImmediately);
3973
+ }
3974
+ /**
3975
+ * Similar to setLookAt, but it interpolates between two states.
3976
+ * @param positionAX
3977
+ * @param positionAY
3978
+ * @param positionAZ
3979
+ * @param targetAX
3980
+ * @param targetAY
3981
+ * @param targetAZ
3982
+ * @param positionBX
3983
+ * @param positionBY
3984
+ * @param positionBZ
3985
+ * @param targetBX
3986
+ * @param targetBY
3987
+ * @param targetBZ
3988
+ * @param t
3989
+ * @param enableTransition
3990
+ * @category Methods
3991
+ */
3992
+ lerpLookAt(positionAX, positionAY, positionAZ, targetAX, targetAY, targetAZ, positionBX, positionBY, positionBZ, targetBX, targetBY, targetBZ, t, enableTransition = false) {
3993
+ return this.lerp({
3994
+ position: [positionAX, positionAY, positionAZ],
3995
+ target: [targetAX, targetAY, targetAZ]
3996
+ }, {
3997
+ position: [positionBX, positionBY, positionBZ],
3998
+ target: [targetBX, targetBY, targetBZ]
3999
+ }, t, enableTransition);
4000
+ }
4001
+ /**
4002
+ * Set angle and distance by given position.
4003
+ * An alias of `setLookAt()`, without target change. Thus keep gazing at the current target
4004
+ * @param positionX
4005
+ * @param positionY
4006
+ * @param positionZ
4007
+ * @param enableTransition
4008
+ * @category Methods
4009
+ */
4010
+ setPosition(positionX, positionY, positionZ, enableTransition = false) {
4011
+ return this.setLookAt(positionX, positionY, positionZ, this._targetEnd.x, this._targetEnd.y, this._targetEnd.z, enableTransition);
4012
+ }
4013
+ /**
4014
+ * Set the target position where gaze at.
4015
+ * An alias of `setLookAt()`, without position change. Thus keep the same position.
4016
+ * @param targetX
4017
+ * @param targetY
4018
+ * @param targetZ
4019
+ * @param enableTransition
4020
+ * @category Methods
4021
+ */
4022
+ setTarget(targetX, targetY, targetZ, enableTransition = false) {
4023
+ const pos = this.getPosition(_v3A);
4024
+ const promise = this.setLookAt(pos.x, pos.y, pos.z, targetX, targetY, targetZ, enableTransition);
4025
+ this._sphericalEnd.phi = clamp(this._sphericalEnd.phi, this.minPolarAngle, this.maxPolarAngle);
4026
+ return promise;
4027
+ }
4028
+ /**
4029
+ * Set focal offset using the screen parallel coordinates. z doesn't affect in Orthographic as with Dolly.
4030
+ * @param x
4031
+ * @param y
4032
+ * @param z
4033
+ * @param enableTransition
4034
+ * @category Methods
4035
+ */
4036
+ setFocalOffset(x, y, z, enableTransition = false) {
4037
+ this._isUserControllingOffset = false;
4038
+ this._focalOffsetEnd.set(x, y, z);
4039
+ this._needsUpdate = true;
4040
+ if (!enableTransition)
4041
+ this._focalOffset.copy(this._focalOffsetEnd);
4042
+ const resolveImmediately = !enableTransition || approxEquals(this._focalOffset.x, this._focalOffsetEnd.x, this.restThreshold) && approxEquals(this._focalOffset.y, this._focalOffsetEnd.y, this.restThreshold) && approxEquals(this._focalOffset.z, this._focalOffsetEnd.z, this.restThreshold);
4043
+ return this._createOnRestPromise(resolveImmediately);
4044
+ }
4045
+ /**
4046
+ * Set orbit point without moving the camera.
4047
+ * SHOULD NOT RUN DURING ANIMATIONS. `setOrbitPoint()` will immediately fix the positions.
4048
+ * @param targetX
4049
+ * @param targetY
4050
+ * @param targetZ
4051
+ * @category Methods
4052
+ */
4053
+ setOrbitPoint(targetX, targetY, targetZ) {
4054
+ this._camera.updateMatrixWorld();
4055
+ _xColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 0);
4056
+ _yColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 1);
4057
+ _zColumn.setFromMatrixColumn(this._camera.matrixWorldInverse, 2);
4058
+ const position = _v3A.set(targetX, targetY, targetZ);
4059
+ const distance = position.distanceTo(this._camera.position);
4060
+ const cameraToPoint = position.sub(this._camera.position);
4061
+ _xColumn.multiplyScalar(cameraToPoint.x);
4062
+ _yColumn.multiplyScalar(cameraToPoint.y);
4063
+ _zColumn.multiplyScalar(cameraToPoint.z);
4064
+ _v3A.copy(_xColumn).add(_yColumn).add(_zColumn);
4065
+ _v3A.z = _v3A.z + distance;
4066
+ this.dollyTo(distance, false);
4067
+ this.setFocalOffset(-_v3A.x, _v3A.y, -_v3A.z, false);
4068
+ this.moveTo(targetX, targetY, targetZ, false);
4069
+ }
4070
+ /**
4071
+ * Set the boundary box that encloses the target of the camera. box3 is in THREE.Box3
4072
+ * @param box3
4073
+ * @category Methods
4074
+ */
4075
+ setBoundary(box3) {
4076
+ if (!box3) {
4077
+ this._boundary.min.set(-Infinity, -Infinity, -Infinity);
4078
+ this._boundary.max.set(Infinity, Infinity, Infinity);
4079
+ this._needsUpdate = true;
4080
+ return;
4081
+ }
4082
+ this._boundary.copy(box3);
4083
+ this._boundary.clampPoint(this._targetEnd, this._targetEnd);
4084
+ this._needsUpdate = true;
4085
+ }
4086
+ /**
4087
+ * Set (or unset) the current viewport.
4088
+ * Set this when you want to use renderer viewport and .dollyToCursor feature at the same time.
4089
+ * @param viewportOrX
4090
+ * @param y
4091
+ * @param width
4092
+ * @param height
4093
+ * @category Methods
4094
+ */
4095
+ setViewport(viewportOrX, y, width, height) {
4096
+ if (viewportOrX === null) {
4097
+ this._viewport = null;
4098
+ return;
4099
+ }
4100
+ this._viewport = this._viewport || new THREE.Vector4();
4101
+ if (typeof viewportOrX === "number") {
4102
+ this._viewport.set(viewportOrX, y, width, height);
4103
+ } else {
4104
+ this._viewport.copy(viewportOrX);
4105
+ }
4106
+ }
4107
+ /**
4108
+ * Calculate the distance to fit the box.
4109
+ * @param width box width
4110
+ * @param height box height
4111
+ * @param depth box depth
4112
+ * @returns distance
4113
+ * @category Methods
4114
+ */
4115
+ getDistanceToFitBox(width, height, depth, cover = false) {
4116
+ if (notSupportedInOrthographicCamera(this._camera, "getDistanceToFitBox"))
4117
+ return this._spherical.radius;
4118
+ const boundingRectAspect = width / height;
4119
+ const fov = this._camera.getEffectiveFOV() * DEG2RAD;
4120
+ const aspect = this._camera.aspect;
4121
+ const heightToFit = (cover ? boundingRectAspect > aspect : boundingRectAspect < aspect) ? height : width / aspect;
4122
+ return heightToFit * 0.5 / Math.tan(fov * 0.5) + depth * 0.5;
4123
+ }
4124
+ /**
4125
+ * Calculate the distance to fit the sphere.
4126
+ * @param radius sphere radius
4127
+ * @returns distance
4128
+ * @category Methods
4129
+ */
4130
+ getDistanceToFitSphere(radius) {
4131
+ if (notSupportedInOrthographicCamera(this._camera, "getDistanceToFitSphere"))
4132
+ return this._spherical.radius;
4133
+ const vFOV = this._camera.getEffectiveFOV() * DEG2RAD;
4134
+ const hFOV = Math.atan(Math.tan(vFOV * 0.5) * this._camera.aspect) * 2;
4135
+ const fov = 1 < this._camera.aspect ? vFOV : hFOV;
4136
+ return radius / Math.sin(fov * 0.5);
4137
+ }
4138
+ /**
4139
+ * Returns the orbit center position, where the camera looking at.
4140
+ * @param out The receiving Vector3 instance to copy the result
4141
+ * @param receiveEndValue Whether receive the transition end coords or current. default is `true`
4142
+ * @category Methods
4143
+ */
4144
+ getTarget(out, receiveEndValue = true) {
4145
+ const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
4146
+ return _out.copy(receiveEndValue ? this._targetEnd : this._target);
4147
+ }
4148
+ /**
4149
+ * Returns the camera position.
4150
+ * @param out The receiving Vector3 instance to copy the result
4151
+ * @param receiveEndValue Whether receive the transition end coords or current. default is `true`
4152
+ * @category Methods
4153
+ */
4154
+ getPosition(out, receiveEndValue = true) {
4155
+ const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
4156
+ return _out.setFromSpherical(receiveEndValue ? this._sphericalEnd : this._spherical).applyQuaternion(this._yAxisUpSpaceInverse).add(receiveEndValue ? this._targetEnd : this._target);
4157
+ }
4158
+ /**
4159
+ * Returns the spherical coordinates of the orbit.
4160
+ * @param out The receiving Spherical instance to copy the result
4161
+ * @param receiveEndValue Whether receive the transition end coords or current. default is `true`
4162
+ * @category Methods
4163
+ */
4164
+ getSpherical(out, receiveEndValue = true) {
4165
+ const _out = out || new THREE.Spherical();
4166
+ return _out.copy(receiveEndValue ? this._sphericalEnd : this._spherical);
4167
+ }
4168
+ /**
4169
+ * Returns the focal offset, which is how much the camera appears to be translated in screen parallel coordinates.
4170
+ * @param out The receiving Vector3 instance to copy the result
4171
+ * @param receiveEndValue Whether receive the transition end coords or current. default is `true`
4172
+ * @category Methods
4173
+ */
4174
+ getFocalOffset(out, receiveEndValue = true) {
4175
+ const _out = !!out && out.isVector3 ? out : new THREE.Vector3();
4176
+ return _out.copy(receiveEndValue ? this._focalOffsetEnd : this._focalOffset);
4177
+ }
4178
+ /**
4179
+ * Normalize camera azimuth angle (horizontal rotation) between -180 and 180 degrees.
4180
+ * @returns This CameraControls instance.
4181
+ * @category Methods
4182
+ */
4183
+ normalizeRotations() {
4184
+ this._sphericalEnd.theta = (this._sphericalEnd.theta % PI_2 + PI_2) % PI_2;
4185
+ if (this._sphericalEnd.theta > Math.PI)
4186
+ this._sphericalEnd.theta -= PI_2;
4187
+ this._spherical.theta += PI_2 * Math.round((this._sphericalEnd.theta - this._spherical.theta) / PI_2);
4188
+ return this;
4189
+ }
4190
+ /**
4191
+ * stop all transitions.
4192
+ */
4193
+ stop() {
4194
+ this._focalOffset.copy(this._focalOffsetEnd);
4195
+ this._target.copy(this._targetEnd);
4196
+ this._spherical.copy(this._sphericalEnd);
4197
+ this._zoom = this._zoomEnd;
4198
+ }
4199
+ /**
4200
+ * Reset all rotation and position to defaults.
4201
+ * @param enableTransition
4202
+ * @category Methods
4203
+ */
4204
+ reset(enableTransition = false) {
4205
+ if (!approxEquals(this._camera.up.x, this._cameraUp0.x) || !approxEquals(this._camera.up.y, this._cameraUp0.y) || !approxEquals(this._camera.up.z, this._cameraUp0.z)) {
4206
+ this._camera.up.copy(this._cameraUp0);
4207
+ const position = this.getPosition(_v3A);
4208
+ this.updateCameraUp();
4209
+ this.setPosition(position.x, position.y, position.z);
4210
+ }
4211
+ const promises = [
4212
+ this.setLookAt(this._position0.x, this._position0.y, this._position0.z, this._target0.x, this._target0.y, this._target0.z, enableTransition),
4213
+ this.setFocalOffset(this._focalOffset0.x, this._focalOffset0.y, this._focalOffset0.z, enableTransition),
4214
+ this.zoomTo(this._zoom0, enableTransition)
4215
+ ];
4216
+ return Promise.all(promises);
4217
+ }
4218
+ /**
4219
+ * Set current camera position as the default position.
4220
+ * @category Methods
4221
+ */
4222
+ saveState() {
4223
+ this._cameraUp0.copy(this._camera.up);
4224
+ this.getTarget(this._target0);
4225
+ this.getPosition(this._position0);
4226
+ this._zoom0 = this._zoom;
4227
+ this._focalOffset0.copy(this._focalOffset);
4228
+ }
4229
+ /**
4230
+ * Sync camera-up direction.
4231
+ * When camera-up vector is changed, `.updateCameraUp()` must be called.
4232
+ * @category Methods
4233
+ */
4234
+ updateCameraUp() {
4235
+ this._yAxisUpSpace.setFromUnitVectors(this._camera.up, _AXIS_Y);
4236
+ this._yAxisUpSpaceInverse.copy(this._yAxisUpSpace).invert();
4237
+ }
4238
+ /**
4239
+ * Apply current camera-up direction to the camera.
4240
+ * The orbit system will be re-initialized with the current position.
4241
+ * @category Methods
4242
+ */
4243
+ applyCameraUp() {
4244
+ const cameraDirection = _v3A.subVectors(this._target, this._camera.position).normalize();
4245
+ const side = _v3B.crossVectors(cameraDirection, this._camera.up);
4246
+ this._camera.up.crossVectors(side, cameraDirection).normalize();
4247
+ this._camera.updateMatrixWorld();
4248
+ const position = this.getPosition(_v3A);
4249
+ this.updateCameraUp();
4250
+ this.setPosition(position.x, position.y, position.z);
4251
+ }
4252
+ /**
4253
+ * Update camera position and directions.
4254
+ * This should be called in your tick loop every time, and returns true if re-rendering is needed.
4255
+ * @param delta
4256
+ * @returns updated
4257
+ * @category Methods
4258
+ */
4259
+ update(delta) {
4260
+ const deltaTheta = this._sphericalEnd.theta - this._spherical.theta;
4261
+ const deltaPhi = this._sphericalEnd.phi - this._spherical.phi;
4262
+ const deltaRadius = this._sphericalEnd.radius - this._spherical.radius;
4263
+ const deltaTarget = _deltaTarget.subVectors(this._targetEnd, this._target);
4264
+ const deltaOffset = _deltaOffset.subVectors(this._focalOffsetEnd, this._focalOffset);
4265
+ const deltaZoom = this._zoomEnd - this._zoom;
4266
+ if (approxZero(deltaTheta)) {
4267
+ this._thetaVelocity.value = 0;
4268
+ this._spherical.theta = this._sphericalEnd.theta;
4269
+ } else {
4270
+ const smoothTime = this._isUserControllingRotate ? this.draggingSmoothTime : this.azimuthTime;
4271
+ this._spherical.theta = smoothDamp(this._spherical.theta, this._sphericalEnd.theta, this._thetaVelocity, smoothTime, Infinity, delta);
4272
+ this._needsUpdate = true;
4273
+ }
4274
+ if (approxZero(deltaPhi)) {
4275
+ this._phiVelocity.value = 0;
4276
+ this._spherical.phi = this._sphericalEnd.phi;
4277
+ } else {
4278
+ const smoothTime = this._isUserControllingRotate ? this.draggingSmoothTime : this.polarTime;
4279
+ this._spherical.phi = smoothDamp(this._spherical.phi, this._sphericalEnd.phi, this._phiVelocity, smoothTime, Infinity, delta);
4280
+ this._needsUpdate = true;
4281
+ }
4282
+ if (approxZero(deltaRadius)) {
4283
+ this._radiusVelocity.value = 0;
4284
+ this._spherical.radius = this._sphericalEnd.radius;
4285
+ } else {
4286
+ const smoothTime = this._isUserControllingDolly ? this._isWheelControllingDolly ? this.wheelSmoothTime : this.draggingSmoothTime : this.dollyTime;
4287
+ this._spherical.radius = smoothDamp(this._spherical.radius, this._sphericalEnd.radius, this._radiusVelocity, smoothTime, this.maxSpeed, delta);
4288
+ this._needsUpdate = true;
4289
+ }
4290
+ if (approxZero(deltaTarget.x) && approxZero(deltaTarget.y) && approxZero(deltaTarget.z)) {
4291
+ this._targetVelocity.set(0, 0, 0);
4292
+ this._target.copy(this._targetEnd);
4293
+ } else {
4294
+ const smoothTime = this._isUserControllingTruck ? this.draggingSmoothTime : this.truckTime;
4295
+ smoothDampVec3(this._target, this._targetEnd, this._targetVelocity, smoothTime, this.maxSpeed, delta, this._target);
4296
+ this._needsUpdate = true;
4297
+ }
4298
+ if (approxZero(deltaOffset.x) && approxZero(deltaOffset.y) && approxZero(deltaOffset.z)) {
4299
+ this._focalOffsetVelocity.set(0, 0, 0);
4300
+ this._focalOffset.copy(this._focalOffsetEnd);
4301
+ } else {
4302
+ const smoothTime = this._isUserControllingOffset ? this.draggingSmoothTime : this.smoothTime;
4303
+ smoothDampVec3(this._focalOffset, this._focalOffsetEnd, this._focalOffsetVelocity, smoothTime, this.maxSpeed, delta, this._focalOffset);
4304
+ this._needsUpdate = true;
4305
+ }
4306
+ if (approxZero(deltaZoom)) {
4307
+ this._zoomVelocity.value = 0;
4308
+ this._zoom = this._zoomEnd;
4309
+ } else {
4310
+ const smoothTime = this._isUserControllingZoom ? this._isWheelControllingZoom ? this.wheelSmoothTime : this.draggingSmoothTime : this.smoothTime;
4311
+ this._zoom = smoothDamp(this._zoom, this._zoomEnd, this._zoomVelocity, smoothTime, Infinity, delta);
4312
+ }
4313
+ if (this.dollyToCursor) {
4314
+ if (isPerspectiveCamera(this._camera) && this._changedDolly !== 0) {
4315
+ const dollyControlAmount = this._spherical.radius - this._lastDistance;
4316
+ const prevRadius = this._sphericalEnd.radius - dollyControlAmount;
4317
+ const lerpRatio = (prevRadius - this._sphericalEnd.radius) / this._sphericalEnd.radius;
4318
+ const intersection = this._cursorToPlane(this._dollyControlCoord.x, this._dollyControlCoord.y);
4319
+ if (intersection) this._dollyToCursorTarget.copy(intersection);
4320
+ const newTargetEnd = _v3B.copy(this._targetEnd).lerp(this._dollyToCursorTarget, lerpRatio);
4321
+ const isMin = this._lastDollyDirection === DOLLY_DIRECTION.IN && this._spherical.radius <= this.minDistance;
4322
+ const isMax = this._lastDollyDirection === DOLLY_DIRECTION.OUT && this.maxDistance <= this._spherical.radius;
4323
+ if (this.infinityDolly && (isMin || isMax)) {
4324
+ this._sphericalEnd.radius -= dollyControlAmount;
4325
+ this._spherical.radius -= dollyControlAmount;
4326
+ const cameraDirection = this._getCameraDirection(_cameraDirection);
4327
+ const dollyAmount = _v3C.copy(cameraDirection).multiplyScalar(-dollyControlAmount);
4328
+ newTargetEnd.add(dollyAmount);
4329
+ }
4330
+ this._boundary.clampPoint(newTargetEnd, newTargetEnd);
4331
+ const targetEndDiff = _v3C.subVectors(newTargetEnd, this._targetEnd);
4332
+ this._targetEnd.copy(newTargetEnd);
4333
+ this._target.add(targetEndDiff);
4334
+ this._changedDolly -= dollyControlAmount;
4335
+ if (approxZero(this._changedDolly)) this._changedDolly = 0;
4336
+ } else if (isOrthographicCamera(this._camera) && this._changedZoom !== 0) {
4337
+ const dollyControlAmount = this._zoom - this._lastZoom;
4338
+ const camera = this._camera;
4339
+ const worldCursorPosition = _v3A.set(this._dollyControlCoord.x, this._dollyControlCoord.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera);
4340
+ const quaternion = _v3B.set(0, 0, -1).applyQuaternion(camera.quaternion);
4341
+ const cursor = _v3C.copy(worldCursorPosition).add(quaternion.multiplyScalar(-worldCursorPosition.dot(camera.up)));
4342
+ const prevZoom = this._zoom - dollyControlAmount;
4343
+ const lerpRatio = -(prevZoom - this._zoom) / this._zoom;
4344
+ const cameraDirection = this._getCameraDirection(_cameraDirection);
4345
+ const prevPlaneConstant = this._targetEnd.dot(cameraDirection);
4346
+ const newTargetEnd = _v3A.copy(this._targetEnd).lerp(cursor, lerpRatio);
4347
+ const newPlaneConstant = newTargetEnd.dot(cameraDirection);
4348
+ const pullBack = cameraDirection.multiplyScalar(newPlaneConstant - prevPlaneConstant);
4349
+ newTargetEnd.sub(pullBack);
4350
+ this._boundary.clampPoint(newTargetEnd, newTargetEnd);
4351
+ const targetEndDiff = _v3B.subVectors(newTargetEnd, this._targetEnd);
4352
+ this._targetEnd.copy(newTargetEnd);
4353
+ this._target.add(targetEndDiff);
4354
+ this._changedZoom -= dollyControlAmount;
4355
+ if (approxZero(this._changedZoom))
4356
+ this._changedZoom = 0;
4357
+ }
4358
+ }
4359
+ if (this._camera.zoom !== this._zoom) {
4360
+ this._camera.zoom = this._zoom;
4361
+ this._camera.updateProjectionMatrix();
4362
+ this._updateNearPlaneCorners();
4363
+ this._needsUpdate = true;
4364
+ }
4365
+ this._dragNeedsUpdate = true;
4366
+ const maxDistance = this._collisionTest();
4367
+ this._spherical.radius = Math.min(this._spherical.radius, maxDistance);
4368
+ this._spherical.makeSafe();
4369
+ this._camera.position.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse).add(this._target);
4370
+ this._camera.lookAt(this._target);
4371
+ const affectOffset = !approxZero(this._focalOffset.x) || !approxZero(this._focalOffset.y) || !approxZero(this._focalOffset.z);
4372
+ if (affectOffset) {
4373
+ this._camera.matrix.compose(this._camera.position, this._camera.quaternion, this._camera.scale);
4374
+ _xColumn.setFromMatrixColumn(this._camera.matrix, 0);
4375
+ _yColumn.setFromMatrixColumn(this._camera.matrix, 1);
4376
+ _zColumn.setFromMatrixColumn(this._camera.matrix, 2);
4377
+ _xColumn.multiplyScalar(this._focalOffset.x);
4378
+ _yColumn.multiplyScalar(-this._focalOffset.y);
4379
+ _zColumn.multiplyScalar(this._focalOffset.z);
4380
+ _v3A.copy(_xColumn).add(_yColumn).add(_zColumn);
4381
+ this._camera.position.add(_v3A);
4382
+ this._camera.updateMatrixWorld();
4383
+ }
4384
+ if (this._boundaryEnclosesCamera) {
4385
+ this._encloseToBoundary(this._camera.position.copy(this._target), _v3A.setFromSpherical(this._spherical).applyQuaternion(this._yAxisUpSpaceInverse), 1);
4386
+ }
4387
+ const updated = this._needsUpdate;
4388
+ if (updated && !this._updatedLastTime) {
4389
+ this._hasRested = false;
4390
+ this.dispatchEvent({ type: "wake" });
4391
+ this.dispatchEvent({ type: "update" });
4392
+ } else if (updated) {
4393
+ this.dispatchEvent({ type: "update" });
4394
+ if (approxZero(deltaTheta, this.restThreshold) && approxZero(deltaPhi, this.restThreshold) && approxZero(deltaRadius, this.restThreshold) && approxZero(deltaTarget.x, this.restThreshold) && approxZero(deltaTarget.y, this.restThreshold) && approxZero(deltaTarget.z, this.restThreshold) && approxZero(deltaOffset.x, this.restThreshold) && approxZero(deltaOffset.y, this.restThreshold) && approxZero(deltaOffset.z, this.restThreshold) && approxZero(deltaZoom, this.restThreshold) && !this._hasRested) {
4395
+ this._hasRested = true;
4396
+ this.dispatchEvent({ type: "rest" });
4397
+ }
4398
+ } else if (!updated && this._updatedLastTime) {
4399
+ this.dispatchEvent({ type: "sleep" });
4400
+ }
4401
+ this._lastDistance = this._spherical.radius;
4402
+ this._lastZoom = this._zoom;
4403
+ this._updatedLastTime = updated;
4404
+ this._needsUpdate = false;
4405
+ return updated;
4406
+ }
4407
+ /**
4408
+ * Get all state in JSON string
4409
+ * @category Methods
4410
+ */
4411
+ toJSON() {
4412
+ return JSON.stringify({
4413
+ enabled: this._enabled,
4414
+ minDistance: this.minDistance,
4415
+ maxDistance: infinityToMaxNumber(this.maxDistance),
4416
+ minZoom: this.minZoom,
4417
+ maxZoom: infinityToMaxNumber(this.maxZoom),
4418
+ minPolarAngle: this.minPolarAngle,
4419
+ maxPolarAngle: infinityToMaxNumber(this.maxPolarAngle),
4420
+ minAzimuthAngle: infinityToMaxNumber(this.minAzimuthAngle),
4421
+ maxAzimuthAngle: infinityToMaxNumber(this.maxAzimuthAngle),
4422
+ smoothTime: this.smoothTime,
4423
+ draggingSmoothTime: this.draggingSmoothTime,
4424
+ wheelSmoothTime: this.wheelSmoothTime,
4425
+ azimuthTime: this.azimuthTime,
4426
+ polarTime: this.polarTime,
4427
+ dollyTime: this.dollyTime,
4428
+ truckTime: this.truckTime,
4429
+ dollySpeed: this.dollySpeed,
4430
+ truckSpeed: this.truckSpeed,
4431
+ dollyToCursor: this.dollyToCursor,
4432
+ target: this._targetEnd.toArray(),
4433
+ position: _v3A.setFromSpherical(this._sphericalEnd).add(this._targetEnd).toArray(),
4434
+ zoom: this._zoomEnd,
4435
+ focalOffset: this._focalOffsetEnd.toArray(),
4436
+ target0: this._target0.toArray(),
4437
+ position0: this._position0.toArray(),
4438
+ zoom0: this._zoom0,
4439
+ focalOffset0: this._focalOffset0.toArray()
4440
+ });
4441
+ }
4442
+ /**
4443
+ * Reproduce the control state with JSON. enableTransition is where anim or not in a boolean.
4444
+ * @param json
4445
+ * @param enableTransition
4446
+ * @category Methods
4447
+ */
4448
+ fromJSON(json, enableTransition = false) {
4449
+ const obj = JSON.parse(json);
4450
+ this.enabled = obj.enabled;
4451
+ this.minDistance = obj.minDistance;
4452
+ this.maxDistance = maxNumberToInfinity(obj.maxDistance);
4453
+ this.minZoom = obj.minZoom;
4454
+ this.maxZoom = maxNumberToInfinity(obj.maxZoom);
4455
+ this.minPolarAngle = obj.minPolarAngle;
4456
+ this.maxPolarAngle = maxNumberToInfinity(obj.maxPolarAngle);
4457
+ this.minAzimuthAngle = maxNumberToInfinity(obj.minAzimuthAngle);
4458
+ this.maxAzimuthAngle = maxNumberToInfinity(obj.maxAzimuthAngle);
4459
+ this.smoothTime = obj.smoothTime;
4460
+ this.draggingSmoothTime = obj.draggingSmoothTime;
4461
+ this.wheelSmoothTime = obj.wheelSmoothTime !== void 0 ? obj.wheelSmoothTime : this.draggingSmoothTime;
4462
+ this.azimuthTime = obj.azimuthTime !== void 0 ? obj.azimuthTime : this.smoothTime;
4463
+ this.polarTime = obj.polarTime !== void 0 ? obj.polarTime : this.smoothTime;
4464
+ this.dollyTime = obj.dollyTime !== void 0 ? obj.dollyTime : this.smoothTime;
4465
+ this.truckTime = obj.truckTime !== void 0 ? obj.truckTime : this.smoothTime;
4466
+ this.dollySpeed = obj.dollySpeed;
4467
+ this.truckSpeed = obj.truckSpeed;
4468
+ this.dollyToCursor = obj.dollyToCursor;
4469
+ this._target0.fromArray(obj.target0);
4470
+ this._position0.fromArray(obj.position0);
4471
+ this._zoom0 = obj.zoom0;
4472
+ this._focalOffset0.fromArray(obj.focalOffset0);
4473
+ this.moveTo(obj.target[0], obj.target[1], obj.target[2], enableTransition);
4474
+ _sphericalA.setFromVector3(_v3A.fromArray(obj.position).sub(this._targetEnd).applyQuaternion(this._yAxisUpSpace));
4475
+ this.rotateTo(_sphericalA.theta, _sphericalA.phi, enableTransition);
4476
+ this.dollyTo(_sphericalA.radius, enableTransition);
4477
+ this.zoomTo(obj.zoom, enableTransition);
4478
+ this.setFocalOffset(obj.focalOffset[0], obj.focalOffset[1], obj.focalOffset[2], enableTransition);
4479
+ this._needsUpdate = true;
4480
+ }
4481
+ /**
4482
+ * Attach all internal event handlers to enable drag control.
4483
+ * @category Methods
4484
+ */
4485
+ connect(domElement) {
4486
+ if (this._domElement) {
4487
+ console.warn("camera-controls is already connected.");
4488
+ return;
4489
+ }
4490
+ domElement.setAttribute("data-camera-controls-version", VERSION);
4491
+ this._addAllEventListeners(domElement);
4492
+ this._getClientRect(this._elementRect);
4493
+ }
4494
+ /**
4495
+ * Detach all internal event handlers to disable drag control.
4496
+ */
4497
+ disconnect() {
4498
+ this.cancel();
4499
+ this._removeAllEventListeners();
4500
+ if (this._domElement) {
4501
+ this._domElement.removeAttribute("data-camera-controls-version");
4502
+ this._domElement = void 0;
4503
+ }
4504
+ }
4505
+ /**
4506
+ * Dispose the cameraControls instance itself, remove all eventListeners.
4507
+ * @category Methods
4508
+ */
4509
+ dispose() {
4510
+ this.removeAllEventListeners();
4511
+ this.disconnect();
4512
+ }
4513
+ // it's okay to expose public though
4514
+ _getTargetDirection(out) {
4515
+ return out.setFromSpherical(this._spherical).divideScalar(this._spherical.radius).applyQuaternion(this._yAxisUpSpaceInverse);
4516
+ }
4517
+ // it's okay to expose public though
4518
+ _getCameraDirection(out) {
4519
+ return this._getTargetDirection(out).negate();
4520
+ }
4521
+ _findPointerById(pointerId) {
4522
+ return this._activePointers.find((activePointer) => activePointer.pointerId === pointerId);
4523
+ }
4524
+ _findPointerByMouseButton(mouseButton) {
4525
+ return this._activePointers.find((activePointer) => activePointer.mouseButton === mouseButton);
4526
+ }
4527
+ _disposePointer(pointer) {
4528
+ this._activePointers.splice(this._activePointers.indexOf(pointer), 1);
4529
+ }
4530
+ _encloseToBoundary(position, offset, friction) {
4531
+ const offsetLength2 = offset.lengthSq();
4532
+ if (offsetLength2 === 0) {
4533
+ return position;
4534
+ }
4535
+ const newTarget = _v3B.copy(offset).add(position);
4536
+ const clampedTarget = this._boundary.clampPoint(newTarget, _v3C);
4537
+ const deltaClampedTarget = clampedTarget.sub(newTarget);
4538
+ const deltaClampedTargetLength2 = deltaClampedTarget.lengthSq();
4539
+ if (deltaClampedTargetLength2 === 0) {
4540
+ return position.add(offset);
4541
+ } else if (deltaClampedTargetLength2 === offsetLength2) {
4542
+ return position;
4543
+ } else if (friction === 0) {
4544
+ return position.add(offset).add(deltaClampedTarget);
4545
+ } else {
4546
+ const offsetFactor = 1 + friction * deltaClampedTargetLength2 / offset.dot(deltaClampedTarget);
4547
+ return position.add(_v3B.copy(offset).multiplyScalar(offsetFactor)).add(deltaClampedTarget.multiplyScalar(1 - friction));
4548
+ }
4549
+ }
4550
+ _updateNearPlaneCorners() {
4551
+ if (isPerspectiveCamera(this._camera)) {
4552
+ const camera = this._camera;
4553
+ const near = camera.near;
4554
+ const fov = camera.getEffectiveFOV() * DEG2RAD;
4555
+ const heightHalf = Math.tan(fov * 0.5) * near;
4556
+ const widthHalf = heightHalf * camera.aspect;
4557
+ this._nearPlaneCorners[0].set(-widthHalf, -heightHalf, 0);
4558
+ this._nearPlaneCorners[1].set(widthHalf, -heightHalf, 0);
4559
+ this._nearPlaneCorners[2].set(widthHalf, heightHalf, 0);
4560
+ this._nearPlaneCorners[3].set(-widthHalf, heightHalf, 0);
4561
+ } else if (isOrthographicCamera(this._camera)) {
4562
+ const camera = this._camera;
4563
+ const zoomInv = 1 / camera.zoom;
4564
+ const left = camera.left * zoomInv;
4565
+ const right = camera.right * zoomInv;
4566
+ const top = camera.top * zoomInv;
4567
+ const bottom = camera.bottom * zoomInv;
4568
+ this._nearPlaneCorners[0].set(left, top, 0);
4569
+ this._nearPlaneCorners[1].set(right, top, 0);
4570
+ this._nearPlaneCorners[2].set(right, bottom, 0);
4571
+ this._nearPlaneCorners[3].set(left, bottom, 0);
4572
+ }
4573
+ }
4574
+ // lateUpdate
4575
+ _collisionTest() {
4576
+ let distance = Infinity;
4577
+ const hasCollider = this.colliderMeshes.length >= 1;
4578
+ if (!hasCollider)
4579
+ return distance;
4580
+ if (notSupportedInOrthographicCamera(this._camera, "_collisionTest"))
4581
+ return distance;
4582
+ const rayDirection = this._getTargetDirection(_cameraDirection);
4583
+ _rotationMatrix.lookAt(_ORIGIN, rayDirection, this._camera.up);
4584
+ for (let i = 0; i < 4; i++) {
4585
+ const nearPlaneCorner = _v3B.copy(this._nearPlaneCorners[i]);
4586
+ nearPlaneCorner.applyMatrix4(_rotationMatrix);
4587
+ const origin2 = _v3C.addVectors(this._target, nearPlaneCorner);
4588
+ _raycaster.set(origin2, rayDirection);
4589
+ _raycaster.far = this._spherical.radius + 1;
4590
+ const intersects = _raycaster.intersectObjects(this.colliderMeshes);
4591
+ if (intersects.length !== 0 && intersects[0].distance < distance) {
4592
+ distance = intersects[0].distance;
4593
+ }
4594
+ }
4595
+ return distance;
4596
+ }
4597
+ /**
4598
+ * Get its client rect and package into given `DOMRect` .
4599
+ */
4600
+ _getClientRect(target) {
4601
+ if (!this._domElement)
4602
+ return;
4603
+ const rect = this._domElement.getBoundingClientRect();
4604
+ target.x = rect.left;
4605
+ target.y = rect.top;
4606
+ if (this._viewport) {
4607
+ target.x += this._viewport.x;
4608
+ target.y += rect.height - this._viewport.w - this._viewport.y;
4609
+ target.width = this._viewport.z;
4610
+ target.height = this._viewport.w;
4611
+ } else {
4612
+ target.width = rect.width;
4613
+ target.height = rect.height;
4614
+ }
4615
+ return target;
4616
+ }
4617
+ _createOnRestPromise(resolveImmediately) {
4618
+ if (resolveImmediately)
4619
+ return Promise.resolve();
4620
+ this._hasRested = false;
4621
+ this.dispatchEvent({ type: "transitionstart" });
4622
+ return new Promise((resolve) => {
4623
+ const onResolve = () => {
4624
+ this.removeEventListener("rest", onResolve);
4625
+ resolve();
4626
+ };
4627
+ this.addEventListener("rest", onResolve);
4628
+ });
4629
+ }
4630
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4631
+ _addAllEventListeners(_domElement) {
4632
+ }
4633
+ _removeAllEventListeners() {
4634
+ }
4635
+ /**
4636
+ * backward compatible
4637
+ * @deprecated use smoothTime (in seconds) instead
4638
+ * @category Properties
4639
+ */
4640
+ get dampingFactor() {
4641
+ console.warn(".dampingFactor has been deprecated. use smoothTime (in seconds) instead.");
4642
+ return 0;
4643
+ }
4644
+ /**
4645
+ * backward compatible
4646
+ * @deprecated use smoothTime (in seconds) instead
4647
+ * @category Properties
4648
+ */
4649
+ set dampingFactor(_) {
4650
+ console.warn(".dampingFactor has been deprecated. use smoothTime (in seconds) instead.");
4651
+ }
4652
+ /**
4653
+ * backward compatible
4654
+ * @deprecated use draggingSmoothTime (in seconds) instead
4655
+ * @category Properties
4656
+ */
4657
+ get draggingDampingFactor() {
4658
+ console.warn(".draggingDampingFactor has been deprecated. use draggingSmoothTime (in seconds) instead.");
4659
+ return 0;
4660
+ }
4661
+ /**
4662
+ * backward compatible
4663
+ * @deprecated use draggingSmoothTime (in seconds) instead
4664
+ * @category Properties
4665
+ */
4666
+ set draggingDampingFactor(_) {
4667
+ console.warn(".draggingDampingFactor has been deprecated. use draggingSmoothTime (in seconds) instead.");
4668
+ }
4669
+ static createBoundingSphere(object3d, out = new THREE.Sphere()) {
4670
+ const boundingSphere = out;
4671
+ const center = boundingSphere.center;
4672
+ _box3A.makeEmpty();
4673
+ object3d.traverseVisible((object) => {
4674
+ if (!object.isMesh)
4675
+ return;
4676
+ _box3A.expandByObject(object);
4677
+ });
4678
+ _box3A.getCenter(center);
4679
+ let maxRadiusSq = 0;
4680
+ object3d.traverseVisible((object) => {
4681
+ if (!object.isMesh)
4682
+ return;
4683
+ const mesh = object;
4684
+ if (!mesh.geometry)
4685
+ return;
4686
+ const geometry = mesh.geometry.clone();
4687
+ geometry.applyMatrix4(mesh.matrixWorld);
4688
+ const bufferGeometry = geometry;
4689
+ const position = bufferGeometry.attributes.position;
4690
+ for (let i = 0, l = position.count; i < l; i++) {
4691
+ _v3A.fromBufferAttribute(position, i);
4692
+ maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_v3A));
4693
+ }
4694
+ });
4695
+ boundingSphere.radius = Math.sqrt(maxRadiusSq);
4696
+ return boundingSphere;
4697
+ }
4698
+ }
2184
4699
  const subsetOfTHREE = {
2185
4700
  Vector2,
2186
4701
  Vector3,
@@ -2204,9 +4719,9 @@ class CameraController extends CameraControls {
2204
4719
  this.renderer = renderer;
2205
4720
  }
2206
4721
  update(delta) {
2207
- var _a;
4722
+ var _a2;
2208
4723
  const needsUpdate = super.update(delta);
2209
- if (needsUpdate && ((_a = this.renderer) == null ? void 0 : _a.debugLog)) {
4724
+ if (needsUpdate && ((_a2 = this.renderer) == null ? void 0 : _a2.debugLog)) {
2210
4725
  const position = this.camera.position.toArray().map((value) => value.toFixed(2)).join(", ");
2211
4726
  const target = this._target.toArray().map((value) => value.toFixed(2)).join(", ");
2212
4727
  const spherical = [this._spherical.theta * RAD2DEG, this._spherical.phi * RAD2DEG, this._spherical.radius].map((value) => value.toFixed(2)).join(", ");
@@ -2234,9 +4749,10 @@ class CameraSystem {
2234
4749
  this.camera = new PerspectiveCamera(90, w / (h || 1));
2235
4750
  this.camera.up.set(0, 0, -1);
2236
4751
  this.zoomIdentityDistance = h / 2;
2237
- this.camera.position.y = this.zoomIdentityDistance;
4752
+ this.camera.position.z = this.zoomIdentityDistance;
2238
4753
  this.controller = new CameraController(this.camera, renderer);
2239
- void this.controller.rotatePolarTo(0);
4754
+ this.controller.polarAngle = 0;
4755
+ this.controller.distance = this.zoomIdentityDistance;
2240
4756
  }
2241
4757
  /** Current camera instance. */
2242
4758
  get currentCamera() {
@@ -2277,9 +4793,9 @@ class CameraSystem {
2277
4793
  this.syncController(minDistance, maxDistance, zoomFactor);
2278
4794
  }
2279
4795
  computeCameraClipPlanes(minDistance, maxDistance, nearSafetyFactor = 0.5, farSafetyFactor = 1.5) {
2280
- const fov = this.camera.fov * DEG2RAD;
4796
+ const fov = this.camera.fov * DEG2RAD$1;
2281
4797
  const aspect = this.camera.aspect;
2282
- const maxPolarAngle = 85 * DEG2RAD;
4798
+ const maxPolarAngle = 85 * DEG2RAD$1;
2283
4799
  const halfFovY = fov / 2;
2284
4800
  const halfFovX = Math.atan(Math.tan(halfFovY) * aspect);
2285
4801
  const diagonalFov = 2 * Math.atan(Math.sqrt(Math.tan(halfFovX) ** 2 + Math.tan(halfFovY) ** 2));
@@ -2412,6 +4928,10 @@ class ViewportSystem {
2412
4928
  get scaleFactor() {
2413
4929
  return this.sceneSystem.scaleFactor;
2414
4930
  }
4931
+ /** Pixel to SVG scale factor */
4932
+ get pxToSvgScale() {
4933
+ return 1 / (this.scaleFactor * this.zoomFactor);
4934
+ }
2415
4935
  /**
2416
4936
  * Initializes the viewport and zoom bounds with the given scene definition.
2417
4937
  * @param sceneDef {@link SceneDef} scene definition
@@ -2419,13 +4939,6 @@ class ViewportSystem {
2419
4939
  initViewport(sceneDef) {
2420
4940
  this.sceneSystem.initScene(sceneDef.viewbox);
2421
4941
  this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.width > 1e5 ? 100 : 35]);
2422
- if (sceneDef.bounds) {
2423
- const boundary = new Box3(
2424
- new Vector3(sceneDef.bounds.min.x, sceneDef.bounds.min.y, 0),
2425
- new Vector3(sceneDef.bounds.max.x, sceneDef.bounds.max.y, 0)
2426
- ).applyMatrix4(this.sceneSystem.worldMatrix);
2427
- this.cameraController.setBoundary(boundary);
2428
- }
2429
4942
  }
2430
4943
  /** Updates the viewport when the renderer size changes. */
2431
4944
  updateViewport() {
@@ -2436,8 +4949,7 @@ class ViewportSystem {
2436
4949
  * Recalculates the svg to pixel scale factor and emits the event if necessary.
2437
4950
  */
2438
4951
  updatePtScale() {
2439
- const denominator = this.sceneSystem.scaleFactor * this.cameraSystem.zoomFactor;
2440
- const pxToSvgScale = 1 / denominator;
4952
+ const pxToSvgScale = this.pxToSvgScale;
2441
4953
  if (Math.abs(pxToSvgScale - (this.prevPxToSvgScale ?? 0)) < this.pxToSvgScaleThreshold) return;
2442
4954
  if (this.renderer.debugLog) console.log("pxToSvgScale", +pxToSvgScale.toFixed(3));
2443
4955
  this.eventSystem.emit("viewport:ptscale", pxToSvgScale);
@@ -2464,6 +4976,15 @@ class ViewportSystem {
2464
4976
  svg3D.applyMatrix4(this.sceneSystem.worldMatrix);
2465
4977
  return new Vector2(svg3D.x, svg3D.y);
2466
4978
  }
4979
+ /**
4980
+ * Converts a point from world coordinates to SVG coordinates. Z axis is ignored.
4981
+ * @param worldCoords Point in world coordinates
4982
+ * @returns Point in SVG coordinates
4983
+ */
4984
+ worldToSvg(worldCoords) {
4985
+ const svgCoords = worldCoords.clone().applyMatrix4(this.sceneSystem.inverseWorldMatrix);
4986
+ return new Vector2(svgCoords.x, svgCoords.y);
4987
+ }
2467
4988
  /**
2468
4989
  * Converts a point from screen coordinates to the given coordinate space.
2469
4990
  * @param space Space to convert to (either "svg" or "world")
@@ -2592,7 +5113,7 @@ class ControlsSystem {
2592
5113
  * @returns Promise that resolves when the roll animation completes
2593
5114
  */
2594
5115
  rollBy(angle, immediate) {
2595
- const angleRad = -angle * DEG2RAD;
5116
+ const angleRad = -angle * DEG2RAD$1;
2596
5117
  const spherical = this.controller.getSpherical(new Spherical());
2597
5118
  const azimuthAngle = spherical.theta;
2598
5119
  const newAzimuthAngle = azimuthAngle + angleRad;
@@ -2606,7 +5127,7 @@ class ControlsSystem {
2606
5127
  * @returns Promise that resolves when the roll animation completes
2607
5128
  */
2608
5129
  rollTo(angle, immediate) {
2609
- const targetAngleRad = -angle * DEG2RAD;
5130
+ const targetAngleRad = -angle * DEG2RAD$1;
2610
5131
  const spherical = this.controller.getSpherical(new Spherical());
2611
5132
  const azimuthAngle = spherical.theta;
2612
5133
  const deltaAngleRad = shortestRotationAngle(targetAngleRad, azimuthAngle);
@@ -2622,7 +5143,7 @@ class ControlsSystem {
2622
5143
  * @returns Promise that resolves when the pitch animation completes
2623
5144
  */
2624
5145
  pitchBy(angle, immediate) {
2625
- const angleRad = angle * DEG2RAD;
5146
+ const angleRad = angle * DEG2RAD$1;
2626
5147
  const spherical = this.controller.getSpherical(new Spherical());
2627
5148
  const polarAngle = spherical.phi;
2628
5149
  const newPolarAngle = polarAngle + angleRad;
@@ -2637,7 +5158,7 @@ class ControlsSystem {
2637
5158
  * @returns Promise that resolves when the pitch animation completes
2638
5159
  */
2639
5160
  pitchTo(angle, immediate) {
2640
- const angleRad = angle * DEG2RAD;
5161
+ const angleRad = angle * DEG2RAD$1;
2641
5162
  const clampedAngleRad = MathUtils.euclideanModulo(angleRad, Math.PI / 2);
2642
5163
  return this.controller.rotatePolarTo(clampedAngleRad, !immediate);
2643
5164
  }
@@ -2669,6 +5190,34 @@ class ControlsSystem {
2669
5190
  if (options.rollTime !== void 0) controller.azimuthTime = options.rollTime;
2670
5191
  if (options.pitchTime !== void 0) controller.polarTime = options.pitchTime;
2671
5192
  }
5193
+ /**
5194
+ * Sets the camera bounds to restrict camera movement.
5195
+ * Note: This method must be called after the viewport is initialized by `renderer.start()`.
5196
+ * @param rect Rectangle in SVG coordinates defining the camera boundary or `undefined` to remove the boundary
5197
+ */
5198
+ setCameraBounds(rect) {
5199
+ if (!rect) {
5200
+ this.controller.setBoundary(void 0);
5201
+ return;
5202
+ }
5203
+ const worldMin = this.viewportSystem.svgToWorld(rect.min);
5204
+ const worldMax = this.viewportSystem.svgToWorld(rect.max);
5205
+ const boundary = new Box3(new Vector3(worldMin.x, worldMin.y, 0), new Vector3(worldMax.x, worldMax.y, 0));
5206
+ this.controller.setBoundary(boundary);
5207
+ }
5208
+ /**
5209
+ * Gets the current camera state.
5210
+ * @returns Camera state object with center, zoom, roll, pitch, and ptScale
5211
+ */
5212
+ getCameraState() {
5213
+ const target = this.controller.getTarget(new Vector3());
5214
+ const center = this.viewportSystem.worldToSvg(target);
5215
+ const zoom = this.viewportSystem.zoomFactor;
5216
+ const roll = this.interactionsSystem.bearing * RAD2DEG;
5217
+ const pitch = this.controller.polarAngle * RAD2DEG;
5218
+ const ptScale = this.viewportSystem.pxToSvgScale;
5219
+ return { center, zoom, roll, pitch, ptScale };
5220
+ }
2672
5221
  }
2673
5222
  function asControlsAPI(controlsSystem) {
2674
5223
  return {
@@ -2682,7 +5231,9 @@ function asControlsAPI(controlsSystem) {
2682
5231
  pitchBy: controlsSystem.pitchBy.bind(controlsSystem),
2683
5232
  pitchTo: controlsSystem.pitchTo.bind(controlsSystem),
2684
5233
  resetCamera: controlsSystem.resetCamera.bind(controlsSystem),
2685
- configure: controlsSystem.configure.bind(controlsSystem)
5234
+ configure: controlsSystem.configure.bind(controlsSystem),
5235
+ setCameraBounds: controlsSystem.setCameraBounds.bind(controlsSystem),
5236
+ getCameraState: controlsSystem.getCameraState.bind(controlsSystem)
2686
5237
  };
2687
5238
  }
2688
5239
  function shortestRotationAngle(targetAngle, sourceAngle) {
@@ -3033,13 +5584,13 @@ class PitchHandler extends Handler {
3033
5584
  this.lastPoints = [p0, p1];
3034
5585
  const yDeltaAverage = (vectorA.y + vectorB.y) / 2;
3035
5586
  const degreesPerPixelMoved = -0.5;
3036
- const deltaAngle = yDeltaAverage * degreesPerPixelMoved * DEG2RAD;
5587
+ const deltaAngle = yDeltaAverage * degreesPerPixelMoved * DEG2RAD$1;
3037
5588
  void this.controller.rotatePolarTo(this.controller.polarAngle + deltaAngle, false);
3038
5589
  });
3039
5590
  this.updatePolarAngles();
3040
5591
  }
3041
5592
  reset(enableTransition = true) {
3042
- const polarAngle = this.enabled ? this.minPitch * DEG2RAD : 0;
5593
+ const polarAngle = this.enabled ? this.minPitch * DEG2RAD$1 : 0;
3043
5594
  return this.controller.rotatePolarTo(polarAngle, enableTransition);
3044
5595
  }
3045
5596
  /**
@@ -3074,8 +5625,8 @@ class PitchHandler extends Handler {
3074
5625
  * Update controller polar angles based on current configuration
3075
5626
  */
3076
5627
  updatePolarAngles() {
3077
- this.controller.minPolarAngle = this.minPitch * DEG2RAD;
3078
- this.controller.maxPolarAngle = this.maxPitch * DEG2RAD;
5628
+ this.controller.minPolarAngle = this.minPitch * DEG2RAD$1;
5629
+ this.controller.maxPolarAngle = this.maxPitch * DEG2RAD$1;
3079
5630
  if (this.controller.polarAngle < this.controller.minPolarAngle) {
3080
5631
  void this.controller.rotatePolarTo(this.controller.minPolarAngle, false);
3081
5632
  }
@@ -3146,7 +5697,7 @@ class RollHandler extends Handler {
3146
5697
  this.isRolling = true;
3147
5698
  this.prevAngle = e.rotation;
3148
5699
  }
3149
- const deltaAngle = (e.rotation - this.prevAngle) * -DEG2RAD;
5700
+ const deltaAngle = (e.rotation - this.prevAngle) * -DEG2RAD$1;
3150
5701
  this.prevAngle = e.rotation;
3151
5702
  if (Math.abs(deltaAngle) < 1e-3) return;
3152
5703
  this.setPivot(e);
@@ -3165,14 +5716,6 @@ class RollHandler extends Handler {
3165
5716
  );
3166
5717
  });
3167
5718
  }
3168
- /**
3169
- * Get bearing angle between current camera orientation and true north (in radians).
3170
- * Angle is in range [0, 2π), going clockwise from north.
3171
- */
3172
- get bearing() {
3173
- const tau = Math.PI * 2;
3174
- return MathUtils$1.euclideanModulo(-this.controller.azimuthAngle, tau);
3175
- }
3176
5719
  reset(enableTransition = true) {
3177
5720
  return this.controller.normalizeRotations().rotateAzimuthTo(0, enableTransition);
3178
5721
  }
@@ -3312,8 +5855,15 @@ class InteractionsSystem {
3312
5855
  this.handlerArray = Object.values(handlers);
3313
5856
  this.handlers.pan.enable();
3314
5857
  this.handlers.zoom.enable();
3315
- this.handlers.roll.enable();
3316
- this.handlers.pitch.enable();
5858
+ }
5859
+ /**
5860
+ * Get bearing angle between current camera orientation and true north (in radians).
5861
+ * Angle is in range [0, 2π), going clockwise from north.
5862
+ */
5863
+ // TODO: Move somewhere else
5864
+ get bearing() {
5865
+ const tau = Math.PI * 2;
5866
+ return MathUtils.euclideanModulo(-this.viewportSystem.cameraController.azimuthAngle, tau);
3317
5867
  }
3318
5868
  /**
3319
5869
  * Update camera position and directions.
@@ -3328,9 +5878,9 @@ class InteractionsSystem {
3328
5878
  needsUpdate = handler.update(delta) || needsUpdate;
3329
5879
  }
3330
5880
  }
3331
- if (this.handlers.roll.bearing !== this.prevBearing) {
3332
- this.prevBearing = this.handlers.roll.bearing;
3333
- this.events.emit("navigation:roll", this.handlers.roll.bearing);
5881
+ if (this.bearing !== this.prevBearing) {
5882
+ this.prevBearing = this.bearing;
5883
+ this.events.emit("navigation:roll", this.bearing);
3334
5884
  }
3335
5885
  return needsUpdate;
3336
5886
  }
@@ -3417,7 +5967,7 @@ class Renderer {
3417
5967
  __publicField(this, "needsRedraw", true);
3418
5968
  __publicField(this, "memoryInfoExtension");
3419
5969
  __publicField(this, "memoryInfo", "");
3420
- var _a, _b;
5970
+ var _a2, _b;
3421
5971
  const { canvas, gl, debugLog = false, ui } = opts;
3422
5972
  this.canvas = canvas;
3423
5973
  this.debugLog = debugLog;
@@ -3440,7 +5990,7 @@ class Renderer {
3440
5990
  this.memoryInfoExtension = this.renderer.getContext().getExtension("GMAN_webgl_memory");
3441
5991
  this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
3442
5992
  this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
3443
- void ((_b = (_a = this.ui) == null ? void 0 : _a.stats) == null ? void 0 : _b.init(this.renderer.getContext()));
5993
+ void ((_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.init(this.renderer.getContext()));
3444
5994
  }
3445
5995
  /**
3446
5996
  * {@link ControlsAPI} instance for controlling the viewport
@@ -3531,8 +6081,8 @@ class Renderer {
3531
6081
  * Main rendering loop
3532
6082
  */
3533
6083
  render() {
3534
- var _a, _b, _c, _d, _e, _f;
3535
- (_b = (_a = this.ui) == null ? void 0 : _a.stats) == null ? void 0 : _b.begin();
6084
+ var _a2, _b, _c, _d, _e, _f;
6085
+ (_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.begin();
3536
6086
  if (this.gl !== void 0) this.renderer.resetState();
3537
6087
  else this.resizeCanvasToDisplaySize();
3538
6088
  this.viewportSystem.updatePtScale();
@@ -3563,8 +6113,8 @@ class Renderer {
3563
6113
  }
3564
6114
  }
3565
6115
  updateMemoryInfo() {
3566
- var _a;
3567
- if (this.memoryInfoExtension && ((_a = this.ui) == null ? void 0 : _a.memoryInfoPanel)) {
6116
+ var _a2;
6117
+ if (this.memoryInfoExtension && ((_a2 = this.ui) == null ? void 0 : _a2.memoryInfoPanel)) {
3568
6118
  const memoryInfo = this.memoryInfoExtension.getMemoryInfo();
3569
6119
  const memoryInfoContent = JSON.stringify(memoryInfo.memory, null, 2);
3570
6120
  const elapsedTime = this.clock.getElapsedTime() * 1e3;