@ericchen1990/pano-view 3.6.4 → 3.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2537,7 +2537,136 @@ import {
2537
2537
  useLayoutEffect,
2538
2538
  useRef
2539
2539
  } from "react";
2540
- import { Euler, MathUtils, PerspectiveCamera } from "three";
2540
+ import { Euler as Euler2, MathUtils as MathUtils2, PerspectiveCamera } from "three";
2541
+
2542
+ // src/gyro-orientation.ts
2543
+ import { Euler, MathUtils, Quaternion, Vector3 } from "three";
2544
+
2545
+ // src/gyro-types.ts
2546
+ var GYRO_POSE_EPSILON = 0.02;
2547
+ var GYRO_DISCONTINUITY_ANGLE_DEG = 15;
2548
+ var GYRO_DISCONTINUITY_SPEED_DEG_PER_S = 1200;
2549
+
2550
+ // src/gyro-orientation.ts
2551
+ var DEVICE_FORWARD_CORRECTION = new Quaternion().setFromAxisAngle(
2552
+ new Vector3(1, 0, 0),
2553
+ -Math.PI / 2
2554
+ );
2555
+ var SCREEN_AXIS = new Vector3(0, 0, 1);
2556
+ var LOOK_LOCAL = new Vector3(0, 0, -1);
2557
+ var UP_LOCAL = new Vector3(0, 1, 0);
2558
+ var scratchEuler = new Euler(0, 0, 0, "YXZ");
2559
+ var scratchScreenCorrection = new Quaternion();
2560
+ var scratchInvert = new Quaternion();
2561
+ var scratchWithoutRoll = new Quaternion();
2562
+ var scratchForward = new Vector3();
2563
+ var scratchUp = new Vector3();
2564
+ var scratchExpectedUp = new Vector3();
2565
+ var ZENITH_PITCH_DEG = 89;
2566
+ function getScreenOrientationAngle() {
2567
+ if (typeof window === "undefined") {
2568
+ return 0;
2569
+ }
2570
+ const angle = window.screen.orientation?.angle;
2571
+ if (Number.isFinite(angle)) {
2572
+ return angle;
2573
+ }
2574
+ const legacyAngle = window.orientation;
2575
+ return Number.isFinite(legacyAngle) ? legacyAngle : 0;
2576
+ }
2577
+ function normalizeGyroYaw(value) {
2578
+ return ((value + 180) % 360 + 360) % 360 - 180;
2579
+ }
2580
+ function shortestGyroYawDelta(from, to) {
2581
+ return normalizeGyroYaw(to - from);
2582
+ }
2583
+ function deviceOrientationToQuaternion(sample, screenOrientation, target) {
2584
+ const alpha = Number.isFinite(sample.compassHeading) ? 360 - sample.compassHeading : sample.alpha;
2585
+ scratchEuler.set(
2586
+ MathUtils.degToRad(sample.beta),
2587
+ MathUtils.degToRad(alpha),
2588
+ MathUtils.degToRad(-sample.gamma),
2589
+ "YXZ"
2590
+ );
2591
+ scratchScreenCorrection.setFromAxisAngle(
2592
+ SCREEN_AXIS,
2593
+ -MathUtils.degToRad(screenOrientation)
2594
+ );
2595
+ return target.setFromEuler(scratchEuler).multiply(DEVICE_FORWARD_CORRECTION).multiply(scratchScreenCorrection).normalize();
2596
+ }
2597
+ function quaternionToGyroPose(quaternion, previousYaw) {
2598
+ scratchForward.copy(LOOK_LOCAL).applyQuaternion(quaternion);
2599
+ const pitchRad = Math.asin(MathUtils.clamp(scratchForward.y, -1, 1));
2600
+ const pitch = MathUtils.radToDeg(pitchRad);
2601
+ const nearZenith = Math.abs(pitch) >= ZENITH_PITCH_DEG;
2602
+ const yaw = nearZenith && previousYaw !== void 0 ? previousYaw : MathUtils.radToDeg(Math.atan2(scratchForward.x, -scratchForward.z));
2603
+ scratchEuler.set(pitchRad, -MathUtils.degToRad(yaw), 0, "YXZ");
2604
+ scratchWithoutRoll.setFromEuler(scratchEuler);
2605
+ scratchUp.copy(UP_LOCAL).applyQuaternion(quaternion);
2606
+ scratchExpectedUp.copy(UP_LOCAL).applyQuaternion(scratchWithoutRoll);
2607
+ const look = scratchForward;
2608
+ const crossX = scratchUp.y * scratchExpectedUp.z - scratchUp.z * scratchExpectedUp.y;
2609
+ const crossY = scratchUp.z * scratchExpectedUp.x - scratchUp.x * scratchExpectedUp.z;
2610
+ const crossZ = scratchUp.x * scratchExpectedUp.y - scratchUp.y * scratchExpectedUp.x;
2611
+ const sinTwist = crossX * look.x + crossY * look.y + crossZ * look.z;
2612
+ const cosTwist = MathUtils.clamp(scratchExpectedUp.dot(scratchUp), -1, 1);
2613
+ const roll = MathUtils.radToDeg(Math.atan2(sinTwist, cosTwist));
2614
+ return {
2615
+ yaw: normalizeGyroYaw(yaw),
2616
+ pitch,
2617
+ roll
2618
+ };
2619
+ }
2620
+ function createRelativeCalibration(deviceQuaternion, yaw, pitch, target) {
2621
+ scratchInvert.copy(deviceQuaternion).invert();
2622
+ scratchEuler.set(
2623
+ MathUtils.degToRad(pitch),
2624
+ MathUtils.degToRad(-yaw),
2625
+ 0,
2626
+ "YXZ"
2627
+ );
2628
+ return target.setFromEuler(scratchEuler).multiply(scratchInvert).normalize();
2629
+ }
2630
+ function applyGyroCalibration(deviceQuaternion, calibration, target, previousYaw) {
2631
+ return quaternionToGyroPose(
2632
+ target.copy(calibration).multiply(deviceQuaternion).normalize(),
2633
+ previousYaw
2634
+ );
2635
+ }
2636
+ function reduceGyroPitchOffset(offset, pitchDelta) {
2637
+ if (offset === 0 || pitchDelta === 0) {
2638
+ return offset;
2639
+ }
2640
+ const absDelta = Math.abs(pitchDelta);
2641
+ if (offset > 0) {
2642
+ return Math.max(0, offset - absDelta);
2643
+ }
2644
+ return Math.min(0, offset + absDelta);
2645
+ }
2646
+ function reclaimGyroOffsets(poseYaw, posePitch, constrainedYaw, constrainedPitch) {
2647
+ return {
2648
+ yaw: shortestGyroYawDelta(poseYaw, constrainedYaw),
2649
+ pitch: constrainedPitch - posePitch
2650
+ };
2651
+ }
2652
+ function gyroPoseAngleDelta(from, to) {
2653
+ return Math.max(
2654
+ Math.abs(shortestGyroYawDelta(from.yaw, to.yaw)),
2655
+ Math.abs(to.pitch - from.pitch),
2656
+ Math.abs(shortestGyroYawDelta(from.roll, to.roll))
2657
+ );
2658
+ }
2659
+ function shouldReanchorGyro(angleDeltaDegrees, deltaSeconds) {
2660
+ if (!(deltaSeconds > 0) || angleDeltaDegrees <= GYRO_DISCONTINUITY_ANGLE_DEG) {
2661
+ return false;
2662
+ }
2663
+ return angleDeltaDegrees / deltaSeconds >= GYRO_DISCONTINUITY_SPEED_DEG_PER_S;
2664
+ }
2665
+ function posesNearlyEqual(a, b, epsilon = GYRO_POSE_EPSILON) {
2666
+ return Math.abs(shortestGyroYawDelta(a.yaw, b.yaw)) < epsilon && Math.abs(a.pitch - b.pitch) < epsilon && Math.abs(a.roll - b.roll) < epsilon;
2667
+ }
2668
+
2669
+ // src/panorama-view-runtime.tsx
2541
2670
  var MAX_PITCH = 90;
2542
2671
  var BOUNCE_OVERSHOOT_PITCH = 12;
2543
2672
  var BOUNCE_OVERSHOOT_FOV_RATIO = 0.15;
@@ -2554,7 +2683,7 @@ function clamp(value, min, max) {
2554
2683
  return Math.max(min, Math.min(max, value));
2555
2684
  }
2556
2685
  function normalizeYaw(yaw) {
2557
- return MathUtils.euclideanModulo(yaw + 180, 360) - 180;
2686
+ return MathUtils2.euclideanModulo(yaw + 180, 360) - 180;
2558
2687
  }
2559
2688
  function shortestYawDelta(from, to) {
2560
2689
  return normalizeYaw(to - from);
@@ -2602,6 +2731,7 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
2602
2731
  const interactionLockCountRef = useRef(0);
2603
2732
  const keyboardActiveRef = useRef(false);
2604
2733
  const gyroActiveRef = useRef(false);
2734
+ const gyroPoseDrivingRef = useRef(false);
2605
2735
  const gyroPoseRef = useRef({ yaw: 0, pitch: 0, roll: 0 });
2606
2736
  const gyroOffsetRef = useRef({ yaw: 0, pitch: 0 });
2607
2737
  const gyroTouchModeRef = useRef("full");
@@ -2610,8 +2740,8 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
2610
2740
  const optionsRef = useRef(options);
2611
2741
  const eventBusRef = useRef(eventBus);
2612
2742
  const lastEmittedViewRef = useRef(null);
2613
- const eulerRef = useRef(new Euler(0, 0, 0, "YXZ"));
2614
- const xrEulerRef = useRef(new Euler(0, 0, 0, "YXZ"));
2743
+ const eulerRef = useRef(new Euler2(0, 0, 0, "YXZ"));
2744
+ const xrEulerRef = useRef(new Euler2(0, 0, 0, "YXZ"));
2615
2745
  const baseLimitsRef = useRef(viewLimits);
2616
2746
  const sceneLimitsRef = useRef(void 0);
2617
2747
  const legacyFovRef = useRef({ minFov, maxFov });
@@ -2940,6 +3070,7 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
2940
3070
  gyroOffsetRef.current = { yaw: 0, pitch: 0 };
2941
3071
  stopVelocity();
2942
3072
  if (!active) {
3073
+ gyroPoseDrivingRef.current = false;
2943
3074
  cameraRollRef.current = 0;
2944
3075
  dirtyRef.current = true;
2945
3076
  }
@@ -2949,18 +3080,35 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
2949
3080
  return;
2950
3081
  }
