@ericchen1990/pano-view 3.1.3 → 3.2.1

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
@@ -3745,7 +3745,9 @@ var DEFAULT_PANO_FILTER_INTENSITY = 1;
3745
3745
  var DEFAULT_PANO_FILTER_SNAPSHOT = {
3746
3746
  preset: "none",
3747
3747
  intensity: DEFAULT_PANO_FILTER_INTENSITY,
3748
- enabled: true
3748
+ enabled: true,
3749
+ colorMatrix: null,
3750
+ colorMatrixScope: "panorama"
3749
3751
  };
3750
3752
  function clampPanoFilterIntensity(value) {
3751
3753
  if (!Number.isFinite(value)) {
@@ -3754,15 +3756,17 @@ function clampPanoFilterIntensity(value) {
3754
3756
  return Math.max(0, Math.min(value, 1));
3755
3757
  }
3756
3758
  function resolvePanoFilterSnapshot(input) {
3759
+ if (input.colorMatrix && (input.colorMatrix.length !== 20 || input.colorMatrix.some((value) => !Number.isFinite(value)))) {
3760
+ throw new Error("colorMatrix must contain 20 finite numbers");
3761
+ }
3757
3762
  return {
3763
+ colorMatrix: input.colorMatrix ? [...input.colorMatrix] : null,
3764
+ colorMatrixScope: input.colorMatrixScope ?? "panorama",
3758
3765
  preset: input.preset ?? "none",
3759
3766
  intensity: clampPanoFilterIntensity(input.intensity),
3760
3767
  enabled: input.enabled !== false
3761
3768
  };
3762
3769
  }
3763
- function isPanoFilterActive(snapshot) {
3764
- return snapshot.enabled && snapshot.preset !== "none";
3765
- }
3766
3770
 
3767
3771
  // src/pano-filter/host.ts
3768
3772
  var PanoFilterHostContext = createContext7(null);
@@ -8040,15 +8044,23 @@ import { useContext as useContext17, useEffect as useEffect15, useMemo as useMem
8040
8044
  function PanoFilter({
8041
8045
  preset = "none",
8042
8046
  intensity,
8043
- enabled = true
8047
+ enabled = true,
8048
+ colorMatrix,
8049
+ colorMatrixScope
8044
8050
  }) {
8045
8051
  const host = useContext17(PanoFilterHostContext);
8046
8052
  if (!host) {
8047
8053
  throw new Error("<PanoFilter> must be rendered inside <PanoViewer>.");
8048
8054
  }
8049
8055
  const snapshot = useMemo9(
8050
- () => resolvePanoFilterSnapshot({ preset, intensity, enabled }),
8051
- [enabled, intensity, preset]
8056
+ () => resolvePanoFilterSnapshot({
8057
+ preset,
8058
+ intensity,
8059
+ enabled,
8060
+ colorMatrix,
8061
+ colorMatrixScope
8062
+ }),
8063
+ [enabled, intensity, preset, colorMatrix, colorMatrixScope]
8052
8064
  );
8053
8065
  useEffect15(() => {
8054
8066
  claimPanoFilter(host);
@@ -9702,18 +9714,10 @@ function usePanoViewer() {
9702
9714
  return useMemo12(() => ({ ref, ...handle }), [handle]);
9703
9715
  }
9704
9716
 
9705
- // src/sphere.tsx
9706
- import { useThree as useThree13 } from "@react-three/fiber";
9707
- import { useEffect as useEffect21, useMemo as useMemo14, useRef as useRef22, useState as useState9 } from "react";
9708
- import {
9709
- BackSide as BackSide2,
9710
- LinearFilter,
9711
- LinearMipmapLinearFilter,
9712
- MathUtils as MathUtils5,
9713
- RepeatWrapping,
9714
- SRGBColorSpace,
9715
- TextureLoader
9716
- } from "three";
9717
+ // src/sphere-tile/sphere-tile.tsx
9718
+ import { useFrame as useFrame9, useThree as useThree13 } from "@react-three/fiber";
9719
+ import { memo, useCallback as useCallback9, useEffect as useEffect22, useMemo as useMemo14, useRef as useRef23, useState as useState10 } from "react";
9720
+ import { BackSide as BackSide2, Frustum, Matrix4 as Matrix42, PerspectiveCamera as PerspectiveCamera3 } from "three";
9717
9721
 
9718
9722
  // src/asset-url.ts
9719
9723
  function isAbsoluteAssetUrl(url) {
@@ -9738,6 +9742,10 @@ function resolveUrlAgainstFile(fileUrl, path) {
9738
9742
 
9739
9743
  // src/pano-filter/pano-basic-material.tsx
9740
9744
  import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo13, useRef as useRef21 } from "react";
9745
+ import {
9746
+ Matrix4,
9747
+ Vector4 as Vector42
9748
+ } from "three";
9741
9749
 
9742
9750
  // src/pano-filter/shaders.ts
9743
9751
  var GLSL_FILTER_COMMON = `
@@ -9886,264 +9894,996 @@ ${applyPanoFilterFunction(panoFilterBody(preset))}`
9886
9894
  diffuseColor.rgb = mix(diffuseColor.rgb, applyPanoFilter(diffuseColor.rgb), clamp(uPanoFilterIntensity, 0.0, 1.0));`
9887
9895
  );
9888
9896
  }
9897
+ function injectPanoColorMatrixShader(shader) {
9898
+ shader.fragmentShader = shader.fragmentShader.replace(
9899
+ "#include <common>",
9900
+ `#include <common>
9901
+ uniform mat4 uPanoColorMatrix;
9902
+ uniform vec4 uPanoColorOffset;
9903
+ uniform float uPanoColorMatrixIntensity;`
9904
+ ).replace(
9905
+ "#include <colorspace_fragment>",
9906
+ `#include <colorspace_fragment>
9907
+ gl_FragColor = mix(gl_FragColor, clamp(uPanoColorMatrix * gl_FragColor + uPanoColorOffset, 0.0, 1.0), uPanoColorMatrixIntensity);`
9908
+ );
9909
+ }
9889
9910
 
9890
9911
  // src/pano-filter/pano-basic-material.tsx
9891
9912
  import { jsx as jsx14 } from "react/jsx-runtime";
9913
+ function matrixUniforms(matrix) {
9914
+ return {
9915
+ matrix: new Matrix4().set(
9916
+ matrix[0],
9917
+ matrix[1],
9918
+ matrix[2],
9919
+ matrix[3],
9920
+ matrix[5],
9921
+ matrix[6],
9922
+ matrix[7],
9923
+ matrix[8],
9924
+ matrix[10],
9925
+ matrix[11],
9926
+ matrix[12],
9927
+ matrix[13],
9928
+ matrix[15],
9929
+ matrix[16],
9930
+ matrix[17],
9931
+ matrix[18]
9932
+ ),
9933
+ offset: new Vector42(matrix[4], matrix[9], matrix[14], matrix[19])
9934
+ };
9935
+ }
9892
9936
  function PanoBasicMaterial({
9893
- map,
9894
- side,
9895
- depthTest,
9896
- depthWrite,
9897
- toneMapped = false
9937
+ target = "panorama",
9938
+ ...props
9898
9939
  }) {
9899
9940
  const snapshot = usePanoFilterSnapshot();
9900
- const materialRef = useRef21(null);
9901
- const intensityRef = useRef21(snapshot.intensity);
9902
- const intensityUniformRef = useRef21(null);
9903
- intensityRef.current = snapshot.intensity;
9904
- const active = isPanoFilterActive(snapshot);
9905
- const preset = snapshot.preset;
9941
+ const preset = snapshot.enabled && target === "panorama" ? snapshot.preset : "none";
9942
+ const matrix = snapshot.enabled && (target === "panorama" || snapshot.colorMatrixScope === "all") ? snapshot.colorMatrix : null;
9943
+ const hasMatrix = matrix !== null;
9944
+ const currentRef = useRef21({ matrix, intensity: snapshot.intensity });
9945
+ currentRef.current = { matrix, intensity: snapshot.intensity };
9946
+ const shaderRef = useRef21(null);
9906
9947
  const onBeforeCompile = useMemo13(() => {
9907
- if (!active || preset === "none") {
9908
- return void 0;
9909
- }
9910
9948
  const compile = (shader) => {
9911
- injectPanoFilterShader(shader, preset);
9912
- const intensityUniform = shader.uniforms.uPanoFilterIntensity;
9913
- intensityUniform.value = intensityRef.current;
9914
- intensityUniformRef.current = intensityUniform;
9915
- const material = materialRef.current;
9916
- if (material) {
9917
- material.userData.uPanoFilterIntensity = intensityUniform;
9949
+ shaderRef.current = shader;
9950
+ if (preset !== "none") {
9951
+ injectPanoFilterShader(shader, preset);
9952
+ shader.uniforms.uPanoFilterIntensity.value = currentRef.current.intensity;
9953
+ }
9954
+ if (hasMatrix && currentRef.current.matrix) {
9955
+ injectPanoColorMatrixShader(shader);
9956
+ const values = matrixUniforms(currentRef.current.matrix);
9957
+ shader.uniforms.uPanoColorMatrix = { value: values.matrix };
9958
+ shader.uniforms.uPanoColorOffset = { value: values.offset };
9959
+ shader.uniforms.uPanoColorMatrixIntensity = {
9960
+ value: currentRef.current.intensity
9961
+ };
9918
9962
  }
9919
9963
  };
9920
- compile.toString = () => `pano-filter:${preset}`;
9964
+ compile.toString = () => `pano-filter:${preset}:matrix:${hasMatrix}`;
9921
9965
  return compile;
9922
- }, [active, preset]);
9923
- useLayoutEffect2(() => {
9924
- if (!active) {
9925
- intensityUniformRef.current = null;
9926
- }
9927
- const material = materialRef.current;
9928
- if (!material) {
9929
- return;
9930
- }
9931
- material.needsUpdate = true;
9932
- }, [active, preset]);
9966
+ }, [preset, hasMatrix]);
9933
9967
  useLayoutEffect2(() => {
9934
- if (intensityUniformRef.current) {
9935
- intensityUniformRef.current.value = snapshot.intensity;
9936
- }
9937
- }, [snapshot.intensity]);
9968
+ const shader = shaderRef.current;
9969
+ if (!shader) return;
9970
+ if (shader.uniforms.uPanoFilterIntensity)
9971
+ shader.uniforms.uPanoFilterIntensity.value = snapshot.intensity;
9972
+ if (shader.uniforms.uPanoColorMatrix && matrix) {
9973
+ const values = matrixUniforms(matrix);
9974
+ shader.uniforms.uPanoColorMatrix.value = values.matrix;
9975
+ shader.uniforms.uPanoColorOffset.value = values.offset;
9976
+ shader.uniforms.uPanoColorMatrixIntensity.value = snapshot.intensity;
9977
+ }
9978
+ }, [matrix, snapshot.intensity]);
9938
9979
  return /* @__PURE__ */ jsx14(
9939
9980
  "meshBasicMaterial",
9940
9981
  {
9941
- ref: materialRef,
9942
- depthTest,
9943
- depthWrite,
9944
- map,
9945
- onBeforeCompile,
9946
- side,
9947
- toneMapped
9948
- },
9949
- active ? preset : "none"
9950
- );
9951
- }
9952
-
9953
- // src/sphere.tsx
9954
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
9955
- function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
9956
- const [texture, setTexture] = useState9(null);
9957
- const onLoadRef = useRef22(onLoad);
9958
- const onErrorRef = useRef22(onError);
9959
- onLoadRef.current = onLoad;
9960
- onErrorRef.current = onError;
9961
- useEffect21(() => {
9962
- if (!url) {
9963
- setTexture(null);
9964
- return;
9965
- }
9966
- let active = true;
9967
- const loader = new TextureLoader();
9968
- setTexture(null);
9969
- loader.load(
9970
- url,
9971
- (loadedTexture) => {
9972
- if (!active) {
9973
- loadedTexture.dispose();
9974
- return;
9975
- }
9976
- loadedTexture.colorSpace = SRGBColorSpace;
9977
- loadedTexture.wrapS = RepeatWrapping;
9978
- loadedTexture.repeat.x = -1;
9979
- loadedTexture.offset.x = 1;
9980
- loadedTexture.anisotropy = anisotropy;
9981
- loadedTexture.magFilter = LinearFilter;
9982
- loadedTexture.minFilter = generateMipmaps ? LinearMipmapLinearFilter : LinearFilter;
9983
- loadedTexture.generateMipmaps = generateMipmaps;
9984
- loadedTexture.needsUpdate = true;
9985
- setTexture(loadedTexture);
9986
- onLoadRef.current?.(loadedTexture);
9987
- },
9988
- void 0,
9989
- (error) => {
9990
- if (active) {
9991
- setTexture(null);
9992
- onErrorRef.current?.(error);
9993
- }
9994
- }
9995
- );
9996
- return () => {
9997
- active = false;
9998
- };
9999
- }, [anisotropy, generateMipmaps, url]);
10000
- useEffect21(
10001
- () => () => {
10002
- texture?.dispose();
9982
+ toneMapped: false,
9983
+ ...props,
9984
+ onBeforeCompile
10003
9985
  },
10004
- [texture]
10005
- );
10006
- return texture;
10007
- }
10008
- function Sphere({
10009
- src,
10010
- previewUrl,
10011
- yawOffset = 0,
10012
- onReady,
10013
- onLoad,
10014
- onError,
10015
- onPreviewError,
10016
- visible = true
10017
- }) {
10018
- const gl = useThree13((state) => state.gl);
10019
- const anisotropy = gl.capabilities.getMaxAnisotropy();
10020
- const resolvedPreviewUrl = useMemo14(
10021
- () => resolveUrlAgainstFile(src, previewUrl),
10022
- [previewUrl, src]
10023
- );
10024
- const texture = useEquirectTexture(
10025
- src,
10026
- anisotropy,
10027
- true,
10028
- onLoad,
10029
- onError
10030
- );
10031
- const previewTexture = useEquirectTexture(
10032
- texture ? null : resolvedPreviewUrl,
10033
- anisotropy,
10034
- false,
10035
- void 0,
10036
- onPreviewError
10037
- );
10038
- const displayTexture = texture ?? previewTexture;
10039
- const readyRef = useRef22(false);
10040
- useEffect21(() => {
10041
- readyRef.current = false;
10042
- }, [resolvedPreviewUrl, src]);
10043
- useEffect21(() => {
10044
- if (!displayTexture || readyRef.current) {
10045
- return;
10046
- }
10047
- readyRef.current = true;
10048
- onReady?.();
10049
- }, [displayTexture, onReady]);
10050
- if (!displayTexture) {
10051
- return null;
10052
- }
10053
- return /* @__PURE__ */ jsxs9(
10054
- "mesh",
10055
- {
10056
- rotation: [0, MathUtils5.degToRad(90 + yawOffset), 0],
10057
- visible,
10058
- children: [
10059
- /* @__PURE__ */ jsx15("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10060
- /* @__PURE__ */ jsx15(
10061
- PanoBasicMaterial,
10062
- {
10063
- map: displayTexture,
10064
- side: BackSide2,
10065
- toneMapped: false
10066
- }
10067
- )
10068
- ]
10069
- }
9986
+ `${preset}:${hasMatrix}`
10070
9987
  );
10071
9988
  }
10072
9989
 
10073
- // src/video/pano-video.tsx
10074
- import { useThree as useThree14 } from "@react-three/fiber";
10075
- import {
10076
- useCallback as useCallback9,
10077
- useContext as useContext21,
10078
- useEffect as useEffect22,
10079
- useLayoutEffect as useLayoutEffect3,
10080
- useMemo as useMemo15,
10081
- useRef as useRef23,
10082
- useState as useState10
10083
- } from "react";
9990
+ // src/tile/texture-manager.ts
10084
9991
  import {
10085
- BackSide as BackSide3,
10086
- LinearFilter as LinearFilter2,
10087
- LinearMipmapLinearFilter as LinearMipmapLinearFilter2,
10088
- MathUtils as MathUtils6,
10089
- RepeatWrapping as RepeatWrapping2,
10090
- SRGBColorSpace as SRGBColorSpace2,
10091
- TextureLoader as TextureLoader2,
10092
- VideoTexture
9992
+ ClampToEdgeWrapping,
9993
+ LinearFilter,
9994
+ SRGBColorSpace,
9995
+ TextureLoader
10093
9996
  } from "three";
10094
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
10095
- var EMPTY_TRACKS = [];
10096
- var EMPTY_RATES = DEFAULT_PANO_VIDEO_PLAYBACK_RATES;
10097
- function createEmptySnapshot() {
10098
- return {
10099
- ready: false,
10100
- playing: false,
10101
- blocked: false,
10102
- ended: false,
10103
- currentTime: 0,
10104
- duration: 0,
10105
- volume: 1,
10106
- volumeAdjustable: true,
10107
- muted: true,
10108
- playbackRate: 1,
10109
- variantId: "default",
10110
- variants: [],
10111
- trackId: null,
10112
- tracks: [],
10113
- captionText: "",
10114
- captionAppearance: DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE,
10115
- captionsEnabled: true,
10116
- playbackRates: EMPTY_RATES,
10117
- playbackState: "paused"
10118
- };
10119
- }
10120
- function playbackStateFromVideo(video, blocked) {
10121
- if (blocked) {
10122
- return "blocked";
10123
- }
10124
- if (video.ended) {
10125
- return "ended";
9997
+ var EMPTY_TEXTURE_SNAPSHOT = {
9998
+ status: "idle",
9999
+ texture: null,
10000
+ error: null
10001
+ };
10002
+ var TileTextureManager = class {
10003
+ constructor(options) {
10004
+ this.loader = new TextureLoader();
10005
+ this.entries = /* @__PURE__ */ new Map();
10006
+ this.queue = [];
10007
+ this.activeLoads = 0;
10008
+ this.totalBytes = 0;
10009
+ this.requested = 0;
10010
+ this.loaded = 0;
10011
+ this.failed = 0;
10012
+ this.disposed = false;
10013
+ this.generation = 0;
10014
+ this.options = options;
10126
10015
  }
10127
- if (!video.paused) {
10128
- return "playing";
10016
+ resume() {
10017
+ this.disposed = false;
10018
+ this.drain();
10129
10019
  }
10130
- return "paused";
10131
- }
10132
- function collectCaptionText(track) {
10133
- if (!track?.activeCues || track.activeCues.length === 0) {
10134
- return "";
10020
+ getSnapshot(key) {
10021
+ const entry = this.entries.get(key);
10022
+ return entry ? this.snapshot(entry) : EMPTY_TEXTURE_SNAPSHOT;
10135
10023
  }
10136
- const parts = [];
10137
- for (let index = 0; index < track.activeCues.length; index += 1) {
10138
- const cue = track.activeCues[index];
10139
- if (!cue) {
10140
- continue;
10024
+ acquire(key, url, address, listener) {
10025
+ let entry = this.entries.get(key);
10026
+ if (!entry) {
10027
+ entry = {
10028
+ key,
10029
+ url,
10030
+ address,
10031
+ status: "idle",
10032
+ texture: null,
10033
+ error: null,
10034
+ refs: 0,
10035
+ listeners: /* @__PURE__ */ new Set(),
10036
+ lastUsed: performance.now(),
10037
+ bytes: 0
10038
+ };
10039
+ this.entries.set(key, entry);
10141
10040
  }
10142
- const text = cueText(cue);
10143
- if (text) {
10144
- parts.push(text);
10041
+ entry.refs += 1;
10042
+ entry.lastUsed = performance.now();
10043
+ entry.listeners.add(listener);
10044
+ listener(this.snapshot(entry));
10045
+ if (entry.status === "idle") {
10046
+ entry.status = "queued";
10047
+ this.queue.push(entry);
10048
+ this.requested += 1;
10049
+ this.emitProgress();
10050
+ this.drain();
10145
10051
  }
10146
- }
10052
+ return () => {
10053
+ entry.listeners.delete(listener);
10054
+ entry.refs = Math.max(0, entry.refs - 1);
10055
+ entry.lastUsed = performance.now();
10056
+ if (entry.refs === 0 && entry.status === "queued") {
10057
+ const queueIndex = this.queue.indexOf(entry);
10058
+ if (queueIndex >= 0) {
10059
+ this.queue.splice(queueIndex, 1);
10060
+ }
10061
+ entry.status = "idle";
10062
+ this.entries.delete(entry.key);
10063
+ this.requested = Math.max(0, this.requested - 1);
10064
+ this.emitProgress();
10065
+ this.drain();
10066
+ return;
10067
+ }
10068
+ this.evict();
10069
+ };
10070
+ }
10071
+ dispose() {
10072
+ this.disposed = true;
10073
+ this.generation += 1;
10074
+ this.queue.length = 0;
10075
+ for (const entry of this.entries.values()) {
10076
+ entry.texture?.dispose();
10077
+ entry.listeners.clear();
10078
+ }
10079
+ this.entries.clear();
10080
+ this.totalBytes = 0;
10081
+ }
10082
+ /**
10083
+ * Releases textures that are no longer rendered by any Tile instance.
10084
+ * Scene transitions call this after the outgoing panorama has been
10085
+ * snapshotted, so the incoming scene never competes with stale GPU tiles.
10086
+ */
10087
+ releaseUnused() {
10088
+ for (const entry of Array.from(this.entries.values())) {
10089
+ if (entry.refs > 0) {
10090
+ continue;
10091
+ }
10092
+ entry.texture?.dispose();
10093
+ this.totalBytes -= entry.bytes;
10094
+ this.entries.delete(entry.key);
10095
+ }
10096
+ this.totalBytes = Math.max(0, this.totalBytes);
10097
+ }
10098
+ snapshot(entry) {
10099
+ return {
10100
+ status: entry.status,
10101
+ texture: entry.texture,
10102
+ error: entry.error
10103
+ };
10104
+ }
10105
+ notify(entry) {
10106
+ const snapshot = this.snapshot(entry);
10107
+ for (const listener of entry.listeners) {
10108
+ listener(snapshot);
10109
+ }
10110
+ }
10111
+ emitProgress() {
10112
+ this.options.onProgress?.({
10113
+ requested: this.requested,
10114
+ loaded: this.loaded,
10115
+ failed: this.failed,
10116
+ active: this.activeLoads,
10117
+ queued: this.queue.length
10118
+ });
10119
+ }
10120
+ drain() {
10121
+ while (!this.disposed && this.activeLoads < this.options.concurrency && this.queue.length > 0) {
10122
+ const entry = this.queue.shift();
10123
+ if (entry.refs === 0) {
10124
+ entry.status = "idle";
10125
+ this.entries.delete(entry.key);
10126
+ this.requested = Math.max(0, this.requested - 1);
10127
+ continue;
10128
+ }
10129
+ entry.status = "loading";
10130
+ this.activeLoads += 1;
10131
+ this.notify(entry);
10132
+ this.emitProgress();
10133
+ void this.loadAndFinish(entry);
10134
+ }
10135
+ }
10136
+ async load(entry, generation) {
10137
+ let lastError = null;
10138
+ for (let attempt = 0; attempt <= this.options.retryCount; attempt += 1) {
10139
+ if (this.disposed || generation !== this.generation) return;
10140
+ try {
10141
+ const texture = await this.loader.loadAsync(entry.url);
10142
+ if (this.disposed || generation !== this.generation) {
10143
+ texture.dispose();
10144
+ return;
10145
+ }
10146
+ this.configureTexture(texture);
10147
+ entry.texture = texture;
10148
+ entry.status = "loaded";
10149
+ entry.error = null;
10150
+ entry.bytes = entry.address.width * entry.address.height * 4;
10151
+ entry.lastUsed = performance.now();
10152
+ this.totalBytes += entry.bytes;
10153
+ this.loaded += 1;
10154
+ this.notify(entry);
10155
+ return;
10156
+ } catch (error) {
10157
+ lastError = error;
10158
+ }
10159
+ }
10160
+ if (!this.disposed && generation === this.generation) {
10161
+ entry.status = "error";
10162
+ entry.error = lastError;
10163
+ this.failed += 1;
10164
+ this.notify(entry);
10165
+ this.options.onError?.({
10166
+ address: entry.address,
10167
+ url: entry.url,
10168
+ error: lastError
10169
+ });
10170
+ }
10171
+ }
10172
+ configureTexture(texture) {
10173
+ texture.colorSpace = SRGBColorSpace;
10174
+ texture.wrapS = ClampToEdgeWrapping;
10175
+ texture.wrapT = ClampToEdgeWrapping;
10176
+ texture.magFilter = LinearFilter;
10177
+ texture.minFilter = LinearFilter;
10178
+ texture.generateMipmaps = false;
10179
+ texture.anisotropy = Math.min(4, this.options.anisotropy);
10180
+ texture.needsUpdate = true;
10181
+ }
10182
+ evict() {
10183
+ if (this.totalBytes <= this.options.maxBytes) {
10184
+ return;
10185
+ }
10186
+ const candidates = Array.from(this.entries.values()).filter((entry) => entry.refs === 0 && entry.texture).sort((a, b) => a.lastUsed - b.lastUsed);
10187
+ for (const entry of candidates) {
10188
+ if (this.totalBytes <= this.options.maxBytes) {
10189
+ break;
10190
+ }
10191
+ entry.texture?.dispose();
10192
+ this.totalBytes -= entry.bytes;
10193
+ this.entries.delete(entry.key);
10194
+ }
10195
+ this.totalBytes = Math.max(0, this.totalBytes);
10196
+ }
10197
+ finishLoad(generation) {
10198
+ this.activeLoads = Math.max(0, this.activeLoads - 1);
10199
+ if (!this.disposed && generation === this.generation) {
10200
+ this.emitProgress();
10201
+ this.evict();
10202
+ }
10203
+ if (!this.disposed) {
10204
+ this.drain();
10205
+ }
10206
+ }
10207
+ async loadAndFinish(entry) {
10208
+ const generation = this.generation;
10209
+ try {
10210
+ await this.load(entry, generation);
10211
+ } finally {
10212
+ this.finishLoad(generation);
10213
+ }
10214
+ }
10215
+ };
10216
+
10217
+ // src/tile/texture-hooks.ts
10218
+ import { useEffect as useEffect21, useRef as useRef22, useState as useState9 } from "react";
10219
+ import {
10220
+ ClampToEdgeWrapping as ClampToEdgeWrapping2,
10221
+ LinearFilter as LinearFilter2,
10222
+ SRGBColorSpace as SRGBColorSpace2,
10223
+ TextureLoader as TextureLoader2
10224
+ } from "three";
10225
+ function usePreviewTexture(url, anisotropy, onError) {
10226
+ const [texture, setTexture] = useState9(null);
10227
+ const onErrorRef = useRef22(onError);
10228
+ onErrorRef.current = onError;
10229
+ useEffect21(() => {
10230
+ if (!url) {
10231
+ setTexture(null);
10232
+ return;
10233
+ }
10234
+ let active = true;
10235
+ const loader = new TextureLoader2();
10236
+ setTexture(null);
10237
+ loader.load(
10238
+ url,
10239
+ (loadedTexture) => {
10240
+ if (!active) {
10241
+ loadedTexture.dispose();
10242
+ return;
10243
+ }
10244
+ loadedTexture.colorSpace = SRGBColorSpace2;
10245
+ loadedTexture.wrapS = ClampToEdgeWrapping2;
10246
+ loadedTexture.wrapT = ClampToEdgeWrapping2;
10247
+ loadedTexture.magFilter = LinearFilter2;
10248
+ loadedTexture.minFilter = LinearFilter2;
10249
+ loadedTexture.generateMipmaps = false;
10250
+ loadedTexture.anisotropy = Math.min(4, anisotropy);
10251
+ loadedTexture.needsUpdate = true;
10252
+ setTexture(loadedTexture);
10253
+ },
10254
+ void 0,
10255
+ (error) => {
10256
+ if (active) {
10257
+ setTexture(null);
10258
+ onErrorRef.current?.(error);
10259
+ }
10260
+ }
10261
+ );
10262
+ return () => {
10263
+ active = false;
10264
+ };
10265
+ }, [anisotropy, url]);
10266
+ useEffect21(
10267
+ () => () => {
10268
+ texture?.dispose();
10269
+ },
10270
+ [texture]
10271
+ );
10272
+ return texture;
10273
+ }
10274
+ function useManagedTexture(manager, key, url, address) {
10275
+ const [managedState, setManagedState] = useState9(() => ({
10276
+ manager,
10277
+ key,
10278
+ url,
10279
+ snapshot: manager.getSnapshot(key)
10280
+ }));
10281
+ const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
10282
+ useEffect21(
10283
+ () => manager.acquire(key, url, address, (nextSnapshot) => {
10284
+ setManagedState({
10285
+ manager,
10286
+ key,
10287
+ url,
10288
+ snapshot: nextSnapshot
10289
+ });
10290
+ }),
10291
+ [key, manager, url]
10292
+ );
10293
+ return snapshot;
10294
+ }
10295
+
10296
+ // src/sphere-tile/layout.ts
10297
+ import {
10298
+ Box3,
10299
+ BufferGeometry,
10300
+ Float32BufferAttribute,
10301
+ MathUtils as MathUtils5,
10302
+ Vector3 as Vector38
10303
+ } from "three";
10304
+ function positiveInteger(value, name) {
10305
+ if (!Number.isSafeInteger(value) || value <= 0) {
10306
+ throw new Error(`${name} must be a positive safe integer`);
10307
+ }
10308
+ return value;
10309
+ }
10310
+ function parseSphereMultires(input) {
10311
+ const parts = typeof input === "string" ? input.split(",").map((part) => part.trim()) : null;
10312
+ const tileSize = positiveInteger(
10313
+ parts ? Number(parts[0]) : input.tileSize,
10314
+ "tileSize"
10315
+ );
10316
+ const levels = parts ? parts.slice(1).map((part) => {
10317
+ const match = /^(\d+)\s*x\s*(\d+)$/i.exec(part);
10318
+ if (!match)
10319
+ throw new Error("sphere multires levels must use widthxheight");
10320
+ return { width: Number(match[1]), height: Number(match[2]) };
10321
+ }) : input.levels.map((level) => ({ ...level }));
10322
+ if (levels.length === 0)
10323
+ throw new Error("sphere multires needs at least one level");
10324
+ levels.forEach((level, index) => {
10325
+ positiveInteger(level.width, "level width");
10326
+ positiveInteger(level.height, "level height");
10327
+ if (Math.abs(level.width - level.height * 2) > 2)
10328
+ throw new Error("sphere levels must be full 2:1 equirectangular images");
10329
+ const previous = levels[index - 1];
10330
+ if (previous && (level.width <= previous.width || level.height <= previous.height)) {
10331
+ throw new Error("sphere multires dimensions must be strictly ascending");
10332
+ }
10333
+ });
10334
+ return { tileSize, levels };
10335
+ }
10336
+ function resolveSphereTemplateUrl(template, address, indexBase = 1) {
10337
+ return template.replace(/%l/g, String(address.level + indexBase)).replace(
10338
+ /%(0*)([hxucvywr])/g,
10339
+ (_match, zeroes, placeholder) => String(
10340
+ ("hxuc".includes(placeholder) ? address.col : address.row) + indexBase
10341
+ ).padStart(zeroes.length + 1, "0")
10342
+ );
10343
+ }
10344
+ function sphereTileKey(address) {
10345
+ return `${address.level}/${address.row}/${address.col}`;
10346
+ }
10347
+ function sphereTileAddresses(size, level, tileSize) {
10348
+ const addresses = [];
10349
+ for (let row = 0; row < Math.ceil(size.height / tileSize); row++) {
10350
+ for (let col = 0; col < Math.ceil(size.width / tileSize); col++) {
10351
+ addresses.push({
10352
+ level,
10353
+ col,
10354
+ row,
10355
+ imageWidth: size.width,
10356
+ imageHeight: size.height,
10357
+ tileSize,
10358
+ width: Math.min(tileSize, size.width - col * tileSize),
10359
+ height: Math.min(tileSize, size.height - row * tileSize)
10360
+ });
10361
+ }
10362
+ }
10363
+ return addresses;
10364
+ }
10365
+ function sphereTileRange(address) {
10366
+ return {
10367
+ left: address.col * address.tileSize / address.imageWidth,
10368
+ right: (address.col * address.tileSize + address.width) / address.imageWidth,
10369
+ top: address.row * address.tileSize / address.imageHeight,
10370
+ bottom: (address.row * address.tileSize + address.height) / address.imageHeight
10371
+ };
10372
+ }
10373
+ function spherePoint(u, v, yawOffset = 0, radius = DEFAULT_PANORAMA_RADIUS) {
10374
+ const yaw = (u - 0.5) * Math.PI * 2 + MathUtils5.degToRad(yawOffset);
10375
+ const pitch = (0.5 - v) * Math.PI;
10376
+ return new Vector38(
10377
+ Math.sin(yaw) * Math.cos(pitch) * radius,
10378
+ Math.sin(pitch) * radius,
10379
+ -Math.cos(yaw) * Math.cos(pitch) * radius
10380
+ );
10381
+ }
10382
+ function sphereTileBounds(address, yawOffset) {
10383
+ const { left, right, top, bottom } = sphereTileRange(address);
10384
+ const us = [left, right];
10385
+ for (let i = -4; i <= 8; i++) {
10386
+ const u = 0.5 + (i * 90 - yawOffset % 360) / 360;
10387
+ if (u > left && u < right) us.push(u);
10388
+ }
10389
+ const vs = top < 0.5 && bottom > 0.5 ? [top, 0.5, bottom] : [top, bottom];
10390
+ return new Box3().setFromPoints(
10391
+ us.flatMap((u) => vs.map((v) => spherePoint(u, v, yawOffset)))
10392
+ );
10393
+ }
10394
+ function sphereTileGeometry(address, yawOffset, radius = DEFAULT_PANORAMA_RADIUS) {
10395
+ const { left, right, top, bottom } = sphereTileRange(address);
10396
+ const columns = Math.max(2, Math.ceil((right - left) * 360 / 3));
10397
+ const rows = Math.max(2, Math.ceil((bottom - top) * 180 / 3));
10398
+ const positions = [], uvs = [], indices = [];
10399
+ for (let row = 0; row <= rows; row++) {
10400
+ for (let col = 0; col <= columns; col++) {
10401
+ const u = col / columns, v = row / rows;
10402
+ const point = spherePoint(
10403
+ left + (right - left) * u,
10404
+ top + (bottom - top) * v,
10405
+ yawOffset,
10406
+ radius
10407
+ );
10408
+ positions.push(point.x, point.y, point.z);
10409
+ uvs.push(u, 1 - v);
10410
+ if (row < rows && col < columns) {
10411
+ const a = row * (columns + 1) + col, b = a + 1, c = a + columns + 1, d = c + 1;
10412
+ indices.push(a, b, c, b, d, c);
10413
+ }
10414
+ }
10415
+ }
10416
+ const geometry = new BufferGeometry();
10417
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
10418
+ geometry.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
10419
+ geometry.setIndex(indices);
10420
+ geometry.computeBoundingSphere();
10421
+ return geometry;
10422
+ }
10423
+ function preferredSphereLevel(levels, height, dpr, verticalFov) {
10424
+ const needed = height * dpr * 180 / Math.max(1, verticalFov);
10425
+ const index = levels.findIndex((level) => level.height >= needed);
10426
+ return index < 0 ? levels.length - 1 : index;
10427
+ }
10428
+
10429
+ // src/sphere-tile/sphere-tile.tsx
10430
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
10431
+ function SpherePatch({
10432
+ address,
10433
+ texture,
10434
+ yawOffset,
10435
+ preview = false
10436
+ }) {
10437
+ const geometry = useMemo14(
10438
+ () => sphereTileGeometry(
10439
+ address,
10440
+ yawOffset,
10441
+ DEFAULT_PANORAMA_RADIUS + (preview ? 0.05 : 0)
10442
+ ),
10443
+ [address, yawOffset, preview]
10444
+ );
10445
+ useEffect22(() => () => geometry.dispose(), [geometry]);
10446
+ return /* @__PURE__ */ jsx15(
10447
+ "mesh",
10448
+ {
10449
+ geometry,
10450
+ renderOrder: preview ? -1e3 : -900 + address.level,
10451
+ children: /* @__PURE__ */ jsx15(
10452
+ PanoBasicMaterial,
10453
+ {
10454
+ map: texture,
10455
+ side: BackSide2,
10456
+ depthTest: false,
10457
+ depthWrite: false,
10458
+ toneMapped: false
10459
+ }
10460
+ )
10461
+ }
10462
+ );
10463
+ }
10464
+ var ManagedPatch = memo(function ManagedPatch2({
10465
+ address,
10466
+ manager,
10467
+ resolveUrl,
10468
+ yawOffset,
10469
+ ready
10470
+ }) {
10471
+ const url = resolveUrl(address);
10472
+ const snapshot = useManagedTexture(
10473
+ manager,
10474
+ `${url}\0${sphereTileKey(address)}`,
10475
+ url,
10476
+ address
10477
+ );
10478
+ useEffect22(() => {
10479
+ if (snapshot.texture) ready();
10480
+ }, [snapshot.texture, ready]);
10481
+ return snapshot.texture ? /* @__PURE__ */ jsx15(
10482
+ SpherePatch,
10483
+ {
10484
+ address,
10485
+ texture: snapshot.texture,
10486
+ yawOffset
10487
+ }
10488
+ ) : null;
10489
+ });
10490
+ function SphereTileSource({
10491
+ config,
10492
+ baseUrl,
10493
+ previewUrl,
10494
+ urlTemplate = "tiles/%l/%h_%v.webp",
10495
+ indexBase = 1,
10496
+ resolveTileUrl,
10497
+ yawOffset = 0,
10498
+ maxTextureMemoryMb = 128,
10499
+ maxConcurrentLoads = 4,
10500
+ retryCount = 1,
10501
+ loadMode = "full",
10502
+ visible = true,
10503
+ onReady,
10504
+ onPreviewError,
10505
+ onLoadProgress,
10506
+ onTileError,
10507
+ onLevelChange
10508
+ }) {
10509
+ const { camera, gl, size } = useThree13();
10510
+ const anisotropy = gl.capabilities.getMaxAnisotropy();
10511
+ const preview = usePreviewTexture(
10512
+ resolveAssetUrl(baseUrl, previewUrl),
10513
+ anisotropy,
10514
+ onPreviewError
10515
+ );
10516
+ const callbacks = useRef23({
10517
+ onReady,
10518
+ onLoadProgress,
10519
+ onTileError,
10520
+ onLevelChange
10521
+ });
10522
+ callbacks.current = { onReady, onLoadProgress, onTileError, onLevelChange };
10523
+ const budget = Math.max(
10524
+ 1,
10525
+ Number.isFinite(maxTextureMemoryMb) ? maxTextureMemoryMb : 128
10526
+ ) * 1024 * 1024;
10527
+ const manager = useMemo14(
10528
+ () => new TileTextureManager({
10529
+ anisotropy,
10530
+ maxBytes: budget,
10531
+ concurrency: Math.max(
10532
+ 1,
10533
+ Math.floor(
10534
+ Number.isFinite(maxConcurrentLoads) ? maxConcurrentLoads : 4
10535
+ )
10536
+ ),
10537
+ retryCount: Math.max(
10538
+ 0,
10539
+ Math.floor(Number.isFinite(retryCount) ? retryCount : 1)
10540
+ ),
10541
+ onProgress: (progress) => callbacks.current.onLoadProgress?.(progress),
10542
+ onError: (error) => callbacks.current.onTileError?.(error)
10543
+ }),
10544
+ [anisotropy, budget, maxConcurrentLoads, retryCount]
10545
+ );
10546
+ const readyRef = useRef23(false);
10547
+ const ready = useCallback9(() => {
10548
+ if (!readyRef.current) {
10549
+ readyRef.current = true;
10550
+ callbacks.current.onReady?.();
10551
+ }
10552
+ }, []);
10553
+ useEffect22(() => {
10554
+ if (preview) ready();
10555
+ }, [preview, ready]);
10556
+ useEffect22(() => {
10557
+ manager.resume();
10558
+ return () => manager.dispose();
10559
+ }, [manager]);
10560
+ const resolveUrl = useMemo14(
10561
+ () => (address) => resolveAssetUrl(
10562
+ baseUrl,
10563
+ resolveTileUrl ? resolveTileUrl(address) : resolveSphereTemplateUrl(urlTemplate, address, indexBase)
10564
+ ),
10565
+ [baseUrl, resolveTileUrl, urlTemplate, indexBase]
10566
+ );
10567
+ const candidates = useMemo14(
10568
+ () => config.levels.map(
10569
+ (level, index) => sphereTileAddresses(level, index, config.tileSize).map((address) => ({
10570
+ address,
10571
+ bounds: sphereTileBounds(address, yawOffset)
10572
+ }))
10573
+ ),
10574
+ [config, yawOffset]
10575
+ );
10576
+ const previewAddress = useMemo14(
10577
+ () => sphereTileAddresses({ width: 2, height: 1 }, 0, 2)[0],
10578
+ []
10579
+ );
10580
+ const [layers, setLayers] = useState10([]);
10581
+ const scratch = useMemo14(
10582
+ () => ({
10583
+ camera: new PerspectiveCamera3(),
10584
+ frustum: new Frustum(),
10585
+ matrix: new Matrix42(),
10586
+ quaternion: camera.quaternion.clone(),
10587
+ fov: NaN,
10588
+ width: 0,
10589
+ height: 0,
10590
+ dpr: 0,
10591
+ time: -Infinity,
10592
+ token: "",
10593
+ level: -1,
10594
+ loadMode: "",
10595
+ manager: null
10596
+ }),
10597
+ [camera, candidates]
10598
+ );
10599
+ useFrame9(() => {
10600
+ if (!(camera instanceof PerspectiveCamera3)) return;
10601
+ const now = performance.now(), dpr = gl.getPixelRatio();
10602
+ const reset = scratch.manager !== manager || scratch.loadMode !== loadMode;
10603
+ if (!reset && (now - scratch.time < 100 || scratch.fov === camera.fov && scratch.width === size.width && scratch.height === size.height && scratch.dpr === dpr && scratch.quaternion.angleTo(camera.quaternion) < 1e-4))
10604
+ return;
10605
+ scratch.time = now;
10606
+ scratch.fov = camera.fov;
10607
+ scratch.width = size.width;
10608
+ scratch.height = size.height;
10609
+ scratch.dpr = dpr;
10610
+ scratch.quaternion.copy(camera.quaternion);
10611
+ scratch.manager = manager;
10612
+ scratch.loadMode = loadMode;
10613
+ scratch.camera.copy(camera, false);
10614
+ scratch.camera.fov = Math.min(179, camera.fov + 8);
10615
+ scratch.camera.updateProjectionMatrix();
10616
+ scratch.camera.updateMatrixWorld();
10617
+ scratch.matrix.multiplyMatrices(
10618
+ scratch.camera.projectionMatrix,
10619
+ scratch.camera.matrixWorldInverse
10620
+ );
10621
+ scratch.frustum.setFromProjectionMatrix(scratch.matrix);
10622
+ let level = loadMode === "preview" ? -1 : loadMode === "base" ? 0 : preferredSphereLevel(config.levels, size.height, dpr, camera.fov);
10623
+ const next = [];
10624
+ let residentBytes = 0;
10625
+ for (let index = 0; index <= level; index++) {
10626
+ const addresses = candidates[index].filter(
10627
+ ({ bounds }) => scratch.frustum.intersectsBox(bounds)
10628
+ ).map(({ address }) => address);
10629
+ const bytes = addresses.reduce(
10630
+ (total, address) => total + address.width * address.height * 4,
10631
+ 0
10632
+ );
10633
+ if (residentBytes + bytes > budget) {
10634
+ level = index - 1;
10635
+ break;
10636
+ }
10637
+ residentBytes += bytes;
10638
+ next.push({ level: index, addresses });
10639
+ }
10640
+ if (level !== scratch.level) {
10641
+ scratch.level = level;
10642
+ if (level >= 0) callbacks.current.onLevelChange?.(level);
10643
+ }
10644
+ const token = next.map((layer) => layer.addresses.map(sphereTileKey).join(",")).join(";");
10645
+ if (token !== scratch.token || reset) {
10646
+ scratch.token = token;
10647
+ setLayers(next);
10648
+ }
10649
+ });
10650
+ return /* @__PURE__ */ jsxs9("group", { visible, children: [
10651
+ preview ? /* @__PURE__ */ jsx15(
10652
+ SpherePatch,
10653
+ {
10654
+ address: previewAddress,
10655
+ texture: preview,
10656
+ yawOffset,
10657
+ preview: true
10658
+ }
10659
+ ) : null,
10660
+ loadMode !== "preview" ? layers.flatMap(
10661
+ (layer) => layer.addresses.map((address) => /* @__PURE__ */ jsx15(
10662
+ ManagedPatch,
10663
+ {
10664
+ address,
10665
+ manager,
10666
+ resolveUrl,
10667
+ yawOffset,
10668
+ ready
10669
+ },
10670
+ sphereTileKey(address)
10671
+ ))
10672
+ ) : null
10673
+ ] });
10674
+ }
10675
+ function SphereTile({ multires, ...props }) {
10676
+ const token = typeof multires === "string" ? multires : JSON.stringify(multires);
10677
+ const config = useMemo14(() => parseSphereMultires(multires), [token]);
10678
+ const sourceKey = `${props.baseUrl}\0${token}\0${props.previewUrl}\0${props.urlTemplate ?? ""}\0${props.indexBase ?? 1}`;
10679
+ return /* @__PURE__ */ jsx15(SphereTileSource, { ...props, config }, sourceKey);
10680
+ }
10681
+
10682
+ // src/sphere.tsx
10683
+ import { useThree as useThree14 } from "@react-three/fiber";
10684
+ import { useEffect as useEffect23, useMemo as useMemo15, useRef as useRef24, useState as useState11 } from "react";
10685
+ import {
10686
+ BackSide as BackSide3,
10687
+ LinearFilter as LinearFilter3,
10688
+ LinearMipmapLinearFilter,
10689
+ MathUtils as MathUtils6,
10690
+ RepeatWrapping,
10691
+ SRGBColorSpace as SRGBColorSpace3,
10692
+ TextureLoader as TextureLoader3
10693
+ } from "three";
10694
+ import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
10695
+ function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10696
+ const [texture, setTexture] = useState11(null);
10697
+ const onLoadRef = useRef24(onLoad);
10698
+ const onErrorRef = useRef24(onError);
10699
+ onLoadRef.current = onLoad;
10700
+ onErrorRef.current = onError;
10701
+ useEffect23(() => {
10702
+ if (!url) {
10703
+ setTexture(null);
10704
+ return;
10705
+ }
10706
+ let active = true;
10707
+ const loader = new TextureLoader3();
10708
+ setTexture(null);
10709
+ loader.load(
10710
+ url,
10711
+ (loadedTexture) => {
10712
+ if (!active) {
10713
+ loadedTexture.dispose();
10714
+ return;
10715
+ }
10716
+ loadedTexture.colorSpace = SRGBColorSpace3;
10717
+ loadedTexture.wrapS = RepeatWrapping;
10718
+ loadedTexture.repeat.x = -1;
10719
+ loadedTexture.offset.x = 1;
10720
+ loadedTexture.anisotropy = anisotropy;
10721
+ loadedTexture.magFilter = LinearFilter3;
10722
+ loadedTexture.minFilter = generateMipmaps ? LinearMipmapLinearFilter : LinearFilter3;
10723
+ loadedTexture.generateMipmaps = generateMipmaps;
10724
+ loadedTexture.needsUpdate = true;
10725
+ setTexture(loadedTexture);
10726
+ onLoadRef.current?.(loadedTexture);
10727
+ },
10728
+ void 0,
10729
+ (error) => {
10730
+ if (active) {
10731
+ setTexture(null);
10732
+ onErrorRef.current?.(error);
10733
+ }
10734
+ }
10735
+ );
10736
+ return () => {
10737
+ active = false;
10738
+ };
10739
+ }, [anisotropy, generateMipmaps, url]);
10740
+ useEffect23(
10741
+ () => () => {
10742
+ texture?.dispose();
10743
+ },
10744
+ [texture]
10745
+ );
10746
+ return texture;
10747
+ }
10748
+ function Sphere({
10749
+ src,
10750
+ previewUrl,
10751
+ yawOffset = 0,
10752
+ onReady,
10753
+ onLoad,
10754
+ onError,
10755
+ onPreviewError,
10756
+ visible = true
10757
+ }) {
10758
+ const gl = useThree14((state) => state.gl);
10759
+ const anisotropy = gl.capabilities.getMaxAnisotropy();
10760
+ const resolvedPreviewUrl = useMemo15(
10761
+ () => resolveUrlAgainstFile(src, previewUrl),
10762
+ [previewUrl, src]
10763
+ );
10764
+ const texture = useEquirectTexture(
10765
+ src,
10766
+ anisotropy,
10767
+ true,
10768
+ onLoad,
10769
+ onError
10770
+ );
10771
+ const previewTexture = useEquirectTexture(
10772
+ texture ? null : resolvedPreviewUrl,
10773
+ anisotropy,
10774
+ false,
10775
+ void 0,
10776
+ onPreviewError
10777
+ );
10778
+ const displayTexture = texture ?? previewTexture;
10779
+ const readyRef = useRef24(false);
10780
+ useEffect23(() => {
10781
+ readyRef.current = false;
10782
+ }, [resolvedPreviewUrl, src]);
10783
+ useEffect23(() => {
10784
+ if (!displayTexture || readyRef.current) {
10785
+ return;
10786
+ }
10787
+ readyRef.current = true;
10788
+ onReady?.();
10789
+ }, [displayTexture, onReady]);
10790
+ if (!displayTexture) {
10791
+ return null;
10792
+ }
10793
+ return /* @__PURE__ */ jsxs10(
10794
+ "mesh",
10795
+ {
10796
+ rotation: [0, MathUtils6.degToRad(90 + yawOffset), 0],
10797
+ visible,
10798
+ children: [
10799
+ /* @__PURE__ */ jsx16("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10800
+ /* @__PURE__ */ jsx16(
10801
+ PanoBasicMaterial,
10802
+ {
10803
+ map: displayTexture,
10804
+ side: BackSide3,
10805
+ toneMapped: false
10806
+ }
10807
+ )
10808
+ ]
10809
+ }
10810
+ );
10811
+ }
10812
+
10813
+ // src/video/pano-video.tsx
10814
+ import { useThree as useThree15 } from "@react-three/fiber";
10815
+ import {
10816
+ useCallback as useCallback10,
10817
+ useContext as useContext21,
10818
+ useEffect as useEffect24,
10819
+ useLayoutEffect as useLayoutEffect3,
10820
+ useMemo as useMemo16,
10821
+ useRef as useRef25,
10822
+ useState as useState12
10823
+ } from "react";
10824
+ import {
10825
+ BackSide as BackSide4,
10826
+ LinearFilter as LinearFilter4,
10827
+ LinearMipmapLinearFilter as LinearMipmapLinearFilter2,
10828
+ MathUtils as MathUtils7,
10829
+ RepeatWrapping as RepeatWrapping2,
10830
+ SRGBColorSpace as SRGBColorSpace4,
10831
+ TextureLoader as TextureLoader4,
10832
+ VideoTexture
10833
+ } from "three";
10834
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
10835
+ var EMPTY_TRACKS = [];
10836
+ var EMPTY_RATES = DEFAULT_PANO_VIDEO_PLAYBACK_RATES;
10837
+ function createEmptySnapshot() {
10838
+ return {
10839
+ ready: false,
10840
+ playing: false,
10841
+ blocked: false,
10842
+ ended: false,
10843
+ currentTime: 0,
10844
+ duration: 0,
10845
+ volume: 1,
10846
+ volumeAdjustable: true,
10847
+ muted: true,
10848
+ playbackRate: 1,
10849
+ variantId: "default",
10850
+ variants: [],
10851
+ trackId: null,
10852
+ tracks: [],
10853
+ captionText: "",
10854
+ captionAppearance: DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE,
10855
+ captionsEnabled: true,
10856
+ playbackRates: EMPTY_RATES,
10857
+ playbackState: "paused"
10858
+ };
10859
+ }
10860
+ function playbackStateFromVideo(video, blocked) {
10861
+ if (blocked) {
10862
+ return "blocked";
10863
+ }
10864
+ if (video.ended) {
10865
+ return "ended";
10866
+ }
10867
+ if (!video.paused) {
10868
+ return "playing";
10869
+ }
10870
+ return "paused";
10871
+ }
10872
+ function collectCaptionText(track) {
10873
+ if (!track?.activeCues || track.activeCues.length === 0) {
10874
+ return "";
10875
+ }
10876
+ const parts = [];
10877
+ for (let index = 0; index < track.activeCues.length; index += 1) {
10878
+ const cue = track.activeCues[index];
10879
+ if (!cue) {
10880
+ continue;
10881
+ }
10882
+ const text = cueText(cue);
10883
+ if (text) {
10884
+ parts.push(text);
10885
+ }
10886
+ }
10147
10887
  return parts.join("\n");
10148
10888
  }
10149
10889
  function findTextTrack(video, tracks, trackId) {
@@ -10167,7 +10907,7 @@ function findTextTrack(video, tracks, trackId) {
10167
10907
  return list.length > 0 ? list[0] : null;
10168
10908
  }
10169
10909
  function applyEquirectangularLayout(texture, anisotropy) {
10170
- texture.colorSpace = SRGBColorSpace2;
10910
+ texture.colorSpace = SRGBColorSpace4;
10171
10911
  texture.wrapS = RepeatWrapping2;
10172
10912
  texture.repeat.x = -1;
10173
10913
  texture.offset.x = 1;
@@ -10175,11 +10915,11 @@ function applyEquirectangularLayout(texture, anisotropy) {
10175
10915
  texture.needsUpdate = true;
10176
10916
  }
10177
10917
  function usePosterTexture(poster, onError) {
10178
- const gl = useThree14((state) => state.gl);
10179
- const [texture, setTexture] = useState10(null);
10180
- const onErrorRef = useRef23(onError);
10918
+ const gl = useThree15((state) => state.gl);
10919
+ const [texture, setTexture] = useState12(null);
10920
+ const onErrorRef = useRef25(onError);
10181
10921
  onErrorRef.current = onError;
10182
- useEffect22(() => {
10922
+ useEffect24(() => {
10183
10923
  let active = true;
10184
10924
  setTexture(null);
10185
10925
  if (!poster) {
@@ -10187,7 +10927,7 @@ function usePosterTexture(poster, onError) {
10187
10927
  active = false;
10188
10928
  };
10189
10929
  }
10190
- const loader = new TextureLoader2();
10930
+ const loader = new TextureLoader4();
10191
10931
  loader.load(
10192
10932
  poster,
10193
10933
  (loadedTexture) => {
@@ -10200,7 +10940,7 @@ function usePosterTexture(poster, onError) {
10200
10940
  gl.capabilities.getMaxAnisotropy()
10201
10941
  );
10202
10942
  loadedTexture.wrapT = RepeatWrapping2;
10203
- loadedTexture.magFilter = LinearFilter2;
10943
+ loadedTexture.magFilter = LinearFilter4;
10204
10944
  loadedTexture.minFilter = LinearMipmapLinearFilter2;
10205
10945
  loadedTexture.generateMipmaps = true;
10206
10946
  setTexture(loadedTexture);
@@ -10216,7 +10956,7 @@ function usePosterTexture(poster, onError) {
10216
10956
  active = false;
10217
10957
  };
10218
10958
  }, [gl, poster]);
10219
- useEffect22(
10959
+ useEffect24(
10220
10960
  () => () => {
10221
10961
  texture?.dispose();
10222
10962
  },
@@ -10274,39 +11014,39 @@ function PanoVideo({
10274
11014
  onTrackChange
10275
11015
  }) {
10276
11016
  usePanoMediaActivationIntent(autoPlay);
10277
- const gl = useThree14((state) => state.gl);
11017
+ const gl = useThree15((state) => state.gl);
10278
11018
  const host = useContext21(PanoVideoHostContext);
10279
11019
  const overlay = useContext21(PanoChromeOverlayContext);
10280
- const variants = useMemo15(
11020
+ const variants = useMemo16(
10281
11021
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
10282
11022
  [poster, src, variantsProp]
10283
11023
  );
10284
11024
  const tracks = tracksProp ?? EMPTY_TRACKS;
10285
11025
  const captionsEnabled = captions !== false;
10286
- const captionAppearance = useMemo15(
11026
+ const captionAppearance = useMemo16(
10287
11027
  () => resolvePanoVideoCaptionAppearance(
10288
11028
  typeof captions === "object" ? captions : void 0
10289
11029
  ),
10290
11030
  [captions]
10291
11031
  );
10292
- const [variantId, setVariantId] = useState10(
11032
+ const [variantId, setVariantId] = useState12(
10293
11033
  () => resolvePanoVideoVariantId(variants, defaultVariantId)
10294
11034
  );
10295
- const [trackId, setTrackId] = useState10(
11035
+ const [trackId, setTrackId] = useState12(
10296
11036
  () => captionsEnabled ? defaultPanoVideoTrackId(tracks) : null
10297
11037
  );
10298
- const [ready, setReady] = useState10(false);
10299
- const autoPlayRef = useRef23(autoPlay);
11038
+ const [ready, setReady] = useState12(false);
11039
+ const autoPlayRef = useRef25(autoPlay);
10300
11040
  autoPlayRef.current = autoPlay;
10301
- const blockedRef = useRef23(false);
10302
- const captionAppearanceOverrideRef = useRef23(
11041
+ const blockedRef = useRef25(false);
11042
+ const captionAppearanceOverrideRef = useRef25(
10303
11043
  {}
10304
11044
  );
10305
- const mediaCapabilitiesRef = useRef23({
11045
+ const mediaCapabilitiesRef = useRef25({
10306
11046
  volumeAdjustable: true,
10307
11047
  playbackRates: [...DEFAULT_PANO_VIDEO_PLAYBACK_RATES]
10308
11048
  });
10309
- const storeRef = useRef23(null);
11049
+ const storeRef = useRef25(null);
10310
11050
  if (!storeRef.current) {
10311
11051
  storeRef.current = {
10312
11052
  snapshot: {
@@ -10316,7 +11056,7 @@ function PanoVideo({
10316
11056
  listeners: /* @__PURE__ */ new Set()
10317
11057
  };
10318
11058
  }
10319
- const actionsRef = useRef23({
11059
+ const actionsRef = useRef25({
10320
11060
  play: async () => {
10321
11061
  },
10322
11062
  pause: () => {
@@ -10336,19 +11076,19 @@ function PanoVideo({
10336
11076
  setCaptionAppearance: (_appearance) => {
10337
11077
  }
10338
11078
  });
10339
- const onLoadRef = useRef23(onLoad);
10340
- const onErrorRef = useRef23(onError);
10341
- const onPlaybackStateChangeRef = useRef23(onPlaybackStateChange);
10342
- const onEndedRef = useRef23(onEnded);
10343
- const onVariantChangeRef = useRef23(onVariantChange);
10344
- const onTrackChangeRef = useRef23(onTrackChange);
11079
+ const onLoadRef = useRef25(onLoad);
11080
+ const onErrorRef = useRef25(onError);
11081
+ const onPlaybackStateChangeRef = useRef25(onPlaybackStateChange);
11082
+ const onEndedRef = useRef25(onEnded);
11083
+ const onVariantChangeRef = useRef25(onVariantChange);
11084
+ const onTrackChangeRef = useRef25(onTrackChange);
10345
11085
  onLoadRef.current = onLoad;
10346
11086
  onErrorRef.current = onError;
10347
11087
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
10348
11088
  onEndedRef.current = onEnded;
10349
11089
  onVariantChangeRef.current = onVariantChange;
10350
11090
  onTrackChangeRef.current = onTrackChange;
10351
- const resource = useMemo15(() => {
11091
+ const resource = useMemo16(() => {
10352
11092
  if (typeof document === "undefined") {
10353
11093
  return null;
10354
11094
  }
@@ -10360,12 +11100,12 @@ function PanoVideo({
10360
11100
  video.preload = "metadata";
10361
11101
  const texture = new VideoTexture(video);
10362
11102
  applyEquirectangularLayout(texture, 1);
10363
- texture.magFilter = LinearFilter2;
10364
- texture.minFilter = LinearFilter2;
11103
+ texture.magFilter = LinearFilter4;
11104
+ texture.minFilter = LinearFilter4;
10365
11105
  texture.generateMipmaps = false;
10366
11106
  return { video, texture };
10367
11107
  }, []);
10368
- useEffect22(
11108
+ useEffect24(
10369
11109
  () => () => {
10370
11110
  if (!resource) {
10371
11111
  return;
@@ -10380,13 +11120,13 @@ function PanoVideo({
10380
11120
  },
10381
11121
  [resource]
10382
11122
  );
10383
- useEffect22(() => {
11123
+ useEffect24(() => {
10384
11124
  if (!resource) {
10385
11125
  return;
10386
11126
  }
10387
11127
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
10388
11128
  }, [gl, resource]);
10389
- useEffect22(() => {
11129
+ useEffect24(() => {
10390
11130
  if (!resource || !overlay?.overlayElement) {
10391
11131
  return;
10392
11132
  }
@@ -10409,7 +11149,7 @@ function PanoVideo({
10409
11149
  activeVariant?.poster ?? poster,
10410
11150
  onError
10411
11151
  );
10412
- const emitSnapshot = useCallback9(
11152
+ const emitSnapshot = useCallback10(
10413
11153
  (patch) => {
10414
11154
  const store = storeRef.current;
10415
11155
  const video = resource?.video;
@@ -10461,7 +11201,7 @@ function PanoVideo({
10461
11201
  captionAppearance
10462
11202
  ]
10463
11203
  );
10464
- const controller = useMemo15(
11204
+ const controller = useMemo16(
10465
11205
  () => ({
10466
11206
  subscribe: (onStoreChange) => {
10467
11207
  const store = storeRef.current;
@@ -10539,10 +11279,10 @@ function PanoVideo({
10539
11279
  };
10540
11280
  emitSnapshot();
10541
11281
  }, [emitSnapshot, playbackRates, resource]);
10542
- useEffect22(() => {
11282
+ useEffect24(() => {
10543
11283
  emitSnapshot();
10544
11284
  }, [emitSnapshot]);
10545
- useEffect22(() => {
11285
+ useEffect24(() => {
10546
11286
  if (!resource || !activeVariant || !mediaKey) {
10547
11287
  return;
10548
11288
  }
@@ -10599,7 +11339,7 @@ function PanoVideo({
10599
11339
  video.removeEventListener("error", handleError);
10600
11340
  };
10601
11341
  }, [mediaKey, resource]);
10602
- useEffect22(() => {
11342
+ useEffect24(() => {
10603
11343
  if (!resource) {
10604
11344
  return;
10605
11345
  }
@@ -10691,7 +11431,7 @@ function PanoVideo({
10691
11431
  }
10692
11432
  };
10693
11433
  }, [emitSnapshot, resource]);
10694
- useEffect22(() => {
11434
+ useEffect24(() => {
10695
11435
  if (!resource) {
10696
11436
  return;
10697
11437
  }
@@ -10711,7 +11451,7 @@ function PanoVideo({
10711
11451
  }
10712
11452
  video.preload = preload;
10713
11453
  }, [loop, muted, playsInline, preload, resource, volume]);
10714
- useEffect22(() => {
11454
+ useEffect24(() => {
10715
11455
  if (!resource) {
10716
11456
  return;
10717
11457
  }
@@ -10761,7 +11501,7 @@ function PanoVideo({
10761
11501
  video.removeEventListener("ratechange", handleVolume);
10762
11502
  };
10763
11503
  }, [emitSnapshot, resource]);
10764
- useEffect22(() => {
11504
+ useEffect24(() => {
10765
11505
  if (!resource) {
10766
11506
  return;
10767
11507
  }
@@ -10796,7 +11536,7 @@ function PanoVideo({
10796
11536
  video.textTracks.removeEventListener("change", applyModes);
10797
11537
  };
10798
11538
  }, [emitSnapshot, resource, trackId, tracks]);
10799
- useEffect22(() => {
11539
+ useEffect24(() => {
10800
11540
  if (!captionsEnabled) {
10801
11541
  setTrackId(null);
10802
11542
  return;
@@ -10807,39 +11547,39 @@ function PanoVideo({
10807
11547
  if (variants.length === 0) {
10808
11548
  return null;
10809
11549
  }
10810
- return /* @__PURE__ */ jsxs10(Fragment5, { children: [
10811
- activeTexture ? /* @__PURE__ */ jsxs10(
11550
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
11551
+ activeTexture ? /* @__PURE__ */ jsxs11(
10812
11552
  "mesh",
10813
11553
  {
10814
- rotation: [0, MathUtils6.degToRad(90 + yawOffset), 0],
11554
+ rotation: [0, MathUtils7.degToRad(90 + yawOffset), 0],
10815
11555
  visible,
10816
11556
  children: [
10817
- /* @__PURE__ */ jsx16("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10818
- /* @__PURE__ */ jsx16(
11557
+ /* @__PURE__ */ jsx17("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
11558
+ /* @__PURE__ */ jsx17(
10819
11559
  PanoBasicMaterial,
10820
11560
  {
10821
11561
  map: activeTexture,
10822
- side: BackSide3,
11562
+ side: BackSide4,
10823
11563
  toneMapped: false
10824
11564
  }
10825
11565
  )
10826
11566
  ]
10827
11567
  }
10828
11568
  ) : null,
10829
- captionsEnabled ? /* @__PURE__ */ jsx16(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
11569
+ captionsEnabled ? /* @__PURE__ */ jsx17(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
10830
11570
  ] });
10831
11571
  }
10832
11572
 
10833
11573
  // src/scenes.tsx
10834
- import { useFrame as useFrame12, useThree as useThree19 } from "@react-three/fiber";
11574
+ import { useFrame as useFrame13, useThree as useThree20 } from "@react-three/fiber";
10835
11575
  import {
10836
11576
  Fragment as Fragment6,
10837
- useCallback as useCallback10,
11577
+ useCallback as useCallback11,
10838
11578
  useContext as useContext23,
10839
- useEffect as useEffect27,
10840
- useMemo as useMemo20,
10841
- useRef as useRef27,
10842
- useState as useState12
11579
+ useEffect as useEffect29,
11580
+ useMemo as useMemo21,
11581
+ useRef as useRef29,
11582
+ useState as useState14
10843
11583
  } from "react";
10844
11584
  import {
10845
11585
  LinearSRGBColorSpace,
@@ -10894,8 +11634,8 @@ function isSnapshotTransition(preset) {
10894
11634
  }
10895
11635
 
10896
11636
  // src/scene-transitions/particle-overlay.tsx
10897
- import { useFrame as useFrame9, useThree as useThree15 } from "@react-three/fiber";
10898
- import { useEffect as useEffect23, useMemo as useMemo16, useRef as useRef24 } from "react";
11637
+ import { useFrame as useFrame10, useThree as useThree16 } from "@react-three/fiber";
11638
+ import { useEffect as useEffect25, useMemo as useMemo17, useRef as useRef26 } from "react";
10899
11639
  import {
10900
11640
  DoubleSide as DoubleSide2,
10901
11641
  InstancedBufferAttribute,
@@ -10903,7 +11643,7 @@ import {
10903
11643
  PlaneGeometry as PlaneGeometry3,
10904
11644
  ShaderMaterial as ShaderMaterial2,
10905
11645
  Vector2 as Vector24,
10906
- Vector3 as Vector39
11646
+ Vector3 as Vector310
10907
11647
  } from "three";
10908
11648
 
10909
11649
  // src/scene-transitions/glsl.ts
@@ -10958,7 +11698,7 @@ function overlayProgress(elapsedTime, duration, startTime) {
10958
11698
  }
10959
11699
 
10960
11700
  // src/scene-transitions/particle-overlay.tsx
10961
- import { jsx as jsx17 } from "react/jsx-runtime";
11701
+ import { jsx as jsx18 } from "react/jsx-runtime";
10962
11702
  var PARTICLE_GRID = 48;
10963
11703
  var PARTICLE_COUNT = PARTICLE_GRID * PARTICLE_GRID;
10964
11704
  var PARTICLE_VERTEX_SHADER = `
@@ -11034,13 +11774,13 @@ function ParticleOverlay({
11034
11774
  runId,
11035
11775
  onFinish
11036
11776
  }) {
11037
- const { camera } = useThree15();
11038
- const meshRef = useRef24(null);
11039
- const startRef = useRef24(null);
11040
- const finishedRef = useRef24(false);
11041
- const directionRef = useRef24(new Vector39());
11042
- const geometry = useMemo16(() => createParticleGeometry(), []);
11043
- const material = useMemo16(
11777
+ const { camera } = useThree16();
11778
+ const meshRef = useRef26(null);
11779
+ const startRef = useRef26(null);
11780
+ const finishedRef = useRef26(false);
11781
+ const directionRef = useRef26(new Vector310());
11782
+ const geometry = useMemo17(() => createParticleGeometry(), []);
11783
+ const material = useMemo17(
11044
11784
  () => new ShaderMaterial2({
11045
11785
  transparent: true,
11046
11786
  depthTest: false,
@@ -11056,14 +11796,14 @@ function ParticleOverlay({
11056
11796
  }),
11057
11797
  [snapshot.texture]
11058
11798
  );
11059
- useEffect23(() => () => geometry.dispose(), [geometry]);
11060
- useEffect23(() => () => material.dispose(), [material]);
11061
- useEffect23(() => {
11799
+ useEffect25(() => () => geometry.dispose(), [geometry]);
11800
+ useEffect25(() => () => material.dispose(), [material]);
11801
+ useEffect25(() => {
11062
11802
  startRef.current = null;
11063
11803
  finishedRef.current = false;
11064
11804
  material.uniforms.progress.value = 0;
11065
11805
  }, [material, runId]);
11066
- useFrame9((state) => {
11806
+ useFrame10((state) => {
11067
11807
  const mesh = meshRef.current;
11068
11808
  if (!mesh || !isPerspectiveCamera(camera)) {
11069
11809
  return;
@@ -11080,7 +11820,7 @@ function ParticleOverlay({
11080
11820
  onFinish();
11081
11821
  }
11082
11822
  });
11083
- return /* @__PURE__ */ jsx17(
11823
+ return /* @__PURE__ */ jsx18(
11084
11824
  "mesh",
11085
11825
  {
11086
11826
  ref: meshRef,
@@ -11093,9 +11833,9 @@ function ParticleOverlay({
11093
11833
  }
11094
11834
 
11095
11835
  // src/scene-transitions/snapshot-overlay.tsx
11096
- import { useFrame as useFrame10, useThree as useThree16 } from "@react-three/fiber";
11097
- import { useEffect as useEffect24, useMemo as useMemo17, useRef as useRef25 } from "react";
11098
- import { Vector3 as Vector310 } from "three";
11836
+ import { useFrame as useFrame11, useThree as useThree17 } from "@react-three/fiber";
11837
+ import { useEffect as useEffect26, useMemo as useMemo18, useRef as useRef27 } from "react";
11838
+ import { Vector3 as Vector311 } from "three";
11099
11839
 
11100
11840
  // src/scene-transitions/snapshot-shaders.ts
11101
11841
  import { DoubleSide as DoubleSide3, ShaderMaterial as ShaderMaterial3, Vector2 as Vector25 } from "three";
@@ -11374,7 +12114,7 @@ function createSnapshotMaterial(preset, texture) {
11374
12114
  }
11375
12115
 
11376
12116
  // src/scene-transitions/snapshot-overlay.tsx
11377
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
12117
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
11378
12118
  function SnapshotOverlay({
11379
12119
  snapshot,
11380
12120
  transition,
@@ -11382,26 +12122,26 @@ function SnapshotOverlay({
11382
12122
  runId,
11383
12123
  onFinish
11384
12124
  }) {
11385
- const { camera, gl } = useThree16();
11386
- const meshRef = useRef25(null);
11387
- const startRef = useRef25(null);
11388
- const finishedRef = useRef25(false);
11389
- const directionRef = useRef25(new Vector310());
11390
- const material = useMemo17(() => {
12125
+ const { camera, gl } = useThree17();
12126
+ const meshRef = useRef27(null);
12127
+ const startRef = useRef27(null);
12128
+ const finishedRef = useRef27(false);
12129
+ const directionRef = useRef27(new Vector311());
12130
+ const material = useMemo18(() => {
11391
12131
  if (!isSnapshotTransition(transition.preset)) {
11392
12132
  return null;
11393
12133
  }
11394
12134
  return createSnapshotMaterial(transition.preset, snapshot.texture);
11395
12135
  }, [snapshot.texture, transition.preset]);
11396
- useEffect24(() => () => material?.dispose(), [material]);
11397
- useEffect24(() => {
12136
+ useEffect26(() => () => material?.dispose(), [material]);
12137
+ useEffect26(() => {
11398
12138
  startRef.current = null;
11399
12139
  finishedRef.current = false;
11400
12140
  if (material) {
11401
12141
  material.uniforms.progress.value = 0;
11402
12142
  }
11403
12143
  }, [material, runId]);
11404
- useFrame10((state) => {
12144
+ useFrame11((state) => {
11405
12145
  const mesh = meshRef.current;
11406
12146
  if (!mesh || !material || !isPerspectiveCamera(camera)) {
11407
12147
  return;
@@ -11423,21 +12163,21 @@ function SnapshotOverlay({
11423
12163
  if (!material) {
11424
12164
  return null;
11425
12165
  }
11426
- return /* @__PURE__ */ jsxs11("mesh", { ref: meshRef, renderOrder: OVERLAY_RENDER_ORDER, frustumCulled: false, children: [
11427
- /* @__PURE__ */ jsx18("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
11428
- /* @__PURE__ */ jsx18("primitive", { attach: "material", object: material })
12166
+ return /* @__PURE__ */ jsxs12("mesh", { ref: meshRef, renderOrder: OVERLAY_RENDER_ORDER, frustumCulled: false, children: [
12167
+ /* @__PURE__ */ jsx19("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
12168
+ /* @__PURE__ */ jsx19("primitive", { attach: "material", object: material })
11429
12169
  ] });
11430
12170
  }
11431
12171
 
11432
12172
  // src/scene-transitions/transition-overlay.tsx
11433
- import { jsx as jsx19 } from "react/jsx-runtime";
12173
+ import { jsx as jsx20 } from "react/jsx-runtime";
11434
12174
  function TransitionOverlay(props) {
11435
12175
  const { preset } = props.transition;
11436
12176
  switch (preset) {
11437
12177
  case "none":
11438
12178
  return null;
11439
12179
  case "particles":
11440
- return /* @__PURE__ */ jsx19(ParticleOverlay, { ...props });
12180
+ return /* @__PURE__ */ jsx20(ParticleOverlay, { ...props });
11441
12181
  case "crossfade":
11442
12182
  case "zoom":
11443
12183
  case "blackout":
@@ -11465,7 +12205,7 @@ function TransitionOverlay(props) {
11465
12205
  case "zoomBlur":
11466
12206
  case "hexDissolve":
11467
12207
  case "filmBurn":
11468
- return /* @__PURE__ */ jsx19(SnapshotOverlay, { ...props });
12208
+ return /* @__PURE__ */ jsx20(SnapshotOverlay, { ...props });
11469
12209
  default: {
11470
12210
  const exhaustive = preset;
11471
12211
  void exhaustive;
@@ -11475,242 +12215,14 @@ function TransitionOverlay(props) {
11475
12215
  }
11476
12216
 
11477
12217
  // src/tile/texture-manager-context.tsx
11478
- import { useThree as useThree17 } from "@react-three/fiber";
12218
+ import { useThree as useThree18 } from "@react-three/fiber";
11479
12219
  import {
11480
12220
  createContext as createContext15,
11481
12221
  useContext as useContext22,
11482
- useEffect as useEffect25,
11483
- useMemo as useMemo18
12222
+ useEffect as useEffect27,
12223
+ useMemo as useMemo19
11484
12224
  } from "react";
11485
-
11486
- // src/tile/texture-manager.ts
11487
- import {
11488
- ClampToEdgeWrapping,
11489
- LinearFilter as LinearFilter3,
11490
- SRGBColorSpace as SRGBColorSpace3,
11491
- TextureLoader as TextureLoader3
11492
- } from "three";
11493
- var EMPTY_TEXTURE_SNAPSHOT = {
11494
- status: "idle",
11495
- texture: null,
11496
- error: null
11497
- };
11498
- var TileTextureManager = class {
11499
- constructor(options) {
11500
- this.loader = new TextureLoader3();
11501
- this.entries = /* @__PURE__ */ new Map();
11502
- this.queue = [];
11503
- this.activeLoads = 0;
11504
- this.totalBytes = 0;
11505
- this.requested = 0;
11506
- this.loaded = 0;
11507
- this.failed = 0;
11508
- this.disposed = false;
11509
- this.generation = 0;
11510
- this.options = options;
11511
- }
11512
- resume() {
11513
- this.disposed = false;
11514
- this.drain();
11515
- }
11516
- getSnapshot(key) {
11517
- const entry = this.entries.get(key);
11518
- return entry ? this.snapshot(entry) : EMPTY_TEXTURE_SNAPSHOT;
11519
- }
11520
- acquire(key, url, address, listener) {
11521
- let entry = this.entries.get(key);
11522
- if (!entry) {
11523
- entry = {
11524
- key,
11525
- url,
11526
- address,
11527
- status: "idle",
11528
- texture: null,
11529
- error: null,
11530
- refs: 0,
11531
- listeners: /* @__PURE__ */ new Set(),
11532
- lastUsed: performance.now(),
11533
- bytes: 0
11534
- };
11535
- this.entries.set(key, entry);
11536
- }
11537
- entry.refs += 1;
11538
- entry.lastUsed = performance.now();
11539
- entry.listeners.add(listener);
11540
- listener(this.snapshot(entry));
11541
- if (entry.status === "idle") {
11542
- entry.status = "queued";
11543
- this.queue.push(entry);
11544
- this.requested += 1;
11545
- this.emitProgress();
11546
- this.drain();
11547
- }
11548
- return () => {
11549
- entry.listeners.delete(listener);
11550
- entry.refs = Math.max(0, entry.refs - 1);
11551
- entry.lastUsed = performance.now();
11552
- if (entry.refs === 0 && entry.status === "queued") {
11553
- const queueIndex = this.queue.indexOf(entry);
11554
- if (queueIndex >= 0) {
11555
- this.queue.splice(queueIndex, 1);
11556
- }
11557
- entry.status = "idle";
11558
- this.entries.delete(entry.key);
11559
- this.requested = Math.max(0, this.requested - 1);
11560
- this.emitProgress();
11561
- this.drain();
11562
- return;
11563
- }
11564
- this.evict();
11565
- };
11566
- }
11567
- dispose() {
11568
- this.disposed = true;
11569
- this.generation += 1;
11570
- this.queue.length = 0;
11571
- for (const entry of this.entries.values()) {
11572
- entry.texture?.dispose();
11573
- entry.listeners.clear();
11574
- }
11575
- this.entries.clear();
11576
- this.totalBytes = 0;
11577
- }
11578
- /**
11579
- * Releases textures that are no longer rendered by any Tile instance.
11580
- * Scene transitions call this after the outgoing panorama has been
11581
- * snapshotted, so the incoming scene never competes with stale GPU tiles.
11582
- */
11583
- releaseUnused() {
11584
- for (const entry of Array.from(this.entries.values())) {
11585
- if (entry.refs > 0) {
11586
- continue;
11587
- }
11588
- entry.texture?.dispose();
11589
- this.totalBytes -= entry.bytes;
11590
- this.entries.delete(entry.key);
11591
- }
11592
- this.totalBytes = Math.max(0, this.totalBytes);
11593
- }
11594
- snapshot(entry) {
11595
- return {
11596
- status: entry.status,
11597
- texture: entry.texture,
11598
- error: entry.error
11599
- };
11600
- }
11601
- notify(entry) {
11602
- const snapshot = this.snapshot(entry);
11603
- for (const listener of entry.listeners) {
11604
- listener(snapshot);
11605
- }
11606
- }
11607
- emitProgress() {
11608
- this.options.onProgress?.({
11609
- requested: this.requested,
11610
- loaded: this.loaded,
11611
- failed: this.failed,
11612
- active: this.activeLoads,
11613
- queued: this.queue.length
11614
- });
11615
- }
11616
- drain() {
11617
- while (!this.disposed && this.activeLoads < this.options.concurrency && this.queue.length > 0) {
11618
- const entry = this.queue.shift();
11619
- if (entry.refs === 0) {
11620
- entry.status = "idle";
11621
- this.entries.delete(entry.key);
11622
- this.requested = Math.max(0, this.requested - 1);
11623
- continue;
11624
- }
11625
- entry.status = "loading";
11626
- this.activeLoads += 1;
11627
- this.notify(entry);
11628
- this.emitProgress();
11629
- void this.loadAndFinish(entry);
11630
- }
11631
- }
11632
- async load(entry, generation) {
11633
- let lastError = null;
11634
- for (let attempt = 0; attempt <= this.options.retryCount; attempt += 1) {
11635
- try {
11636
- const texture = await this.loader.loadAsync(entry.url);
11637
- if (this.disposed || generation !== this.generation) {
11638
- texture.dispose();
11639
- return;
11640
- }
11641
- this.configureTexture(texture);
11642
- entry.texture = texture;
11643
- entry.status = "loaded";
11644
- entry.error = null;
11645
- entry.bytes = entry.address.width * entry.address.height * 4;
11646
- entry.lastUsed = performance.now();
11647
- this.totalBytes += entry.bytes;
11648
- this.loaded += 1;
11649
- this.notify(entry);
11650
- return;
11651
- } catch (error) {
11652
- lastError = error;
11653
- }
11654
- }
11655
- if (!this.disposed && generation === this.generation) {
11656
- entry.status = "error";
11657
- entry.error = lastError;
11658
- this.failed += 1;
11659
- this.notify(entry);
11660
- this.options.onError?.({
11661
- address: entry.address,
11662
- url: entry.url,
11663
- error: lastError
11664
- });
11665
- }
11666
- }
11667
- configureTexture(texture) {
11668
- texture.colorSpace = SRGBColorSpace3;
11669
- texture.wrapS = ClampToEdgeWrapping;
11670
- texture.wrapT = ClampToEdgeWrapping;
11671
- texture.magFilter = LinearFilter3;
11672
- texture.minFilter = LinearFilter3;
11673
- texture.generateMipmaps = false;
11674
- texture.anisotropy = Math.min(4, this.options.anisotropy);
11675
- texture.needsUpdate = true;
11676
- }
11677
- evict() {
11678
- if (this.totalBytes <= this.options.maxBytes) {
11679
- return;
11680
- }
11681
- const candidates = Array.from(this.entries.values()).filter((entry) => entry.refs === 0 && entry.texture).sort((a, b) => a.lastUsed - b.lastUsed);
11682
- for (const entry of candidates) {
11683
- if (this.totalBytes <= this.options.maxBytes) {
11684
- break;
11685
- }
11686
- entry.texture?.dispose();
11687
- this.totalBytes -= entry.bytes;
11688
- this.entries.delete(entry.key);
11689
- }
11690
- this.totalBytes = Math.max(0, this.totalBytes);
11691
- }
11692
- finishLoad(generation) {
11693
- this.activeLoads = Math.max(0, this.activeLoads - 1);
11694
- if (!this.disposed && generation === this.generation) {
11695
- this.emitProgress();
11696
- this.evict();
11697
- }
11698
- if (!this.disposed) {
11699
- this.drain();
11700
- }
11701
- }
11702
- async loadAndFinish(entry) {
11703
- const generation = this.generation;
11704
- try {
11705
- await this.load(entry, generation);
11706
- } finally {
11707
- this.finishLoad(generation);
11708
- }
11709
- }
11710
- };
11711
-
11712
- // src/tile/texture-manager-context.tsx
11713
- import { jsx as jsx20 } from "react/jsx-runtime";
12225
+ import { jsx as jsx21 } from "react/jsx-runtime";
11714
12226
  var TileTextureManagerContext = createContext15(
11715
12227
  null
11716
12228
  );
@@ -11719,9 +12231,9 @@ function TileTextureManagerProvider({
11719
12231
  maxTextureMemoryMb = 128,
11720
12232
  maxConcurrentLoads = 4
11721
12233
  }) {
11722
- const gl = useThree17((state) => state.gl);
12234
+ const gl = useThree18((state) => state.gl);
11723
12235
  const anisotropy = gl.capabilities.getMaxAnisotropy();
11724
- const manager = useMemo18(
12236
+ const manager = useMemo19(
11725
12237
  () => new TileTextureManager({
11726
12238
  anisotropy,
11727
12239
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -11730,43 +12242,33 @@ function TileTextureManagerProvider({
11730
12242
  }),
11731
12243
  [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
11732
12244
  );
11733
- useEffect25(() => {
12245
+ useEffect27(() => {
11734
12246
  manager.resume();
11735
12247
  return () => manager.dispose();
11736
12248
  }, [manager]);
11737
- return /* @__PURE__ */ jsx20(TileTextureManagerContext.Provider, { value: manager, children });
12249
+ return /* @__PURE__ */ jsx21(TileTextureManagerContext.Provider, { value: manager, children });
11738
12250
  }
11739
12251
  function useSharedTileTextureManager() {
11740
12252
  return useContext22(TileTextureManagerContext);
11741
12253
  }
11742
12254
 
11743
12255
  // src/tile/tile.tsx
11744
- import { useFrame as useFrame11, useThree as useThree18 } from "@react-three/fiber";
11745
- import {
11746
- memo,
11747
- useEffect as useEffect26,
11748
- useMemo as useMemo19,
11749
- useRef as useRef26,
11750
- useState as useState11
11751
- } from "react";
12256
+ import { useFrame as useFrame12, useThree as useThree19 } from "@react-three/fiber";
12257
+ import { memo as memo2, useEffect as useEffect28, useMemo as useMemo20, useRef as useRef28, useState as useState13 } from "react";
11752
12258
  import {
11753
- ClampToEdgeWrapping as ClampToEdgeWrapping2,
11754
- Frustum,
11755
- LinearFilter as LinearFilter4,
11756
- Matrix4,
12259
+ Frustum as Frustum2,
12260
+ Matrix4 as Matrix43,
11757
12261
  PlaneGeometry as PlaneGeometry4,
11758
- PerspectiveCamera as PerspectiveCamera4,
11759
- SRGBColorSpace as SRGBColorSpace4,
11760
- TextureLoader as TextureLoader4,
11761
- Vector3 as Vector312
12262
+ PerspectiveCamera as PerspectiveCamera5,
12263
+ Vector3 as Vector313
11762
12264
  } from "three";
11763
12265
 
11764
12266
  // src/tile/face-layout.ts
11765
12267
  import {
11766
- Box3,
12268
+ Box3 as Box32,
11767
12269
  Euler as Euler3,
11768
12270
  Quaternion as Quaternion2,
11769
- Vector3 as Vector311
12271
+ Vector3 as Vector312
11770
12272
  } from "three";
11771
12273
  var CUBE_FACES = ["f", "r", "b", "l", "u", "d"];
11772
12274
  var PREVIEW_FACE_ORDER = ["l", "f", "r", "b", "u", "d"];
@@ -11830,17 +12332,17 @@ function getTileBoundingBox(address, radius = CUBE_RADIUS) {
11830
12332
  const halfWidth = local.width / 2;
11831
12333
  const halfHeight = local.height / 2;
11832
12334
  const quaternion = getFaceQuaternion(address.face);
11833
- const position = new Vector311(...transform.position);
12335
+ const position = new Vector312(...transform.position);
11834
12336
  const corners = [
11835
- new Vector311(local.x - halfWidth, local.y - halfHeight, 0),
11836
- new Vector311(local.x + halfWidth, local.y - halfHeight, 0),
11837
- new Vector311(local.x - halfWidth, local.y + halfHeight, 0),
11838
- new Vector311(local.x + halfWidth, local.y + halfHeight, 0)
12337
+ new Vector312(local.x - halfWidth, local.y - halfHeight, 0),
12338
+ new Vector312(local.x + halfWidth, local.y - halfHeight, 0),
12339
+ new Vector312(local.x - halfWidth, local.y + halfHeight, 0),
12340
+ new Vector312(local.x + halfWidth, local.y + halfHeight, 0)
11839
12341
  ];
11840
12342
  for (const corner of corners) {
11841
12343
  corner.applyQuaternion(quaternion).add(position);
11842
12344
  }
11843
- return new Box3().setFromPoints(corners);
12345
+ return new Box32().setFromPoints(corners);
11844
12346
  }
11845
12347
  function makeTileAddress(face, level, faceSize, tileSize, col, row) {
11846
12348
  const left = (col - 1) * tileSize;
@@ -11887,7 +12389,7 @@ function previewAtlasVRange(face, faceOrder = PREVIEW_FACE_ORDER) {
11887
12389
  }
11888
12390
 
11889
12391
  // src/tile/multires.ts
11890
- function positiveInteger(value, name) {
12392
+ function positiveInteger2(value, name) {
11891
12393
  if (!Number.isFinite(value) || value <= 0 || !Number.isInteger(value)) {
11892
12394
  throw new Error(`${name} must be a positive integer, received ${String(value)}`);
11893
12395
  }
@@ -11903,13 +12405,13 @@ function ascendingLevels(levels) {
11903
12405
  }
11904
12406
  function parseMultires(multires) {
11905
12407
  if (typeof multires !== "string") {
11906
- const tileSize2 = positiveInteger(multires.tileSize, "multires.tileSize");
12408
+ const tileSize2 = positiveInteger2(multires.tileSize, "multires.tileSize");
11907
12409
  if (multires.levels.length === 0) {
11908
12410
  throw new Error("multires.levels must contain at least one face size");
11909
12411
  }
11910
12412
  const levels2 = ascendingLevels(
11911
12413
  multires.levels.map(
11912
- (level, index) => positiveInteger(level, `multires.levels[${index}]`)
12414
+ (level, index) => positiveInteger2(level, `multires.levels[${index}]`)
11913
12415
  )
11914
12416
  );
11915
12417
  return { tileSize: tileSize2, levels: levels2 };
@@ -11920,10 +12422,10 @@ function parseMultires(multires) {
11920
12422
  "multires must contain a tile size followed by at least one face size"
11921
12423
  );
11922
12424
  }
11923
- const tileSize = positiveInteger(values[0], "multires tile size");
12425
+ const tileSize = positiveInteger2(values[0], "multires tile size");
11924
12426
  const levels = ascendingLevels(
11925
12427
  values.slice(1).map(
11926
- (level, index) => positiveInteger(level, `multires level ${index + 1}`)
12428
+ (level, index) => positiveInteger2(level, `multires level ${index + 1}`)
11927
12429
  )
11928
12430
  );
11929
12431
  return { tileSize, levels };
@@ -11943,7 +12445,7 @@ function resolveTemplateUrl(template, address) {
11943
12445
  }
11944
12446
 
11945
12447
  // src/tile/tile.tsx
11946
- import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
12448
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
11947
12449
  var MAX_LOD_UPDATE_INTERVAL_MS = 100;
11948
12450
  var FAST_ROTATION_THRESHOLD_RADIANS = Math.PI / 180;
11949
12451
  var FAST_FOV_THRESHOLD_DEGREES = 0.5;
@@ -11951,61 +12453,12 @@ var VIEW_CHANGE_EPSILON = 1e-4;
11951
12453
  var VISIBILITY_FOV_PADDING_DEGREES = 8;
11952
12454
  var PREVIEW_RADIUS = CUBE_RADIUS + 0.05;
11953
12455
  var SHARED_TILE_GEOMETRY = new PlaneGeometry4(1, 1);
11954
- function usePreviewTexture(url, anisotropy, onError) {
11955
- const [texture, setTexture] = useState11(null);
11956
- const onErrorRef = useRef26(onError);
11957
- onErrorRef.current = onError;
11958
- useEffect26(() => {
11959
- if (!url) {
11960
- setTexture(null);
11961
- return;
11962
- }
11963
- let active = true;
11964
- const loader = new TextureLoader4();
11965
- setTexture(null);
11966
- loader.load(
11967
- url,
11968
- (loadedTexture) => {
11969
- if (!active) {
11970
- loadedTexture.dispose();
11971
- return;
11972
- }
11973
- loadedTexture.colorSpace = SRGBColorSpace4;
11974
- loadedTexture.wrapS = ClampToEdgeWrapping2;
11975
- loadedTexture.wrapT = ClampToEdgeWrapping2;
11976
- loadedTexture.magFilter = LinearFilter4;
11977
- loadedTexture.minFilter = LinearFilter4;
11978
- loadedTexture.generateMipmaps = false;
11979
- loadedTexture.anisotropy = Math.min(4, anisotropy);
11980
- loadedTexture.needsUpdate = true;
11981
- setTexture(loadedTexture);
11982
- },
11983
- void 0,
11984
- (error) => {
11985
- if (active) {
11986
- setTexture(null);
11987
- onErrorRef.current?.(error);
11988
- }
11989
- }
11990
- );
11991
- return () => {
11992
- active = false;
11993
- };
11994
- }, [anisotropy, url]);
11995
- useEffect26(
11996
- () => () => {
11997
- texture?.dispose();
11998
- },
11999
- [texture]
12000
- );
12001
- return texture;
12002
- }
12003
12456
  function PreviewFace({
12004
12457
  face,
12005
12458
  texture,
12006
12459
  faceOrder
12007
12460
  }) {
12008
- const geometry = useMemo19(() => {
12461
+ const geometry = useMemo20(() => {
12009
12462
  const nextGeometry = new PlaneGeometry4(
12010
12463
  PREVIEW_RADIUS * 2,
12011
12464
  PREVIEW_RADIUS * 2
@@ -12015,29 +12468,26 @@ function PreviewFace({
12015
12468
  const inset = scale / 512;
12016
12469
  for (let index = 0; index < uv.count; index += 1) {
12017
12470
  const sourceV = uv.getY(index);
12018
- uv.setY(
12019
- index,
12020
- offset + inset + sourceV * Math.max(0, scale - inset * 2)
12021
- );
12471
+ uv.setY(index, offset + inset + sourceV * Math.max(0, scale - inset * 2));
12022
12472
  }
12023
12473
  uv.needsUpdate = true;
12024
12474
  return nextGeometry;
12025
12475
  }, [face, faceOrder]);
12026
- useEffect26(
12476
+ useEffect28(
12027
12477
  () => () => {
12028
12478
  geometry.dispose();
12029
12479
  },
12030
12480
  [geometry]
12031
12481
  );
12032
12482
  const transform = getFaceTransform(face, PREVIEW_RADIUS);
12033
- return /* @__PURE__ */ jsx21(
12483
+ return /* @__PURE__ */ jsx22(
12034
12484
  "mesh",
12035
12485
  {
12036
12486
  geometry,
12037
12487
  position: transform.position,
12038
12488
  renderOrder: -100,
12039
12489
  rotation: transform.rotation,
12040
- children: /* @__PURE__ */ jsx21(
12490
+ children: /* @__PURE__ */ jsx22(
12041
12491
  PanoBasicMaterial,
12042
12492
  {
12043
12493
  depthTest: false,
@@ -12053,7 +12503,7 @@ function PreviewCube({
12053
12503
  texture,
12054
12504
  faceOrder
12055
12505
  }) {
12056
- return CUBE_FACES.map((face) => /* @__PURE__ */ jsx21(
12506
+ return CUBE_FACES.map((face) => /* @__PURE__ */ jsx22(
12057
12507
  PreviewFace,
12058
12508
  {
12059
12509
  face,
@@ -12063,28 +12513,7 @@ function PreviewCube({
12063
12513
  face
12064
12514
  ));
12065
12515
  }
12066
- function useManagedTexture(manager, key, url, address) {
12067
- const [managedState, setManagedState] = useState11(() => ({
12068
- manager,
12069
- key,
12070
- url,
12071
- snapshot: manager.getSnapshot(key)
12072
- }));
12073
- const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
12074
- useEffect26(
12075
- () => manager.acquire(key, url, address, (nextSnapshot) => {
12076
- setManagedState({
12077
- manager,
12078
- key,
12079
- url,
12080
- snapshot: nextSnapshot
12081
- });
12082
- }),
12083
- [key, manager, url]
12084
- );
12085
- return snapshot;
12086
- }
12087
- var TileMesh = memo(function TileMesh2({
12516
+ var TileMesh = memo2(function TileMesh2({
12088
12517
  address,
12089
12518
  manager,
12090
12519
  renderOrder,
@@ -12098,14 +12527,14 @@ var TileMesh = memo(function TileMesh2({
12098
12527
  if (!snapshot.texture) {
12099
12528
  return null;
12100
12529
  }
12101
- return /* @__PURE__ */ jsx21("group", { position: transform.position, rotation: transform.rotation, children: /* @__PURE__ */ jsx21(
12530
+ return /* @__PURE__ */ jsx22("group", { position: transform.position, rotation: transform.rotation, children: /* @__PURE__ */ jsx22(
12102
12531
  "mesh",
12103
12532
  {
12104
12533
  geometry: SHARED_TILE_GEOMETRY,
12105
12534
  position: [layout.x, layout.y, 0],
12106
12535
  renderOrder,
12107
12536
  scale: [layout.width, layout.height, 1],
12108
- children: /* @__PURE__ */ jsx21(
12537
+ children: /* @__PURE__ */ jsx22(
12109
12538
  PanoBasicMaterial,
12110
12539
  {
12111
12540
  depthTest: false,
@@ -12117,12 +12546,12 @@ var TileMesh = memo(function TileMesh2({
12117
12546
  }
12118
12547
  ) });
12119
12548
  });
12120
- var TileLayerMeshes = memo(function TileLayerMeshes2({
12549
+ var TileLayerMeshes = memo2(function TileLayerMeshes2({
12121
12550
  layer,
12122
12551
  manager,
12123
12552
  resolveUrl
12124
12553
  }) {
12125
- return layer.addresses.map((address) => /* @__PURE__ */ jsx21(
12554
+ return layer.addresses.map((address) => /* @__PURE__ */ jsx22(
12126
12555
  TileMesh,
12127
12556
  {
12128
12557
  address,
@@ -12222,8 +12651,8 @@ function parentAddresses(child, parentLevel, parentFaceSize, tileSize) {
12222
12651
  return addresses;
12223
12652
  }
12224
12653
  function centerFacingAddress(candidates, camera) {
12225
- const direction = camera.getWorldDirection(new Vector312());
12226
- const center = new Vector312();
12654
+ const direction = camera.getWorldDirection(new Vector313());
12655
+ const center = new Vector313();
12227
12656
  let bestCandidate = candidates[0];
12228
12657
  let bestAlignment = -Infinity;
12229
12658
  for (const candidate of candidates) {
@@ -12268,9 +12697,7 @@ function buildLayers(targetLevel, levels, tileSize, frustum, camera, candidatesB
12268
12697
  }));
12269
12698
  }
12270
12699
  function layerToken(layers) {
12271
- return layers.map(
12272
- (layer) => `${layer.level}:${layer.addresses.map(tileKey).join(",")}`
12273
- ).join("|");
12700
+ return layers.map((layer) => `${layer.level}:${layer.addresses.map(tileKey).join(",")}`).join("|");
12274
12701
  }
12275
12702
  function Tile({
12276
12703
  baseUrl,
@@ -12290,11 +12717,11 @@ function Tile({
12290
12717
  onTileError,
12291
12718
  onLevelChange
12292
12719
  }) {
12293
- const { camera, gl, size } = useThree18();
12294
- const multiresConfig = useMemo19(() => parseMultires(multires), [multires]);
12720
+ const { camera, gl, size } = useThree19();
12721
+ const multiresConfig = useMemo20(() => parseMultires(multires), [multires]);
12295
12722
  const { levels, tileSize } = multiresConfig;
12296
12723
  const multiresToken = `${tileSize}:${levels.join(",")}`;
12297
- const candidatesByLevel = useMemo19(
12724
+ const candidatesByLevel = useMemo20(
12298
12725
  () => buildTileCandidates(levels, tileSize),
12299
12726
  [levels, tileSize]
12300
12727
  );
@@ -12306,13 +12733,13 @@ function Tile({
12306
12733
  onPreviewError
12307
12734
  );
12308
12735
  const sharedManager = useSharedTileTextureManager();
12309
- const progressRef = useRef26(onLoadProgress);
12310
- const errorRef = useRef26(onTileError);
12311
- const levelChangeRef = useRef26(onLevelChange);
12736
+ const progressRef = useRef28(onLoadProgress);
12737
+ const errorRef = useRef28(onTileError);
12738
+ const levelChangeRef = useRef28(onLevelChange);
12312
12739
  progressRef.current = onLoadProgress;
12313
12740
  errorRef.current = onTileError;
12314
12741
  levelChangeRef.current = onLevelChange;
12315
- const ownedManager = useMemo19(
12742
+ const ownedManager = useMemo20(
12316
12743
  () => new TileTextureManager({
12317
12744
  anisotropy,
12318
12745
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -12333,7 +12760,7 @@ function Tile({
12333
12760
  ]
12334
12761
  );
12335
12762
  const manager = sharedManager ?? ownedManager;
12336
- useEffect26(() => {
12763
+ useEffect28(() => {
12337
12764
  manager.resume();
12338
12765
  return () => {
12339
12766
  if (manager === ownedManager) {
@@ -12343,11 +12770,11 @@ function Tile({
12343
12770
  queueMicrotask(() => manager.releaseUnused());
12344
12771
  };
12345
12772
  }, [manager, ownedManager]);
12346
- const readyRef = useRef26(false);
12347
- useEffect26(() => {
12773
+ const readyRef = useRef28(false);
12774
+ useEffect28(() => {
12348
12775
  readyRef.current = false;
12349
12776
  }, [effectivePreviewUrl]);
12350
- useEffect26(() => {
12777
+ useEffect28(() => {
12351
12778
  if (!previewTexture || readyRef.current) {
12352
12779
  return;
12353
12780
  }
@@ -12355,31 +12782,31 @@ function Tile({
12355
12782
  onReady?.();
12356
12783
  }, [onReady, previewTexture]);
12357
12784
  const template = urlTemplate ?? buildDefaultTileUrlTemplate();
12358
- const resolveUrl = useMemo19(
12785
+ const resolveUrl = useMemo20(
12359
12786
  () => (address) => {
12360
12787
  const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
12361
12788
  return resolveRelativeTileUrl(baseUrl, relativePath);
12362
12789
  },
12363
12790
  [baseUrl, resolveTileUrl, template]
12364
12791
  );
12365
- const [layers, setLayers] = useState11([]);
12366
- const layerTokenRef = useRef26("");
12367
- const currentLevelRef = useRef26(1);
12368
- const lastUpdateAtRef = useRef26(-Infinity);
12369
- const visibilityDirtyRef = useRef26(true);
12370
- const lastVisibilityQuaternionRef = useRef26(camera.quaternion.clone());
12371
- const lastVisibilityFovRef = useRef26(Number.NaN);
12372
- const lastViewportRef = useRef26({ width: 0, height: 0, dpr: 0 });
12373
- const frustumRef = useRef26(new Frustum());
12374
- const projectionMatrixRef = useRef26(new Matrix4());
12375
- const visibilityCameraRef = useRef26(new PerspectiveCamera4());
12376
- useEffect26(() => {
12792
+ const [layers, setLayers] = useState13([]);
12793
+ const layerTokenRef = useRef28("");
12794
+ const currentLevelRef = useRef28(1);
12795
+ const lastUpdateAtRef = useRef28(-Infinity);
12796
+ const visibilityDirtyRef = useRef28(true);
12797
+ const lastVisibilityQuaternionRef = useRef28(camera.quaternion.clone());
12798
+ const lastVisibilityFovRef = useRef28(Number.NaN);
12799
+ const lastViewportRef = useRef28({ width: 0, height: 0, dpr: 0 });
12800
+ const frustumRef = useRef28(new Frustum2());
12801
+ const projectionMatrixRef = useRef28(new Matrix43());
12802
+ const visibilityCameraRef = useRef28(new PerspectiveCamera5());
12803
+ useEffect28(() => {
12377
12804
  currentLevelRef.current = 1;
12378
12805
  layerTokenRef.current = "";
12379
12806
  visibilityDirtyRef.current = true;
12380
12807
  setLayers([]);
12381
12808
  }, [manager, multiresToken]);
12382
- useEffect26(() => {
12809
+ useEffect28(() => {
12383
12810
  if (loadMode !== "preview") {
12384
12811
  return;
12385
12812
  }
@@ -12393,11 +12820,11 @@ function Tile({
12393
12820
  setLayers(nextLayers);
12394
12821
  }
12395
12822
  };
12396
- useFrame11(() => {
12823
+ useFrame12(() => {
12397
12824
  if (loadMode === "preview") {
12398
12825
  return;
12399
12826
  }
12400
- if (!(camera instanceof PerspectiveCamera4)) {
12827
+ if (!(camera instanceof PerspectiveCamera5)) {
12401
12828
  return;
12402
12829
  }
12403
12830
  const now = performance.now();
@@ -12459,15 +12886,9 @@ function Tile({
12459
12886
  };
12460
12887
  commitLayers(nextLayers);
12461
12888
  });
12462
- return /* @__PURE__ */ jsxs12("group", { visible, children: [
12463
- previewTexture ? /* @__PURE__ */ jsx21(
12464
- PreviewCube,
12465
- {
12466
- texture: previewTexture,
12467
- faceOrder: previewFaceOrder
12468
- }
12469
- ) : null,
12470
- layers.map((layer) => /* @__PURE__ */ jsx21(
12889
+ return /* @__PURE__ */ jsxs13("group", { visible, children: [
12890
+ previewTexture ? /* @__PURE__ */ jsx22(PreviewCube, { texture: previewTexture, faceOrder: previewFaceOrder }) : null,
12891
+ layers.map((layer) => /* @__PURE__ */ jsx22(
12471
12892
  TileLayerMeshes,
12472
12893
  {
12473
12894
  layer,
@@ -12480,7 +12901,7 @@ function Tile({
12480
12901
  }
12481
12902
 
12482
12903
  // src/scenes.tsx
12483
- import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
12904
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
12484
12905
  function SceneSource({
12485
12906
  scene,
12486
12907
  visible,
@@ -12493,7 +12914,7 @@ function SceneSource({
12493
12914
  }
12494
12915
  switch (scene.type) {
12495
12916
  case "sphere":
12496
- return /* @__PURE__ */ jsx22(
12917
+ return /* @__PURE__ */ jsx23(
12497
12918
  Sphere,
12498
12919
  {
12499
12920
  src: scene.src,
@@ -12506,7 +12927,7 @@ function SceneSource({
12506
12927
  );
12507
12928
  case "tile": {
12508
12929
  const { id: _id, type: _type, ...tileProps } = scene;
12509
- return /* @__PURE__ */ jsx22(
12930
+ return /* @__PURE__ */ jsx23(
12510
12931
  Tile,
12511
12932
  {
12512
12933
  ...tileProps,
@@ -12529,17 +12950,17 @@ function SnapshotCapture({
12529
12950
  maxPixels,
12530
12951
  onCaptured
12531
12952
  }) {
12532
- const { camera, gl, scene, size } = useThree19();
12533
- const capturedRequestRef = useRef27(0);
12534
- const renderTargetRef = useRef27(null);
12535
- useEffect27(
12953
+ const { camera, gl, scene, size } = useThree20();
12954
+ const capturedRequestRef = useRef29(0);
12955
+ const renderTargetRef = useRef29(null);
12956
+ useEffect29(
12536
12957
  () => () => {
12537
12958
  renderTargetRef.current?.dispose();
12538
12959
  renderTargetRef.current = null;
12539
12960
  },
12540
12961
  []
12541
12962
  );
12542
- useFrame12(() => {
12963
+ useFrame13(() => {
12543
12964
  if (requestId === 0 || requestId === capturedRequestRef.current) {
12544
12965
  return;
12545
12966
  }
@@ -12590,48 +13011,48 @@ function ScenesController({
12590
13011
  }) {
12591
13012
  const controlsRef = useContext23(PanoramaViewContext);
12592
13013
  const manager = useSharedTileTextureManager();
12593
- const transition = useMemo20(
13014
+ const transition = useMemo21(
12594
13015
  () => resolveTransition(transitionInput),
12595
13016
  [transitionInput]
12596
13017
  );
12597
- const sceneMap = useMemo20(
13018
+ const sceneMap = useMemo21(
12598
13019
  () => new Map(scenes.map((scene) => [scene.id, scene])),
12599
13020
  [scenes]
12600
13021
  );
12601
13022
  const initialScene = sceneMap.get(activeSceneId) ?? null;
12602
- const [slots, setSlots] = useState12([
13023
+ const [slots, setSlots] = useState14([
12603
13024
  initialScene,
12604
13025
  null
12605
13026
  ]);
12606
- const [activeSlot, setActiveSlot] = useState12(0);
12607
- const [liveSlot, setLiveSlot] = useState12(0);
12608
- const [pendingSlot, setPendingSlot] = useState12(null);
12609
- const [phase, setPhase] = useState12(initialScene ? "idle" : "preloading");
12610
- const [captureRequestId, setCaptureRequestId] = useState12(0);
12611
- const [transitionRunId, setTransitionRunId] = useState12(0);
12612
- const [snapshot, setSnapshot] = useState12(null);
12613
- const [recoveryKey, setRecoveryKey] = useState12(0);
12614
- const requestedSceneIdRef = useRef27(activeSceneId);
12615
- const captureReasonRef = useRef27("start");
12616
- const interactionReleaseRef = useRef27(null);
12617
- const slotsRef = useRef27(slots);
12618
- const phaseRef = useRef27(phase);
12619
- const liveSlotRef = useRef27(liveSlot);
12620
- const transitionFromRef = useRef27(initialScene);
13027
+ const [activeSlot, setActiveSlot] = useState14(0);
13028
+ const [liveSlot, setLiveSlot] = useState14(0);
13029
+ const [pendingSlot, setPendingSlot] = useState14(null);
13030
+ const [phase, setPhase] = useState14(initialScene ? "idle" : "preloading");
13031
+ const [captureRequestId, setCaptureRequestId] = useState14(0);
13032
+ const [transitionRunId, setTransitionRunId] = useState14(0);
13033
+ const [snapshot, setSnapshot] = useState14(null);
13034
+ const [recoveryKey, setRecoveryKey] = useState14(0);
13035
+ const requestedSceneIdRef = useRef29(activeSceneId);
13036
+ const captureReasonRef = useRef29("start");
13037
+ const interactionReleaseRef = useRef29(null);
13038
+ const slotsRef = useRef29(slots);
13039
+ const phaseRef = useRef29(phase);
13040
+ const liveSlotRef = useRef29(liveSlot);
13041
+ const transitionFromRef = useRef29(initialScene);
12621
13042
  slotsRef.current = slots;
12622
13043
  phaseRef.current = phase;
12623
13044
  if (externalPhaseRef) {
12624
13045
  externalPhaseRef.current = phase;
12625
13046
  }
12626
13047
  liveSlotRef.current = liveSlot;
12627
- const releaseInteraction = useCallback10(() => {
13048
+ const releaseInteraction = useCallback11(() => {
12628
13049
  interactionReleaseRef.current?.();
12629
13050
  interactionReleaseRef.current = null;
12630
13051
  }, []);
12631
- const reportError = useCallback10((sceneId, error) => {
13052
+ const reportError = useCallback11((sceneId, error) => {
12632
13053
  onTransitionError?.({ sceneId, error });
12633
13054
  }, [onTransitionError]);
12634
- const startPreload = useCallback10((scene) => {
13055
+ const startPreload = useCallback11((scene) => {
12635
13056
  const nextSlot = 1 - liveSlotRef.current;
12636
13057
  requestedSceneIdRef.current = scene.id;
12637
13058
  setSlots((current) => {
@@ -12642,7 +13063,7 @@ function ScenesController({
12642
13063
  setPendingSlot(nextSlot);
12643
13064
  setPhase("preloading");
12644
13065
  }, []);
12645
- useEffect27(() => {
13066
+ useEffect29(() => {
12646
13067
  const requested = sceneMap.get(activeSceneId);
12647
13068
  if (!requested) {
12648
13069
  reportError(activeSceneId, new Error(`Unknown panorama scene: ${activeSceneId}`));
@@ -12666,7 +13087,7 @@ function ScenesController({
12666
13087
  }
12667
13088
  startPreload(requested);
12668
13089
  }, [activeSceneId, pendingSlot, reportError, sceneMap, startPreload]);
12669
- const handleReady = useCallback10((slot) => {
13090
+ const handleReady = useCallback11((slot) => {
12670
13091
  if (slot !== pendingSlot || phaseRef.current !== "preloading") {
12671
13092
  return;
12672
13093
  }
@@ -12697,7 +13118,7 @@ function ScenesController({
12697
13118
  setPhase("capturing");
12698
13119
  setCaptureRequestId((current) => current + 1);
12699
13120
  }, [controlsRef, onTransitionEnd, pendingSlot, transition.preset]);
12700
- const handleCaptured = useCallback10((nextSnapshot) => {
13121
+ const handleCaptured = useCallback11((nextSnapshot) => {
12701
13122
  setSnapshot((current) => {
12702
13123
  current?.dispose();
12703
13124
  return nextSnapshot;
@@ -12719,7 +13140,7 @@ function ScenesController({
12719
13140
  setPhase("transitioning");
12720
13141
  setTransitionRunId((current) => current + 1);
12721
13142
  }, [manager, pendingSlot, sceneMap, startPreload]);
12722
- const finishTransition = useCallback10(() => {
13143
+ const finishTransition = useCallback11(() => {
12723
13144
  const nextSlot = pendingSlot;
12724
13145
  const previousScene = transitionFromRef.current;
12725
13146
  const nextScene = nextSlot === null ? null : slotsRef.current[nextSlot];
@@ -12744,12 +13165,12 @@ function ScenesController({
12744
13165
  });
12745
13166
  }
12746
13167
  }, [onTransitionEnd, pendingSlot, releaseInteraction, snapshot, transition.preset]);
12747
- useEffect27(() => () => {
13168
+ useEffect29(() => () => {
12748
13169
  releaseInteraction();
12749
13170
  snapshot?.dispose();
12750
13171
  }, [releaseInteraction, snapshot]);
12751
- const { gl } = useThree19();
12752
- useEffect27(() => {
13172
+ const { gl } = useThree20();
13173
+ useEffect29(() => {
12753
13174
  const canvas = gl.domElement;
12754
13175
  const onContextLost = (event) => {
12755
13176
  event.preventDefault();
@@ -12773,8 +13194,8 @@ function ScenesController({
12773
13194
  }, [gl, manager, releaseInteraction, snapshot]);
12774
13195
  const renderedScene = slots[liveSlot];
12775
13196
  const snapshotVisible = snapshot !== null && (phase === "transitioning" || phase === "recapturing" || phase === "preloading");
12776
- return /* @__PURE__ */ jsxs13(Fragment6, { children: [
12777
- /* @__PURE__ */ jsx22(
13197
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
13198
+ /* @__PURE__ */ jsx23(
12778
13199
  SnapshotCapture,
12779
13200
  {
12780
13201
  requestId: captureRequestId,
@@ -12786,7 +13207,7 @@ function ScenesController({
12786
13207
  const source = slots[slot];
12787
13208
  const isLive = slot === liveSlot;
12788
13209
  const isPending = slot === pendingSlot;
12789
- return /* @__PURE__ */ jsx22(
13210
+ return /* @__PURE__ */ jsx23(
12790
13211
  SceneSource,
12791
13212
  {
12792
13213
  scene: source,
@@ -12802,7 +13223,7 @@ function ScenesController({
12802
13223
  `${slot}:${source?.id ?? "empty"}:${recoveryKey}`
12803
13224
  );
12804
13225
  }),
12805
- snapshotVisible ? /* @__PURE__ */ jsx22(
13226
+ snapshotVisible ? /* @__PURE__ */ jsx23(
12806
13227
  TransitionOverlay,
12807
13228
  {
12808
13229
  snapshot,
@@ -12825,11 +13246,11 @@ function Scenes({
12825
13246
  ...rest
12826
13247
  }) {
12827
13248
  const isControlled = controlledId !== void 0;
12828
- const [internalId, setInternalId] = useState12(
13249
+ const [internalId, setInternalId] = useState14(
12829
13250
  () => controlledId ?? defaultActiveSceneId ?? scenes[0]?.id ?? ""
12830
13251
  );
12831
13252
  const resolvedId = isControlled ? controlledId : internalId;
12832
- const requestSceneChange = useCallback10(
13253
+ const requestSceneChange = useCallback11(
12833
13254
  (id) => {
12834
13255
  if (isControlled) {
12835
13256
  onActiveSceneIdChange?.(id);
@@ -12839,13 +13260,13 @@ function Scenes({
12839
13260
  },
12840
13261
  [isControlled, onActiveSceneIdChange]
12841
13262
  );
12842
- const scenesRef = useRef27(scenes);
13263
+ const scenesRef = useRef29(scenes);
12843
13264
  scenesRef.current = scenes;
12844
- const resolvedIdRef = useRef27(resolvedId);
13265
+ const resolvedIdRef = useRef29(resolvedId);
12845
13266
  resolvedIdRef.current = resolvedId;
12846
13267
  const host = useContext23(ScenesHostContext);
12847
- const phaseRef = useRef27("idle");
12848
- useEffect27(() => {
13268
+ const phaseRef = useRef29("idle");
13269
+ useEffect29(() => {
12849
13270
  if (!host) {
12850
13271
  return;
12851
13272
  }
@@ -12895,12 +13316,12 @@ function Scenes({
12895
13316
  }
12896
13317
  };
12897
13318
  }, [host, requestSceneChange]);
12898
- return /* @__PURE__ */ jsx22(
13319
+ return /* @__PURE__ */ jsx23(
12899
13320
  TileTextureManagerProvider,
12900
13321
  {
12901
13322
  maxTextureMemoryMb,
12902
13323
  maxConcurrentLoads: maxConcurrentTileLoads,
12903
- children: /* @__PURE__ */ jsx22(
13324
+ children: /* @__PURE__ */ jsx23(
12904
13325
  ScenesController,
12905
13326
  {
12906
13327
  ...rest,
@@ -12914,8 +13335,8 @@ function Scenes({
12914
13335
  }
12915
13336
 
12916
13337
  // src/hotspot/image-hotspot.tsx
12917
- import { useThree as useThree21 } from "@react-three/fiber";
12918
- import { useEffect as useEffect30, useRef as useRef29, useState as useState15 } from "react";
13338
+ import { useThree as useThree22 } from "@react-three/fiber";
13339
+ import { useEffect as useEffect32, useRef as useRef31, useState as useState17 } from "react";
12919
13340
  import {
12920
13341
  ClampToEdgeWrapping as ClampToEdgeWrapping3,
12921
13342
  LinearFilter as LinearFilter6,
@@ -12925,35 +13346,35 @@ import {
12925
13346
  } from "three";
12926
13347
 
12927
13348
  // src/hotspot/hotspot-anchor.tsx
12928
- import { useFrame as useFrame13, useThree as useThree20 } from "@react-three/fiber";
13349
+ import { useFrame as useFrame14, useThree as useThree21 } from "@react-three/fiber";
12929
13350
  import {
12930
13351
  useContext as useContext25,
12931
- useEffect as useEffect29,
13352
+ useEffect as useEffect31,
12932
13353
  useLayoutEffect as useLayoutEffect4,
12933
- useMemo as useMemo22,
12934
- useRef as useRef28,
12935
- useState as useState14
13354
+ useMemo as useMemo23,
13355
+ useRef as useRef30,
13356
+ useState as useState16
12936
13357
  } from "react";
12937
13358
  import {
12938
- Box3 as Box33,
13359
+ Box3 as Box34,
12939
13360
  EdgesGeometry,
12940
- MathUtils as MathUtils7,
12941
- Matrix4 as Matrix42,
13361
+ MathUtils as MathUtils8,
13362
+ Matrix4 as Matrix44,
12942
13363
  Plane,
12943
13364
  PlaneGeometry as PlaneGeometry5,
12944
- PerspectiveCamera as PerspectiveCamera5,
13365
+ PerspectiveCamera as PerspectiveCamera6,
12945
13366
  Quaternion as Quaternion3,
12946
13367
  Ray,
12947
- Vector3 as Vector313
13368
+ Vector3 as Vector314
12948
13369
  } from "three";
12949
13370
 
12950
13371
  // src/hotspot/hotspot-tooltip.tsx
12951
- import { useEffect as useEffect28, useMemo as useMemo21, useState as useState13 } from "react";
13372
+ import { useEffect as useEffect30, useMemo as useMemo22, useState as useState15 } from "react";
12952
13373
 
12953
13374
  // src/webvr/stereo-html.tsx
12954
13375
  import { Html } from "@react-three/drei";
12955
13376
  import { useContext as useContext24, useSyncExternalStore as useSyncExternalStore7 } from "react";
12956
- import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
13377
+ import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
12957
13378
  function useFallbackStereoView() {
12958
13379
  const runtime = useContext24(WebVRRuntimeContext);
12959
13380
  const host = runtime?.host ?? null;
@@ -12978,10 +13399,10 @@ function stereoCalculatePosition(eye, stereoView) {
12978
13399
  function PanoHtml({ children, ...htmlProps }) {
12979
13400
  const stereoView = useFallbackStereoView();
12980
13401
  if (!stereoView) {
12981
- return /* @__PURE__ */ jsx23(Html, { ...htmlProps, children });
13402
+ return /* @__PURE__ */ jsx24(Html, { ...htmlProps, children });
12982
13403
  }
12983
- return /* @__PURE__ */ jsxs14(Fragment7, { children: [
12984
- /* @__PURE__ */ jsx23(
13404
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
13405
+ /* @__PURE__ */ jsx24(
12985
13406
  Html,
12986
13407
  {
12987
13408
  ...htmlProps,
@@ -12989,7 +13410,7 @@ function PanoHtml({ children, ...htmlProps }) {
12989
13410
  children
12990
13411
  }
12991
13412
  ),
12992
- /* @__PURE__ */ jsx23(
13413
+ /* @__PURE__ */ jsx24(
12993
13414
  Html,
12994
13415
  {
12995
13416
  ...htmlProps,
@@ -13002,7 +13423,7 @@ function PanoHtml({ children, ...htmlProps }) {
13002
13423
  }
13003
13424
 
13004
13425
  // src/hotspot/hotspot-tooltip.tsx
13005
- import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
13426
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
13006
13427
  var DEFAULT_HOTSPOT_TOOLTIP_OFFSET = 12;
13007
13428
  var DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE = {
13008
13429
  background: "rgba(22, 22, 22, 0.72)",
@@ -13011,7 +13432,10 @@ var DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE = {
13011
13432
  borderRadius: 8,
13012
13433
  shadow: "0 8px 24px rgba(0, 0, 0, 0.35)",
13013
13434
  padding: 8,
13014
- fontSize: 12
13435
+ fontSize: 12,
13436
+ fontFamily: "inherit",
13437
+ lineHeight: 1.4,
13438
+ letterSpacing: "normal"
13015
13439
  };
13016
13440
  var WRAPPER_STYLE = {
13017
13441
  pointerEvents: "none"
@@ -13071,16 +13495,16 @@ function HotspotTooltip({
13071
13495
  offset = DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
13072
13496
  appearance
13073
13497
  }) {
13074
- const [imageFailed, setImageFailed] = useState13(false);
13075
- useEffect28(() => {
13498
+ const [imageFailed, setImageFailed] = useState15(false);
13499
+ useEffect30(() => {
13076
13500
  setImageFailed(false);
13077
13501
  }, [content.image]);
13078
13502
  const showImage = Boolean(content.image) && !imageFailed;
13079
- const resolvedAppearance = useMemo21(
13503
+ const resolvedAppearance = useMemo22(
13080
13504
  () => resolveHotspotTooltipAppearance(appearance),
13081
13505
  [appearance]
13082
13506
  );
13083
- const bubbleStyle = useMemo21(
13507
+ const bubbleStyle = useMemo22(
13084
13508
  () => ({
13085
13509
  background: resolvedAppearance.background,
13086
13510
  border: resolvedAppearance.border,
@@ -13088,6 +13512,9 @@ function HotspotTooltip({
13088
13512
  boxShadow: resolvedAppearance.shadow,
13089
13513
  color: resolvedAppearance.color,
13090
13514
  fontSize: toCssLength(resolvedAppearance.fontSize),
13515
+ fontFamily: resolvedAppearance.fontFamily,
13516
+ lineHeight: resolvedAppearance.lineHeight,
13517
+ letterSpacing: resolvedAppearance.letterSpacing,
13091
13518
  padding: toCssLength(resolvedAppearance.padding),
13092
13519
  transform: tooltipBubbleTransform(
13093
13520
  placement,
@@ -13099,30 +13526,33 @@ function HotspotTooltip({
13099
13526
  if (!showImage && !content.text) {
13100
13527
  return null;
13101
13528
  }
13102
- return /* @__PURE__ */ jsx24(PanoHtml, { pointerEvents: "none", style: WRAPPER_STYLE, zIndexRange: [20, 10], children: /* @__PURE__ */ jsxs15(
13103
- "div",
13104
- {
13105
- className: "pano-hotspot-tooltip",
13106
- role: "tooltip",
13107
- style: bubbleStyle,
13108
- children: [
13109
- showImage ? /* @__PURE__ */ jsx24(
13110
- "img",
13111
- {
13112
- alt: content.imageAlt ?? content.text ?? "",
13113
- className: "pano-hotspot-tooltip-image",
13114
- onError: () => setImageFailed(true),
13115
- src: content.image
13116
- }
13117
- ) : null,
13118
- content.text ? /* @__PURE__ */ jsx24("p", { className: "pano-hotspot-tooltip-text", children: content.text }) : null
13119
- ]
13120
- }
13121
- ) });
13529
+ return /* @__PURE__ */ jsx25(PanoHtml, { pointerEvents: "none", style: WRAPPER_STYLE, zIndexRange: [20, 10], children: /* @__PURE__ */ jsxs16("div", { className: "pano-hotspot-tooltip", role: "tooltip", style: bubbleStyle, children: [
13530
+ showImage ? /* @__PURE__ */ jsx25(
13531
+ "img",
13532
+ {
13533
+ alt: content.imageAlt ?? content.text ?? "",
13534
+ className: "pano-hotspot-tooltip-image",
13535
+ onError: () => setImageFailed(true),
13536
+ src: content.image
13537
+ }
13538
+ ) : null,
13539
+ content.text ? /* @__PURE__ */ jsx25(
13540
+ "p",
13541
+ {
13542
+ className: "pano-hotspot-tooltip-text",
13543
+ style: {
13544
+ lineHeight: "inherit",
13545
+ letterSpacing: "inherit",
13546
+ fontFamily: "inherit"
13547
+ },
13548
+ children: content.text
13549
+ }
13550
+ ) : null
13551
+ ] }) });
13122
13552
  }
13123
13553
 
13124
13554
  // src/hotspot/hotspot-anchor.tsx
13125
- import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
13555
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
13126
13556
  var DEFAULT_FLOATING_DISTANCE = 10;
13127
13557
  var PANORAMA_RADIUS = DEFAULT_PANORAMA_RADIUS;
13128
13558
  var PANORAMA_SURFACE_INSET = 4;
@@ -13130,28 +13560,28 @@ var DEFAULT_REFERENCE_FOV = 75;
13130
13560
  var MIN_ANGULAR_SIZE = 0.01;
13131
13561
  var MAX_ANGULAR_SIZE = 179;
13132
13562
  var DRAG_EPSILON_DEGREES = 1e-3;
13133
- var LOCAL_FORWARD = new Vector313(0, 0, 1);
13134
- var TOOLTIP_WORLD_BOX = new Box33();
13135
- var TOOLTIP_LOCAL_BOX = new Box33();
13136
- var TOOLTIP_CORNER = new Vector313();
13137
- var TOOLTIP_INVERSE = new Matrix42();
13138
- var TOOLTIP_WORLD_CORNER = new Vector313();
13139
- var TOOLTIP_PROJECTED = new Vector313();
13140
- var TOOLTIP_RAY_ORIGIN = new Vector313();
13141
- var TOOLTIP_RAY_FAR = new Vector313();
13142
- var TOOLTIP_RAY_DIRECTION = new Vector313();
13563
+ var LOCAL_FORWARD = new Vector314(0, 0, 1);
13564
+ var TOOLTIP_WORLD_BOX = new Box34();
13565
+ var TOOLTIP_LOCAL_BOX = new Box34();
13566
+ var TOOLTIP_CORNER = new Vector314();
13567
+ var TOOLTIP_INVERSE = new Matrix44();
13568
+ var TOOLTIP_WORLD_CORNER = new Vector314();
13569
+ var TOOLTIP_PROJECTED = new Vector314();
13570
+ var TOOLTIP_RAY_ORIGIN = new Vector314();
13571
+ var TOOLTIP_RAY_FAR = new Vector314();
13572
+ var TOOLTIP_RAY_DIRECTION = new Vector314();
13143
13573
  var TOOLTIP_RAY = new Ray();
13144
13574
  var TOOLTIP_PLANE = new Plane();
13145
- var TOOLTIP_INTERSECTION = new Vector313();
13146
- var TOOLTIP_PLANE_NORMAL = new Vector313();
13147
- var TOOLTIP_PLANE_POINT = new Vector313();
13575
+ var TOOLTIP_INTERSECTION = new Vector314();
13576
+ var TOOLTIP_PLANE_NORMAL = new Vector314();
13577
+ var TOOLTIP_PLANE_POINT = new Vector314();
13148
13578
  var HOTSPOT_MODE_RENDERING = {
13149
13579
  surface: { orientation: "surface", placement: "surface" },
13150
13580
  billboard: { orientation: "billboard", placement: "floating" }
13151
13581
  };
13152
13582
  function angularSizeToWorldSize(size, radius) {
13153
- const safeSize = Number.isFinite(size) ? MathUtils7.clamp(Math.abs(size), MIN_ANGULAR_SIZE, MAX_ANGULAR_SIZE) : MIN_ANGULAR_SIZE;
13154
- return 2 * radius * Math.tan(MathUtils7.degToRad(safeSize) / 2);
13583
+ const safeSize = Number.isFinite(size) ? MathUtils8.clamp(Math.abs(size), MIN_ANGULAR_SIZE, MAX_ANGULAR_SIZE) : MIN_ANGULAR_SIZE;
13584
+ return 2 * radius * Math.tan(MathUtils8.degToRad(safeSize) / 2);
13155
13585
  }
13156
13586
  function resolveScale(scale) {
13157
13587
  return Number.isFinite(scale) && scale > 0 ? scale : 1;
@@ -13164,7 +13594,7 @@ function resolveDistance(placement, distance, width, height) {
13164
13594
  if (!Number.isFinite(distance)) {
13165
13595
  return DEFAULT_FLOATING_DISTANCE;
13166
13596
  }
13167
- return MathUtils7.clamp(Math.abs(distance), 0.1, maximumDistance);
13597
+ return MathUtils8.clamp(Math.abs(distance), 0.1, maximumDistance);
13168
13598
  }
13169
13599
  function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3, rotation, scaleMode, referenceFov, useAngularScale, worldWidth, worldHeight) {
13170
13600
  if (orientation2 === "billboard") {
@@ -13172,8 +13602,8 @@ function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3,
13172
13602
  } else {
13173
13603
  group.quaternion.copy(surfaceQuaternion3);
13174
13604
  }
13175
- group.rotateOnAxis(LOCAL_FORWARD, MathUtils7.degToRad(-rotation));
13176
- const scaleFactor = scaleMode === "fixed" && camera instanceof PerspectiveCamera5 ? fixedScaleFactor(camera.fov, referenceFov) : 1;
13605
+ group.rotateOnAxis(LOCAL_FORWARD, MathUtils8.degToRad(-rotation));
13606
+ const scaleFactor = scaleMode === "fixed" && camera instanceof PerspectiveCamera6 ? fixedScaleFactor(camera.fov, referenceFov) : 1;
13177
13607
  group.scale.set(
13178
13608
  (useAngularScale ? worldWidth : 1) * scaleFactor,
13179
13609
  (useAngularScale ? worldHeight : 1) * scaleFactor,
@@ -13182,39 +13612,39 @@ function applyHotspotTransform(group, camera, orientation2, surfaceQuaternion3,
13182
13612
  group.updateMatrixWorld();
13183
13613
  }
13184
13614
  function maximumHotspotDistance(width, height) {
13185
- const safeWidth = MathUtils7.clamp(
13615
+ const safeWidth = MathUtils8.clamp(
13186
13616
  Math.abs(Number.isFinite(width) ? width : MIN_ANGULAR_SIZE),
13187
13617
  MIN_ANGULAR_SIZE,
13188
13618
  MAX_ANGULAR_SIZE
13189
13619
  );
13190
- const safeHeight = MathUtils7.clamp(
13620
+ const safeHeight = MathUtils8.clamp(
13191
13621
  Math.abs(Number.isFinite(height) ? height : MIN_ANGULAR_SIZE),
13192
13622
  MIN_ANGULAR_SIZE,
13193
13623
  MAX_ANGULAR_SIZE
13194
13624
  );
13195
13625
  const cornerFactor = Math.hypot(
13196
13626
  1,
13197
- Math.tan(MathUtils7.degToRad(safeWidth) / 2),
13198
- Math.tan(MathUtils7.degToRad(safeHeight) / 2)
13627
+ Math.tan(MathUtils8.degToRad(safeWidth) / 2),
13628
+ Math.tan(MathUtils8.degToRad(safeHeight) / 2)
13199
13629
  );
13200
13630
  return (PANORAMA_RADIUS - PANORAMA_SURFACE_INSET) / cornerFactor;
13201
13631
  }
13202
13632
  function fixedScaleFactor(cameraFov, referenceFov) {
13203
- const safeReferenceFov = MathUtils7.clamp(referenceFov, 1, 179);
13204
- return Math.tan(MathUtils7.degToRad(cameraFov) / 2) / Math.tan(MathUtils7.degToRad(safeReferenceFov) / 2);
13633
+ const safeReferenceFov = MathUtils8.clamp(referenceFov, 1, 179);
13634
+ return Math.tan(MathUtils8.degToRad(cameraFov) / 2) / Math.tan(MathUtils8.degToRad(safeReferenceFov) / 2);
13205
13635
  }
13206
13636
  function createSurfaceQuaternion(position) {
13207
13637
  const outward = panoPositionToVector3(position).normalize();
13208
13638
  const inward = outward.clone().negate();
13209
- const pitch = MathUtils7.degToRad(position.pitch);
13210
- const yaw = MathUtils7.degToRad(position.yaw);
13211
- const localUp = new Vector313(
13639
+ const pitch = MathUtils8.degToRad(position.pitch);
13640
+ const yaw = MathUtils8.degToRad(position.yaw);
13641
+ const localUp = new Vector314(
13212
13642
  -Math.sin(pitch) * Math.sin(yaw),
13213
13643
  Math.cos(pitch),
13214
13644
  Math.sin(pitch) * Math.cos(yaw)
13215
13645
  ).normalize();
13216
13646
  const localRight = localUp.clone().cross(inward).normalize();
13217
- const basis = new Matrix42().makeBasis(localRight, localUp, inward);
13647
+ const basis = new Matrix44().makeBasis(localRight, localUp, inward);
13218
13648
  return new Quaternion3().setFromRotationMatrix(basis);
13219
13649
  }
13220
13650
  function makeInteractionEvent(id, position, event) {
@@ -13228,7 +13658,7 @@ function makeInteractionEvent(id, position, event) {
13228
13658
  function angularDistance(a, b) {
13229
13659
  const aVector = panoPositionToVector3(a);
13230
13660
  const bVector = panoPositionToVector3(b);
13231
- return MathUtils7.radToDeg(aVector.angleTo(bVector));
13661
+ return MathUtils8.radToDeg(aVector.angleTo(bVector));
13232
13662
  }
13233
13663
  function isTooltipOpen(trigger, hovered, focused, pinned) {
13234
13664
  switch (trigger) {
@@ -13463,20 +13893,20 @@ function HotspotAnchor({
13463
13893
  const hotspotTargetRegistry = useContext25(HotspotTargetRegistryContext);
13464
13894
  const eventBus = useContext25(PanoEventBusContext);
13465
13895
  const cursorApi = usePanoCursor();
13466
- const { camera, size } = useThree20();
13467
- const groupRef = useRef28(null);
13468
- const tooltipAnchorRef = useRef28(null);
13469
- const dragStateRef = useRef28(null);
13470
- const suppressNextClickRef = useRef28(false);
13471
- const [hovered, setHovered] = useState14(false);
13472
- const [dragging, setDragging] = useState14(false);
13473
- const [pinned, setPinned] = useState14(false);
13474
- const tooltipContent = useMemo22(
13896
+ const { camera, size } = useThree21();
13897
+ const groupRef = useRef30(null);
13898
+ const tooltipAnchorRef = useRef30(null);
13899
+ const dragStateRef = useRef30(null);
13900
+ const suppressNextClickRef = useRef30(false);
13901
+ const [hovered, setHovered] = useState16(false);
13902
+ const [dragging, setDragging] = useState16(false);
13903
+ const [pinned, setPinned] = useState16(false);
13904
+ const tooltipContent = useMemo23(
13475
13905
  () => resolveHotspotTooltipContent(tooltip),
13476
13906
  [tooltip]
13477
13907
  );
13478
13908
  const normalizedPosition = normalizePanoPosition(position);
13479
- useEffect29(() => {
13909
+ useEffect31(() => {
13480
13910
  if (!hotspotTargetRegistry) {
13481
13911
  return;
13482
13912
  }
@@ -13493,17 +13923,17 @@ function HotspotAnchor({
13493
13923
  scaledWidth,
13494
13924
  scaledHeight
13495
13925
  );
13496
- const worldPosition = useMemo22(
13926
+ const worldPosition = useMemo23(
13497
13927
  () => panoPositionToVector3(normalizedPosition, resolvedDistance),
13498
13928
  [normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
13499
13929
  );
13500
- const surfaceQuaternion3 = useMemo22(
13930
+ const surfaceQuaternion3 = useMemo23(
13501
13931
  () => createSurfaceQuaternion(normalizedPosition),
13502
13932
  [normalizedPosition.pitch, normalizedPosition.yaw]
13503
13933
  );
13504
13934
  const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
13505
13935
  const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
13506
- const focusGeometry = useMemo22(() => {
13936
+ const focusGeometry = useMemo23(() => {
13507
13937
  const planeGeometry = new PlaneGeometry5(1, 1);
13508
13938
  const geometry = new EdgesGeometry(planeGeometry);
13509
13939
  planeGeometry.dispose();
@@ -13530,14 +13960,14 @@ function HotspotAnchor({
13530
13960
  });
13531
13961
  const tooltipOpen = visible && tooltipContent !== null && isTooltipOpen(tooltipTrigger, hovered, focused, pinned);
13532
13962
  const cursorClaimId = hotspotCursorClaimId(id);
13533
- useEffect29(
13963
+ useEffect31(
13534
13964
  () => () => {
13535
13965
  dragStateRef.current?.releaseInteractionLock();
13536
13966
  dragStateRef.current = null;
13537
13967
  },
13538
13968
  []
13539
13969
  );
13540
- useEffect29(() => {
13970
+ useEffect31(() => {
13541
13971
  if (!cursorApi) {
13542
13972
  return;
13543
13973
  }
@@ -13559,14 +13989,14 @@ function HotspotAnchor({
13559
13989
  hovered,
13560
13990
  visible
13561
13991
  ]);
13562
- useEffect29(
13992
+ useEffect31(
13563
13993
  () => () => {
13564
13994
  cursorApi?.release(cursorClaimId);
13565
13995
  },
13566
13996
  [cursorApi, cursorClaimId]
13567
13997
  );
13568
- useEffect29(() => () => focusGeometry.dispose(), [focusGeometry]);
13569
- useEffect29(() => {
13998
+ useEffect31(() => () => focusGeometry.dispose(), [focusGeometry]);
13999
+ useEffect31(() => {
13570
14000
  if (tooltipTrigger !== "click") {
13571
14001
  setPinned(false);
13572
14002
  return;
@@ -13575,12 +14005,12 @@ function HotspotAnchor({
13575
14005
  setPinned(false);
13576
14006
  });
13577
14007
  }, [eventBus, tooltipTrigger]);
13578
- useEffect29(() => {
14008
+ useEffect31(() => {
13579
14009
  if (tooltipTrigger !== "hover") {
13580
14010
  setHovered(false);
13581
14011
  }
13582
14012
  }, [tooltipTrigger]);
13583
- useEffect29(() => {
14013
+ useEffect31(() => {
13584
14014
  if (acceptsPointer) {
13585
14015
  return;
13586
14016
  }
@@ -13634,7 +14064,7 @@ function HotspotAnchor({
13634
14064
  worldHeight,
13635
14065
  worldWidth
13636
14066
  ]);
13637
- useFrame13((state) => {
14067
+ useFrame14((state) => {
13638
14068
  const group = groupRef.current;
13639
14069
  if (!group) {
13640
14070
  return;
@@ -13692,7 +14122,7 @@ function HotspotAnchor({
13692
14122
  setHovered(true);
13693
14123
  }
13694
14124
  };
13695
- return /* @__PURE__ */ jsxs16(
14125
+ return /* @__PURE__ */ jsxs17(
13696
14126
  "group",
13697
14127
  {
13698
14128
  ref: groupRef,
@@ -13769,18 +14199,18 @@ function HotspotAnchor({
13769
14199
  } : void 0,
13770
14200
  onPointerUp: acceptsPointer ? (event) => releasePointer(event, true) : void 0,
13771
14201
  children: [
13772
- focused ? focusContent ?? /* @__PURE__ */ jsx25(
14202
+ focused ? focusContent ?? /* @__PURE__ */ jsx26(
13773
14203
  "lineSegments",
13774
14204
  {
13775
14205
  geometry: focusGeometry,
13776
14206
  position: [0, 0, 0.01],
13777
14207
  renderOrder: renderOrder + 1,
13778
14208
  scale: [1.12, 1.12, 1],
13779
- children: /* @__PURE__ */ jsx25("lineBasicMaterial", { color: "#f5fbfc", depthTest: false })
14209
+ children: /* @__PURE__ */ jsx26("lineBasicMaterial", { color: "#f5fbfc", depthTest: false })
13780
14210
  }
13781
14211
  ) : null,
13782
14212
  children,
13783
- tooltipOpen && tooltipContent ? /* @__PURE__ */ jsx25("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ jsx25(
14213
+ tooltipOpen && tooltipContent ? /* @__PURE__ */ jsx26("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ jsx26(
13784
14214
  HotspotTooltip,
13785
14215
  {
13786
14216
  appearance: tooltipAppearance,
@@ -13801,7 +14231,7 @@ import {
13801
14231
  MeshBasicMaterial,
13802
14232
  Vector2 as Vector26
13803
14233
  } from "three";
13804
- import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
14234
+ import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
13805
14235
  var HOTSPOT_ALPHA_CUTOFF = 0.05;
13806
14236
  var SAMPLE_UV = new Vector26();
13807
14237
  var RAYCAST_HITS = [];
@@ -13903,11 +14333,12 @@ function HotspotPlane({
13903
14333
  map,
13904
14334
  opacity
13905
14335
  }) {
13906
- return /* @__PURE__ */ jsxs17("mesh", { raycast: hotspotPlaneRaycast, children: [
13907
- /* @__PURE__ */ jsx26("planeGeometry", { args: [1, 1] }),
13908
- /* @__PURE__ */ jsx26(
13909
- "meshBasicMaterial",
14336
+ return /* @__PURE__ */ jsxs18("mesh", { raycast: hotspotPlaneRaycast, children: [
14337
+ /* @__PURE__ */ jsx27("planeGeometry", { args: [1, 1] }),
14338
+ /* @__PURE__ */ jsx27(
14339
+ PanoBasicMaterial,
13910
14340
  {
14341
+ target: "hotspot",
13911
14342
  alphaTest: HOTSPOT_ALPHA_CUTOFF,
13912
14343
  alphaToCoverage: true,
13913
14344
  map,
@@ -13921,15 +14352,15 @@ function HotspotPlane({
13921
14352
  }
13922
14353
 
13923
14354
  // src/hotspot/image-hotspot.tsx
13924
- import { jsx as jsx27 } from "react/jsx-runtime";
14355
+ import { jsx as jsx28 } from "react/jsx-runtime";
13925
14356
  function useHotspotImageTexture(src, onLoad, onError) {
13926
- const gl = useThree21((state) => state.gl);
13927
- const [texture, setTexture] = useState15(null);
13928
- const onLoadRef = useRef29(onLoad);
13929
- const onErrorRef = useRef29(onError);
14357
+ const gl = useThree22((state) => state.gl);
14358
+ const [texture, setTexture] = useState17(null);
14359
+ const onLoadRef = useRef31(onLoad);
14360
+ const onErrorRef = useRef31(onError);
13930
14361
  onLoadRef.current = onLoad;
13931
14362
  onErrorRef.current = onError;
13932
- useEffect30(() => {
14363
+ useEffect32(() => {
13933
14364
  let active = true;
13934
14365
  setTexture(null);
13935
14366
  if (!src) {
@@ -13967,7 +14398,7 @@ function useHotspotImageTexture(src, onLoad, onError) {
13967
14398
  active = false;
13968
14399
  };
13969
14400
  }, [gl, src]);
13970
- useEffect30(
14401
+ useEffect32(
13971
14402
  () => () => {
13972
14403
  texture?.dispose();
13973
14404
  },
@@ -13985,33 +14416,33 @@ function ImageHotspot({
13985
14416
  ...anchorProps
13986
14417
  }) {
13987
14418
  const texture = useHotspotImageTexture(src, onLoad, onError);
13988
- return /* @__PURE__ */ jsx27(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx27(HotspotPlane, { map: texture, opacity }) });
14419
+ return /* @__PURE__ */ jsx28(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx28(HotspotPlane, { map: texture, opacity }) });
13989
14420
  }
13990
14421
 
13991
14422
  // src/hotspot/polygon-hotspot.tsx
13992
- import { useEffect as useEffect31, useMemo as useMemo23, useRef as useRef31 } from "react";
14423
+ import { useEffect as useEffect33, useMemo as useMemo24, useRef as useRef33 } from "react";
13993
14424
  import {
13994
- BufferGeometry,
14425
+ BufferGeometry as BufferGeometry2,
13995
14426
  DoubleSide as DoubleSide5,
13996
- Float32BufferAttribute,
13997
- Matrix4 as Matrix43,
14427
+ Float32BufferAttribute as Float32BufferAttribute2,
14428
+ Matrix4 as Matrix45,
13998
14429
  Quaternion as Quaternion4,
13999
14430
  ShapeUtils,
14000
14431
  Vector2 as Vector27,
14001
- Vector3 as Vector315
14432
+ Vector3 as Vector316
14002
14433
  } from "three";
14003
14434
 
14004
14435
  // src/hotspot/hotspot-stroke-line.tsx
14005
14436
  import { Line } from "@react-three/drei";
14006
- import { useFrame as useFrame14, useThree as useThree22 } from "@react-three/fiber";
14007
- import { useLayoutEffect as useLayoutEffect5, useRef as useRef30 } from "react";
14437
+ import { useFrame as useFrame15, useThree as useThree23 } from "@react-three/fiber";
14438
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef32 } from "react";
14008
14439
  import {
14009
- MathUtils as MathUtils8,
14010
- PerspectiveCamera as PerspectiveCamera6,
14011
- Vector3 as Vector314
14440
+ MathUtils as MathUtils9,
14441
+ PerspectiveCamera as PerspectiveCamera7,
14442
+ Vector3 as Vector315
14012
14443
  } from "three";
14013
- import { jsx as jsx28 } from "react/jsx-runtime";
14014
- var LINE_WORLD_POSITION = new Vector314();
14444
+ import { jsx as jsx29 } from "react/jsx-runtime";
14445
+ var LINE_WORLD_POSITION = new Vector315();
14015
14446
  function applyScreenSpaceDashScale(object, camera, viewportHeight) {
14016
14447
  const material = object?.material;
14017
14448
  if (!object || typeof material?.dashScale !== "number") {
@@ -14019,8 +14450,8 @@ function applyScreenSpaceDashScale(object, camera, viewportHeight) {
14019
14450
  }
14020
14451
  object.getWorldPosition(LINE_WORLD_POSITION);
14021
14452
  const depth = Math.max(camera.position.distanceTo(LINE_WORLD_POSITION), 1);
14022
- const fov = camera instanceof PerspectiveCamera6 ? camera.fov : 75;
14023
- material.dashScale = viewportHeight / (2 * Math.tan(MathUtils8.degToRad(fov) * 0.5) * depth);
14453
+ const fov = camera instanceof PerspectiveCamera7 ? camera.fov : 75;
14454
+ material.dashScale = viewportHeight / (2 * Math.tan(MathUtils9.degToRad(fov) * 0.5) * depth);
14024
14455
  }
14025
14456
  function resolveDash(dashSize, gapSize) {
14026
14457
  if (dashSize === void 0 || !Number.isFinite(dashSize) || dashSize <= 0) {
@@ -14041,8 +14472,8 @@ function HotspotStrokeLine({
14041
14472
  dashSize,
14042
14473
  gapSize
14043
14474
  }) {
14044
- const lineRef = useRef30(null);
14045
- const { camera, size } = useThree22();
14475
+ const lineRef = useRef32(null);
14476
+ const { camera, size } = useThree23();
14046
14477
  const dash = resolveDash(dashSize, gapSize);
14047
14478
  useLayoutEffect5(() => {
14048
14479
  if (!dash.dashed) {
@@ -14050,13 +14481,13 @@ function HotspotStrokeLine({
14050
14481
  }
14051
14482
  applyScreenSpaceDashScale(lineRef.current, camera, size.height);
14052
14483
  }, [camera, dash.dashed, dash.dashSize, dash.gapSize, size.height]);
14053
- useFrame14((state) => {
14484
+ useFrame15((state) => {
14054
14485
  if (!dash.dashed) {
14055
14486
  return;
14056
14487
  }
14057
14488
  applyScreenSpaceDashScale(lineRef.current, state.camera, state.size.height);
14058
14489
  });
14059
- return /* @__PURE__ */ jsx28(
14490
+ return /* @__PURE__ */ jsx29(
14060
14491
  Line,
14061
14492
  {
14062
14493
  ref: lineRef,
@@ -14078,7 +14509,7 @@ function HotspotStrokeLine({
14078
14509
  }
14079
14510
 
14080
14511
  // src/hotspot/polygon-hotspot.tsx
14081
- import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
14512
+ import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
14082
14513
  var POLYGON_RADIUS = 990;
14083
14514
  var MAX_FILL_SEGMENT_DEGREES = 7;
14084
14515
  function cloneVertices(vertices) {
@@ -14150,7 +14581,7 @@ function polygonContainsDirection(vertices, direction) {
14150
14581
  }
14151
14582
  function fitsHemisphere(vertices) {
14152
14583
  const directions = vertices.map((vertex) => panoPositionToVector3(vertex).normalize());
14153
- const candidates = [new Vector315()];
14584
+ const candidates = [new Vector316()];
14154
14585
  for (const direction of directions) {
14155
14586
  candidates[0].add(direction);
14156
14587
  candidates.push(direction.clone());
@@ -14174,7 +14605,7 @@ function validatePolygonVertices(vertices) {
14174
14605
  if (hasSelfIntersection(unwrapped)) {
14175
14606
  issues.push({ code: "self_intersection", message: "Polygon edges cannot cross." });
14176
14607
  }
14177
- if (polygonContainsDirection(normalized, new Vector315(0, 1, 0)) || polygonContainsDirection(normalized, new Vector315(0, -1, 0))) {
14608
+ if (polygonContainsDirection(normalized, new Vector316(0, 1, 0)) || polygonContainsDirection(normalized, new Vector316(0, -1, 0))) {
14178
14609
  issues.push({ code: "contains_pole", message: "Polygons cannot contain a panorama pole." });
14179
14610
  }
14180
14611
  if (!fitsHemisphere(normalized)) {
@@ -14185,7 +14616,7 @@ function validatePolygonVertices(vertices) {
14185
14616
  function polygonCenter(vertices) {
14186
14617
  const sum = vertices.reduce(
14187
14618
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
14188
- new Vector315()
14619
+ new Vector316()
14189
14620
  );
14190
14621
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
14191
14622
  }
@@ -14194,20 +14625,20 @@ function surfaceQuaternion(position) {
14194
14625
  const inward = outward.clone().negate();
14195
14626
  const pitch = position.pitch * Math.PI / 180;
14196
14627
  const yaw = position.yaw * Math.PI / 180;
14197
- const localUp = new Vector315(
14628
+ const localUp = new Vector316(
14198
14629
  -Math.sin(pitch) * Math.sin(yaw),
14199
14630
  Math.cos(pitch),
14200
14631
  Math.sin(pitch) * Math.cos(yaw)
14201
14632
  ).normalize();
14202
14633
  return new Quaternion4().setFromRotationMatrix(
14203
- new Matrix43().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14634
+ new Matrix45().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14204
14635
  );
14205
14636
  }
14206
14637
  function makeWorldToLocal(center) {
14207
- return new Matrix43().compose(
14638
+ return new Matrix45().compose(
14208
14639
  panoPositionToVector3(center, POLYGON_RADIUS),
14209
14640
  surfaceQuaternion(center),
14210
- new Vector315(1, 1, 1)
14641
+ new Vector316(1, 1, 1)
14211
14642
  ).invert();
14212
14643
  }
14213
14644
  function projectedPoint(a, b, c, weightB, weightC, radius, worldToLocal) {
@@ -14279,7 +14710,7 @@ function makeFillGeometry(vertices, center) {
14279
14710
  steps
14280
14711
  );
14281
14712
  }
14282
- return new BufferGeometry().setAttribute("position", new Float32BufferAttribute(positions, 3)).setIndex(indices);
14713
+ return new BufferGeometry2().setAttribute("position", new Float32BufferAttribute2(positions, 3)).setIndex(indices);
14283
14714
  }
14284
14715
  function makeStrokePositions(vertices, center) {
14285
14716
  const points = [];
@@ -14334,24 +14765,24 @@ function PolygonHotspot({
14334
14765
  onInvalid,
14335
14766
  ...anchorProps
14336
14767
  }) {
14337
- const issues = useMemo23(() => validatePolygonVertices(vertices), [vertices]);
14768
+ const issues = useMemo24(() => validatePolygonVertices(vertices), [vertices]);
14338
14769
  const valid = issues.length === 0;
14339
- const center = useMemo23(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
14340
- const fillGeometry = useMemo23(
14770
+ const center = useMemo24(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
14771
+ const fillGeometry = useMemo24(
14341
14772
  () => valid && center ? makeFillGeometry(vertices, center) : null,
14342
14773
  [center, valid, vertices]
14343
14774
  );
14344
- const strokePoints = useMemo23(
14775
+ const strokePoints = useMemo24(
14345
14776
  () => valid && center ? makeStrokePositions(vertices, center) : null,
14346
14777
  [center, valid, vertices]
14347
14778
  );
14348
14779
  const resolvedStrokeOpacity = Math.max(0, Math.min(strokeOpacity, 1));
14349
- const startVerticesRef = useRef31(null);
14780
+ const startVerticesRef = useRef33(null);
14350
14781
  const issuesKey = issues.map((issue) => issue.code).join(",");
14351
- useEffect31(() => {
14782
+ useEffect33(() => {
14352
14783
  if (!valid) onInvalid?.(issues);
14353
14784
  }, [issues, issuesKey, onInvalid, valid]);
14354
- useEffect31(() => () => fillGeometry?.dispose(), [fillGeometry]);
14785
+ useEffect33(() => () => fillGeometry?.dispose(), [fillGeometry]);
14355
14786
  if (!valid || !center || !fillGeometry || !strokePoints || strokePoints.length < 2) {
14356
14787
  return null;
14357
14788
  }
@@ -14366,7 +14797,7 @@ function PolygonHotspot({
14366
14797
  startPosition: event.startPosition
14367
14798
  });
14368
14799
  };
14369
- return /* @__PURE__ */ jsxs18(
14800
+ return /* @__PURE__ */ jsxs19(
14370
14801
  HotspotAnchor,
14371
14802
  {
14372
14803
  ...anchorProps,
@@ -14381,7 +14812,7 @@ function PolygonHotspot({
14381
14812
  draggable,
14382
14813
  interactive,
14383
14814
  useAngularScale: false,
14384
- focusContent: /* @__PURE__ */ jsx29(
14815
+ focusContent: /* @__PURE__ */ jsx30(
14385
14816
  HotspotStrokeLine,
14386
14817
  {
14387
14818
  color: "#75cbd3",
@@ -14403,7 +14834,7 @@ function PolygonHotspot({
14403
14834
  startVerticesRef.current = null;
14404
14835
  },
14405
14836
  children: [
14406
- /* @__PURE__ */ jsx29("mesh", { geometry: fillGeometry, renderOrder, children: /* @__PURE__ */ jsx29(
14837
+ /* @__PURE__ */ jsx30("mesh", { geometry: fillGeometry, renderOrder, children: /* @__PURE__ */ jsx30(
14407
14838
  "meshBasicMaterial",
14408
14839
  {
14409
14840
  color: fill,
@@ -14413,7 +14844,7 @@ function PolygonHotspot({
14413
14844
  transparent: true
14414
14845
  }
14415
14846
  ) }),
14416
- /* @__PURE__ */ jsx29(
14847
+ /* @__PURE__ */ jsx30(
14417
14848
  HotspotStrokeLine,
14418
14849
  {
14419
14850
  color: stroke,
@@ -14431,14 +14862,14 @@ function PolygonHotspot({
14431
14862
  }
14432
14863
 
14433
14864
  // src/hotspot/polyline-hotspot.tsx
14434
- import { useEffect as useEffect32, useMemo as useMemo24, useRef as useRef32 } from "react";
14865
+ import { useEffect as useEffect34, useMemo as useMemo25, useRef as useRef34 } from "react";
14435
14866
  import {
14436
- MathUtils as MathUtils9,
14437
- Matrix4 as Matrix44,
14867
+ MathUtils as MathUtils10,
14868
+ Matrix4 as Matrix46,
14438
14869
  Quaternion as Quaternion5,
14439
- Vector3 as Vector316
14870
+ Vector3 as Vector317
14440
14871
  } from "three";
14441
- import { jsx as jsx30 } from "react/jsx-runtime";
14872
+ import { jsx as jsx31 } from "react/jsx-runtime";
14442
14873
  var POLYLINE_RADIUS = 990;
14443
14874
  var MAX_SEGMENT_DEGREES = 7;
14444
14875
  function cloneVertices2(vertices) {
@@ -14463,29 +14894,29 @@ function unwrapVertices(vertices) {
14463
14894
  function polylineCenter(vertices) {
14464
14895
  const sum = vertices.reduce(
14465
14896
  (total, vertex) => total.add(panoPositionToVector3(vertex).normalize()),
14466
- new Vector316()
14897
+ new Vector317()
14467
14898
  );
14468
14899
  return sum.lengthSq() > 1e-8 ? vector3ToPanoPosition(sum) : normalizePanoPosition(vertices[0]);
14469
14900
  }
14470
14901
  function surfaceQuaternion2(position) {
14471
14902
  const outward = panoPositionToVector3(position).normalize();
14472
14903
  const inward = outward.clone().negate();
14473
- const pitch = MathUtils9.degToRad(position.pitch);
14474
- const yaw = MathUtils9.degToRad(position.yaw);
14475
- const localUp = new Vector316(
14904
+ const pitch = MathUtils10.degToRad(position.pitch);
14905
+ const yaw = MathUtils10.degToRad(position.yaw);
14906
+ const localUp = new Vector317(
14476
14907
  -Math.sin(pitch) * Math.sin(yaw),
14477
14908
  Math.cos(pitch),
14478
14909
  Math.sin(pitch) * Math.cos(yaw)
14479
14910
  ).normalize();
14480
14911
  return new Quaternion5().setFromRotationMatrix(
14481
- new Matrix44().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14912
+ new Matrix46().makeBasis(localUp.clone().cross(inward).normalize(), localUp, inward)
14482
14913
  );
14483
14914
  }
14484
14915
  function makeWorldToLocal2(center) {
14485
- return new Matrix44().compose(
14916
+ return new Matrix46().compose(
14486
14917
  panoPositionToVector3(center, POLYLINE_RADIUS),
14487
14918
  surfaceQuaternion2(center),
14488
- new Vector316(1, 1, 1)
14919
+ new Vector317(1, 1, 1)
14489
14920
  ).invert();
14490
14921
  }
14491
14922
  function subdivisionSteps(vertices) {
@@ -14500,7 +14931,7 @@ function subdivisionSteps(vertices) {
14500
14931
  }
14501
14932
  return Math.min(
14502
14933
  8,
14503
- Math.max(1, Math.ceil(MathUtils9.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
14934
+ Math.max(1, Math.ceil(MathUtils10.radToDeg(maxAngle) / MAX_SEGMENT_DEGREES))
14504
14935
  );
14505
14936
  }
14506
14937
  function makeLinePositions(vertices, center) {
@@ -14555,16 +14986,16 @@ function PolylineHotspot({
14555
14986
  onInvalid,
14556
14987
  ...anchorProps
14557
14988
  }) {
14558
- const issues = useMemo24(() => validatePolylineVertices(vertices), [vertices]);
14989
+ const issues = useMemo25(() => validatePolylineVertices(vertices), [vertices]);
14559
14990
  const valid = issues.length === 0;
14560
- const center = useMemo24(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
14561
- const linePoints = useMemo24(
14991
+ const center = useMemo25(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
14992
+ const linePoints = useMemo25(
14562
14993
  () => valid && center ? makeLinePositions(vertices, center) : null,
14563
14994
  [center, valid, vertices]
14564
14995
  );
14565
14996
  const opacity = Math.max(0, Math.min(strokeOpacity, 1));
14566
- const startVerticesRef = useRef32(null);
14567
- useEffect32(() => {
14997
+ const startVerticesRef = useRef34(null);
14998
+ useEffect34(() => {
14568
14999
  if (!valid) onInvalid?.(issues);
14569
15000
  }, [issues, onInvalid, valid]);
14570
15001
  if (!valid || !center || !linePoints || linePoints.length < 2) return null;
@@ -14578,7 +15009,7 @@ function PolylineHotspot({
14578
15009
  startPosition: event.startPosition
14579
15010
  });
14580
15011
  };
14581
- return /* @__PURE__ */ jsx30(
15012
+ return /* @__PURE__ */ jsx31(
14582
15013
  HotspotAnchor,
14583
15014
  {
14584
15015
  ...anchorProps,
@@ -14592,7 +15023,7 @@ function PolylineHotspot({
14592
15023
  renderOrder,
14593
15024
  draggable,
14594
15025
  useAngularScale: false,
14595
- focusContent: /* @__PURE__ */ jsx30(
15026
+ focusContent: /* @__PURE__ */ jsx31(
14596
15027
  HotspotStrokeLine,
14597
15028
  {
14598
15029
  color: "#75cbd3",
@@ -14613,7 +15044,7 @@ function PolylineHotspot({
14613
15044
  emitVerticesChange(event, onDragEnd);
14614
15045
  startVerticesRef.current = null;
14615
15046
  },
14616
- children: /* @__PURE__ */ jsx30(
15047
+ children: /* @__PURE__ */ jsx31(
14617
15048
  HotspotStrokeLine,
14618
15049
  {
14619
15050
  color: stroke,
@@ -14630,7 +15061,7 @@ function PolylineHotspot({
14630
15061
  }
14631
15062
 
14632
15063
  // src/hotspot/graphic-hotspot.tsx
14633
- import { useEffect as useEffect33, useRef as useRef33, useState as useState16 } from "react";
15064
+ import { useEffect as useEffect35, useRef as useRef35, useState as useState18 } from "react";
14634
15065
  import {
14635
15066
  CanvasTexture,
14636
15067
  ClampToEdgeWrapping as ClampToEdgeWrapping4,
@@ -14638,7 +15069,7 @@ import {
14638
15069
  LinearMipmapLinearFilter as LinearMipmapLinearFilter4,
14639
15070
  SRGBColorSpace as SRGBColorSpace6
14640
15071
  } from "three";
14641
- import { jsx as jsx31 } from "react/jsx-runtime";
15072
+ import { jsx as jsx32 } from "react/jsx-runtime";
14642
15073
  var GRAPHIC_TEXTURE_SIZE = 512;
14643
15074
  function resolveStrokeWidth(value) {
14644
15075
  return Number.isFinite(value) ? Math.max(value, 0) : 8;
@@ -14827,12 +15258,12 @@ function createGraphicTexture(graphic, aspectWidth, aspectHeight) {
14827
15258
  return texture;
14828
15259
  }
14829
15260
  function useGraphicTexture(graphic, aspectWidth, aspectHeight, onLoad, onError) {
14830
- const [texture, setTexture] = useState16(null);
14831
- const onLoadRef = useRef33(onLoad);
14832
- const onErrorRef = useRef33(onError);
15261
+ const [texture, setTexture] = useState18(null);
15262
+ const onLoadRef = useRef35(onLoad);
15263
+ const onErrorRef = useRef35(onError);
14833
15264
  onLoadRef.current = onLoad;
14834
15265
  onErrorRef.current = onError;
14835
- useEffect33(() => {
15266
+ useEffect35(() => {
14836
15267
  let nextTexture = null;
14837
15268
  try {
14838
15269
  nextTexture = createGraphicTexture(graphic, aspectWidth, aspectHeight);
@@ -14858,20 +15289,20 @@ function CanvasGraphicHotspot({
14858
15289
  ...anchorProps
14859
15290
  }) {
14860
15291
  const texture = useGraphicTexture(graphic, width, height, onLoad, onError);
14861
- return /* @__PURE__ */ jsx31(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx31(HotspotPlane, { map: texture, opacity }) });
15292
+ return /* @__PURE__ */ jsx32(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx32(HotspotPlane, { map: texture, opacity }) });
14862
15293
  }
14863
15294
  function GraphicHotspot(props) {
14864
15295
  const { graphic, ...restProps } = props;
14865
15296
  if (graphic.kind === "svg") {
14866
15297
  const imageProps = restProps;
14867
- return /* @__PURE__ */ jsx31(ImageHotspot, { ...imageProps, src: graphic.src });
15298
+ return /* @__PURE__ */ jsx32(ImageHotspot, { ...imageProps, src: graphic.src });
14868
15299
  }
14869
- return /* @__PURE__ */ jsx31(CanvasGraphicHotspot, { ...restProps, graphic });
15300
+ return /* @__PURE__ */ jsx32(CanvasGraphicHotspot, { ...restProps, graphic });
14870
15301
  }
14871
15302
 
14872
15303
  // src/hotspot/sequence-hotspot.tsx
14873
- import { useFrame as useFrame15, useThree as useThree23 } from "@react-three/fiber";
14874
- import { useEffect as useEffect34, useRef as useRef34, useState as useState17 } from "react";
15304
+ import { useFrame as useFrame16, useThree as useThree24 } from "@react-three/fiber";
15305
+ import { useEffect as useEffect36, useRef as useRef36, useState as useState19 } from "react";
14875
15306
  import {
14876
15307
  ClampToEdgeWrapping as ClampToEdgeWrapping5,
14877
15308
  LinearFilter as LinearFilter8,
@@ -14879,7 +15310,7 @@ import {
14879
15310
  SRGBColorSpace as SRGBColorSpace7,
14880
15311
  TextureLoader as TextureLoader6
14881
15312
  } from "three";
14882
- import { jsx as jsx32 } from "react/jsx-runtime";
15313
+ import { jsx as jsx33 } from "react/jsx-runtime";
14883
15314
  var DEFAULT_SEQUENCE_FPS = 12;
14884
15315
  function resolveFps(value) {
14885
15316
  return Number.isFinite(value) && value > 0 ? value : DEFAULT_SEQUENCE_FPS;
@@ -14893,13 +15324,13 @@ function useSpriteTexture({
14893
15324
  onError,
14894
15325
  onLoadProgress
14895
15326
  }) {
14896
- const gl = useThree23((state) => state.gl);
14897
- const [texture, setTexture] = useState17(null);
14898
- const onErrorRef = useRef34(onError);
14899
- const onLoadProgressRef = useRef34(onLoadProgress);
15327
+ const gl = useThree24((state) => state.gl);
15328
+ const [texture, setTexture] = useState19(null);
15329
+ const onErrorRef = useRef36(onError);
15330
+ const onLoadProgressRef = useRef36(onLoadProgress);
14900
15331
  onErrorRef.current = onError;
14901
15332
  onLoadProgressRef.current = onLoadProgress;
14902
- useEffect34(() => {
15333
+ useEffect36(() => {
14903
15334
  let active = true;
14904
15335
  setTexture(null);
14905
15336
  if (!src) {
@@ -14940,7 +15371,7 @@ function useSpriteTexture({
14940
15371
  active = false;
14941
15372
  };
14942
15373
  }, [gl, id, src]);
14943
- useEffect34(
15374
+ useEffect36(
14944
15375
  () => () => {
14945
15376
  texture?.dispose();
14946
15377
  },
@@ -14969,24 +15400,24 @@ function SequenceHotspot({
14969
15400
  const texture = useSpriteTexture({ id, src, onError, onLoadProgress });
14970
15401
  const frameCount = resolveFrameCount(requestedFrameCount);
14971
15402
  const safeFps = resolveFps(fps);
14972
- const [frameIndex, setFrameIndex] = useState17(0);
14973
- const elapsedRef = useRef34(0);
14974
- const frameIndexRef = useRef34(0);
14975
- const endedRef = useRef34(false);
14976
- const lastPlaybackStateRef = useRef34(null);
14977
- const onFrameChangeRef = useRef34(onFrameChange);
14978
- const onPlaybackStateChangeRef = useRef34(onPlaybackStateChange);
14979
- const onEndedRef = useRef34(onEnded);
15403
+ const [frameIndex, setFrameIndex] = useState19(0);
15404
+ const elapsedRef = useRef36(0);
15405
+ const frameIndexRef = useRef36(0);
15406
+ const endedRef = useRef36(false);
15407
+ const lastPlaybackStateRef = useRef36(null);
15408
+ const onFrameChangeRef = useRef36(onFrameChange);
15409
+ const onPlaybackStateChangeRef = useRef36(onPlaybackStateChange);
15410
+ const onEndedRef = useRef36(onEnded);
14980
15411
  onFrameChangeRef.current = onFrameChange;
14981
15412
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14982
15413
  onEndedRef.current = onEnded;
14983
- useEffect34(() => {
15414
+ useEffect36(() => {
14984
15415
  elapsedRef.current = 0;
14985
15416
  frameIndexRef.current = 0;
14986
15417
  endedRef.current = false;
14987
15418
  setFrameIndex(0);
14988
15419
  }, [frameCount, frameDirection, src]);
14989
- useEffect34(() => {
15420
+ useEffect36(() => {
14990
15421
  if (!texture) {
14991
15422
  return;
14992
15423
  }
@@ -15000,13 +15431,13 @@ function SequenceHotspot({
15000
15431
  );
15001
15432
  texture.needsUpdate = true;
15002
15433
  }, [frameCount, frameDirection, frameIndex, texture]);
15003
- useEffect34(() => {
15434
+ useEffect36(() => {
15004
15435
  if (!texture) {
15005
15436
  return;
15006
15437
  }
15007
15438
  onFrameChangeRef.current?.({ id, index: 0, src });
15008
15439
  }, [id, src, texture]);
15009
- useEffect34(() => {
15440
+ useEffect36(() => {
15010
15441
  if (!texture) {
15011
15442
  return;
15012
15443
  }
@@ -15025,7 +15456,7 @@ function SequenceHotspot({
15025
15456
  onPlaybackStateChangeRef.current?.(nextState);
15026
15457
  }
15027
15458
  }, [playing, texture]);
15028
- useFrame15((_, deltaSeconds) => {
15459
+ useFrame16((_, deltaSeconds) => {
15029
15460
  if (!texture || !playing || endedRef.current) {
15030
15461
  return;
15031
15462
  }
@@ -15055,12 +15486,12 @@ function SequenceHotspot({
15055
15486
  onFrameChangeRef.current?.({ id, index: nextIndex, src });
15056
15487
  }
15057
15488
  });
15058
- return /* @__PURE__ */ jsx32(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx32(HotspotPlane, { map: texture, opacity }) });
15489
+ return /* @__PURE__ */ jsx33(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx33(HotspotPlane, { map: texture, opacity }) });
15059
15490
  }
15060
15491
 
15061
15492
  // src/hotspot/video-hotspot.tsx
15062
- import { useThree as useThree24 } from "@react-three/fiber";
15063
- import { useEffect as useEffect35, useRef as useRef35, useState as useState18 } from "react";
15493
+ import { useThree as useThree25 } from "@react-three/fiber";
15494
+ import { useEffect as useEffect37, useRef as useRef37, useState as useState20 } from "react";
15064
15495
  import {
15065
15496
  ClampToEdgeWrapping as ClampToEdgeWrapping6,
15066
15497
  LinearFilter as LinearFilter9,
@@ -15069,7 +15500,7 @@ import {
15069
15500
  TextureLoader as TextureLoader7,
15070
15501
  VideoTexture as VideoTexture2
15071
15502
  } from "three";
15072
- import { jsx as jsx33 } from "react/jsx-runtime";
15503
+ import { jsx as jsx34 } from "react/jsx-runtime";
15073
15504
  function clampVolume2(volume) {
15074
15505
  if (!Number.isFinite(volume)) {
15075
15506
  return 1;
@@ -15077,11 +15508,11 @@ function clampVolume2(volume) {
15077
15508
  return Math.max(0, Math.min(volume, 1));
15078
15509
  }
15079
15510
  function usePosterTexture2(id, poster, onError) {
15080
- const gl = useThree24((state) => state.gl);
15081
- const [texture, setTexture] = useState18(null);
15082
- const onErrorRef = useRef35(onError);
15511
+ const gl = useThree25((state) => state.gl);
15512
+ const [texture, setTexture] = useState20(null);
15513
+ const onErrorRef = useRef37(onError);
15083
15514
  onErrorRef.current = onError;
15084
- useEffect35(() => {
15515
+ useEffect37(() => {
15085
15516
  let active = true;
15086
15517
  setTexture(null);
15087
15518
  if (!poster) {
@@ -15118,7 +15549,7 @@ function usePosterTexture2(id, poster, onError) {
15118
15549
  active = false;
15119
15550
  };
15120
15551
  }, [gl, id, poster]);
15121
- useEffect35(
15552
+ useEffect37(
15122
15553
  () => () => {
15123
15554
  texture?.dispose();
15124
15555
  },
@@ -15136,14 +15567,14 @@ function useVideoResource({
15136
15567
  poster,
15137
15568
  src
15138
15569
  }) {
15139
- const [resource, setResource] = useState18(null);
15140
- const onEndedRef = useRef35(onEnded);
15141
- const onErrorRef = useRef35(onError);
15142
- const onPlaybackStateChangeRef = useRef35(onPlaybackStateChange);
15570
+ const [resource, setResource] = useState20(null);
15571
+ const onEndedRef = useRef37(onEnded);
15572
+ const onErrorRef = useRef37(onError);
15573
+ const onPlaybackStateChangeRef = useRef37(onPlaybackStateChange);
15143
15574
  onEndedRef.current = onEnded;
15144
15575
  onErrorRef.current = onError;
15145
15576
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
15146
- useEffect35(() => {
15577
+ useEffect37(() => {
15147
15578
  if (!src) {
15148
15579
  setResource(null);
15149
15580
  return;
@@ -15250,11 +15681,11 @@ function VideoHotspot({
15250
15681
  src
15251
15682
  });
15252
15683
  const posterTexture = usePosterTexture2(id, poster, onError);
15253
- const onPlaybackErrorRef = useRef35(onPlaybackError);
15254
- const onPlaybackStateChangeRef = useRef35(onPlaybackStateChange);
15684
+ const onPlaybackErrorRef = useRef37(onPlaybackError);
15685
+ const onPlaybackStateChangeRef = useRef37(onPlaybackStateChange);
15255
15686
  onPlaybackErrorRef.current = onPlaybackError;
15256
15687
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
15257
- useEffect35(() => {
15688
+ useEffect37(() => {
15258
15689
  if (!resource) {
15259
15690
  return;
15260
15691
  }
@@ -15271,7 +15702,7 @@ function VideoHotspot({
15271
15702
  }
15272
15703
  resource.video.preload = preload;
15273
15704
  }, [loop, muted, playsInline, preload, resource, volume]);
15274
- useEffect35(() => {
15705
+ useEffect37(() => {
15275
15706
  if (!resource) {
15276
15707
  return;
15277
15708
  }
@@ -15294,12 +15725,12 @@ function VideoHotspot({
15294
15725
  };
15295
15726
  }, [playing, resource]);
15296
15727
  const activeTexture = resource?.ready ? resource.texture : posterTexture;
15297
- return /* @__PURE__ */ jsx33(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx33(HotspotPlane, { map: activeTexture, opacity }) });
15728
+ return /* @__PURE__ */ jsx34(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx34(HotspotPlane, { map: activeTexture, opacity }) });
15298
15729
  }
15299
15730
 
15300
15731
  // src/hotspot/audio-hotspot.tsx
15301
- import { useFrame as useFrame16, useThree as useThree25 } from "@react-three/fiber";
15302
- import { useEffect as useEffect36, useRef as useRef36, useState as useState19 } from "react";
15732
+ import { useFrame as useFrame17, useThree as useThree26 } from "@react-three/fiber";
15733
+ import { useEffect as useEffect38, useRef as useRef38, useState as useState21 } from "react";
15303
15734
  import {
15304
15735
  CanvasTexture as CanvasTexture2,
15305
15736
  ClampToEdgeWrapping as ClampToEdgeWrapping7,
@@ -15308,7 +15739,7 @@ import {
15308
15739
  SRGBColorSpace as SRGBColorSpace9,
15309
15740
  TextureLoader as TextureLoader8
15310
15741
  } from "three";
15311
- import { jsx as jsx34 } from "react/jsx-runtime";
15742
+ import { jsx as jsx35 } from "react/jsx-runtime";
15312
15743
  var SPEAKER_TEXTURE_SIZE = 512;
15313
15744
  function paintSpeakerBody(context, center) {
15314
15745
  context.fillStyle = "#f5fbfc";
@@ -15372,8 +15803,8 @@ function createSpeakerTexture(state) {
15372
15803
  return texture;
15373
15804
  }
15374
15805
  function useSpeakerTextures(enabled) {
15375
- const [textures, setTextures] = useState19({ playing: null, stopped: null });
15376
- useEffect36(() => {
15806
+ const [textures, setTextures] = useState21({ playing: null, stopped: null });
15807
+ useEffect38(() => {
15377
15808
  if (!enabled) {
15378
15809
  setTextures({ playing: null, stopped: null });
15379
15810
  return;
@@ -15389,11 +15820,11 @@ function useSpeakerTextures(enabled) {
15389
15820
  return textures;
15390
15821
  }
15391
15822
  function useIconTexture(icon, enabled, onError, id) {
15392
- const gl = useThree25((state) => state.gl);
15393
- const [texture, setTexture] = useState19(null);
15394
- const onErrorRef = useRef36(onError);
15823
+ const gl = useThree26((state) => state.gl);
15824
+ const [texture, setTexture] = useState21(null);
15825
+ const onErrorRef = useRef38(onError);
15395
15826
  onErrorRef.current = onError;
15396
- useEffect36(() => {
15827
+ useEffect38(() => {
15397
15828
  let active = true;
15398
15829
  setTexture(null);
15399
15830
  if (!enabled || !icon) {
@@ -15430,7 +15861,7 @@ function useIconTexture(icon, enabled, onError, id) {
15430
15861
  active = false;
15431
15862
  };
15432
15863
  }, [enabled, gl, icon, id]);
15433
- useEffect36(
15864
+ useEffect38(
15434
15865
  () => () => {
15435
15866
  texture?.dispose();
15436
15867
  },
@@ -15449,13 +15880,13 @@ function useAudioHowl({
15449
15880
  playing,
15450
15881
  src
15451
15882
  }) {
15452
- const [howl, setHowl] = useState19(null);
15453
- const playingRef = useRef36(playing);
15454
- const loopRef = useRef36(loop ?? false);
15455
- const onEndedRef = useRef36(onEnded);
15456
- const onErrorRef = useRef36(onError);
15457
- const onPlaybackErrorRef = useRef36(onPlaybackError);
15458
- const onPlaybackStateChangeRef = useRef36(onPlaybackStateChange);
15883
+ const [howl, setHowl] = useState21(null);
15884
+ const playingRef = useRef38(playing);
15885
+ const loopRef = useRef38(loop ?? false);
15886
+ const onEndedRef = useRef38(onEnded);
15887
+ const onErrorRef = useRef38(onError);
15888
+ const onPlaybackErrorRef = useRef38(onPlaybackError);
15889
+ const onPlaybackStateChangeRef = useRef38(onPlaybackStateChange);
15459
15890
  playingRef.current = playing;
15460
15891
  loopRef.current = loop ?? false;
15461
15892
  onEndedRef.current = onEnded;
@@ -15463,7 +15894,7 @@ function useAudioHowl({
15463
15894
  onPlaybackErrorRef.current = onPlaybackError;
15464
15895
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
15465
15896
  const sourcesKey = sourceList(src).join("\0");
15466
- useEffect36(() => {
15897
+ useEffect38(() => {
15467
15898
  const sources = sourcesKey.length > 0 ? sourcesKey.split("\0") : [];
15468
15899
  if (sources.length === 0) {
15469
15900
  setHowl(null);
@@ -15500,7 +15931,7 @@ function useAudioHowl({
15500
15931
  handle.dispose();
15501
15932
  };
15502
15933
  }, [crossOrigin, id, sourcesKey]);
15503
- useEffect36(() => {
15934
+ useEffect38(() => {
15504
15935
  howl?.loop(loop ?? false);
15505
15936
  }, [howl, loop]);
15506
15937
  return howl;
@@ -15528,7 +15959,7 @@ function AudioHotspot({
15528
15959
  ...anchorProps
15529
15960
  }) {
15530
15961
  usePanoMediaActivationIntent(playing);
15531
- const camera = useThree25((state) => state.camera);
15962
+ const camera = useThree26((state) => state.camera);
15532
15963
  const howl = useAudioHowl({
15533
15964
  crossOrigin,
15534
15965
  id,
@@ -15544,15 +15975,15 @@ function AudioHotspot({
15544
15975
  const stoppedIconTexture = useIconTexture(icon, marker, onError, id);
15545
15976
  const playingIconTexture = useIconTexture(playingIcon, marker, onError, id);
15546
15977
  const markerTexture = playing ? playingIconTexture ?? stoppedIconTexture ?? speakerTextures.playing : stoppedIconTexture ?? speakerTextures.stopped;
15547
- const playingRef = useRef36(playing);
15978
+ const playingRef = useRef38(playing);
15548
15979
  playingRef.current = playing;
15549
- const mutedRef = useRef36(muted);
15550
- const volumeRef = useRef36(volume);
15551
- const rangeRef = useRef36(range);
15980
+ const mutedRef = useRef38(muted);
15981
+ const volumeRef = useRef38(volume);
15982
+ const rangeRef = useRef38(range);
15552
15983
  mutedRef.current = muted;
15553
15984
  volumeRef.current = volume;
15554
15985
  rangeRef.current = range;
15555
- useEffect36(() => {
15986
+ useEffect38(() => {
15556
15987
  if (!howl) {
15557
15988
  return;
15558
15989
  }
@@ -15562,7 +15993,7 @@ function AudioHotspot({
15562
15993
  }
15563
15994
  howl.play();
15564
15995
  }, [howl, playing]);
15565
- useEffect36(() => {
15996
+ useEffect38(() => {
15566
15997
  if (!howl || !pauseWhenHidden) {
15567
15998
  return;
15568
15999
  }
@@ -15580,7 +16011,7 @@ function AudioHotspot({
15580
16011
  document.removeEventListener("visibilitychange", handleVisibility);
15581
16012
  };
15582
16013
  }, [howl, pauseWhenHidden]);
15583
- useFrame16(() => {
16014
+ useFrame17(() => {
15584
16015
  if (!howl) {
15585
16016
  return;
15586
16017
  }
@@ -15593,11 +16024,11 @@ function AudioHotspot({
15593
16024
  howl.mute(mutedRef.current);
15594
16025
  howl.volume(clampAudioVolume(volumeRef.current) * spatial.gain);
15595
16026
  });
15596
- return /* @__PURE__ */ jsx34(HotspotAnchor, { ...anchorProps, id, height, width, children: marker ? /* @__PURE__ */ jsx34(HotspotPlane, { map: markerTexture, opacity }) : null });
16027
+ return /* @__PURE__ */ jsx35(HotspotAnchor, { ...anchorProps, id, height, width, children: marker ? /* @__PURE__ */ jsx35(HotspotPlane, { map: markerTexture, opacity }) : null });
15597
16028
  }
15598
16029
 
15599
16030
  // src/hotspot/text-hotspot.tsx
15600
- import { useEffect as useEffect37, useRef as useRef37, useState as useState20 } from "react";
16031
+ import { useEffect as useEffect39, useRef as useRef39, useState as useState22 } from "react";
15601
16032
  import {
15602
16033
  CanvasTexture as CanvasTexture3,
15603
16034
  ClampToEdgeWrapping as ClampToEdgeWrapping8,
@@ -15605,7 +16036,7 @@ import {
15605
16036
  LinearMipmapLinearFilter as LinearMipmapLinearFilter8,
15606
16037
  SRGBColorSpace as SRGBColorSpace10
15607
16038
  } from "three";
15608
- import { jsx as jsx35 } from "react/jsx-runtime";
16039
+ import { jsx as jsx36 } from "react/jsx-runtime";
15609
16040
  var TEXT_TEXTURE_LONG_SIDE = 1024;
15610
16041
  var DEFAULT_FONT_FAMILY = "system-ui, sans-serif";
15611
16042
  var DEFAULT_FONT_SIZE = 96;
@@ -15825,12 +16256,12 @@ function createTextTexture(text, width, height, style) {
15825
16256
  return texture;
15826
16257
  }
15827
16258
  function useTextTexture(text, width, height, style, onLoad, onError) {
15828
- const [texture, setTexture] = useState20(null);
15829
- const onLoadRef = useRef37(onLoad);
15830
- const onErrorRef = useRef37(onError);
16259
+ const [texture, setTexture] = useState22(null);
16260
+ const onLoadRef = useRef39(onLoad);
16261
+ const onErrorRef = useRef39(onError);
15831
16262
  onLoadRef.current = onLoad;
15832
16263
  onErrorRef.current = onError;
15833
- useEffect37(() => {
16264
+ useEffect39(() => {
15834
16265
  let cancelled = false;
15835
16266
  let nextTexture = null;
15836
16267
  const run = async () => {
@@ -15923,13 +16354,13 @@ function TextHotspot({
15923
16354
  whiteSpace
15924
16355
  };
15925
16356
  const texture = useTextTexture(text, width, height, style, onLoad, onError);
15926
- return /* @__PURE__ */ jsx35(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx35(HotspotPlane, { map: texture, opacity }) });
16357
+ return /* @__PURE__ */ jsx36(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx36(HotspotPlane, { map: texture, opacity }) });
15927
16358
  }
15928
16359
 
15929
16360
  // src/hotspot/iframe-hotspot.tsx
15930
16361
  import { Html as Html2 } from "@react-three/drei";
15931
16362
  import { DoubleSide as DoubleSide6 } from "three";
15932
- import { jsx as jsx36, jsxs as jsxs19 } from "react/jsx-runtime";
16363
+ import { jsx as jsx37, jsxs as jsxs20 } from "react/jsx-runtime";
15933
16364
  var IFRAME_LONG_SIDE = 800;
15934
16365
  var CSS3D_DISTANCE_FACTOR = 400;
15935
16366
  var DEFAULT_SANDBOX = "allow-scripts allow-popups allow-forms";
@@ -15986,7 +16417,7 @@ function IframeHotspot({
15986
16417
  interactive,
15987
16418
  pointerEvents
15988
16419
  );
15989
- return /* @__PURE__ */ jsxs19(
16420
+ return /* @__PURE__ */ jsxs20(
15990
16421
  HotspotAnchor,
15991
16422
  {
15992
16423
  ...anchorProps,
@@ -15995,9 +16426,9 @@ function IframeHotspot({
15995
16426
  pointerEvents,
15996
16427
  width,
15997
16428
  children: [
15998
- /* @__PURE__ */ jsxs19("mesh", { children: [
15999
- /* @__PURE__ */ jsx36("planeGeometry", { args: [1, 1] }),
16000
- /* @__PURE__ */ jsx36(
16429
+ /* @__PURE__ */ jsxs20("mesh", { children: [
16430
+ /* @__PURE__ */ jsx37("planeGeometry", { args: [1, 1] }),
16431
+ /* @__PURE__ */ jsx37(
16001
16432
  "meshBasicMaterial",
16002
16433
  {
16003
16434
  depthWrite: false,
@@ -16008,7 +16439,7 @@ function IframeHotspot({
16008
16439
  }
16009
16440
  )
16010
16441
  ] }),
16011
- /* @__PURE__ */ jsx36(
16442
+ /* @__PURE__ */ jsx37(
16012
16443
  Html2,
16013
16444
  {
16014
16445
  distanceFactor: CSS3D_DISTANCE_FACTOR,
@@ -16023,7 +16454,7 @@ function IframeHotspot({
16023
16454
  },
16024
16455
  transform: true,
16025
16456
  zIndexRange: [10, 0],
16026
- children: /* @__PURE__ */ jsx36(
16457
+ children: /* @__PURE__ */ jsx37(
16027
16458
  "iframe",
16028
16459
  {
16029
16460
  allow,
@@ -16075,6 +16506,7 @@ export {
16075
16506
  Scenes,
16076
16507
  SequenceHotspot,
16077
16508
  Sphere,
16509
+ SphereTile,
16078
16510
  TextHotspot,
16079
16511
  Tile,
16080
16512
  TouchControls,