@ericchen1990/pano-view 3.6.5 → 3.6.7

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
@@ -11669,7 +11821,7 @@ import {
11669
11821
  useSyncExternalStore as useSyncExternalStore7
11670
11822
  } from "react";
11671
11823
  import { createRoot } from "react-dom/client";
11672
- import { Vector3 as Vector313 } from "three";
11824
+ import { Vector3 as Vector314 } from "three";
11673
11825
  import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
11674
11826
  function useFallbackStereoView() {
11675
11827
  const runtime = useContext20(WebVRRuntimeContext);
@@ -11692,7 +11844,7 @@ function ProjectedHtml({
11692
11844
  const { camera, gl, events, size } = useThree17();
11693
11845
  const group = useRef23(null);
11694
11846
  const overlay = useRef23(null);
11695
- const scratch = useRef23(new Vector313());
11847
+ const scratch = useRef23(new Vector314());
11696
11848
  const target = events.connected || gl.domElement.parentElement;
11697
11849
  const updatePosition = () => {
11698
11850
  const anchor = group.current;
@@ -11893,19 +12045,19 @@ function HotspotTooltip({
11893
12045
 
11894
12046
  // src/hotspot/transform.ts
11895
12047
  import {
11896
- MathUtils as MathUtils6,
12048
+ MathUtils as MathUtils7,
11897
12049
  PerspectiveCamera as PerspectiveCamera6,
11898
- Vector3 as Vector314
12050
+ Vector3 as Vector315
11899
12051
  } from "three";
11900
- var LOCAL_FORWARD = new Vector314(0, 0, 1);
11901
- var CAMERA_POSITION = new Vector314();
12052
+ var LOCAL_FORWARD = new Vector315(0, 0, 1);
12053
+ var CAMERA_POSITION = new Vector315();
11902
12054
  function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3, rotation, scaleMode, referenceFov, useAngularScale, worldWidth, worldHeight, distance) {
11903
12055
  if (orientation2 === "billboard") {
11904
12056
  group.quaternion.copy(camera.quaternion);
11905
12057
  } else {
11906
12058
  group.quaternion.copy(surfaceQuaternion3);
11907
12059
  }
11908
- group.rotateOnAxis(LOCAL_FORWARD, MathUtils6.degToRad(-rotation));
12060
+ group.rotateOnAxis(LOCAL_FORWARD, MathUtils7.degToRad(-rotation));
11909
12061
  let scaleFactor = scaleMode === "fixed" && camera instanceof PerspectiveCamera6 ? fixedScaleFactor(camera.fov, referenceFov) : 1;
11910
12062
  if (scaleMode === "fixed" && orientation2 === "billboard" && useAngularScale && camera instanceof PerspectiveCamera6) {
11911
12063
  group.updateWorldMatrix(true, false);
@@ -11922,15 +12074,15 @@ function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3,
11922
12074
  group.updateMatrixWorld();
11923
12075
  }
11924
12076
  function fixedScaleFactor(cameraFov, referenceFov) {
11925
- const safeReferenceFov = MathUtils6.clamp(referenceFov, 1, 179);
11926
- 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);
11927
12079
  }
11928
12080
  function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegrees, viewportHeight, outsetPixels) {
11929
12081
  const pixels = Number.isFinite(outsetPixels) && outsetPixels > 0 ? outsetPixels : 0;
11930
12082
  const depth = Number.isFinite(cameraSpaceDepth) ? Math.max(cameraSpaceDepth, 1e-4) : 1e-4;
11931
12083
  const height = Number.isFinite(viewportHeight) ? Math.max(viewportHeight, 1) : 1;
11932
- const fov = Number.isFinite(fovDegrees) ? MathUtils6.clamp(fovDegrees, 1, 179) : 75;
11933
- 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);
11934
12086
  const axis = (worldScale) => {
11935
12087
  const pixelsPerLocalUnit = pixelsPerWorldUnit * Math.abs(worldScale);
11936
12088
  if (pixelsPerLocalUnit < 1e-4) {
@@ -11946,12 +12098,12 @@ import { Line } from "@react-three/drei";
11946
12098
  import { useFrame as useFrame15, useThree as useThree18 } from "@react-three/fiber";
11947
12099
  import { useLayoutEffect as useLayoutEffect6, useRef as useRef24 } from "react";
11948
12100
  import {
11949
- MathUtils as MathUtils7,
12101
+ MathUtils as MathUtils8,
11950
12102
  PerspectiveCamera as PerspectiveCamera7,
11951
- Vector3 as Vector315
12103
+ Vector3 as Vector316
11952
12104
  } from "three";
11953
12105
  import { jsx as jsx25 } from "react/jsx-runtime";
11954
- var LINE_WORLD_POSITION = new Vector315();
12106
+ var LINE_WORLD_POSITION = new Vector316();
11955
12107
  function applyScreenSpaceDashScale(object, camera, viewportHeight) {
11956
12108
  const material = object?.material;
11957
12109
  if (!object || typeof material?.dashScale !== "number") {
@@ -11960,7 +12112,7 @@ function applyScreenSpaceDashScale(object, camera, viewportHeight) {
11960
12112
  object.getWorldPosition(LINE_WORLD_POSITION);
11961
12113
  const depth = Math.max(camera.position.distanceTo(LINE_WORLD_POSITION), 1);
11962
12114
  const fov = camera instanceof PerspectiveCamera7 ? camera.fov : 75;
11963
- 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);
11964
12116
  }
11965
12117
  function resolveDash(dashSize, gapSize) {
11966
12118
  if (dashSize === void 0 || !Number.isFinite(dashSize) || dashSize <= 0) {
@@ -12029,8 +12181,8 @@ var DRAG_EPSILON_DEGREES = 1e-3;
12029
12181
  var SELECTION_BORDER_WIDTH = 3;
12030
12182
  var SELECTION_BORDER_GAP = 3;
12031
12183
  var SELECTION_BORDER_OUTSET = SELECTION_BORDER_GAP + SELECTION_BORDER_WIDTH / 2;
12032
- var SELECTION_CAMERA_POSITION = new Vector316();
12033
- var SELECTION_WORLD_SCALE = new Vector316();
12184
+ var SELECTION_CAMERA_POSITION = new Vector317();
12185
+ var SELECTION_WORLD_SCALE = new Vector317();
12034
12186
  var SELECTION_BORDER_POINTS = [
12035
12187
  [-0.5, -0.5, 1e-3],
12036
12188
  [0.5, -0.5, 1e-3],
@@ -12040,25 +12192,25 @@ var SELECTION_BORDER_POINTS = [
12040
12192
  ];
12041
12193
  var TOOLTIP_WORLD_BOX = new Box34();
12042
12194
  var TOOLTIP_LOCAL_BOX = new Box34();
12043
- var TOOLTIP_CORNER = new Vector316();
12195
+ var TOOLTIP_CORNER = new Vector317();
12044
12196
  var TOOLTIP_INVERSE = new Matrix44();
12045
- var TOOLTIP_WORLD_CORNER = new Vector316();
12046
- var TOOLTIP_PROJECTED = new Vector316();
12047
- var TOOLTIP_RAY_ORIGIN = new Vector316();
12048
- var TOOLTIP_RAY_FAR = new Vector316();
12049
- 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();
12050
12202
  var TOOLTIP_RAY = new Ray();
12051
12203
  var TOOLTIP_PLANE = new Plane();
12052
- var TOOLTIP_INTERSECTION = new Vector316();
12053
- var TOOLTIP_PLANE_NORMAL = new Vector316();
12054
- 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();
12055
12207
  var HOTSPOT_MODE_RENDERING = {
12056
12208
  surface: { orientation: "surface", placement: "surface" },
12057
12209
  billboard: { orientation: "billboard", placement: "floating" }
12058
12210
  };
12059
12211
  function angularSizeToWorldSize(size, radius) {
12060
- const safeSize = Number.isFinite(size) ? MathUtils8.clamp(Math.abs(size), MIN_ANGULAR_SIZE, MAX_ANGULAR_SIZE) : MIN_ANGULAR_SIZE;
12061
- 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);
12062
12214
  }
12063
12215
  function resolveScale(scale) {
12064
12216
  return Number.isFinite(scale) && scale > 0 ? scale : 1;
@@ -12071,39 +12223,39 @@ function resolveDistance(placement, distance, width, height) {
12071
12223
  if (!Number.isFinite(distance)) {
12072
12224
  return DEFAULT_FLOATING_DISTANCE;
12073
12225
  }
12074
- return MathUtils8.clamp(Math.abs(distance), 0.1, maximumDistance);
12226
+ return MathUtils9.clamp(Math.abs(distance), 0.1, maximumDistance);
12075
12227
  }
12076
12228
  function maximumHotspotDistance(width, height) {
12077
- const safeWidth = MathUtils8.clamp(
12229
+ const safeWidth = MathUtils9.clamp(
12078
12230
  Math.abs(Number.isFinite(width) ? width : MIN_ANGULAR_SIZE),
12079
12231
  MIN_ANGULAR_SIZE,
12080
12232
  MAX_ANGULAR_SIZE
12081
12233
  );
12082
- const safeHeight = MathUtils8.clamp(
12234
+ const safeHeight = MathUtils9.clamp(
12083
12235
  Math.abs(Number.isFinite(height) ? height : MIN_ANGULAR_SIZE),
12084
12236
  MIN_ANGULAR_SIZE,
12085
12237
  MAX_ANGULAR_SIZE
12086
12238
  );
12087
12239
  const cornerFactor = Math.hypot(
12088
12240
  1,
12089
- Math.tan(MathUtils8.degToRad(safeWidth) / 2),
12090
- Math.tan(MathUtils8.degToRad(safeHeight) / 2)
12241
+ Math.tan(MathUtils9.degToRad(safeWidth) / 2),
12242
+ Math.tan(MathUtils9.degToRad(safeHeight) / 2)
12091
12243
  );
12092
12244
  return (PANORAMA_RADIUS - PANORAMA_SURFACE_INSET) / cornerFactor;
12093
12245
  }
12094
12246
  function createSurfaceQuaternion(position) {
12095
12247
  const outward = panoPositionToVector3(position).normalize();
12096
12248
  const inward = outward.clone().negate();
12097
- const pitch = MathUtils8.degToRad(position.pitch);
12098
- const yaw = MathUtils8.degToRad(position.yaw);
12099
- const localUp = new Vector316(
12249
+ const pitch = MathUtils9.degToRad(position.pitch);
12250
+ const yaw = MathUtils9.degToRad(position.yaw);
12251
+ const localUp = new Vector317(
12100
12252
  -Math.sin(pitch) * Math.sin(yaw),
12101
12253
  Math.cos(pitch),
12102
12254
  Math.sin(pitch) * Math.cos(yaw)
12103
12255
  ).normalize();
12104
12256
  const localRight = localUp.clone().cross(inward).normalize();
12105
12257
  const basis = new Matrix44().makeBasis(localRight, localUp, inward);
12106
- return new Quaternion3().setFromRotationMatrix(basis);
12258
+ return new Quaternion4().setFromRotationMatrix(basis);
12107
12259
  }
12108
12260
  function makeInteractionEvent(id, position, event) {
12109
12261
  return {
@@ -12116,7 +12268,7 @@ function makeInteractionEvent(id, position, event) {
12116
12268
  function angularDistance(a, b) {
12117
12269
  const aVector = panoPositionToVector3(a);
12118
12270
  const bVector = panoPositionToVector3(b);
12119
- return MathUtils8.radToDeg(aVector.angleTo(bVector));
12271
+ return MathUtils9.radToDeg(aVector.angleTo(bVector));
12120
12272
  }
12121
12273
  var DRAG_POINTER = new Vector24();
12122
12274
  var DRAG_RAYCASTER = new Raycaster2();
@@ -13761,10 +13913,10 @@ import {
13761
13913
  } from "three";
13762
13914
 
13763
13915
  // src/hotspot/audio-spatial.ts
13764
- import { MathUtils as MathUtils9, Vector3 as Vector317 } from "three";
13765
- var LOOK_DIR = new Vector317();
13766
- var SOURCE_DIR = new Vector317();
13767
- 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();
13768
13920
  function clampAudioVolume(volume) {
13769
13921
  if (!Number.isFinite(volume)) {
13770
13922
  return 1;
@@ -13789,11 +13941,11 @@ function lookRangeGain(angleDegrees, range) {
13789
13941
  function audioSpatialFromView(camera, position, range) {
13790
13942
  camera.getWorldDirection(LOOK_DIR);
13791
13943
  SOURCE_DIR.copy(panoPositionToVector3(position)).normalize();
13792
- const angleDegrees = MathUtils9.radToDeg(
13793
- 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))
13794
13946
  );
13795
13947
  RIGHT_DIR.set(1, 0, 0).applyQuaternion(camera.quaternion);
13796
- const pan = MathUtils9.clamp(
13948
+ const pan = MathUtils10.clamp(
13797
13949
  Math.atan2(SOURCE_DIR.dot(RIGHT_DIR), SOURCE_DIR.dot(LOOK_DIR)) / (Math.PI / 2),
13798
13950
  -1,
13799
13951
  1
@@ -14105,7 +14257,7 @@ import {
14105
14257
  CanvasTexture as CanvasTexture3,
14106
14258
  ClampToEdgeWrapping as ClampToEdgeWrapping8,
14107
14259
  LinearFilter as LinearFilter10,
14108
- MathUtils as MathUtils10,
14260
+ MathUtils as MathUtils11,
14109
14261
  PerspectiveCamera as PerspectiveCamera9,
14110
14262
  SRGBColorSpace as SRGBColorSpace9
14111
14263
  } from "three";
@@ -14180,7 +14332,7 @@ function projectedDevicePixels(angleDeg, fovDeg, viewportHeight) {
14180
14332
  }
14181
14333
  const fov = Math.min(179, Math.max(1, resolvePositive(fovDeg, DEFAULT_REFERENCE_FOV2)));
14182
14334
  const angle = Math.min(179, Math.max(0.01, Math.abs(angleDeg)));
14183
- 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;
14184
14336
  }
14185
14337
  function snapRasterLongSide(screenLong, layoutLong) {
14186
14338
  if (!(screenLong > 0)) {
@@ -14655,10 +14807,10 @@ import {
14655
14807
  DoubleSide as DoubleSide5,
14656
14808
  Float32BufferAttribute as Float32BufferAttribute2,
14657
14809
  Matrix4 as Matrix45,
14658
- Quaternion as Quaternion4,
14810
+ Quaternion as Quaternion5,
14659
14811
  ShapeUtils,
14660
14812
  Vector2 as Vector26,
14661
- Vector3 as Vector318
14813
+ Vector3 as Vector319
14662
14814
  } from "three";
14663
14815
  import { jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
14664
14816
  var POLYGON_RADIUS = 990;
@@ -14732,7 +14884,7 @@ function polygonContainsDirection(vertices, direction) {
14732
14884
  }
14733
14885
  function fitsHemisphere(vertices) {
14734
14886
  const directions = vertices.map((vertex) => panoPositionToVector3(vertex).normalize());
14735
- const candidates = [new Vector318()];
14887
+ const candidates = [new Vector319()];
14736
14888
  for (const direction of directions) {
14737
14889
  candidates[0].add(direction);
14738
14890
  candidates.push(direction.clone());
@@ -14756,7 +14908,7 @@ function validatePolygonVertices(vertices) {
14756
14908
  if (hasSelfIntersection(unwrapped)) {
14757
14909
  issues.push({ code: "self_intersection", message: "Polygon edges cannot cross." });
14758
14910
  }
14759
- 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))) {
14760
14912
  issues.push({ code: "contains_pole", message: "Polygons cannot contain a panorama pole." });
14761
14913
  }
14762
14914
  if (!fitsHemisphere(normalized)) {
@@ -14767,7 +14919,7 @@ function validatePolygonVertices(vertices) {
14767
14919
  function polygonCenter(vertices) {
14768
14920
  const sum = vertices.reduce(
14769
14921
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
14770
- new Vector318()
14922
+ new Vector319()
14771
14923
  );
14772
14924
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
14773
14925
  }
@@ -14776,12 +14928,12 @@ function surfaceQuaternion(position) {
14776
14928
  const inward = outward.clone().negate();
14777
14929
  const pitch = position.pitch * Math.PI / 180;
14778
14930
  const yaw = position.yaw * Math.PI / 180;
14779
- const localUp = new Vector318(
14931
+ const localUp = new Vector319(
14780
14932
  -Math.sin(pitch) * Math.sin(yaw),
14781
14933
  Math.cos(pitch),
14782
14934
  Math.sin(pitch) * Math.cos(yaw)
14783
14935
  ).normalize();
14784
- return new Quaternion4().setFromRotationMatrix(
14936
+ return new Quaternion5().setFromRotationMatrix(
14785
14937
  new Matrix45().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14786
14938
  );
14787
14939
  }
@@ -14789,7 +14941,7 @@ function makeWorldToLocal(center) {
14789
14941
  return new Matrix45().compose(
14790
14942
  panoPositionToVector3(center, POLYGON_RADIUS),
14791
14943
  surfaceQuaternion(center),
14792
- new Vector318(1, 1, 1)
14944
+ new Vector319(1, 1, 1)
14793
14945
  ).invert();
14794
14946
  }
14795
14947
  function projectedPoint(a, b, c, weightB, weightC, radius, worldToLocal) {
@@ -15015,10 +15167,10 @@ function PolygonHotspot({
15015
15167
  // src/hotspot/polyline-hotspot.tsx
15016
15168
  import { useEffect as useEffect32, useMemo as useMemo21, useRef as useRef33 } from "react";
15017
15169
  import {
15018
- MathUtils as MathUtils11,
15170
+ MathUtils as MathUtils12,
15019
15171
  Matrix4 as Matrix46,
15020
- Quaternion as Quaternion5,
15021
- Vector3 as Vector319
15172
+ Quaternion as Quaternion6,
15173
+ Vector3 as Vector320
15022
15174
  } from "three";
15023
15175
  import { jsx as jsx36 } from "react/jsx-runtime";
15024
15176
  var POLYLINE_RADIUS = 990;
@@ -15045,21 +15197,21 @@ function unwrapVertices(vertices) {
15045
15197
  function polylineCenter(vertices) {
15046
15198
  const sum = vertices.reduce(
15047
15199
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
15048
- new Vector319()
15200
+ new Vector320()
15049
15201
  );
15050
15202
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
15051
15203
  }
15052
15204
  function surfaceQuaternion2(position) {
15053
15205
  const outward = panoPositionToVector3(position).normalize();
15054
15206
  const inward = outward.clone().negate();
15055
- const pitch = MathUtils11.degToRad(position.pitch);
15056
- const yaw = MathUtils11.degToRad(position.yaw);
15057
- const localUp = new Vector319(
15207
+ const pitch = MathUtils12.degToRad(position.pitch);
15208
+ const yaw = MathUtils12.degToRad(position.yaw);
15209
+ const localUp = new Vector320(
15058
15210
  -Math.sin(pitch) * Math.sin(yaw),
15059
15211
  Math.cos(pitch),
15060
15212
  Math.sin(pitch) * Math.cos(yaw)
15061
15213
  ).normalize();
15062
- return new Quaternion5().setFromRotationMatrix(
15214
+ return new Quaternion6().setFromRotationMatrix(
15063
15215
  new Matrix46().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
15064
15216
  );
15065
15217
  }
@@ -15067,7 +15219,7 @@ function makeWorldToLocal2(center) {
15067
15219
  return new Matrix46().compose(
15068
15220
  panoPositionToVector3(center, POLYLINE_RADIUS),
15069
15221
  surfaceQuaternion2(center),
15070
- new Vector319(1, 1, 1)
15222
+ new Vector320(1, 1, 1)
15071
15223
  ).invert();
15072
15224
  }
15073
15225
  function subdivisionSteps(vertices) {
@@ -15082,7 +15234,7 @@ function subdivisionSteps(vertices) {
15082
15234
  }
15083
15235
  return Math.min(
15084
15236
  8,
15085
- Math.max(1, Math.ceil(MathUtils11.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
15237
+ Math.max(1, Math.ceil(MathUtils12.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
15086
15238
  );
15087
15239
  }
15088
15240
  function makeLinePositions(vertices, center) {
@@ -15855,65 +16007,7 @@ import {
15855
16007
  useRef as useRef35,
15856
16008
  useState as useState22
15857
16009
  } from "react";
15858
-
15859
- // src/gyro-orientation.ts
15860
- import { Euler as Euler4, MathUtils as MathUtils12, Quaternion as Quaternion6, Vector3 as Vector320 } from "three";
15861
- var DEVICE_FORWARD_CORRECTION = new Quaternion6().setFromAxisAngle(
15862
- new Vector320(1, 0, 0),
15863
- -Math.PI / 2
15864
- );
15865
- var SCREEN_AXIS = new Vector320(0, 0, 1);
15866
- function getScreenOrientationAngle() {
15867
- if (typeof window === "undefined") {
15868
- return 0;
15869
- }
15870
- const angle = window.screen.orientation?.angle;
15871
- if (Number.isFinite(angle)) {
15872
- return angle;
15873
- }
15874
- const legacyAngle = window.orientation;
15875
- return Number.isFinite(legacyAngle) ? legacyAngle : 0;
15876
- }
15877
- function deviceOrientationToQuaternion(sample, screenOrientation) {
15878
- const alpha = Number.isFinite(sample.compassHeading) ? 360 - sample.compassHeading : sample.alpha;
15879
- const deviceEuler = new Euler4(
15880
- MathUtils12.degToRad(sample.beta),
15881
- MathUtils12.degToRad(alpha),
15882
- MathUtils12.degToRad(-sample.gamma),
15883
- "YXZ"
15884
- );
15885
- const screenCorrection = new Quaternion6().setFromAxisAngle(
15886
- SCREEN_AXIS,
15887
- -MathUtils12.degToRad(screenOrientation)
15888
- );
15889
- return new Quaternion6().setFromEuler(deviceEuler).multiply(DEVICE_FORWARD_CORRECTION).multiply(screenCorrection).normalize();
15890
- }
15891
- function quaternionToGyroPose(quaternion) {
15892
- const euler = new Euler4().setFromQuaternion(quaternion, "YXZ");
15893
- return {
15894
- yaw: MathUtils12.radToDeg(-euler.y),
15895
- pitch: MathUtils12.radToDeg(euler.x),
15896
- roll: MathUtils12.radToDeg(euler.z)
15897
- };
15898
- }
15899
- function createRelativeCalibration(deviceQuaternion, yaw, pitch) {
15900
- const targetQuaternion = new Quaternion6().setFromEuler(
15901
- new Euler4(
15902
- MathUtils12.degToRad(pitch),
15903
- MathUtils12.degToRad(-yaw),
15904
- 0,
15905
- "YXZ"
15906
- )
15907
- );
15908
- return targetQuaternion.multiply(deviceQuaternion.clone().invert()).normalize();
15909
- }
15910
- function applyGyroCalibration(deviceQuaternion, calibration) {
15911
- return quaternionToGyroPose(
15912
- calibration.clone().multiply(deviceQuaternion).normalize()
15913
- );
15914
- }
15915
-
15916
- // src/gyro.tsx
16010
+ import { Quaternion as Quaternion7 } from "three";
15917
16011
  var deviceOrientationPermissionState = "unknown";
15918
16012
  function isMobileDevice() {
15919
16013
  if (typeof navigator === "undefined") {
@@ -15921,12 +16015,6 @@ function isMobileDevice() {
15921
16015
  }
15922
16016
  return /Android|iPad|iPhone|iPod|Mobile/i.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
15923
16017
  }
15924
- function normalizeYaw2(value) {
15925
- return ((value + 180) % 360 + 360) % 360 - 180;
15926
- }
15927
- function shortestYawDelta2(from, to) {
15928
- return normalizeYaw2(to - from);
15929
- }
15930
16018
  function finiteOr(value, fallback) {
15931
16019
  return Number.isFinite(value) ? value : fallback;
15932
16020
  }
@@ -15936,9 +16024,6 @@ function resolveFriction(value) {
15936
16024
  function resolveSoftstart(value) {
15937
16025
  return Math.max(finiteOr(value, 0.5), 0);
15938
16026
  }
15939
- function posesNearlyEqual(a, b) {
15940
- 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;
15941
- }
15942
16027
  var Gyro = forwardRef3(function Gyro2({
15943
16028
  enabled = false,
15944
16029
  camroll = true,
@@ -15958,19 +16043,30 @@ var Gyro = forwardRef3(function Gyro2({
15958
16043
  const eventBus = useContext25(PanoEventBusContext);
15959
16044
  const { gl } = useThree25();
15960
16045
  const [internallyDisabled, setInternallyDisabled] = useState22(false);
16046
+ const [available, setAvailableState] = useState22(false);
15961
16047
  const availableRef = useRef35(false);
15962
16048
  const availabilityReportedRef = useRef35(null);
15963
16049
  const activeRef = useRef35(false);
15964
16050
  const permissionRef = useRef35(deviceOrientationPermissionState);
15965
16051
  const absoluteEventSourceRef = useRef35(null);
15966
- const rawQuaternionRef = useRef35(null);
15967
- 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());
15968
16057
  const absoluteOffsetRef = useRef35({ yaw: 0, pitch: 0 });
15969
16058
  const screenAngleRef = useRef35(getScreenOrientationAngle());
15970
16059
  const startPoseRef = useRef35(null);
15971
16060
  const enabledAtRef = useRef35(0);
15972
16061
  const filteredPoseRef = useRef35(null);
15973
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;
15974
16070
  const callbacksRef = useRef35({
15975
16071
  onAvailable,
15976
16072
  onUnavailable,
@@ -16016,13 +16112,14 @@ var Gyro = forwardRef3(function Gyro2({
16016
16112
  [eventBus]
16017
16113
  );
16018
16114
  const setAvailable = useCallback9(
16019
- (available) => {
16020
- availableRef.current = available;
16021
- if (availabilityReportedRef.current === available) {
16115
+ (nextAvailable) => {
16116
+ availableRef.current = nextAvailable;
16117
+ setAvailableState(nextAvailable);
16118
+ if (availabilityReportedRef.current === nextAvailable) {
16022
16119
  return;
16023
16120
  }
16024
- availabilityReportedRef.current = available;
16025
- emit(available ? "gyroavailable" : "gyrounavailable");
16121
+ availabilityReportedRef.current = nextAvailable;
16122
+ emit(nextAvailable ? "gyroavailable" : "gyrounavailable");
16026
16123
  },
16027
16124
  [emit]
16028
16125
  );
@@ -16065,37 +16162,54 @@ var Gyro = forwardRef3(function Gyro2({
16065
16162
  controlsRef.current?.setGyroActive(false);
16066
16163
  emit("gyrodisable");
16067
16164
  }
16068
- rawQuaternionRef.current = null;
16165
+ hasRawQuaternionRef.current = false;
16069
16166
  absoluteEventSourceRef.current = null;
16070
- calibrationRef.current = null;
16167
+ hasCalibrationRef.current = false;
16071
16168
  filteredPoseRef.current = null;
16072
16169
  lastAppliedPoseRef.current = null;
16170
+ lastSamplePoseRef.current = null;
16171
+ lastSampleAtRef.current = null;
16172
+ previousYawRef.current = void 0;
16073
16173
  }, [controlsRef, emit]);
16074
- const resetSensor = useCallback9(
16174
+ const anchorToView = useCallback9(
16075
16175
  (yaw, pitch) => {
16076
- const rawQuaternion = rawQuaternionRef.current;
16077
- if (!rawQuaternion) {
16176
+ if (!hasRawQuaternionRef.current) {
16078
16177
  return;
16079
16178
  }
16080
- const view = controlsRef.current?.getTargetView();
16081
- const targetYaw = finiteOr(yaw, view?.yaw ?? 0);
16082
- const targetPitch = finiteOr(pitch, view?.pitch ?? 0);
16083
- if (absolute) {
16084
- const rawPose = quaternionToGyroPose(rawQuaternion);
16179
+ if (absoluteRef.current) {
16180
+ const rawPose = quaternionToGyroPose(
16181
+ rawQuaternionRef.current,
16182
+ previousYawRef.current
16183
+ );
16184
+ previousYawRef.current = rawPose.yaw;
16085
16185
  absoluteOffsetRef.current = {
16086
- yaw: shortestYawDelta2(
16087
- rawPose.yaw + finiteOr(north, 0),
16088
- targetYaw
16186
+ yaw: shortestGyroYawDelta(
16187
+ rawPose.yaw + finiteOr(northRef.current, 0),
16188
+ yaw
16089
16189
  ),
16090
- pitch: targetPitch - rawPose.pitch
16190
+ pitch: pitch - rawPose.pitch
16091
16191
  };
16092
16192
  } else {
16093
- calibrationRef.current = createRelativeCalibration(
16094
- rawQuaternion,
16095
- targetYaw,
16096
- targetPitch
16193
+ createRelativeCalibration(
16194
+ rawQuaternionRef.current,
16195
+ yaw,
16196
+ pitch,
16197
+ calibrationRef.current
16097
16198
  );
16199
+ hasCalibrationRef.current = true;
16098
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);
16099
16213
  startPoseRef.current = {
16100
16214
  yaw: targetYaw,
16101
16215
  pitch: targetPitch,
@@ -16103,8 +16217,10 @@ var Gyro = forwardRef3(function Gyro2({
16103
16217
  };
16104
16218
  enabledAtRef.current = performance.now();
16105
16219
  filteredPoseRef.current = null;
16220
+ lastSamplePoseRef.current = null;
16221
+ lastSampleAtRef.current = null;
16106
16222
  },
16107
- [absolute, controlsRef, north]
16223
+ [anchorToView, controlsRef]
16108
16224
  );
16109
16225
  useImperativeHandle3(
16110
16226
  ref,
@@ -16124,9 +16240,6 @@ var Gyro = forwardRef3(function Gyro2({
16124
16240
  useEffect35(() => {
16125
16241
  const supported = typeof window !== "undefined" && window.isSecureContext && typeof DeviceOrientationEvent !== "undefined" && (desktopSupport || isMobileDevice());
16126
16242
  setAvailable(supported);
16127
- return () => {
16128
- availableRef.current = false;
16129
- };
16130
16243
  }, [desktopSupport, setAvailable]);
16131
16244
  useEffect35(() => {
16132
16245
  controlsRef.current?.setGyroTouchMode(touchMode);
@@ -16146,7 +16259,7 @@ var Gyro = forwardRef3(function Gyro2({
16146
16259
  };
16147
16260
  }, [enabled, gl, internallyDisabled, touchMode]);
16148
16261
  useEffect35(() => {
16149
- if (!enabled || internallyDisabled || !availableRef.current) {
16262
+ if (!enabled || internallyDisabled || !available) {
16150
16263
  deactivate();
16151
16264
  return;
16152
16265
  }
@@ -16175,10 +16288,12 @@ var Gyro = forwardRef3(function Gyro2({
16175
16288
  gamma: event.gamma,
16176
16289
  compassHeading: absolute && hasCompassHeading ? compassEvent.webkitCompassHeading : void 0
16177
16290
  };
16178
- rawQuaternionRef.current = deviceOrientationToQuaternion(
16291
+ deviceOrientationToQuaternion(
16179
16292
  sample,
16180
- screenAngleRef.current
16293
+ screenAngleRef.current,
16294
+ rawQuaternionRef.current
16181
16295
  );
16296
+ hasRawQuaternionRef.current = true;
16182
16297
  if (!activeRef.current) {
16183
16298
  const view = controlsRef.current?.getTargetView() ?? {
16184
16299
  yaw: 0,
@@ -16186,11 +16301,13 @@ var Gyro = forwardRef3(function Gyro2({
16186
16301
  fov: 75
16187
16302
  };
16188
16303
  if (!absolute) {
16189
- calibrationRef.current = createRelativeCalibration(
16304
+ createRelativeCalibration(
16190
16305
  rawQuaternionRef.current,
16191
16306
  view.yaw,
16192
- view.pitch
16307
+ view.pitch,
16308
+ calibrationRef.current
16193
16309
  );
16310
+ hasCalibrationRef.current = true;
16194
16311
  }
16195
16312
  absoluteOffsetRef.current = { yaw: 0, pitch: 0 };
16196
16313
  startPoseRef.current = { ...view, roll: 0 };
@@ -16238,6 +16355,7 @@ var Gyro = forwardRef3(function Gyro2({
16238
16355
  };
16239
16356
  }, [
16240
16357
  absolute,
16358
+ available,
16241
16359
  controlsRef,
16242
16360
  deactivate,
16243
16361
  emit,
@@ -16246,28 +16364,84 @@ var Gyro = forwardRef3(function Gyro2({
16246
16364
  requestPermission
16247
16365
  ]);
16248
16366
  useFrame20((_, deltaSeconds) => {
16249
- const rawQuaternion = rawQuaternionRef.current;
16250
- if (!activeRef.current || !rawQuaternion) {
16367
+ if (!activeRef.current || !hasRawQuaternionRef.current) {
16251
16368
  return;
16252
16369
  }
16253
16370
  let targetPose;
16254
16371
  if (absolute) {
16255
- const rawPose = quaternionToGyroPose(rawQuaternion);
16372
+ const rawPose = quaternionToGyroPose(
16373
+ rawQuaternionRef.current,
16374
+ previousYawRef.current
16375
+ );
16376
+ previousYawRef.current = rawPose.yaw;
16256
16377
  targetPose = {
16257
16378
  yaw: rawPose.yaw + finiteOr(north, 0) + absoluteOffsetRef.current.yaw,
16258
16379
  pitch: rawPose.pitch + absoluteOffsetRef.current.pitch,
16259
16380
  roll: rawPose.roll
16260
16381
  };
16261
16382
  } else {
16262
- const calibration = calibrationRef.current;
16263
- if (!calibration) {
16383
+ if (!hasCalibrationRef.current) {
16264
16384
  return;
16265
16385
  }
16266
- 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;
16267
16393
  }
16268
16394
  if (!camroll) {
16269
16395
  targetPose.roll = 0;
16270
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;
16271
16445
  const softstartMs = resolveSoftstart(softstart) * 1e3;
16272
16446
  const startPose = startPoseRef.current;
16273
16447
  if (startPose && softstartMs > 0) {
@@ -16276,9 +16450,9 @@ var Gyro = forwardRef3(function Gyro2({
16276
16450
  1
16277
16451
  );
16278
16452
  targetPose = {
16279
- yaw: startPose.yaw + shortestYawDelta2(startPose.yaw, targetPose.yaw) * progress,
16453
+ yaw: startPose.yaw + shortestGyroYawDelta(startPose.yaw, targetPose.yaw) * progress,
16280
16454
  pitch: startPose.pitch + (targetPose.pitch - startPose.pitch) * progress,
16281
- roll: startPose.roll + shortestYawDelta2(startPose.roll, targetPose.roll) * progress
16455
+ roll: startPose.roll + shortestGyroYawDelta(startPose.roll, targetPose.roll) * progress
16282
16456
  };
16283
16457
  if (progress === 1) {
16284
16458
  startPoseRef.current = null;
@@ -16288,9 +16462,9 @@ var Gyro = forwardRef3(function Gyro2({
16288
16462
  const retention = resolveFriction(friction);
16289
16463
  const follow = retention === 0 ? 1 : 1 - Math.pow(retention, deltaSeconds * 60);
16290
16464
  const filteredPose = previous ? {
16291
- yaw: previous.yaw + shortestYawDelta2(previous.yaw, targetPose.yaw) * follow,
16465
+ yaw: previous.yaw + shortestGyroYawDelta(previous.yaw, targetPose.yaw) * follow,
16292
16466
  pitch: previous.pitch + (targetPose.pitch - previous.pitch) * follow,
16293
- roll: previous.roll + shortestYawDelta2(previous.roll, targetPose.roll) * follow
16467
+ roll: previous.roll + shortestGyroYawDelta(previous.roll, targetPose.roll) * follow
16294
16468
  } : targetPose;
16295
16469
  filteredPoseRef.current = filteredPose;
16296
16470
  if (controlsRef.current?.isInteractionLocked()) {
@@ -16301,7 +16475,7 @@ var Gyro = forwardRef3(function Gyro2({
16301
16475
  lastAppliedPoseRef.current = { ...filteredPose };
16302
16476
  controlsRef.current?.setGyroPose(filteredPose);
16303
16477
  }
16304
- });
16478
+ }, -1);
16305
16479
  return null;
16306
16480
  });
16307
16481