2951
3082
  const previous = gyroPoseRef.current;
2952
- const viewMovement = Math.abs(shortestYawDelta(previous.yaw, pose.yaw)) + Math.abs(previous.pitch - pose.pitch);
3083
+ const pitchDelta = pose.pitch - previous.pitch;
3084
+ const viewMovement = Math.abs(shortestYawDelta(previous.yaw, pose.yaw)) + Math.abs(pitchDelta);
2953
3085
  const movement = viewMovement + Math.abs(previous.roll - pose.roll);
2954
- if (movement < 1e-4) {
3086
+ if (movement < GYRO_POSE_EPSILON) {
2955
3087
  return;
2956
3088
  }
3089
+ if (gyroTouchModeRef.current === "full") {
3090
+ gyroOffsetRef.current.pitch = reduceGyroPitchOffset(
3091
+ gyroOffsetRef.current.pitch,
3092
+ pitchDelta
3093
+ );
3094
+ }
2957
3095
  gyroPoseRef.current = { ...pose };
2958
- targetViewRef.current = hardConstrainView({
3096
+ gyroPoseDrivingRef.current = true;
3097
+ const constrained = hardConstrainView({
2959
3098
  yaw: pose.yaw + gyroOffsetRef.current.yaw,
2960
3099
  pitch: pose.pitch + gyroOffsetRef.current.pitch
2961
3100
  });
3101
+ targetViewRef.current = constrained;
3102
+ const reclaimed = reclaimGyroOffsets(
3103
+ pose.yaw,
3104
+ pose.pitch,
3105
+ constrained.yaw,
3106
+ constrained.pitch
3107
+ );
3108
+ gyroOffsetRef.current.yaw = reclaimed.yaw;
3109
+ gyroOffsetRef.current.pitch = reclaimed.pitch;
2962
3110
  cameraRollRef.current = pose.roll;
2963
- if (viewMovement >= 1e-4) {
3111
+ if (viewMovement >= GYRO_POSE_EPSILON) {
2964
3112
  markDirty();
2965
3113
  } else {
2966
3114
  dirtyRef.current = true;
@@ -3006,9 +3154,9 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
3006
3154
  const xrCamera = gl.xr.getCamera();
3007
3155
  xrEulerRef.current.setFromQuaternion(xrCamera.quaternion, "YXZ");
3008
3156
  const nextView2 = {
3009
- yaw: normalizeYaw(-MathUtils.radToDeg(xrEulerRef.current.y)),
3157
+ yaw: normalizeYaw(-MathUtils2.radToDeg(xrEulerRef.current.y)),
3010
3158
  pitch: clamp(
3011
- MathUtils.radToDeg(xrEulerRef.current.x),
3159
+ MathUtils2.radToDeg(xrEulerRef.current.x),
3012
3160
  -MAX_PITCH,
3013
3161
  MAX_PITCH
3014
3162
  ),
@@ -3038,14 +3186,16 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
3038
3186
  const completed = progress === 1;
3039
3187
  viewRef.current = completed ? { ...navigation.target } : nextView2;
3040
3188
  eulerRef.current.set(
3041
- MathUtils.degToRad(viewRef.current.pitch),
3042
- MathUtils.degToRad(-viewRef.current.yaw),
3043
- MathUtils.degToRad(cameraRollRef.current),
3189
+ MathUtils2.degToRad(viewRef.current.pitch),
3190
+ MathUtils2.degToRad(-viewRef.current.yaw),
3191
+ MathUtils2.degToRad(cameraRollRef.current),
3044
3192
  "YXZ"
3045
3193
  );
3046
3194
  camera.quaternion.setFromEuler(eulerRef.current);
3047
- camera.fov = viewRef.current.fov;
3048
- camera.updateProjectionMatrix();
3195
+ if (camera.fov !== viewRef.current.fov) {
3196
+ camera.fov = viewRef.current.fov;
3197
+ camera.updateProjectionMatrix();
3198
+ }
3049
3199
  camera.updateMatrixWorld();
3050
3200
  const emittedView = { ...viewRef.current };
3051
3201
  const lastEmitted2 = lastEmittedViewRef.current;
@@ -3114,7 +3264,7 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
3114
3264
  optionsRef.current.zoomDamping,
3115
3265
  DEFAULT_ZOOM_DAMPING
3116
3266
  );
3117
- const rotationFactor = dampingFactor(rotateDamping, deltaSeconds);
3267
+ const rotationFactor = gyroPoseDrivingRef.current ? 1 : dampingFactor(rotateDamping, deltaSeconds);
3118
3268
  const zoomFactor = dampingFactor(zoomDamping, deltaSeconds);
3119
3269
  const yawDelta = limitedYawDelta(viewRef.current.yaw, targetView.yaw, limitsRef.current.yaw);
3120
3270
  const smoothedView = {
@@ -3125,14 +3275,16 @@ var PanoramaViewRuntime = forwardRef(function PanoramaViewRuntime2({ initialView
3125
3275
  const hasSettled = Math.abs(shortestYawDelta(smoothedView.yaw, targetView.yaw)) < VIEW_SETTLE_EPSILON && Math.abs(smoothedView.pitch - targetView.pitch) < VIEW_SETTLE_EPSILON && Math.abs(smoothedView.fov - targetView.fov) < VIEW_SETTLE_EPSILON;
3126
3276
  viewRef.current = hasSettled ? { ...targetView } : smoothedView;
3127
3277
  eulerRef.current.set(
3128
- MathUtils.degToRad(viewRef.current.pitch),
3129
- MathUtils.degToRad(-viewRef.current.yaw),
3130
- MathUtils.degToRad(cameraRollRef.current),
3278
+ MathUtils2.degToRad(viewRef.current.pitch),
3279
+ MathUtils2.degToRad(-viewRef.current.yaw),
3280
+ MathUtils2.degToRad(cameraRollRef.current),
3131
3281
  "YXZ"
3132
3282
  );
3133
3283
  camera.quaternion.setFromEuler(eulerRef.current);
3134
- camera.fov = viewRef.current.fov;
3135
- camera.updateProjectionMatrix();
3284
+ if (camera.fov !== viewRef.current.fov) {
3285
+ camera.fov = viewRef.current.fov;
3286
+ camera.updateProjectionMatrix();
3287
+ }
3136
3288
  camera.updateMatrixWorld();
3137
3289
  const nextView = { ...viewRef.current };
3138
3290
  const lastEmitted = lastEmittedViewRef.current;
@@ -3692,7 +3844,7 @@ import { useFrame as useFrame4, useThree as useThree3 } from "@react-three/fiber
3692
3844
  import { useContext as useContext5, useEffect as useEffect5, useRef as useRef5 } from "react";
3693
3845
 
3694
3846
  // src/pointer-drag.ts
3695
- import { Euler as Euler2, MathUtils as MathUtils2, PerspectiveCamera as PerspectiveCamera2, Vector3 } from "three";
3847
+ import { Euler as Euler3, MathUtils as MathUtils3, PerspectiveCamera as PerspectiveCamera2, Vector3 as Vector32 } from "three";
3696
3848
  var DEFAULT_POINTER_DRAG_SPEED = 0.35;
3697
3849
  var POINTER_FOLLOW_TIME_SECONDS = 0.01;
3698
3850
  var MIN_VELOCITY_SAMPLE_SECONDS = 1 / 120;
@@ -3721,18 +3873,18 @@ var PointerDragAnchor = class {
3721
3873
  this.origin = origin;
3722
3874
  this.rect = rect;
3723
3875
  this.projection = projection;
3724
- this.local = new Vector3();
3725
- this.rotation = new Euler2(0, 0, 0, "YXZ");
3876
+ this.local = new Vector32();
3877
+ this.rotation = new Euler3(0, 0, 0, "YXZ");
3726
3878
  const startRay = this.ray(start).clone();
3727
3879
  this.baseAzimuth = Math.atan2(startRay.x, -startRay.z);
3728
3880
  this.baseElevation = Math.atan2(startRay.y, -startRay.z);
3729
3881
  this.anchor = startRay.clone().applyEuler(this.rotation.set(
3730
- MathUtils2.degToRad(origin.pitch),
3731
- MathUtils2.degToRad(-origin.yaw),
3882
+ MathUtils3.degToRad(origin.pitch),
3883
+ MathUtils3.degToRad(-origin.yaw),
3732
3884
  0,
3733
3885
  "YXZ"
3734
3886
  ));
3735
- const originPitch = MathUtils2.degToRad(origin.pitch);
3887
+ const originPitch = MathUtils3.degToRad(origin.pitch);
3736
3888
  const roots = this.pitchRoots(startRay, originPitch);
3737
3889
  this.useSupplementary = Math.abs(roots.supplementary - originPitch) + 1e-6 < Math.abs(roots.principal - originPitch);
3738
3890
  }
@@ -3745,7 +3897,7 @@ var PointerDragAnchor = class {
3745
3897
  }
3746
3898
  /** Pitch roots of the level-camera constraint. The supplementary root is unwrapped toward the press. */
3747
3899
  pitchRoots(ray, originPitch) {
3748
- const latitude = Math.asin(MathUtils2.clamp(
3900
+ const latitude = Math.asin(MathUtils3.clamp(
3749
3901
  this.anchor.y / Math.max(Math.hypot(ray.y, ray.z), 1e-12),
3750
3902
  -1,
3751
3903
  1
@@ -3765,28 +3917,28 @@ var PointerDragAnchor = class {
3765
3917
  x: this.start.x + (point.x - this.start.x) * multiplier,
3766
3918
  y: this.start.y + (point.y - this.start.y) * multiplier
3767
3919
  });
3768
- const originPitch = MathUtils2.degToRad(this.origin.pitch);
3920
+ const originPitch = MathUtils3.degToRad(this.origin.pitch);
3769
3921
  const roots = this.pitchRoots(ray, originPitch);
3770
- const exactPitch = MathUtils2.clamp(
3922
+ const exactPitch = MathUtils3.clamp(
3771
3923
  this.useSupplementary ? roots.supplementary : roots.principal,
3772
3924
  -Math.PI / 2,
3773
3925
  Math.PI / 2
3774
3926
  );
3775
3927
  const stablePitch = originPitch - (Math.atan2(ray.y, -ray.z) - this.baseElevation);
3776
3928
  const fromPole = Math.asin(Math.min(1, Math.hypot(this.anchor.x, this.anchor.z)));
3777
- const pointerLock = MathUtils2.smoothstep(fromPole, MathUtils2.degToRad(8), MathUtils2.degToRad(18));
3929
+ const pointerLock = MathUtils3.smoothstep(fromPole, MathUtils3.degToRad(8), MathUtils3.degToRad(18));
3778
3930
  const pitchTurn = Math.atan2(Math.sin(exactPitch - stablePitch), Math.cos(exactPitch - stablePitch));
3779
- const pitch = MathUtils2.clamp(
3931
+ const pitch = MathUtils3.clamp(
3780
3932
  pointerLock >= 1 ? exactPitch : stablePitch + pointerLock * pitchTurn,
3781
3933
  -Math.PI / 2,
3782
3934
  Math.PI / 2
3783
3935
  );
3784
3936
  const pitchedZ = Math.sin(exactPitch) * ray.y + Math.cos(exactPitch) * ray.z;
3785
3937
  const exactYaw = Math.atan2(this.anchor.x, -this.anchor.z) - Math.atan2(ray.x, -pitchedZ);
3786
- const stableYaw = MathUtils2.degToRad(this.origin.yaw) + Math.atan2(ray.x, -ray.z) - this.baseAzimuth;
3938
+ const stableYaw = MathUtils3.degToRad(this.origin.yaw) + Math.atan2(ray.x, -ray.z) - this.baseAzimuth;
3787
3939
  const yawTurn = Math.atan2(Math.sin(exactYaw - stableYaw), Math.cos(exactYaw - stableYaw));
3788
- const yaw = MathUtils2.radToDeg(stableYaw + pointerLock * yawTurn);
3789
- return { yaw: normalizeViewYaw(yaw - this.origin.yaw), pitch: MathUtils2.radToDeg(pitch) - this.origin.pitch };
3940
+ const yaw = MathUtils3.radToDeg(stableYaw + pointerLock * yawTurn);
3941
+ return { yaw: normalizeViewYaw(yaw - this.origin.yaw), pitch: MathUtils3.radToDeg(pitch) - this.origin.pitch };
3790
3942
  }
3791
3943
  };
3792
3944
  function createPointerDrag(camera, element, runtime, source) {
@@ -5561,10 +5713,10 @@ function usePanoOverlayHost(isFullscreen) {
5561
5713
  // src/panorama-event-surface.tsx
5562
5714
  import { useThree as useThree6 } from "@react-three/fiber";
5563
5715
  import { useContext as useContext12, useEffect as useEffect10 } from "react";
5564
- import { BackSide, Raycaster, Vector2, Vector3 as Vector33 } from "three";
5716
+ import { BackSide, Raycaster, Vector2, Vector3 as Vector34 } from "three";
5565
5717
 
5566
5718
  // src/hotspot/coordinates.ts
5567
- import { MathUtils as MathUtils3, Vector3 as Vector32 } from "three";
5719
+ import { MathUtils as MathUtils4, Vector3 as Vector33 } from "three";
5568
5720
 
5569
5721
  // src/hotspot/types.ts
5570
5722
  var MAX_HOTSPOT_PITCH = 90;
@@ -5587,13 +5739,13 @@ function normalizePanoYaw(yaw) {
5587
5739
  if (!Number.isFinite(yaw)) {
5588
5740
  return 0;
5589
5741
  }
5590
- return MathUtils3.euclideanModulo(yaw + 180, 360) - 180;
5742
+ return MathUtils4.euclideanModulo(yaw + 180, 360) - 180;
5591
5743
  }
5592
5744
  function clampPanoPitch(pitch) {
5593
5745
  if (!Number.isFinite(pitch)) {
5594
5746
  return 0;
5595
5747
  }
5596
- const clamped = MathUtils3.clamp(
5748
+ const clamped = MathUtils4.clamp(
5597
5749
  pitch,
5598
5750
  -MAX_HOTSPOT_PITCH,
5599
5751
  MAX_HOTSPOT_PITCH
@@ -5610,10 +5762,10 @@ function normalizePanoPosition(position) {
5610
5762
  function panoPositionToVector3(position, radius = 1) {
5611
5763
  const normalized = normalizePanoPosition(position);
5612
5764
  const safeRadius = Number.isFinite(radius) ? Math.max(0, radius) : 1;
5613
- const yaw = MathUtils3.degToRad(normalized.yaw);
5614
- const pitch = MathUtils3.degToRad(normalized.pitch);
5765
+ const yaw = MathUtils4.degToRad(normalized.yaw);
5766
+ const pitch = MathUtils4.degToRad(normalized.pitch);
5615
5767
  const cosPitch = Math.cos(pitch);
5616
- return new Vector32(
5768
+ return new Vector33(
5617
5769
  cosPitch * Math.sin(yaw) * safeRadius,
5618
5770
  Math.sin(pitch) * safeRadius,
5619
5771
  -cosPitch * Math.cos(yaw) * safeRadius
@@ -5625,9 +5777,9 @@ function vector3ToPanoPosition(vector) {
5625
5777
  return { yaw: 0, pitch: 0 };
5626
5778
  }
5627
5779
  return normalizePanoPosition({
5628
- yaw: MathUtils3.radToDeg(Math.atan2(vector.x, -vector.z)),
5629
- pitch: MathUtils3.radToDeg(
5630
- Math.asin(MathUtils3.clamp(vector.y / length, -1, 1))
5780
+ yaw: MathUtils4.radToDeg(Math.atan2(vector.x, -vector.z)),
5781
+ pitch: MathUtils4.radToDeg(
5782
+ Math.asin(MathUtils4.clamp(vector.y / length, -1, 1))
5631
5783
  )
5632
5784
  });
5633
5785
  }
@@ -5668,7 +5820,7 @@ function PanoramaEventSurface() {
5668
5820
  const element = gl.domElement;
5669
5821
  const ndc = new Vector2();
5670
5822
  const raycaster = new Raycaster();
5671
- const direction = new Vector33();
5823
+ const direction = new Vector34();
5672
5824
  const handleContextMenu = (nativeEvent) => {
5673
5825
  const rect = element.getBoundingClientRect();
5674
5826
  if (rect.width <= 0 || rect.height <= 0) {
@@ -7558,8 +7710,8 @@ function WebVRChromeBridge({ host }) {
7558
7710
  }
7559
7711
 
7560
7712
  // src/webvr/stereo-view.ts
7561
- import { StereoCamera, Vector3 as Vector34 } from "three";
7562
- var PROJECT = new Vector34();
7713
+ import { StereoCamera, Vector3 as Vector35 } from "three";
7714
+ var PROJECT = new Vector35();
7563
7715
  function createWebVRStereoView() {
7564
7716
  return {
7565
7717
  active: false,
@@ -7587,11 +7739,11 @@ function projectStereoHtmlPosition(el, camera, size, eye) {
7587
7739
  import { useFrame as useFrame7, useThree as useThree8 } from "@react-three/fiber";
7588
7740
  import { useXR as useXR2, XROrigin } from "@react-three/xr";
7589
7741
  import { useRef as useRef12 } from "react";
7590
- import { Vector3 as Vector35 } from "three";
7742
+ import { Vector3 as Vector36 } from "three";
7591
7743
  import { jsx as jsx11 } from "react/jsx-runtime";
7592
7744
  function PanoramaXROrigin() {
7593
7745
  const originRef = useRef12(null);
7594
- const localHeadPositionRef = useRef12(new Vector35());
7746
+ const localHeadPositionRef = useRef12(new Vector36());
7595
7747
  const session2 = useXR2((state) => state.session);
7596
7748
  const gl = useThree8((state) => state.gl);
7597
7749
  useFrame7(() => {
@@ -8429,7 +8581,7 @@ function isSnapshotTransition(preset) {
8429
8581
  // src/scene-transitions/snapshot-hold.tsx
8430
8582
  import { useFrame as useFrame8, useThree as useThree9 } from "@react-three/fiber";
8431
8583
  import { useEffect as useEffect14, useMemo as useMemo9, useRef as useRef14 } from "react";
8432
- import { Vector3 as Vector37 } from "three";
8584
+ import { Vector3 as Vector38 } from "three";
8433
8585
 
8434
8586
  // src/scene-transitions/overlay-utils.ts
8435
8587
  import { PlaneGeometry } from "three";
@@ -8765,7 +8917,7 @@ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
8765
8917
  function SnapshotHold({ snapshot }) {
8766
8918
  const { camera, gl } = useThree9();
8767
8919
  const meshRef = useRef14(null);
8768
- const directionRef = useRef14(new Vector37());
8920
+ const directionRef = useRef14(new Vector38());
8769
8921
  const material = useMemo9(
8770
8922
  () => createSnapshotMaterial("crossfade", snapshot.texture),
8771
8923
  [snapshot.texture]
@@ -8856,7 +9008,7 @@ import {
8856
9008
  PlaneGeometry as PlaneGeometry2,
8857
9009
  ShaderMaterial as ShaderMaterial2,
8858
9010
  Vector2 as Vector23,
8859
- Vector3 as Vector38
9011
+ Vector3 as Vector39
8860
9012
  } from "three";
8861
9013
  import { jsx as jsx14 } from "react/jsx-runtime";
8862
9014
  var PARTICLE_GRID = 48;
@@ -8938,7 +9090,7 @@ function ParticleOverlay({
8938
9090
  const meshRef = useRef15(null);
8939
9091
  const startRef = useRef15(null);
8940
9092
  const finishedRef = useRef15(false);
8941
- const directionRef = useRef15(new Vector38());
9093
+ const directionRef = useRef15(new Vector39());
8942
9094
  const geometry = useMemo10(() => createParticleGeometry(), []);
8943
9095
  const material = useMemo10(
8944
9096
  () => new ShaderMaterial2({
@@ -8995,7 +9147,7 @@ function ParticleOverlay({
8995
9147
  // src/scene-transitions/snapshot-overlay.tsx
8996
9148
  import { useFrame as useFrame10, useThree as useThree11 } from "@react-three/fiber";
8997
9149
  import { useEffect as useEffect16, useMemo as useMemo11, useRef as useRef16 } from "react";
8998
- import { Vector3 as Vector39 } from "three";
9150
+ import { Vector3 as Vector310 } from "three";
8999
9151
  import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
9000
9152
  function SnapshotOverlay({
9001
9153
  snapshot,
@@ -9008,7 +9160,7 @@ function SnapshotOverlay({
9008
9160
  const meshRef = useRef16(null);
9009
9161
  const startRef = useRef16(null);
9010
9162
  const finishedRef = useRef16(false);
9011
- const directionRef = useRef16(new Vector39());
9163
+ const directionRef = useRef16(new Vector310());
9012
9164
  const material = useMemo11(() => {
9013
9165
  if (!isSnapshotTransition(transition.preset)) {
9014
9166
  return null;
@@ -9103,7 +9255,7 @@ import {
9103
9255
  BackSide as BackSide2,
9104
9256
  LinearFilter,
9105
9257
  LinearMipmapLinearFilter,
9106
- MathUtils as MathUtils4,
9258
+ MathUtils as MathUtils5,
9107
9259
  RepeatWrapping,
9108
9260
  SRGBColorSpace,
9109
9261
  TextureLoader
@@ -9469,7 +9621,7 @@ function Sphere({
9469
9621
  return /* @__PURE__ */ jsxs9(
9470
9622
  "mesh",
9471
9623
  {
9472
- rotation: [0, MathUtils4.degToRad(90 + yawOffset2), 0],
9624
+ rotation: [0, MathUtils5.degToRad(90 + yawOffset2), 0],
9473
9625
  visible,
9474
9626
  children: [
9475
9627
  /* @__PURE__ */ jsx18("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
@@ -9841,8 +9993,8 @@ import {
9841
9993
  Box3,
9842
9994
  BufferGeometry,
9843
9995
  Float32BufferAttribute,
9844
- MathUtils as MathUtils5,
9845
- Vector3 as Vector310
9996
+ MathUtils as MathUtils6,
9997
+ Vector3 as Vector311
9846
9998
  } from "three";
9847
9999
  function positiveInteger2(value, name) {
9848
10000
  if (!Number.isSafeInteger(value) || value <= 0) {
@@ -9914,9 +10066,9 @@ function sphereTileRange(address) {
9914
10066
  };
9915
10067
  }
9916
10068
  function spherePoint(u, v, yawOffset2 = 0, radius = DEFAULT_PANORAMA_RADIUS) {
9917
- const yaw = (u - 0.5) * Math.PI * 2 + MathUtils5.degToRad(yawOffset2);
10069
+ const yaw = (u - 0.5) * Math.PI * 2 + MathUtils6.degToRad(yawOffset2);
9918
10070
  const pitch = (0.5 - v) * Math.PI;
9919
- return new Vector310(
10071
+ return new Vector311(
9920
10072
  Math.sin(yaw) * Math.cos(pitch) * radius,
9921
10073
  Math.sin(pitch) * radius,
9922
10074
  -Math.cos(yaw) * Math.cos(pitch) * radius
@@ -10272,15 +10424,15 @@ import {
10272
10424
  Matrix4 as Matrix43,
10273
10425
  PlaneGeometry as PlaneGeometry3,
10274
10426
  PerspectiveCamera as PerspectiveCamera5,
10275
- Vector3 as Vector312
10427
+ Vector3 as Vector313
10276
10428
  } from "three";
10277
10429
 
10278
10430
  // src/tile/face-layout.ts
10279
10431
  import {
10280
10432
  Box3 as Box32,
10281
- Euler as Euler3,
10282
- Quaternion,
10283
- Vector3 as Vector311
10433
+ Euler as Euler4,
10434
+ Quaternion as Quaternion2,
10435
+ Vector3 as Vector312
10284
10436
  } from "three";
10285
10437
  var CUBE_FACES = ["f", "r", "b", "l", "u", "d"];
10286
10438
  var PREVIEW_FACE_ORDER = ["l", "f", "r", "b", "u", "d"];
@@ -10332,8 +10484,8 @@ function getFaceQuaternion(face) {
10332
10484
  return cached;
10333
10485
  }
10334
10486
  const transform = getFaceTransform(face);
10335
- const quaternion = new Quaternion().setFromEuler(
10336
- new Euler3(...transform.rotation, "XYZ")
10487
+ const quaternion = new Quaternion2().setFromEuler(
10488
+ new Euler4(...transform.rotation, "XYZ")
10337
10489
  );
10338
10490
  faceQuaternionCache.set(face, quaternion);
10339
10491
  return quaternion;
@@ -10344,12 +10496,12 @@ function getTileBoundingBox(address, radius = CUBE_RADIUS) {
10344
10496
  const halfWidth = local.width / 2;
10345
10497
  const halfHeight = local.height / 2;
10346
10498
  const quaternion = getFaceQuaternion(address.face);
10347
- const position = new Vector311(...transform.position);
10499
+ const position = new Vector312(...transform.position);
10348
10500
  const corners = [
10349
- new Vector311(local.x - halfWidth, local.y - halfHeight, 0),
10350
- new Vector311(local.x + halfWidth, local.y - halfHeight, 0),
10351
- new Vector311(local.x - halfWidth, local.y + halfHeight, 0),
10352
- new Vector311(local.x + halfWidth, local.y + halfHeight, 0)
10501
+ new Vector312(local.x - halfWidth, local.y - halfHeight, 0),
10502
+ new Vector312(local.x + halfWidth, local.y - halfHeight, 0),
10503
+ new Vector312(local.x - halfWidth, local.y + halfHeight, 0),
10504
+ new Vector312(local.x + halfWidth, local.y + halfHeight, 0)
10353
10505
  ];
10354
10506
  for (const corner of corners) {
10355
10507
  corner.applyQuaternion(quaternion).add(position);
@@ -10623,8 +10775,8 @@ function parentAddresses(child, parentLevel, parentFaceSize, tileSize) {
10623
10775
  return addresses;
10624
10776
  }
10625
10777
  function centerFacingAddress(candidates, camera) {
10626
- const direction = camera.getWorldDirection(new Vector312());
10627
- const center = new Vector312();
10778
+ const direction = camera.getWorldDirection(new Vector313());
10779
+ const center = new Vector313();
10628
10780
  let bestCandidate = candidates[0];
10629
10781
  let bestAlignment = -Infinity;
10630
10782
  for (const candidate of candidates) {
@@ -11557,16 +11709,16 @@ import {
11557
11709
  import {
11558
11710
  Box3 as Box34,
11559
11711
  EdgesGeometry,
11560
- MathUtils as MathUtils8,
11712
+ MathUtils as MathUtils9,
11561
11713
  Matrix4 as Matrix44,
11562
11714
  PerspectiveCamera as PerspectiveCamera8,
11563
11715
  Plane,
11564
11716
  PlaneGeometry as PlaneGeometry4,
11565
- Quaternion as Quaternion3,
11717
+ Quaternion as Quaternion4,
11566
11718
  Ray,
11567
11719
  Raycaster as Raycaster2,
11568
11720
  Vector2 as Vector24,
11569
- Vector3 as Vector316
11721
+ Vector3 as Vector317
11570
11722
  } from "three";
11571
11723
 
11572
11724
  // src/hotspot/drag-session.ts
@@ -11632,6 +11784,7 @@ function trackPointerDrag(source, element, pointerId, onMove, onEnd) {
11632
11784
  element.addEventListener("lostpointercapture", onLostCapture);
11633
11785
  return stop;
11634
11786
  }
11787
+ var CAPTURE = { capture: true };
11635
11788
  function suppressFollowingClick(target) {
11636
11789
  let cleaned = false;
11637
11790
  const cleanup = () => {
@@ -11639,8 +11792,8 @@ function suppressFollowingClick(target) {
11639
11792
  return;
11640
11793
  }
11641
11794
  cleaned = true;
11642
- target.removeEventListener("click", swallowClick, true);
11643
- target.removeEventListener("pointerdown", cancel, true);
11795
+ target.removeEventListener("click", swallowClick, CAPTURE);
11796
+ target.removeEventListener("pointerdown", cancel, CAPTURE);
11644
11797
  };
11645
11798
  const swallowClick = (event) => {
11646
11799
  event.preventDefault();
@@ -11651,8 +11804,8 @@ function suppressFollowingClick(target) {
11651
11804
  const cancel = () => {
11652
11805
  cleanup();
11653
11806
  };
11654
- target.addEventListener("click", swallowClick, true);
11655
- target.addEventListener("pointerdown", cancel, true);
11807
+ target.addEventListener("click", swallowClick, CAPTURE);
11808
+ target.addEventListener("pointerdown", cancel, CAPTURE);
11656
11809
  setTimeout(cleanup, CLICK_SUPPRESS_MS);
11657
11810
  }
11658
11811
 
@@ -11668,7 +11821,7 @@ import {
11668
11821
  useSyncExternalStore as useSyncExternalStore7
11669
11822
  } from "react";
11670
11823
  import { createRoot } from "react-dom/client";
11671
- import { Vector3 as Vector313 } from "three";
11824
+ import { Vector3 as Vector314 } from "three";
11672
11825
  import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
11673
11826
  function useFallbackStereoView() {
11674
11827
  const runtime = useContext20(WebVRRuntimeContext);
@@ -11691,7 +11844,7 @@ function ProjectedHtml({
11691
11844
  const { camera, gl, events, size } = useThree17();
11692
11845
  const group = useRef23(null);
11693
11846
  const overlay = useRef23(null);
11694
- const scratch = useRef23(new Vector313());
11847
+ const scratch = useRef23(new Vector314());
11695
11848
  const target = events.connected || gl.domElement.parentElement;
11696
11849
  const updatePosition = () => {
11697
11850
  const anchor = group.current;
@@ -11892,19 +12045,19 @@ function HotspotTooltip({
11892
12045
 
11893
12046
  // src/hotspot/transform.ts
11894
12047
  import {
11895
- MathUtils as MathUtils6,
12048
+ MathUtils as MathUtils7,
11896
12049
  PerspectiveCamera as PerspectiveCamera6,
11897
- Vector3 as Vector314
12050
+ Vector3 as Vector315
11898
12051
  } from "three";
11899
- var LOCAL_FORWARD = new Vector314(0, 0, 1);
11900
- var CAMERA_POSITION = new Vector314();
12052
+ var LOCAL_FORWARD = new Vector315(0, 0, 1);
12053
+ var CAMERA_POSITION = new Vector315();
11901
12054
  function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3, rotation, scaleMode, referenceFov, useAngularScale, worldWidth, worldHeight, distance) {
11902
12055
  if (orientation2 === "billboard") {
11903
12056
  group.quaternion.copy(camera.quaternion);
11904
12057
  } else {
11905
12058
  group.quaternion.copy(surfaceQuaternion3);
11906
12059
  }
11907
- group.rotateOnAxis(LOCAL_FORWARD, MathUtils6.degToRad(-rotation));
12060
+ group.rotateOnAxis(LOCAL_FORWARD, MathUtils7.degToRad(-rotation));
11908
12061
  let scaleFactor = scaleMode === "fixed" && camera instanceof PerspectiveCamera6 ? fixedScaleFactor(camera.fov, referenceFov) : 1;
11909
12062
  if (scaleMode === "fixed" && orientation2 === "billboard" && useAngularScale && camera instanceof PerspectiveCamera6) {
11910
12063
  group.updateWorldMatrix(true, false);
@@ -11921,15 +12074,15 @@ function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3,
11921
12074
  group.updateMatrixWorld();
11922
12075
  }
11923
12076
  function fixedScaleFactor(cameraFov, referenceFov) {
11924
- const safeReferenceFov = MathUtils6.clamp(referenceFov, 1, 179);
11925
- return Math.tan(MathUtils6.degToRad(cameraFov) / 2) / Math.tan(MathUtils6.degToRad(safeReferenceFov) / 2);
12077
+ const safeReferenceFov = MathUtils7.clamp(referenceFov, 1, 179);
12078
+ return Math.tan(MathUtils7.degToRad(cameraFov) / 2) / Math.tan(MathUtils7.degToRad(safeReferenceFov) / 2);
11926
12079
  }
11927
12080
  function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegrees, viewportHeight, outsetPixels) {
11928
12081
  const pixels = Number.isFinite(outsetPixels) && outsetPixels > 0 ? outsetPixels : 0;
11929
12082
  const depth = Number.isFinite(cameraSpaceDepth) ? Math.max(cameraSpaceDepth, 1e-4) : 1e-4;
11930
12083
  const height = Number.isFinite(viewportHeight) ? Math.max(viewportHeight, 1) : 1;
11931
- const fov = Number.isFinite(fovDegrees) ? MathUtils6.clamp(fovDegrees, 1, 179) : 75;
11932
- const pixelsPerWorldUnit = height / (2 * Math.tan(MathUtils6.degToRad(fov) * 0.5) * depth);
12084
+ const fov = Number.isFinite(fovDegrees) ? MathUtils7.clamp(fovDegrees, 1, 179) : 75;
12085
+ const pixelsPerWorldUnit = height / (2 * Math.tan(MathUtils7.degToRad(fov) * 0.5) * depth);
11933
12086
  const axis = (worldScale) => {
11934
12087
  const pixelsPerLocalUnit = pixelsPerWorldUnit * Math.abs(worldScale);
11935
12088
  if (pixelsPerLocalUnit < 1e-4) {
@@ -11945,12 +12098,12 @@ import { Line } from "@react-three/drei";
11945
12098
  import { useFrame as useFrame15, useThree as useThree18 } from "@react-three/fiber";
11946
12099
  import { useLayoutEffect as useLayoutEffect6, useRef as useRef24 } from "react";
11947
12100
  import {
11948
- MathUtils as MathUtils7,
12101
+ MathUtils as MathUtils8,
11949
12102
  PerspectiveCamera as PerspectiveCamera7,
11950
- Vector3 as Vector315
12103
+ Vector3 as Vector316
11951
12104
  } from "three";
11952
12105
  import { jsx as jsx25 } from "react/jsx-runtime";
11953
- var LINE_WORLD_POSITION = new Vector315();
12106
+ var LINE_WORLD_POSITION = new Vector316();
11954
12107
  function applyScreenSpaceDashScale(object, camera, viewportHeight) {
11955
12108
  const material = object?.material;
11956
12109
  if (!object || typeof material?.dashScale !== "number") {
@@ -11959,7 +12112,7 @@ function applyScreenSpaceDashScale(object, camera, viewportHeight) {
11959
12112
  object.getWorldPosition(LINE_WORLD_POSITION);
11960
12113
  const depth = Math.max(camera.position.distanceTo(LINE_WORLD_POSITION), 1);
11961
12114
  const fov = camera instanceof PerspectiveCamera7 ? camera.fov : 75;
11962
- material.dashScale = viewportHeight / (2 * Math.tan(MathUtils7.degToRad(fov) * 0.5) * depth);
12115
+ material.dashScale = viewportHeight / (2 * Math.tan(MathUtils8.degToRad(fov) * 0.5) * depth);
11963
12116
  }
11964
12117
  function resolveDash(dashSize, gapSize) {
11965
12118
  if (dashSize === void 0 || !Number.isFinite(dashSize) || dashSize <= 0) {
@@ -12028,8 +12181,8 @@ var DRAG_EPSILON_DEGREES = 1e-3;
12028
12181
  var SELECTION_BORDER_WIDTH = 3;
12029
12182
  var SELECTION_BORDER_GAP = 3;
12030
12183
  var SELECTION_BORDER_OUTSET = SELECTION_BORDER_GAP + SELECTION_BORDER_WIDTH / 2;
12031
- var SELECTION_CAMERA_POSITION = new Vector316();
12032
- var SELECTION_WORLD_SCALE = new Vector316();
12184
+ var SELECTION_CAMERA_POSITION = new Vector317();
12185
+ var SELECTION_WORLD_SCALE = new Vector317();
12033
12186
  var SELECTION_BORDER_POINTS = [
12034
12187
  [-0.5, -0.5, 1e-3],
12035
12188
  [0.5, -0.5, 1e-3],
@@ -12039,25 +12192,25 @@ var SELECTION_BORDER_POINTS = [
12039
12192
  ];
12040
12193
  var TOOLTIP_WORLD_BOX = new Box34();
12041
12194
  var TOOLTIP_LOCAL_BOX = new Box34();
12042
- var TOOLTIP_CORNER = new Vector316();
12195
+ var TOOLTIP_CORNER = new Vector317();
12043
12196
  var TOOLTIP_INVERSE = new Matrix44();
12044
- var TOOLTIP_WORLD_CORNER = new Vector316();
12045
- var TOOLTIP_PROJECTED = new Vector316();
12046
- var TOOLTIP_RAY_ORIGIN = new Vector316();
12047
- var TOOLTIP_RAY_FAR = new Vector316();
12048
- var TOOLTIP_RAY_DIRECTION = new Vector316();
12197
+ var TOOLTIP_WORLD_CORNER = new Vector317();
12198
+ var TOOLTIP_PROJECTED = new Vector317();
12199
+ var TOOLTIP_RAY_ORIGIN = new Vector317();
12200
+ var TOOLTIP_RAY_FAR = new Vector317();
12201
+ var TOOLTIP_RAY_DIRECTION = new Vector317();
12049
12202
  var TOOLTIP_RAY = new Ray();
12050
12203
  var TOOLTIP_PLANE = new Plane();
12051
- var TOOLTIP_INTERSECTION = new Vector316();
12052
- var TOOLTIP_PLANE_NORMAL = new Vector316();
12053
- var TOOLTIP_PLANE_POINT = new Vector316();
12204
+ var TOOLTIP_INTERSECTION = new Vector317();
12205
+ var TOOLTIP_PLANE_NORMAL = new Vector317();
12206
+ var TOOLTIP_PLANE_POINT = new Vector317();
12054
12207
  var HOTSPOT_MODE_RENDERING = {
12055
12208
  surface: { orientation: "surface", placement: "surface" },
12056
12209
  billboard: { orientation: "billboard", placement: "floating" }
12057
12210
  };
12058
12211
  function angularSizeToWorldSize(size, radius) {
12059
- const safeSize = Number.isFinite(size) ? MathUtils8.clamp(Math.abs(size), MIN_ANGULAR_SIZE, MAX_ANGULAR_SIZE) : MIN_ANGULAR_SIZE;
12060
- return 2 * radius * Math.tan(MathUtils8.degToRad(safeSize) / 2);
12212
+ const safeSize = Number.isFinite(size) ? MathUtils9.clamp(Math.abs(size), MIN_ANGULAR_SIZE, MAX_ANGULAR_SIZE) : MIN_ANGULAR_SIZE;
12213
+ return 2 * radius * Math.tan(MathUtils9.degToRad(safeSize) / 2);
12061
12214
  }
12062
12215
  function resolveScale(scale) {
12063
12216
  return Number.isFinite(scale) && scale > 0 ? scale : 1;
@@ -12070,39 +12223,39 @@ function resolveDistance(placement, distance, width, height) {
12070
12223
  if (!Number.isFinite(distance)) {
12071
12224
  return DEFAULT_FLOATING_DISTANCE;
12072
12225
  }
12073
- return MathUtils8.clamp(Math.abs(distance), 0.1, maximumDistance);
12226
+ return MathUtils9.clamp(Math.abs(distance), 0.1, maximumDistance);
12074
12227
  }
12075
12228
  function maximumHotspotDistance(width, height) {
12076
- const safeWidth = MathUtils8.clamp(
12229
+ const safeWidth = MathUtils9.clamp(
12077
12230
  Math.abs(Number.isFinite(width) ? width : MIN_ANGULAR_SIZE),
12078
12231
  MIN_ANGULAR_SIZE,
12079
12232
  MAX_ANGULAR_SIZE
12080
12233
  );
12081
- const safeHeight = MathUtils8.clamp(
12234
+ const safeHeight = MathUtils9.clamp(
12082
12235
  Math.abs(Number.isFinite(height) ? height : MIN_ANGULAR_SIZE),
12083
12236
  MIN_ANGULAR_SIZE,
12084
12237
  MAX_ANGULAR_SIZE
12085
12238
  );
12086
12239
  const cornerFactor = Math.hypot(
12087
12240
  1,
12088
- Math.tan(MathUtils8.degToRad(safeWidth) / 2),
12089
- Math.tan(MathUtils8.degToRad(safeHeight) / 2)
12241
+ Math.tan(MathUtils9.degToRad(safeWidth) / 2),
12242
+ Math.tan(MathUtils9.degToRad(safeHeight) / 2)
12090
12243
  );
12091
12244
  return (PANORAMA_RADIUS - PANORAMA_SURFACE_INSET) / cornerFactor;
12092
12245
  }
12093
12246
  function createSurfaceQuaternion(position) {
12094
12247
  const outward = panoPositionToVector3(position).normalize();
12095
12248
  const inward = outward.clone().negate();
12096
- const pitch = MathUtils8.degToRad(position.pitch);
12097
- const yaw = MathUtils8.degToRad(position.yaw);
12098
- const localUp = new Vector316(
12249
+ const pitch = MathUtils9.degToRad(position.pitch);
12250
+ const yaw = MathUtils9.degToRad(position.yaw);
12251
+ const localUp = new Vector317(
12099
12252
  -Math.sin(pitch) * Math.sin(yaw),
12100
12253
  Math.cos(pitch),
12101
12254
  Math.sin(pitch) * Math.cos(yaw)
12102
12255
  ).normalize();
12103
12256
  const localRight = localUp.clone().cross(inward).normalize();
12104
12257
  const basis = new Matrix44().makeBasis(localRight, localUp, inward);
12105
- return new Quaternion3().setFromRotationMatrix(basis);
12258
+ return new Quaternion4().setFromRotationMatrix(basis);
12106
12259
  }
12107
12260
  function makeInteractionEvent(id, position, event) {
12108
12261
  return {
@@ -12115,7 +12268,7 @@ function makeInteractionEvent(id, position, event) {
12115
12268
  function angularDistance(a, b) {
12116
12269
  const aVector = panoPositionToVector3(a);
12117
12270
  const bVector = panoPositionToVector3(b);
12118
- return MathUtils8.radToDeg(aVector.angleTo(bVector));
12271
+ return MathUtils9.radToDeg(aVector.angleTo(bVector));
12119
12272
  }
12120
12273
  var DRAG_POINTER = new Vector24();
12121
12274
  var DRAG_RAYCASTER = new Raycaster2();
@@ -13760,10 +13913,10 @@ import {
13760
13913
  } from "three";
13761
13914
 
13762
13915
  // src/hotspot/audio-spatial.ts
13763
- import { MathUtils as MathUtils9, Vector3 as Vector317 } from "three";
13764
- var LOOK_DIR = new Vector317();
13765
- var SOURCE_DIR = new Vector317();
13766
- var RIGHT_DIR = new Vector317();
13916
+ import { MathUtils as MathUtils10, Vector3 as Vector318 } from "three";
13917
+ var LOOK_DIR = new Vector318();
13918
+ var SOURCE_DIR = new Vector318();
13919
+ var RIGHT_DIR = new Vector318();
13767
13920
  function clampAudioVolume(volume) {
13768
13921
  if (!Number.isFinite(volume)) {
13769
13922
  return 1;
@@ -13788,11 +13941,11 @@ function lookRangeGain(angleDegrees, range) {
13788
13941
  function audioSpatialFromView(camera, position, range) {
13789
13942
  camera.getWorldDirection(LOOK_DIR);
13790
13943
  SOURCE_DIR.copy(panoPositionToVector3(position)).normalize();
13791
- const angleDegrees = MathUtils9.radToDeg(
13792
- Math.acos(MathUtils9.clamp(LOOK_DIR.dot(SOURCE_DIR), -1, 1))
13944
+ const angleDegrees = MathUtils10.radToDeg(
13945
+ Math.acos(MathUtils10.clamp(LOOK_DIR.dot(SOURCE_DIR), -1, 1))
13793
13946
  );
13794
13947
  RIGHT_DIR.set(1, 0, 0).applyQuaternion(camera.quaternion);
13795
- const pan = MathUtils9.clamp(
13948
+ const pan = MathUtils10.clamp(
13796
13949
  Math.atan2(SOURCE_DIR.dot(RIGHT_DIR), SOURCE_DIR.dot(LOOK_DIR)) / (Math.PI / 2),
13797
13950
  -1,
13798
13951
  1
@@ -14104,7 +14257,7 @@ import {
14104
14257
  CanvasTexture as CanvasTexture3,
14105
14258
  ClampToEdgeWrapping as ClampToEdgeWrapping8,
14106
14259
  LinearFilter as LinearFilter10,
14107
- MathUtils as MathUtils10,
14260
+ MathUtils as MathUtils11,
14108
14261
  PerspectiveCamera as PerspectiveCamera9,
14109
14262
  SRGBColorSpace as SRGBColorSpace9
14110
14263
  } from "three";
@@ -14179,7 +14332,7 @@ function projectedDevicePixels(angleDeg, fovDeg, viewportHeight) {
14179
14332
  }
14180
14333
  const fov = Math.min(179, Math.max(1, resolvePositive(fovDeg, DEFAULT_REFERENCE_FOV2)));
14181
14334
  const angle = Math.min(179, Math.max(0.01, Math.abs(angleDeg)));
14182
- return Math.tan(MathUtils10.degToRad(angle) / 2) / Math.tan(MathUtils10.degToRad(fov) / 2) * viewportHeight;
14335
+ return Math.tan(MathUtils11.degToRad(angle) / 2) / Math.tan(MathUtils11.degToRad(fov) / 2) * viewportHeight;
14183
14336
  }
14184
14337
  function snapRasterLongSide(screenLong, layoutLong) {
14185
14338
  if (!(screenLong > 0)) {
@@ -14654,10 +14807,10 @@ import {
14654
14807
  DoubleSide as DoubleSide5,
14655
14808
  Float32BufferAttribute as Float32BufferAttribute2,
14656
14809
  Matrix4 as Matrix45,
14657
- Quaternion as Quaternion4,
14810
+ Quaternion as Quaternion5,
14658
14811
  ShapeUtils,
14659
14812
  Vector2 as Vector26,
14660
- Vector3 as Vector318
14813
+ Vector3 as Vector319
14661
14814
  } from "three";
14662
14815
  import { jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
14663
14816
  var POLYGON_RADIUS = 990;
@@ -14731,7 +14884,7 @@ function polygonContainsDirection(vertices, direction) {
14731
14884
  }
14732
14885
  function fitsHemisphere(vertices) {
14733
14886
  const directions = vertices.map((vertex) => panoPositionToVector3(vertex).normalize());
14734
- const candidates = [new Vector318()];
14887
+ const candidates = [new Vector319()];
14735
14888
  for (const direction of directions) {
14736
14889
  candidates[0].add(direction);
14737
14890
  candidates.push(direction.clone());
@@ -14755,7 +14908,7 @@ function validatePolygonVertices(vertices) {
14755
14908
  if (hasSelfIntersection(unwrapped)) {
14756
14909
  issues.push({ code: "self_intersection", message: "Polygon edges cannot cross." });
14757
14910
  }
14758
- if (polygonContainsDirection(normalized, new Vector318(0, 1, 0)) || polygonContainsDirection(normalized, new Vector318(0, -1, 0))) {
14911
+ if (polygonContainsDirection(normalized, new Vector319(0, 1, 0)) || polygonContainsDirection(normalized, new Vector319(0, -1, 0))) {
14759
14912
  issues.push({ code: "contains_pole", message: "Polygons cannot contain a panorama pole." });
14760
14913
  }
14761
14914
  if (!fitsHemisphere(normalized)) {
@@ -14766,7 +14919,7 @@ function validatePolygonVertices(vertices) {
14766
14919
  function polygonCenter(vertices) {
14767
14920
  const sum = vertices.reduce(
14768
14921
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
14769
- new Vector318()
14922
+ new Vector319()
14770
14923
  );
14771
14924
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
14772
14925
  }
@@ -14775,12 +14928,12 @@ function surfaceQuaternion(position) {
14775
14928
  const inward = outward.clone().negate();
14776
14929
  const pitch = position.pitch * Math.PI / 180;
14777
14930
  const yaw = position.yaw * Math.PI / 180;
14778
- const localUp = new Vector318(
14931
+ const localUp = new Vector319(
14779
14932
  -Math.sin(pitch) * Math.sin(yaw),
14780
14933
  Math.cos(pitch),
14781
14934
  Math.sin(pitch) * Math.cos(yaw)
14782
14935
  ).normalize();
14783
- return new Quaternion4().setFromRotationMatrix(
14936
+ return new Quaternion5().setFromRotationMatrix(
14784
14937
  new Matrix45().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14785
14938
  );
14786
14939
  }
@@ -14788,7 +14941,7 @@ function makeWorldToLocal(center) {
14788
14941
  return new Matrix45().compose(
14789
14942
  panoPositionToVector3(center, POLYGON_RADIUS),
14790
14943
  surfaceQuaternion(center),
14791
- new Vector318(1, 1, 1)
14944
+ new Vector319(1, 1, 1)
14792
14945
  ).invert();
14793
14946
  }
14794
14947
  function projectedPoint(a, b, c, weightB, weightC, radius, worldToLocal) {
@@ -15014,10 +15167,10 @@ function PolygonHotspot({
15014
15167
  // src/hotspot/polyline-hotspot.tsx
15015
15168
  import { useEffect as useEffect32, useMemo as useMemo21, useRef as useRef33 } from "react";
15016
15169
  import {
15017
- MathUtils as MathUtils11,
15170
+ MathUtils as MathUtils12,
15018
15171
  Matrix4 as Matrix46,
15019
- Quaternion as Quaternion5,
15020
- Vector3 as Vector319
15172
+ Quaternion as Quaternion6,
15173
+ Vector3 as Vector320
15021
15174
  } from "three";
15022
15175
  import { jsx as jsx36 } from "react/jsx-runtime";
15023
15176
  var POLYLINE_RADIUS = 990;
@@ -15044,21 +15197,21 @@ function unwrapVertices(vertices) {
15044
15197
  function polylineCenter(vertices) {
15045
15198
  const sum = vertices.reduce(
15046
15199
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
15047
- new Vector319()
15200
+ new Vector320()
15048
15201
  );
15049
15202
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
15050
15203
  }
15051
15204
  function surfaceQuaternion2(position) {
15052
15205
  const outward = panoPositionToVector3(position).normalize();
15053
15206
  const inward = outward.clone().negate();
15054
- const pitch = MathUtils11.degToRad(position.pitch);
15055
- const yaw = MathUtils11.degToRad(position.yaw);
15056
- const localUp = new Vector319(
15207
+ const pitch = MathUtils12.degToRad(position.pitch);
15208
+ const yaw = MathUtils12.degToRad(position.yaw);
15209
+ const localUp = new Vector320(
15057
15210
  -Math.sin(pitch) * Math.sin(yaw),
15058
15211
  Math.cos(pitch),
15059
15212
  Math.sin(pitch) * Math.cos(yaw)
15060
15213
  ).normalize();
15061
- return new Quaternion5().setFromRotationMatrix(
15214
+ return new Quaternion6().setFromRotationMatrix(
15062
15215
  new Matrix46().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
15063
15216
  );
15064
15217
  }
@@ -15066,7 +15219,7 @@ function makeWorldToLocal2(center) {
15066
15219
  return new Matrix46().compose(
15067
15220
  panoPositionToVector3(center, POLYLINE_RADIUS),
15068
15221
  surfaceQuaternion2(center),
15069
- new Vector319(1, 1, 1)
15222
+ new Vector320(1, 1, 1)
15070
15223
  ).invert();
15071
15224
  }
15072
15225
  function subdivisionSteps(vertices) {
@@ -15081,7 +15234,7 @@ function subdivisionSteps(vertices) {
15081
15234
  }
15082
15235
  return Math.min(
15083
15236
  8,
15084
- Math.max(1, Math.ceil(MathUtils11.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
15237
+ Math.max(1, Math.ceil(MathUtils12.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
15085
15238
  );
15086
15239
  }
15087
15240
  function makeLinePositions(vertices, center) {
@@ -15854,65 +16007,7 @@ import {
15854
16007
  useRef as useRef35,
15855
16008
  useState as useState22
15856
16009
  } from "react";
15857
-
15858
- // src/gyro-orientation.ts
15859
- import { Euler as Euler4, MathUtils as MathUtils12, Quaternion as Quaternion6, Vector3 as Vector320 } from "three";
15860
- var DEVICE_FORWARD_CORRECTION = new Quaternion6().setFromAxisAngle(
15861
- new Vector320(1, 0, 0),
15862
- -Math.PI / 2
15863
- );
15864
- var SCREEN_AXIS = new Vector320(0, 0, 1);
15865
- function getScreenOrientationAngle() {
15866
- if (typeof window === "undefined") {
15867
- return 0;
15868
- }
15869
- const angle = window.screen.orientation?.angle;
15870
- if (Number.isFinite(angle)) {
15871
- return angle;
15872
- }
15873
- const legacyAngle = window.orientation;
15874
- return Number.isFinite(legacyAngle) ? legacyAngle : 0;
15875
- }
15876
- function deviceOrientationToQuaternion(sample, screenOrientation) {
15877
- const alpha = Number.isFinite(sample.compassHeading) ? 360 - sample.compassHeading : sample.alpha;
15878
- const deviceEuler = new Euler4(
15879
- MathUtils12.degToRad(sample.beta),
15880
- MathUtils12.degToRad(alpha),
15881
- MathUtils12.degToRad(-sample.gamma),
15882
- "YXZ"
15883
- );
15884
- const screenCorrection = new Quaternion6().setFromAxisAngle(
15885
- SCREEN_AXIS,
15886
- -MathUtils12.degToRad(screenOrientation)
15887
- );
15888
- return new Quaternion6().setFromEuler(deviceEuler).multiply(DEVICE_FORWARD_CORRECTION).multiply(screenCorrection).normalize();
15889
- }
15890
- function quaternionToGyroPose(quaternion) {
15891
- const euler = new Euler4().setFromQuaternion(quaternion, "YXZ");
15892
- return {
15893
- yaw: MathUtils12.radToDeg(-euler.y),
15894
- pitch: MathUtils12.radToDeg(euler.x),
15895
- roll: MathUtils12.radToDeg(euler.z)
15896
- };
15897
- }
15898
- function createRelativeCalibration(deviceQuaternion, yaw, pitch) {
15899
- const targetQuaternion = new Quaternion6().setFromEuler(
15900
- new Euler4(
15901
- MathUtils12.degToRad(pitch),
15902
- MathUtils12.degToRad(-yaw),
15903
- 0,
15904
- "YXZ"
15905
- )
15906
- );
15907
- return targetQuaternion.multiply(deviceQuaternion.clone().invert()).normalize();
15908
- }
15909
- function applyGyroCalibration(deviceQuaternion, calibration) {
15910
- return quaternionToGyroPose(
15911
- calibration.clone().multiply(deviceQuaternion).normalize()
15912
- );
15913
- }
15914
-
15915
- // src/gyro.tsx
16010
+ import { Quaternion as Quaternion7 } from "three";
15916
16011
  var deviceOrientationPermissionState = "unknown";
15917
16012
  function isMobileDevice() {
15918
16013
  if (typeof navigator === "undefined") {
@@ -15920,12 +16015,6 @@ function isMobileDevice() {
15920
16015
  }
15921
16016
  return /Android|iPad|iPhone|iPod|Mobile/i.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
15922
16017
  }
15923
- function normalizeYaw2(value) {
15924
- return ((value + 180) % 360 + 360) % 360 - 180;
15925
- }
15926
- function shortestYawDelta2(from, to) {
15927
- return normalizeYaw2(to - from);
15928
- }
15929
16018
  function finiteOr(value, fallback) {
15930
16019
  return Number.isFinite(value) ? value : fallback;
15931
16020
  }
@@ -15935,9 +16024,6 @@ function resolveFriction(value) {
15935
16024
  function resolveSoftstart(value) {
15936
16025
  return Math.max(finiteOr(value, 0.5), 0);
15937
16026
  }
15938
- function posesNearlyEqual(a, b) {
15939
- return Math.abs(shortestYawDelta2(a.yaw, b.yaw)) < 1e-4 && Math.abs(a.pitch - b.pitch) < 1e-4 && Math.abs(a.roll - b.roll) < 1e-4;
15940
- }
15941
16027
  var Gyro = forwardRef3(function Gyro2({
15942
16028
  enabled = false,
15943
16029
  camroll = true,
@@ -15957,19 +16043,30 @@ var Gyro = forwardRef3(function Gyro2({
15957
16043
  const eventBus = useContext25(PanoEventBusContext);
15958
16044
  const { gl } = useThree25();
15959
16045
  const [internallyDisabled, setInternallyDisabled] = useState22(false);
16046
+ const [available, setAvailableState] = useState22(false);
15960
16047
  const availableRef = useRef35(false);
15961
16048
  const availabilityReportedRef = useRef35(null);
15962
16049
  const activeRef = useRef35(false);
15963
16050
  const permissionRef = useRef35(deviceOrientationPermissionState);
15964
16051
  const absoluteEventSourceRef = useRef35(null);
15965
- const rawQuaternionRef = useRef35(null);
15966
- const calibrationRef = useRef35(null);
16052
+ const rawQuaternionRef = useRef35(new Quaternion7());
16053
+ const hasRawQuaternionRef = useRef35(false);
16054
+ const calibrationRef = useRef35(new Quaternion7());
16055
+ const hasCalibrationRef = useRef35(false);
16056
+ const calibratedQuaternionRef = useRef35(new Quaternion7());
15967
16057
  const absoluteOffsetRef = useRef35({ yaw: 0, pitch: 0 });
15968
16058
  const screenAngleRef = useRef35(getScreenOrientationAngle());
15969
16059
  const startPoseRef = useRef35(null);
15970
16060
  const enabledAtRef = useRef35(0);
15971
16061
  const filteredPoseRef = useRef35(null);
15972
16062
  const lastAppliedPoseRef = useRef35(null);
16063
+ const lastSamplePoseRef = useRef35(null);
16064
+ const lastSampleAtRef = useRef35(null);
16065
+ const previousYawRef = useRef35(void 0);
16066
+ const absoluteRef = useRef35(absolute);
16067
+ const northRef = useRef35(north);
16068
+ absoluteRef.current = absolute;
16069
+ northRef.current = north;
15973
16070
  const callbacksRef = useRef35({
15974
16071
  onAvailable,
15975
16072
  onUnavailable,
@@ -16015,13 +16112,14 @@ var Gyro = forwardRef3(function Gyro2({
16015
16112
  [eventBus]
16016
16113
  );
16017
16114
  const setAvailable = useCallback9(
16018
- (available) => {
16019
- availableRef.current = available;
16020
- if (availabilityReportedRef.current === available) {
16115
+ (nextAvailable) => {
16116
+ availableRef.current = nextAvailable;
16117
+ setAvailableState(nextAvailable);
16118
+ if (availabilityReportedRef.current === nextAvailable) {
16021
16119
  return;
16022
16120
  }
16023
- availabilityReportedRef.current = available;
16024
- emit(available ? "gyroavailable" : "gyrounavailable");
16121
+ availabilityReportedRef.current = nextAvailable;
16122
+ emit(nextAvailable ? "gyroavailable" : "gyrounavailable");
16025
16123
  },
16026
16124
  [emit]
16027
16125
  );
@@ -16064,37 +16162,54 @@ var Gyro = forwardRef3(function Gyro2({
16064
16162
  controlsRef.current?.setGyroActive(false);
16065
16163
  emit("gyrodisable");
16066
16164
  }
16067
- rawQuaternionRef.current = null;
16165
+ hasRawQuaternionRef.current = false;
16068
16166
  absoluteEventSourceRef.current = null;
16069
- calibrationRef.current = null;
16167
+ hasCalibrationRef.current = false;
16070
16168
  filteredPoseRef.current = null;
16071
16169
  lastAppliedPoseRef.current = null;
16170
+ lastSamplePoseRef.current = null;
16171
+ lastSampleAtRef.current = null;
16172
+ previousYawRef.current = void 0;
16072
16173
  }, [controlsRef, emit]);
16073
- const resetSensor = useCallback9(
16174
+ const anchorToView = useCallback9(
16074
16175
  (yaw, pitch) => {
16075
- const rawQuaternion = rawQuaternionRef.current;
16076
- if (!rawQuaternion) {
16176
+ if (!hasRawQuaternionRef.current) {
16077
16177
  return;
16078
16178
  }
16079
- const view = controlsRef.current?.getTargetView();
16080
- const targetYaw = finiteOr(yaw, view?.yaw ?? 0);
16081
- const targetPitch = finiteOr(pitch, view?.pitch ?? 0);
16082
- if (absolute) {
16083
- const rawPose = quaternionToGyroPose(rawQuaternion);
16179
+ if (absoluteRef.current) {
16180
+ const rawPose = quaternionToGyroPose(
16181
+ rawQuaternionRef.current,
16182
+ previousYawRef.current
16183
+ );
16184
+ previousYawRef.current = rawPose.yaw;
16084
16185
  absoluteOffsetRef.current = {
16085
- yaw: shortestYawDelta2(
16086
- rawPose.yaw + finiteOr(north, 0),
16087
- targetYaw
16186
+ yaw: shortestGyroYawDelta(
16187
+ rawPose.yaw + finiteOr(northRef.current, 0),
16188
+ yaw
16088
16189
  ),
16089
- pitch: targetPitch - rawPose.pitch
16190
+ pitch: pitch - rawPose.pitch
16090
16191
  };
16091
16192
  } else {
16092
- calibrationRef.current = createRelativeCalibration(
16093
- rawQuaternion,
16094
- targetYaw,
16095
- targetPitch
16193
+ createRelativeCalibration(
16194
+ rawQuaternionRef.current,
16195
+ yaw,
16196
+ pitch,
16197
+ calibrationRef.current
16096
16198
  );
16199
+ hasCalibrationRef.current = true;
16097
16200
  }
16201
+ },
16202
+ []
16203
+ );
16204
+ const resetSensor = useCallback9(
16205
+ (yaw, pitch) => {
16206
+ if (!hasRawQuaternionRef.current) {
16207
+ return;
16208
+ }
16209
+ const view = controlsRef.current?.getTargetView();
16210
+ const targetYaw = finiteOr(yaw, view?.yaw ?? 0);
16211
+ const targetPitch = finiteOr(pitch, view?.pitch ?? 0);
16212
+ anchorToView(targetYaw, targetPitch);
16098
16213
  startPoseRef.current = {
16099
16214
  yaw: targetYaw,
16100
16215
  pitch: targetPitch,
@@ -16102,8 +16217,10 @@ var Gyro = forwardRef3(function Gyro2({
16102
16217
  };
16103
16218
  enabledAtRef.current = performance.now();
16104
16219
  filteredPoseRef.current = null;
16220
+ lastSamplePoseRef.current = null;
16221
+ lastSampleAtRef.current = null;
16105
16222
  },
16106
- [absolute, controlsRef, north]
16223
+ [anchorToView, controlsRef]
16107
16224
  );
16108
16225
  useImperativeHandle3(
16109
16226
  ref,
@@ -16123,9 +16240,6 @@ var Gyro = forwardRef3(function Gyro2({
16123
16240
  useEffect35(() => {
16124
16241
  const supported = typeof window !== "undefined" && window.isSecureContext && typeof DeviceOrientationEvent !== "undefined" && (desktopSupport || isMobileDevice());
16125
16242
  setAvailable(supported);
16126
- return () => {
16127
- availableRef.current = false;
16128
- };
16129
16243
  }, [desktopSupport, setAvailable]);
16130
16244
  useEffect35(() => {
16131
16245
  controlsRef.current?.setGyroTouchMode(touchMode);
@@ -16145,7 +16259,7 @@ var Gyro = forwardRef3(function Gyro2({
16145
16259
  };
16146
16260
  }, [enabled, gl, internallyDisabled, touchMode]);
16147
16261
  useEffect35(() => {
16148
- if (!enabled || internallyDisabled || !availableRef.current) {
16262
+ if (!enabled || internallyDisabled || !available) {
16149
16263
  deactivate();
16150
16264
  return;
16151
16265
  }
@@ -16174,10 +16288,12 @@ var Gyro = forwardRef3(function Gyro2({
16174
16288
  gamma: event.gamma,
16175
16289
  compassHeading: absolute && hasCompassHeading ? compassEvent.webkitCompassHeading : void 0
16176
16290
  };
16177
- rawQuaternionRef.current = deviceOrientationToQuaternion(
16291
+ deviceOrientationToQuaternion(
16178
16292
  sample,
16179
- screenAngleRef.current
16293
+ screenAngleRef.current,
16294
+ rawQuaternionRef.current
16180
16295
  );
16296
+ hasRawQuaternionRef.current = true;
16181
16297
  if (!activeRef.current) {
16182
16298
  const view = controlsRef.current?.getTargetView() ?? {
16183
16299
  yaw: 0,
@@ -16185,11 +16301,13 @@ var Gyro = forwardRef3(function Gyro2({
16185
16301
  fov: 75
16186
16302
  };
16187
16303
  if (!absolute) {
16188
- calibrationRef.current = createRelativeCalibration(
16304
+ createRelativeCalibration(
16189
16305
  rawQuaternionRef.current,
16190
16306
  view.yaw,
16191
- view.pitch
16307
+ view.pitch,
16308
+ calibrationRef.current
16192
16309
  );
16310
+ hasCalibrationRef.current = true;
16193
16311
  }
16194
16312
  absoluteOffsetRef.current = { yaw: 0, pitch: 0 };
16195
16313
  startPoseRef.current = { ...view, roll: 0 };
@@ -16237,6 +16355,7 @@ var Gyro = forwardRef3(function Gyro2({
16237
16355
  };
16238
16356
  }, [
16239
16357
  absolute,
16358
+ available,
16240
16359
  controlsRef,
16241
16360
  deactivate,
16242
16361
  emit,
@@ -16245,28 +16364,84 @@ var Gyro = forwardRef3(function Gyro2({
16245
16364
  requestPermission
16246
16365
  ]);
16247
16366
  useFrame20((_, deltaSeconds) => {
16248
- const rawQuaternion = rawQuaternionRef.current;
16249
- if (!activeRef.current || !rawQuaternion) {
16367
+ if (!activeRef.current || !hasRawQuaternionRef.current) {
16250
16368
  return;
16251
16369
  }
16252
16370
  let targetPose;
16253
16371
  if (absolute) {
16254
- const rawPose = quaternionToGyroPose(rawQuaternion);
16372
+ const rawPose = quaternionToGyroPose(
16373
+ rawQuaternionRef.current,
16374
+ previousYawRef.current
16375
+ );
16376
+ previousYawRef.current = rawPose.yaw;
16255
16377
  targetPose = {
16256
16378
  yaw: rawPose.yaw + finiteOr(north, 0) + absoluteOffsetRef.current.yaw,
16257
16379
  pitch: rawPose.pitch + absoluteOffsetRef.current.pitch,
16258
16380
  roll: rawPose.roll
16259
16381
  };
16260
16382
  } else {
16261
- const calibration = calibrationRef.current;
16262
- if (!calibration) {
16383
+ if (!hasCalibrationRef.current) {
16263
16384
  return;
16264
16385
  }
16265
- targetPose = applyGyroCalibration(rawQuaternion, calibration);
16386
+ targetPose = applyGyroCalibration(
16387
+ rawQuaternionRef.current,
16388
+ calibrationRef.current,
16389
+ calibratedQuaternionRef.current,
16390
+ previousYawRef.current
16391
+ );
16392
+ previousYawRef.current = targetPose.yaw;
16266
16393
  }
16267
16394
  if (!camroll) {
16268
16395
  targetPose.roll = 0;
16269
16396
  }
16397
+ const sampleAt = performance.now();
16398
+ const previousSample = lastSamplePoseRef.current;
16399
+ const previousSampleAt = lastSampleAtRef.current;
16400
+ if (previousSample && previousSampleAt !== null) {
16401
+ const angleDelta = gyroPoseAngleDelta(previousSample, targetPose);
16402
+ const sampleDeltaSeconds = Math.max(
16403
+ (sampleAt - previousSampleAt) / 1e3,
16404
+ deltaSeconds
16405
+ );
16406
+ if (shouldReanchorGyro(angleDelta, sampleDeltaSeconds)) {
16407
+ const view = controlsRef.current?.getTargetView() ?? {
16408
+ yaw: targetPose.yaw,
16409
+ pitch: targetPose.pitch,
16410
+ fov: 75
16411
+ };
16412
+ anchorToView(view.yaw, view.pitch);
16413
+ if (absolute) {
16414
+ const rawPose = quaternionToGyroPose(
16415
+ rawQuaternionRef.current,
16416
+ previousYawRef.current
16417
+ );
16418
+ previousYawRef.current = rawPose.yaw;
16419
+ targetPose = {
16420
+ yaw: rawPose.yaw + finiteOr(north, 0) + absoluteOffsetRef.current.yaw,
16421
+ pitch: rawPose.pitch + absoluteOffsetRef.current.pitch,
16422
+ roll: rawPose.roll
16423
+ };
16424
+ } else {
16425
+ targetPose = applyGyroCalibration(
16426
+ rawQuaternionRef.current,
16427
+ calibrationRef.current,
16428
+ calibratedQuaternionRef.current,
16429
+ previousYawRef.current
16430
+ );
16431
+ previousYawRef.current = targetPose.yaw;
16432
+ }
16433
+ if (!camroll) {
16434
+ targetPose.roll = 0;
16435
+ }
16436
+ filteredPoseRef.current = {
16437
+ yaw: view.yaw,
16438
+ pitch: view.pitch,
16439
+ roll: targetPose.roll
16440
+ };
16441
+ }
16442
+ }
16443
+ lastSamplePoseRef.current = { ...targetPose };
16444
+ lastSampleAtRef.current = sampleAt;
16270
16445
  const softstartMs = resolveSoftstart(softstart) * 1e3;
16271
16446
  const startPose = startPoseRef.current;
16272
16447
  if (startPose && softstartMs > 0) {
@@ -16275,9 +16450,9 @@ var Gyro = forwardRef3(function Gyro2({
16275
16450
  1
16276
16451
  );
16277
16452
  targetPose = {
16278
- yaw: startPose.yaw + shortestYawDelta2(startPose.yaw, targetPose.yaw) * progress,
16453
+ yaw: startPose.yaw + shortestGyroYawDelta(startPose.yaw, targetPose.yaw) * progress,
16279
16454
  pitch: startPose.pitch + (targetPose.pitch - startPose.pitch) * progress,
16280
- roll: startPose.roll + shortestYawDelta2(startPose.roll, targetPose.roll) * progress
16455
+ roll: startPose.roll + shortestGyroYawDelta(startPose.roll, targetPose.roll) * progress
16281
16456
  };
16282
16457
  if (progress === 1) {
16283
16458
  startPoseRef.current = null;
@@ -16287,9 +16462,9 @@ var Gyro = forwardRef3(function Gyro2({
16287
16462
  const retention = resolveFriction(friction);
16288
16463
  const follow = retention === 0 ? 1 : 1 - Math.pow(retention, deltaSeconds * 60);
16289
16464
  const filteredPose = previous ? {
16290
- yaw: previous.yaw + shortestYawDelta2(previous.yaw, targetPose.yaw) * follow,
16465
+ yaw: previous.yaw + shortestGyroYawDelta(previous.yaw, targetPose.yaw) * follow,
16291
16466
  pitch: previous.pitch + (targetPose.pitch - previous.pitch) * follow,
16292
- roll: previous.roll + shortestYawDelta2(previous.roll, targetPose.roll) * follow
16467
+ roll: previous.roll + shortestGyroYawDelta(previous.roll, targetPose.roll) * follow
16293
16468
  } : targetPose;
16294
16469
  filteredPoseRef.current = filteredPose;
16295
16470
  if (controlsRef.current?.isInteractionLocked()) {
@@ -16300,7 +16475,7 @@ var Gyro = forwardRef3(function Gyro2({
16300
16475
  lastAppliedPoseRef.current = { ...filteredPose };
16301
16476
  controlsRef.current?.setGyroPose(filteredPose);
16302
16477
  }
16303
- });
16478
+ }, -1);
16304
16479
  return null;
16305
16480
  });
16306
16481