@ericchen1990/pano-view 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -8
- package/dist/index.cjs +2383 -645
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +389 -9
- package/dist/index.d.ts +389 -9
- package/dist/index.mjs +2400 -667
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,12 @@ __export(index_exports, {
|
|
|
24
24
|
AutoRotate: () => AutoRotate,
|
|
25
25
|
GraphicHotspot: () => GraphicHotspot,
|
|
26
26
|
ImageHotspot: () => ImageHotspot,
|
|
27
|
+
KeyboardControls: () => KeyboardControls,
|
|
27
28
|
MAX_HOTSPOT_PITCH: () => MAX_HOTSPOT_PITCH,
|
|
29
|
+
MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY: () => MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY,
|
|
30
|
+
MouseControls: () => MouseControls,
|
|
31
|
+
PanoContextMenu: () => PanoContextMenu,
|
|
32
|
+
PanoEvents: () => PanoEvents,
|
|
28
33
|
PanoView: () => PanoView,
|
|
29
34
|
PanoramaScenes: () => PanoramaScenes,
|
|
30
35
|
PolygonHotspot: () => PolygonHotspot,
|
|
@@ -32,12 +37,19 @@ __export(index_exports, {
|
|
|
32
37
|
SequenceHotspot: () => SequenceHotspot,
|
|
33
38
|
Sphere: () => Sphere,
|
|
34
39
|
Tile: () => Tile,
|
|
40
|
+
TouchControls: () => TouchControls,
|
|
35
41
|
VideoHotspot: () => VideoHotspot,
|
|
36
42
|
clampPanoPitch: () => clampPanoPitch,
|
|
43
|
+
composePanoContextMenuItems: () => composePanoContextMenuItems,
|
|
44
|
+
createDefaultPanoContextMenuItems: () => createDefaultPanoContextMenuItems,
|
|
45
|
+
createPanoContextMenuPresets: () => createPanoContextMenuPresets,
|
|
46
|
+
cycleSceneId: () => cycleSceneId,
|
|
37
47
|
normalizePanoPosition: () => normalizePanoPosition,
|
|
38
48
|
normalizePanoYaw: () => normalizePanoYaw,
|
|
39
49
|
panoPositionToVector3: () => panoPositionToVector3,
|
|
50
|
+
resolvePanoContextMenuEntries: () => resolvePanoContextMenuEntries,
|
|
40
51
|
unwrapPolygonVertices: () => unwrapPolygonVertices,
|
|
52
|
+
usePanoEvents: () => usePanoEvents,
|
|
41
53
|
validatePolygonVertices: () => validatePolygonVertices,
|
|
42
54
|
validatePolylineVertices: () => validatePolylineVertices,
|
|
43
55
|
vector3ToPanoPosition: () => vector3ToPanoPosition
|
|
@@ -45,21 +57,59 @@ __export(index_exports, {
|
|
|
45
57
|
module.exports = __toCommonJS(index_exports);
|
|
46
58
|
|
|
47
59
|
// src/pano-view.tsx
|
|
48
|
-
var
|
|
49
|
-
var
|
|
60
|
+
var import_fiber8 = require("@react-three/fiber");
|
|
61
|
+
var import_react11 = require("react");
|
|
50
62
|
|
|
51
|
-
// src/
|
|
52
|
-
var
|
|
63
|
+
// src/auto-rotate.tsx
|
|
64
|
+
var import_fiber2 = require("@react-three/fiber");
|
|
65
|
+
var import_react3 = require("react");
|
|
66
|
+
|
|
67
|
+
// src/pano-event-bus.ts
|
|
53
68
|
var import_react = require("react");
|
|
69
|
+
function createPanoEventBus() {
|
|
70
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
71
|
+
return {
|
|
72
|
+
subscribe(type, listener) {
|
|
73
|
+
let set = listeners.get(type);
|
|
74
|
+
if (!set) {
|
|
75
|
+
set = /* @__PURE__ */ new Set();
|
|
76
|
+
listeners.set(type, set);
|
|
77
|
+
}
|
|
78
|
+
set.add(listener);
|
|
79
|
+
return () => {
|
|
80
|
+
set?.delete(listener);
|
|
81
|
+
if (set && set.size === 0) {
|
|
82
|
+
listeners.delete(type);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
emit(type, payload) {
|
|
87
|
+
const set = listeners.get(type);
|
|
88
|
+
if (!set || set.size === 0) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
for (const listener of [...set]) {
|
|
92
|
+
listener(payload);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var PanoEventBusContext = (0, import_react.createContext)(null);
|
|
98
|
+
|
|
99
|
+
// src/panorama-view-runtime.tsx
|
|
100
|
+
var import_fiber = require("@react-three/fiber");
|
|
101
|
+
var import_react2 = require("react");
|
|
54
102
|
var import_three = require("three");
|
|
55
103
|
var MAX_PITCH = 90;
|
|
104
|
+
var BOUNCE_OVERSHOOT_PITCH = 12;
|
|
105
|
+
var BOUNCE_OVERSHOOT_FOV_RATIO = 0.15;
|
|
56
106
|
var INERTIA_ROTATION_DAMPING = 8;
|
|
57
107
|
var INERTIA_ZOOM_DAMPING = 12;
|
|
58
108
|
var DEFAULT_ROTATE_DAMPING = 14;
|
|
59
109
|
var DEFAULT_ZOOM_DAMPING = 16;
|
|
60
|
-
var
|
|
61
|
-
var MIN_ZOOM_VELOCITY = 0.01;
|
|
110
|
+
var DEFAULT_FRICTION_STOP = 0.01;
|
|
62
111
|
var VIEW_SETTLE_EPSILON = 1e-3;
|
|
112
|
+
var PanoramaViewContext = (0, import_react2.createContext)(null);
|
|
63
113
|
function clamp(value, min, max) {
|
|
64
114
|
return Math.max(min, Math.min(max, value));
|
|
65
115
|
}
|
|
@@ -75,54 +125,104 @@ function dampingFactor(damping, deltaSeconds) {
|
|
|
75
125
|
function resolveDamping(value, fallback) {
|
|
76
126
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
77
127
|
}
|
|
78
|
-
function
|
|
79
|
-
return
|
|
128
|
+
function resolveFrictionStop(value) {
|
|
129
|
+
return Number.isFinite(value) && value >= 0 ? value : DEFAULT_FRICTION_STOP;
|
|
80
130
|
}
|
|
81
131
|
function viewsEqual(a, b) {
|
|
82
132
|
return a.yaw === b.yaw && a.pitch === b.pitch && a.fov === b.fov;
|
|
83
133
|
}
|
|
84
|
-
var
|
|
85
|
-
const { camera
|
|
86
|
-
const viewRef = (0,
|
|
87
|
-
const targetViewRef = (0,
|
|
88
|
-
const initialViewRef = (0,
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
134
|
+
var PanoramaViewRuntime = (0, import_react2.forwardRef)(function PanoramaViewRuntime2({ initialView, minFov, maxFov, options, eventBus }, ref) {
|
|
135
|
+
const { camera } = (0, import_fiber.useThree)();
|
|
136
|
+
const viewRef = (0, import_react2.useRef)({ ...initialView });
|
|
137
|
+
const targetViewRef = (0, import_react2.useRef)({ ...initialView });
|
|
138
|
+
const initialViewRef = (0, import_react2.useRef)({ ...initialView });
|
|
139
|
+
const yawVelocityRef = (0, import_react2.useRef)(0);
|
|
140
|
+
const pitchVelocityRef = (0, import_react2.useRef)(0);
|
|
141
|
+
const zoomVelocityRef = (0, import_react2.useRef)(0);
|
|
142
|
+
const dirtyRef = (0, import_react2.useRef)(true);
|
|
143
|
+
const settledRef = (0, import_react2.useRef)(false);
|
|
144
|
+
const interactingRef = (0, import_react2.useRef)(false);
|
|
145
|
+
const mouseActiveRef = (0, import_react2.useRef)(false);
|
|
146
|
+
const touchActiveRef = (0, import_react2.useRef)(false);
|
|
147
|
+
const interactionLockCountRef = (0, import_react2.useRef)(0);
|
|
148
|
+
const keyboardActiveRef = (0, import_react2.useRef)(false);
|
|
149
|
+
const lastVelocitySampleAtRef = (0, import_react2.useRef)(null);
|
|
150
|
+
const optionsRef = (0, import_react2.useRef)(options);
|
|
151
|
+
const eventBusRef = (0, import_react2.useRef)(eventBus);
|
|
152
|
+
const lastEmittedViewRef = (0, import_react2.useRef)(null);
|
|
153
|
+
const eulerRef = (0, import_react2.useRef)(new import_three.Euler(0, 0, 0, "YXZ"));
|
|
154
|
+
const minFovRef = (0, import_react2.useRef)(minFov);
|
|
155
|
+
const maxFovRef = (0, import_react2.useRef)(maxFov);
|
|
101
156
|
optionsRef.current = options;
|
|
102
|
-
|
|
103
|
-
|
|
157
|
+
eventBusRef.current = eventBus;
|
|
158
|
+
minFovRef.current = minFov;
|
|
159
|
+
maxFovRef.current = maxFov;
|
|
160
|
+
const hasInertia = () => yawVelocityRef.current !== 0 || pitchVelocityRef.current !== 0 || zoomVelocityRef.current !== 0;
|
|
161
|
+
const isUserInteracting = () => mouseActiveRef.current || touchActiveRef.current || keyboardActiveRef.current || interactionLockCountRef.current > 0 || hasInertia();
|
|
162
|
+
const syncInteracting = () => {
|
|
163
|
+
interactingRef.current = mouseActiveRef.current || touchActiveRef.current;
|
|
164
|
+
};
|
|
165
|
+
const hardConstrainView = (view, current = targetViewRef.current) => {
|
|
104
166
|
return {
|
|
105
167
|
yaw: normalizeYaw(view.yaw ?? current.yaw),
|
|
106
168
|
pitch: clamp(view.pitch ?? current.pitch, -MAX_PITCH, MAX_PITCH),
|
|
107
|
-
fov: clamp(
|
|
169
|
+
fov: clamp(
|
|
170
|
+
view.fov ?? current.fov,
|
|
171
|
+
minFovRef.current,
|
|
172
|
+
maxFovRef.current
|
|
173
|
+
)
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const softConstrainView = (view, current = targetViewRef.current) => {
|
|
177
|
+
const bouncing = optionsRef.current.bouncingLimits === true;
|
|
178
|
+
if (!bouncing || !interactingRef.current) {
|
|
179
|
+
return hardConstrainView(view, current);
|
|
180
|
+
}
|
|
181
|
+
const fovRange = Math.max(maxFovRef.current - minFovRef.current, 1);
|
|
182
|
+
const fovOvershoot = fovRange * BOUNCE_OVERSHOOT_FOV_RATIO;
|
|
183
|
+
return {
|
|
184
|
+
yaw: normalizeYaw(view.yaw ?? current.yaw),
|
|
185
|
+
pitch: clamp(
|
|
186
|
+
view.pitch ?? current.pitch,
|
|
187
|
+
-MAX_PITCH - BOUNCE_OVERSHOOT_PITCH,
|
|
188
|
+
MAX_PITCH + BOUNCE_OVERSHOOT_PITCH
|
|
189
|
+
),
|
|
190
|
+
fov: clamp(
|
|
191
|
+
view.fov ?? current.fov,
|
|
192
|
+
minFovRef.current - fovOvershoot,
|
|
193
|
+
maxFovRef.current + fovOvershoot
|
|
194
|
+
)
|
|
108
195
|
};
|
|
109
196
|
};
|
|
110
197
|
const stopVelocity = () => {
|
|
111
198
|
yawVelocityRef.current = 0;
|
|
112
199
|
pitchVelocityRef.current = 0;
|
|
113
200
|
zoomVelocityRef.current = 0;
|
|
201
|
+
lastVelocitySampleAtRef.current = null;
|
|
202
|
+
};
|
|
203
|
+
const markDirty = () => {
|
|
204
|
+
dirtyRef.current = true;
|
|
205
|
+
settledRef.current = false;
|
|
206
|
+
};
|
|
207
|
+
const snapTargetToHardLimits = () => {
|
|
208
|
+
const next = hardConstrainView(targetViewRef.current);
|
|
209
|
+
if (next.yaw !== targetViewRef.current.yaw || next.pitch !== targetViewRef.current.pitch || next.fov !== targetViewRef.current.fov) {
|
|
210
|
+
targetViewRef.current = next;
|
|
211
|
+
markDirty();
|
|
212
|
+
}
|
|
114
213
|
};
|
|
115
214
|
const acquireInteractionLock = () => {
|
|
116
215
|
interactionLockCountRef.current += 1;
|
|
117
216
|
stopVelocity();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
217
|
+
if (mouseActiveRef.current) {
|
|
218
|
+
mouseActiveRef.current = false;
|
|
219
|
+
eventBusRef.current.emit("viewinteractionend", { source: "mouse" });
|
|
220
|
+
}
|
|
221
|
+
if (touchActiveRef.current) {
|
|
222
|
+
touchActiveRef.current = false;
|
|
223
|
+
eventBusRef.current.emit("viewinteractionend", { source: "touch" });
|
|
124
224
|
}
|
|
125
|
-
|
|
225
|
+
syncInteracting();
|
|
126
226
|
let released = false;
|
|
127
227
|
return () => {
|
|
128
228
|
if (released) {
|
|
@@ -136,233 +236,145 @@ var PanoramaControls = (0, import_react.forwardRef)(function PanoramaControls2({
|
|
|
136
236
|
};
|
|
137
237
|
};
|
|
138
238
|
const setView = (view, setOptions) => {
|
|
139
|
-
const nextView =
|
|
239
|
+
const nextView = hardConstrainView(view);
|
|
140
240
|
targetViewRef.current = nextView;
|
|
141
241
|
viewRef.current = nextView;
|
|
142
242
|
if (setOptions?.immediate !== false) {
|
|
143
243
|
stopVelocity();
|
|
144
244
|
}
|
|
145
|
-
|
|
245
|
+
markDirty();
|
|
246
|
+
};
|
|
247
|
+
const applyViewDelta = (delta, applyOptions) => {
|
|
248
|
+
if (interactionLockCountRef.current > 0) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const current = targetViewRef.current;
|
|
252
|
+
const nextYaw = delta.yaw !== void 0 && Number.isFinite(delta.yaw) ? current.yaw + delta.yaw : current.yaw;
|
|
253
|
+
const nextPitch = delta.pitch !== void 0 && Number.isFinite(delta.pitch) ? current.pitch + delta.pitch : current.pitch;
|
|
254
|
+
const nextFov = delta.fov !== void 0 && Number.isFinite(delta.fov) ? current.fov + delta.fov : current.fov;
|
|
255
|
+
targetViewRef.current = softConstrainView({
|
|
256
|
+
yaw: nextYaw,
|
|
257
|
+
pitch: nextPitch,
|
|
258
|
+
fov: nextFov
|
|
259
|
+
});
|
|
260
|
+
markDirty();
|
|
261
|
+
if (applyOptions?.recordVelocity) {
|
|
262
|
+
const now = performance.now();
|
|
263
|
+
const previousAt = lastVelocitySampleAtRef.current;
|
|
264
|
+
const elapsed = previousAt === null ? 1 / 60 : Math.max((now - previousAt) / 1e3, 1 / 120);
|
|
265
|
+
lastVelocitySampleAtRef.current = now;
|
|
266
|
+
if (delta.yaw !== void 0 && Number.isFinite(delta.yaw)) {
|
|
267
|
+
yawVelocityRef.current = delta.yaw / elapsed;
|
|
268
|
+
}
|
|
269
|
+
if (delta.pitch !== void 0 && Number.isFinite(delta.pitch)) {
|
|
270
|
+
pitchVelocityRef.current = delta.pitch / elapsed;
|
|
271
|
+
}
|
|
272
|
+
if (delta.fov !== void 0 && Number.isFinite(delta.fov)) {
|
|
273
|
+
zoomVelocityRef.current = clamp(delta.fov / elapsed, -180, 180);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
146
277
|
};
|
|
147
|
-
(0,
|
|
278
|
+
(0, import_react2.useImperativeHandle)(
|
|
148
279
|
ref,
|
|
149
280
|
() => ({
|
|
150
281
|
getView: () => ({ ...viewRef.current }),
|
|
282
|
+
getTargetView: () => ({ ...targetViewRef.current }),
|
|
151
283
|
setView,
|
|
152
284
|
reset: () => setView(initialViewRef.current),
|
|
285
|
+
applyViewDelta,
|
|
286
|
+
setKeyboardActive: (active) => {
|
|
287
|
+
const wasActive = keyboardActiveRef.current;
|
|
288
|
+
keyboardActiveRef.current = active;
|
|
289
|
+
if (active && !wasActive) {
|
|
290
|
+
eventBusRef.current.emit("viewinteractionstart", {
|
|
291
|
+
source: "keyboard"
|
|
292
|
+
});
|
|
293
|
+
} else if (!active && wasActive) {
|
|
294
|
+
eventBusRef.current.emit("viewinteractionend", {
|
|
295
|
+
source: "keyboard"
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
setPointerActive: (source, active) => {
|
|
300
|
+
const wasActive = source === "mouse" ? mouseActiveRef.current : touchActiveRef.current;
|
|
301
|
+
if (source === "mouse") {
|
|
302
|
+
mouseActiveRef.current = active;
|
|
303
|
+
} else {
|
|
304
|
+
touchActiveRef.current = active;
|
|
305
|
+
}
|
|
306
|
+
syncInteracting();
|
|
307
|
+
if (active && !wasActive) {
|
|
308
|
+
eventBusRef.current.emit("viewinteractionstart", { source });
|
|
309
|
+
} else if (!active && wasActive) {
|
|
310
|
+
eventBusRef.current.emit("viewinteractionend", { source });
|
|
311
|
+
}
|
|
312
|
+
if (active) {
|
|
313
|
+
stopVelocity();
|
|
314
|
+
} else if (!mouseActiveRef.current && !touchActiveRef.current) {
|
|
315
|
+
lastVelocitySampleAtRef.current = null;
|
|
316
|
+
snapTargetToHardLimits();
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
isInteractionLocked: () => interactionLockCountRef.current > 0,
|
|
320
|
+
isUserInteracting,
|
|
153
321
|
acquireInteractionLock,
|
|
154
322
|
applyAutoRotation: (yawDelta) => {
|
|
155
323
|
const inertiaFinished = yawVelocityRef.current === 0 && pitchVelocityRef.current === 0 && zoomVelocityRef.current === 0;
|
|
156
|
-
if (!Number.isFinite(yawDelta) || interactionLockCountRef.current > 0 || interactingRef.current || !inertiaFinished) {
|
|
324
|
+
if (!Number.isFinite(yawDelta) || interactionLockCountRef.current > 0 || interactingRef.current || keyboardActiveRef.current || !inertiaFinished) {
|
|
157
325
|
return false;
|
|
158
326
|
}
|
|
159
327
|
targetViewRef.current.yaw += yawDelta;
|
|
160
|
-
|
|
328
|
+
markDirty();
|
|
161
329
|
return true;
|
|
162
330
|
}
|
|
163
331
|
}),
|
|
164
|
-
[
|
|
332
|
+
[]
|
|
165
333
|
);
|
|
166
|
-
(0,
|
|
334
|
+
(0, import_react2.useEffect)(() => {
|
|
167
335
|
initialViewRef.current = { ...initialView };
|
|
168
336
|
setView(initialView);
|
|
169
337
|
}, [initialView.fov, initialView.pitch, initialView.yaw]);
|
|
170
|
-
(0, import_react.useEffect)(() => {
|
|
171
|
-
const element = gl.domElement;
|
|
172
|
-
if (!enabled) {
|
|
173
|
-
pointersRef.current.clear();
|
|
174
|
-
interactingRef.current = false;
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const updatePointer = (event) => {
|
|
178
|
-
const previous = pointersRef.current.get(event.pointerId);
|
|
179
|
-
const now = performance.now();
|
|
180
|
-
const next = { x: event.clientX, y: event.clientY, at: now };
|
|
181
|
-
pointersRef.current.set(event.pointerId, next);
|
|
182
|
-
return { previous, next };
|
|
183
|
-
};
|
|
184
|
-
const onPointerDown = (event) => {
|
|
185
|
-
if (interactionLockCountRef.current > 0) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
event.preventDefault();
|
|
189
|
-
interactingRef.current = true;
|
|
190
|
-
stopVelocity();
|
|
191
|
-
pointersRef.current.set(event.pointerId, {
|
|
192
|
-
x: event.clientX,
|
|
193
|
-
y: event.clientY,
|
|
194
|
-
at: performance.now()
|
|
195
|
-
});
|
|
196
|
-
element.setPointerCapture?.(event.pointerId);
|
|
197
|
-
if (pointersRef.current.size === 2) {
|
|
198
|
-
const [first, second] = Array.from(pointersRef.current.values());
|
|
199
|
-
pinchDistanceRef.current = pointerDistance(first, second);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
const onPointerMove = (event) => {
|
|
203
|
-
if (interactionLockCountRef.current > 0) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
if (!pointersRef.current.has(event.pointerId)) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
event.preventDefault();
|
|
210
|
-
const { previous, next } = updatePointer(event);
|
|
211
|
-
if (!previous) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const pointers = Array.from(pointersRef.current.values());
|
|
215
|
-
if (pointers.length >= 2) {
|
|
216
|
-
const distance = pointerDistance(pointers[0], pointers[1]);
|
|
217
|
-
const previousDistance = pinchDistanceRef.current;
|
|
218
|
-
pinchDistanceRef.current = distance;
|
|
219
|
-
if (previousDistance && distance > 0) {
|
|
220
|
-
const currentFov = targetViewRef.current.fov;
|
|
221
|
-
const nextFov = clamp(
|
|
222
|
-
currentFov * (previousDistance / distance),
|
|
223
|
-
minFov,
|
|
224
|
-
maxFov
|
|
225
|
-
);
|
|
226
|
-
const elapsed2 = Math.max((next.at - previous.at) / 1e3, 1 / 120);
|
|
227
|
-
zoomVelocityRef.current = (nextFov - currentFov) / elapsed2;
|
|
228
|
-
targetViewRef.current = {
|
|
229
|
-
...targetViewRef.current,
|
|
230
|
-
fov: nextFov
|
|
231
|
-
};
|
|
232
|
-
dirtyRef.current = true;
|
|
233
|
-
}
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
const rect = element.getBoundingClientRect();
|
|
237
|
-
const rotateSpeed = optionsRef.current.rotateSpeed ?? 0.35;
|
|
238
|
-
const deltaYaw = -(next.x - previous.x) * 360 * rotateSpeed / Math.max(rect.width, 1);
|
|
239
|
-
const deltaPitch = (next.y - previous.y) * 180 * rotateSpeed / Math.max(rect.height, 1);
|
|
240
|
-
const elapsed = Math.max((next.at - previous.at) / 1e3, 1 / 120);
|
|
241
|
-
targetViewRef.current = constrainView({
|
|
242
|
-
yaw: targetViewRef.current.yaw + deltaYaw,
|
|
243
|
-
pitch: targetViewRef.current.pitch + deltaPitch
|
|
244
|
-
});
|
|
245
|
-
yawVelocityRef.current = deltaYaw / elapsed;
|
|
246
|
-
pitchVelocityRef.current = deltaPitch / elapsed;
|
|
247
|
-
dirtyRef.current = true;
|
|
248
|
-
};
|
|
249
|
-
const releasePointer = (event) => {
|
|
250
|
-
pointersRef.current.delete(event.pointerId);
|
|
251
|
-
if (element.hasPointerCapture?.(event.pointerId)) {
|
|
252
|
-
element.releasePointerCapture?.(event.pointerId);
|
|
253
|
-
}
|
|
254
|
-
if (pointersRef.current.size < 2) {
|
|
255
|
-
pinchDistanceRef.current = null;
|
|
256
|
-
}
|
|
257
|
-
interactingRef.current = pointersRef.current.size > 0;
|
|
258
|
-
};
|
|
259
|
-
const onWheel = (event) => {
|
|
260
|
-
if (interactionLockCountRef.current > 0) {
|
|
261
|
-
event.preventDefault();
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
event.preventDefault();
|
|
265
|
-
const zoomSpeed = optionsRef.current.zoomSpeed ?? 0.08;
|
|
266
|
-
const delta = event.deltaY * zoomSpeed;
|
|
267
|
-
const nextFov = clamp(
|
|
268
|
-
targetViewRef.current.fov + delta,
|
|
269
|
-
minFov,
|
|
270
|
-
maxFov
|
|
271
|
-
);
|
|
272
|
-
zoomVelocityRef.current = clamp(
|
|
273
|
-
zoomVelocityRef.current + delta * 12,
|
|
274
|
-
-180,
|
|
275
|
-
180
|
|
276
|
-
);
|
|
277
|
-
targetViewRef.current = {
|
|
278
|
-
...targetViewRef.current,
|
|
279
|
-
fov: nextFov
|
|
280
|
-
};
|
|
281
|
-
dirtyRef.current = true;
|
|
282
|
-
};
|
|
283
|
-
const onKeyDown = (event) => {
|
|
284
|
-
if (interactionLockCountRef.current > 0) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (optionsRef.current.keyboard === false) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
const step = event.shiftKey ? 10 : 3;
|
|
291
|
-
let next = null;
|
|
292
|
-
if (event.key === "ArrowLeft") {
|
|
293
|
-
next = { yaw: targetViewRef.current.yaw - step };
|
|
294
|
-
} else if (event.key === "ArrowRight") {
|
|
295
|
-
next = { yaw: targetViewRef.current.yaw + step };
|
|
296
|
-
} else if (event.key === "ArrowUp") {
|
|
297
|
-
next = { pitch: targetViewRef.current.pitch + step };
|
|
298
|
-
} else if (event.key === "ArrowDown") {
|
|
299
|
-
next = { pitch: targetViewRef.current.pitch - step };
|
|
300
|
-
} else if (event.key === "+" || event.key === "=") {
|
|
301
|
-
next = { fov: targetViewRef.current.fov - step };
|
|
302
|
-
} else if (event.key === "-" || event.key === "_") {
|
|
303
|
-
next = { fov: targetViewRef.current.fov + step };
|
|
304
|
-
} else if (event.key === "0") {
|
|
305
|
-
setView(initialViewRef.current);
|
|
306
|
-
event.preventDefault();
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (next) {
|
|
310
|
-
event.preventDefault();
|
|
311
|
-
targetViewRef.current = constrainView(next);
|
|
312
|
-
dirtyRef.current = true;
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
element.addEventListener("pointerdown", onPointerDown);
|
|
316
|
-
element.addEventListener("pointermove", onPointerMove);
|
|
317
|
-
element.addEventListener("pointerup", releasePointer);
|
|
318
|
-
element.addEventListener("pointercancel", releasePointer);
|
|
319
|
-
element.addEventListener("wheel", onWheel, { passive: false });
|
|
320
|
-
element.addEventListener("keydown", onKeyDown);
|
|
321
|
-
return () => {
|
|
322
|
-
element.removeEventListener("pointerdown", onPointerDown);
|
|
323
|
-
element.removeEventListener("pointermove", onPointerMove);
|
|
324
|
-
element.removeEventListener("pointerup", releasePointer);
|
|
325
|
-
element.removeEventListener("pointercancel", releasePointer);
|
|
326
|
-
element.removeEventListener("wheel", onWheel);
|
|
327
|
-
element.removeEventListener("keydown", onKeyDown);
|
|
328
|
-
pointersRef.current.clear();
|
|
329
|
-
};
|
|
330
|
-
}, [enabled, gl, maxFov, minFov]);
|
|
331
338
|
(0, import_fiber.useFrame)((_, deltaSeconds) => {
|
|
332
339
|
if (!(camera instanceof import_three.PerspectiveCamera)) {
|
|
333
340
|
return;
|
|
334
341
|
}
|
|
342
|
+
const frictionStop = resolveFrictionStop(optionsRef.current.frictionStop);
|
|
335
343
|
const inertiaEnabled = optionsRef.current.inertia !== false;
|
|
336
344
|
if (!interactingRef.current && inertiaEnabled) {
|
|
337
345
|
const rotationDamping = Math.exp(
|
|
338
346
|
-INERTIA_ROTATION_DAMPING * deltaSeconds
|
|
339
347
|
);
|
|
340
348
|
const zoomDamping2 = Math.exp(-INERTIA_ZOOM_DAMPING * deltaSeconds);
|
|
341
|
-
if (Math.abs(yawVelocityRef.current) >=
|
|
349
|
+
if (Math.abs(yawVelocityRef.current) >= frictionStop) {
|
|
342
350
|
targetViewRef.current.yaw += yawVelocityRef.current * deltaSeconds;
|
|
343
351
|
yawVelocityRef.current *= rotationDamping;
|
|
344
|
-
|
|
352
|
+
markDirty();
|
|
345
353
|
} else {
|
|
346
354
|
yawVelocityRef.current = 0;
|
|
347
355
|
}
|
|
348
|
-
if (Math.abs(pitchVelocityRef.current) >=
|
|
356
|
+
if (Math.abs(pitchVelocityRef.current) >= frictionStop) {
|
|
349
357
|
targetViewRef.current.pitch += pitchVelocityRef.current * deltaSeconds;
|
|
350
358
|
pitchVelocityRef.current *= rotationDamping;
|
|
351
|
-
|
|
359
|
+
markDirty();
|
|
352
360
|
} else {
|
|
353
361
|
pitchVelocityRef.current = 0;
|
|
354
362
|
}
|
|
355
|
-
if (Math.abs(zoomVelocityRef.current) >=
|
|
363
|
+
if (Math.abs(zoomVelocityRef.current) >= frictionStop) {
|
|
356
364
|
targetViewRef.current.fov += zoomVelocityRef.current * deltaSeconds;
|
|
357
365
|
zoomVelocityRef.current *= zoomDamping2;
|
|
358
|
-
|
|
366
|
+
markDirty();
|
|
359
367
|
} else {
|
|
360
368
|
zoomVelocityRef.current = 0;
|
|
361
369
|
}
|
|
362
370
|
} else if (!inertiaEnabled) {
|
|
363
371
|
stopVelocity();
|
|
364
372
|
}
|
|
365
|
-
|
|
373
|
+
if (!interactingRef.current) {
|
|
374
|
+
targetViewRef.current = hardConstrainView(targetViewRef.current);
|
|
375
|
+
} else {
|
|
376
|
+
targetViewRef.current = softConstrainView(targetViewRef.current);
|
|
377
|
+
}
|
|
366
378
|
if (!dirtyRef.current) {
|
|
367
379
|
return;
|
|
368
380
|
}
|
|
@@ -399,7 +411,12 @@ var PanoramaControls = (0, import_react.forwardRef)(function PanoramaControls2({
|
|
|
399
411
|
const lastEmitted = lastEmittedViewRef.current;
|
|
400
412
|
if (!lastEmitted || !viewsEqual(lastEmitted, nextView)) {
|
|
401
413
|
lastEmittedViewRef.current = nextView;
|
|
402
|
-
|
|
414
|
+
eventBusRef.current.emit("viewchange", nextView);
|
|
415
|
+
}
|
|
416
|
+
const wasSettled = settledRef.current;
|
|
417
|
+
settledRef.current = hasSettled;
|
|
418
|
+
if (hasSettled && !wasSettled) {
|
|
419
|
+
eventBusRef.current.emit("viewsettled", nextView);
|
|
403
420
|
}
|
|
404
421
|
dirtyRef.current = !hasSettled;
|
|
405
422
|
});
|
|
@@ -407,12 +424,9 @@ var PanoramaControls = (0, import_react.forwardRef)(function PanoramaControls2({
|
|
|
407
424
|
});
|
|
408
425
|
|
|
409
426
|
// src/auto-rotate.tsx
|
|
410
|
-
var import_fiber2 = require("@react-three/fiber");
|
|
411
|
-
var import_react2 = require("react");
|
|
412
427
|
var DEFAULT_AUTO_ROTATE_SPEED = 18;
|
|
413
428
|
var DEFAULT_AUTO_ROTATE_ACCELERATION = 18;
|
|
414
429
|
var DEFAULT_AUTO_ROTATE_START_DELAY = 2;
|
|
415
|
-
var PanoramaControlsContext = (0, import_react2.createContext)(null);
|
|
416
430
|
function resolveStartDelay(value) {
|
|
417
431
|
return Number.isFinite(value) ? Math.max(value, 0) : 0;
|
|
418
432
|
}
|
|
@@ -425,19 +439,31 @@ function AutoRotate({
|
|
|
425
439
|
acceleration = DEFAULT_AUTO_ROTATE_ACCELERATION,
|
|
426
440
|
startDelay = DEFAULT_AUTO_ROTATE_START_DELAY
|
|
427
441
|
}) {
|
|
428
|
-
const controlsRef = (0,
|
|
429
|
-
const
|
|
430
|
-
const
|
|
442
|
+
const controlsRef = (0, import_react3.useContext)(PanoramaViewContext);
|
|
443
|
+
const eventBus = (0, import_react3.useContext)(PanoEventBusContext);
|
|
444
|
+
const elapsedRef = (0, import_react3.useRef)(0);
|
|
445
|
+
const rotationSpeedRef = (0, import_react3.useRef)(0);
|
|
446
|
+
const rotatingRef = (0, import_react3.useRef)(false);
|
|
447
|
+
const yawAccumulatedRef = (0, import_react3.useRef)(0);
|
|
431
448
|
if (!controlsRef) {
|
|
432
449
|
throw new Error("<AutoRotate> must be rendered inside <PanoView>.");
|
|
433
450
|
}
|
|
434
|
-
(0,
|
|
451
|
+
(0, import_react3.useEffect)(() => {
|
|
435
452
|
elapsedRef.current = 0;
|
|
436
453
|
rotationSpeedRef.current = 0;
|
|
437
|
-
|
|
454
|
+
yawAccumulatedRef.current = 0;
|
|
455
|
+
if (rotatingRef.current) {
|
|
456
|
+
rotatingRef.current = false;
|
|
457
|
+
eventBus?.emit("autorotatestop", void 0);
|
|
458
|
+
}
|
|
459
|
+
}, [enabled, eventBus, startDelay]);
|
|
438
460
|
(0, import_fiber2.useFrame)((_, deltaSeconds) => {
|
|
439
461
|
if (!enabled || !Number.isFinite(speed) || speed === 0) {
|
|
440
462
|
rotationSpeedRef.current = 0;
|
|
463
|
+
if (rotatingRef.current) {
|
|
464
|
+
rotatingRef.current = false;
|
|
465
|
+
eventBus?.emit("autorotatestop", void 0);
|
|
466
|
+
}
|
|
441
467
|
return;
|
|
442
468
|
}
|
|
443
469
|
const delaySeconds = resolveStartDelay(startDelay) / 1e3;
|
|
@@ -456,17 +482,30 @@ function AutoRotate({
|
|
|
456
482
|
const yawDelta = Math.sign(speed) * averageSpeed * deltaSeconds;
|
|
457
483
|
if (!controlsRef.current?.applyAutoRotation(yawDelta)) {
|
|
458
484
|
rotationSpeedRef.current = 0;
|
|
485
|
+
if (rotatingRef.current) {
|
|
486
|
+
rotatingRef.current = false;
|
|
487
|
+
eventBus?.emit("autorotatestop", void 0);
|
|
488
|
+
}
|
|
459
489
|
return;
|
|
460
490
|
}
|
|
491
|
+
if (!rotatingRef.current) {
|
|
492
|
+
rotatingRef.current = true;
|
|
493
|
+
eventBus?.emit("autorotatestart", void 0);
|
|
494
|
+
}
|
|
495
|
+
yawAccumulatedRef.current += Math.abs(yawDelta);
|
|
496
|
+
while (yawAccumulatedRef.current >= 360) {
|
|
497
|
+
yawAccumulatedRef.current -= 360;
|
|
498
|
+
eventBus?.emit("autorotateoneround", void 0);
|
|
499
|
+
}
|
|
461
500
|
rotationSpeedRef.current = nextSpeed;
|
|
462
501
|
});
|
|
463
502
|
return null;
|
|
464
503
|
}
|
|
465
504
|
|
|
466
505
|
// src/hotspot/accessibility.tsx
|
|
467
|
-
var
|
|
506
|
+
var import_react4 = require("react");
|
|
468
507
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
469
|
-
var HotspotAccessibilityContext = (0,
|
|
508
|
+
var HotspotAccessibilityContext = (0, import_react4.createContext)(null);
|
|
470
509
|
var VISUALLY_HIDDEN_STYLE = {
|
|
471
510
|
border: 0,
|
|
472
511
|
clip: "rect(0 0 0 0)",
|
|
@@ -492,12 +531,12 @@ function useHotspotAccessibility({
|
|
|
492
531
|
ariaLabel,
|
|
493
532
|
onActivate
|
|
494
533
|
}) {
|
|
495
|
-
const registry = (0,
|
|
496
|
-
const [focused, setFocused] = (0,
|
|
497
|
-
const activateRef = (0,
|
|
534
|
+
const registry = (0, import_react4.useContext)(HotspotAccessibilityContext);
|
|
535
|
+
const [focused, setFocused] = (0, import_react4.useState)(false);
|
|
536
|
+
const activateRef = (0, import_react4.useRef)(onActivate);
|
|
498
537
|
const enabled = Boolean(onActivate);
|
|
499
538
|
activateRef.current = onActivate;
|
|
500
|
-
(0,
|
|
539
|
+
(0, import_react4.useEffect)(() => {
|
|
501
540
|
if (!enabled) {
|
|
502
541
|
setFocused(false);
|
|
503
542
|
return;
|
|
@@ -519,8 +558,8 @@ function useHotspotAccessibility({
|
|
|
519
558
|
return focused;
|
|
520
559
|
}
|
|
521
560
|
function useHotspotAccessibilityLayer() {
|
|
522
|
-
const [entries, setEntries] = (0,
|
|
523
|
-
const register = (0,
|
|
561
|
+
const [entries, setEntries] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
|
|
562
|
+
const register = (0, import_react4.useCallback)(
|
|
524
563
|
(entry) => {
|
|
525
564
|
const token = Symbol(entry.id);
|
|
526
565
|
const nextEntry = { ...entry, token };
|
|
@@ -542,11 +581,11 @@ function useHotspotAccessibilityLayer() {
|
|
|
542
581
|
},
|
|
543
582
|
[]
|
|
544
583
|
);
|
|
545
|
-
const registry = (0,
|
|
584
|
+
const registry = (0, import_react4.useMemo)(
|
|
546
585
|
() => ({ register }),
|
|
547
586
|
[register]
|
|
548
587
|
);
|
|
549
|
-
const controls = (0,
|
|
588
|
+
const controls = (0, import_react4.useMemo)(
|
|
550
589
|
() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-label": "Panorama hotspots", style: { pointerEvents: "none" }, children: Array.from(entries.values()).map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
551
590
|
"button",
|
|
552
591
|
{
|
|
@@ -571,111 +610,1439 @@ function useHotspotAccessibilityLayer() {
|
|
|
571
610
|
return { controls, registry };
|
|
572
611
|
}
|
|
573
612
|
|
|
574
|
-
// src/
|
|
575
|
-
var
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
var
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
613
|
+
// src/keyboard-controls.tsx
|
|
614
|
+
var import_fiber3 = require("@react-three/fiber");
|
|
615
|
+
var import_react5 = require("react");
|
|
616
|
+
var DEFAULT_ROTATE_SPEED = 60;
|
|
617
|
+
var DEFAULT_ZOOM_SPEED = 30;
|
|
618
|
+
var DEFAULT_SHIFT_MULTIPLIER = 3;
|
|
619
|
+
var DEFAULT_KEYS = {
|
|
620
|
+
left: ["ArrowLeft"],
|
|
621
|
+
right: ["ArrowRight"],
|
|
622
|
+
up: ["ArrowUp"],
|
|
623
|
+
down: ["ArrowDown"],
|
|
624
|
+
zoomIn: ["+", "="],
|
|
625
|
+
zoomOut: ["-", "_"],
|
|
626
|
+
previousScene: ["[", "PageUp"],
|
|
627
|
+
nextScene: ["]", "PageDown"],
|
|
628
|
+
reset: ["0"]
|
|
629
|
+
};
|
|
630
|
+
var CONTINUOUS_ACTIONS = /* @__PURE__ */ new Set([
|
|
631
|
+
"left",
|
|
632
|
+
"right",
|
|
633
|
+
"up",
|
|
634
|
+
"down",
|
|
635
|
+
"zoomIn",
|
|
636
|
+
"zoomOut"
|
|
637
|
+
]);
|
|
638
|
+
function normalizeBinding(value) {
|
|
639
|
+
if (value === void 0) {
|
|
640
|
+
return [];
|
|
588
641
|
}
|
|
589
|
-
return
|
|
642
|
+
return (typeof value === "string" ? [value] : [...value]).filter(
|
|
643
|
+
(entry) => entry.length > 0
|
|
644
|
+
);
|
|
590
645
|
}
|
|
591
|
-
function
|
|
592
|
-
|
|
593
|
-
|
|
646
|
+
function resolveKeyMap(overrides) {
|
|
647
|
+
const map = /* @__PURE__ */ new Map();
|
|
648
|
+
for (const action of Object.keys(DEFAULT_KEYS)) {
|
|
649
|
+
const bindings = overrides && action in overrides ? normalizeBinding(overrides[action]) : [...DEFAULT_KEYS[action]];
|
|
650
|
+
for (const binding of bindings) {
|
|
651
|
+
map.set(binding.toLowerCase(), action);
|
|
652
|
+
}
|
|
594
653
|
}
|
|
595
|
-
|
|
596
|
-
pitch,
|
|
597
|
-
-MAX_HOTSPOT_PITCH,
|
|
598
|
-
MAX_HOTSPOT_PITCH
|
|
599
|
-
);
|
|
600
|
-
return Math.abs(clamped) >= MAX_HOTSPOT_PITCH - POLE_EPSILON_DEGREES ? Math.sign(clamped) * MAX_HOTSPOT_PITCH : clamped;
|
|
654
|
+
return map;
|
|
601
655
|
}
|
|
602
|
-
function
|
|
603
|
-
|
|
604
|
-
return {
|
|
605
|
-
yaw: Math.abs(pitch) === MAX_HOTSPOT_PITCH ? 0 : normalizePanoYaw(position.yaw),
|
|
606
|
-
pitch
|
|
607
|
-
};
|
|
656
|
+
function matchAction(event, keyMap) {
|
|
657
|
+
return keyMap.get(event.key.toLowerCase()) ?? keyMap.get(event.code.toLowerCase()) ?? null;
|
|
608
658
|
}
|
|
609
|
-
function
|
|
610
|
-
|
|
611
|
-
const safeRadius = Number.isFinite(radius) ? Math.max(0, radius) : 1;
|
|
612
|
-
const yaw = import_three2.MathUtils.degToRad(normalized.yaw);
|
|
613
|
-
const pitch = import_three2.MathUtils.degToRad(normalized.pitch);
|
|
614
|
-
const cosPitch = Math.cos(pitch);
|
|
615
|
-
return new import_three2.Vector3(
|
|
616
|
-
cosPitch * Math.sin(yaw) * safeRadius,
|
|
617
|
-
Math.sin(pitch) * safeRadius,
|
|
618
|
-
-cosPitch * Math.cos(yaw) * safeRadius
|
|
619
|
-
);
|
|
659
|
+
function resolveSpeed(value, fallback) {
|
|
660
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
620
661
|
}
|
|
621
|
-
function
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
return { yaw: 0, pitch: 0 };
|
|
662
|
+
function cycleSceneId(scenes, activeSceneId, step) {
|
|
663
|
+
if (scenes.length === 0) {
|
|
664
|
+
return null;
|
|
625
665
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
Math.asin(import_three2.MathUtils.clamp(vector.y / length, -1, 1))
|
|
630
|
-
)
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// src/panorama-radius.ts
|
|
635
|
-
var DEFAULT_PANORAMA_RADIUS = 1e3;
|
|
636
|
-
var DEFAULT_PANORAMA_CAMERA_NEAR = 0.1;
|
|
637
|
-
var DEFAULT_PANORAMA_CAMERA_FAR = DEFAULT_PANORAMA_RADIUS * 2;
|
|
638
|
-
|
|
639
|
-
// src/panorama-event-surface.tsx
|
|
640
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
641
|
-
var EVENT_SURFACE_RADIUS = DEFAULT_PANORAMA_RADIUS - 1;
|
|
642
|
-
function makePanoramaPointerEvent(event) {
|
|
643
|
-
const nativeEvent = event.nativeEvent;
|
|
644
|
-
return {
|
|
645
|
-
position: vector3ToPanoPosition(event.ray.direction),
|
|
646
|
-
nativeEvent,
|
|
647
|
-
pointerId: "pointerId" in nativeEvent ? nativeEvent.pointerId : void 0,
|
|
648
|
-
button: nativeEvent.button,
|
|
649
|
-
buttons: nativeEvent.buttons,
|
|
650
|
-
clientX: nativeEvent.clientX,
|
|
651
|
-
clientY: nativeEvent.clientY
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
function emitPanoramaEvent(event, callback) {
|
|
655
|
-
if (!callback) {
|
|
656
|
-
return;
|
|
666
|
+
const index = scenes.findIndex((scene) => scene.id === activeSceneId);
|
|
667
|
+
if (index < 0) {
|
|
668
|
+
return null;
|
|
657
669
|
}
|
|
658
|
-
|
|
659
|
-
|
|
670
|
+
const nextIndex = (index + step + scenes.length) % scenes.length;
|
|
671
|
+
return scenes[nextIndex]?.id ?? null;
|
|
660
672
|
}
|
|
661
|
-
function
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
673
|
+
function KeyboardControls({
|
|
674
|
+
enabled = true,
|
|
675
|
+
keys,
|
|
676
|
+
rotateSpeed = DEFAULT_ROTATE_SPEED,
|
|
677
|
+
zoomSpeed = DEFAULT_ZOOM_SPEED,
|
|
678
|
+
shiftMultiplier = DEFAULT_SHIFT_MULTIPLIER,
|
|
679
|
+
invert = false,
|
|
680
|
+
fovSpeed,
|
|
681
|
+
onPreviousScene,
|
|
682
|
+
onNextScene
|
|
665
683
|
}) {
|
|
666
|
-
|
|
667
|
-
|
|
684
|
+
const controlsRef = (0, import_react5.useContext)(PanoramaViewContext);
|
|
685
|
+
const { gl } = (0, import_fiber3.useThree)();
|
|
686
|
+
const pressedRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
|
|
687
|
+
const shiftHeldRef = (0, import_react5.useRef)(false);
|
|
688
|
+
const invertRef = (0, import_react5.useRef)(invert);
|
|
689
|
+
const fovSpeedRef = (0, import_react5.useRef)(fovSpeed);
|
|
690
|
+
const onPreviousSceneRef = (0, import_react5.useRef)(onPreviousScene);
|
|
691
|
+
const onNextSceneRef = (0, import_react5.useRef)(onNextScene);
|
|
692
|
+
const keyMap = (0, import_react5.useMemo)(() => resolveKeyMap(keys), [keys]);
|
|
693
|
+
if (!controlsRef) {
|
|
694
|
+
throw new Error("<KeyboardControls> must be rendered inside <PanoView>.");
|
|
668
695
|
}
|
|
669
|
-
|
|
696
|
+
invertRef.current = invert;
|
|
697
|
+
fovSpeedRef.current = fovSpeed;
|
|
698
|
+
onPreviousSceneRef.current = onPreviousScene;
|
|
699
|
+
onNextSceneRef.current = onNextScene;
|
|
700
|
+
(0, import_react5.useEffect)(() => {
|
|
701
|
+
const element = gl.domElement;
|
|
702
|
+
if (!enabled) {
|
|
703
|
+
pressedRef.current.clear();
|
|
704
|
+
shiftHeldRef.current = false;
|
|
705
|
+
controlsRef.current?.setKeyboardActive(false);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const syncKeyboardActive = () => {
|
|
709
|
+
let continuous = false;
|
|
710
|
+
for (const action of pressedRef.current) {
|
|
711
|
+
if (CONTINUOUS_ACTIONS.has(action)) {
|
|
712
|
+
continuous = true;
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
controlsRef.current?.setKeyboardActive(continuous);
|
|
717
|
+
};
|
|
718
|
+
const clearPressed = () => {
|
|
719
|
+
pressedRef.current.clear();
|
|
720
|
+
shiftHeldRef.current = false;
|
|
721
|
+
controlsRef.current?.setKeyboardActive(false);
|
|
722
|
+
};
|
|
723
|
+
const onKeyDown = (event) => {
|
|
724
|
+
if (event.key === "Shift") {
|
|
725
|
+
shiftHeldRef.current = true;
|
|
726
|
+
}
|
|
727
|
+
const action = matchAction(event, keyMap);
|
|
728
|
+
if (!action) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
event.preventDefault();
|
|
735
|
+
if (action === "reset") {
|
|
736
|
+
if (!event.repeat) {
|
|
737
|
+
controlsRef.current?.reset();
|
|
738
|
+
}
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (action === "previousScene") {
|
|
742
|
+
if (!event.repeat) {
|
|
743
|
+
onPreviousSceneRef.current?.();
|
|
744
|
+
}
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (action === "nextScene") {
|
|
748
|
+
if (!event.repeat) {
|
|
749
|
+
onNextSceneRef.current?.();
|
|
750
|
+
}
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
pressedRef.current.add(action);
|
|
754
|
+
syncKeyboardActive();
|
|
755
|
+
};
|
|
756
|
+
const onKeyUp = (event) => {
|
|
757
|
+
if (event.key === "Shift") {
|
|
758
|
+
shiftHeldRef.current = false;
|
|
759
|
+
}
|
|
760
|
+
const action = matchAction(event, keyMap);
|
|
761
|
+
if (!action || !CONTINUOUS_ACTIONS.has(action)) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
pressedRef.current.delete(action);
|
|
765
|
+
syncKeyboardActive();
|
|
766
|
+
};
|
|
767
|
+
const onBlur = () => {
|
|
768
|
+
clearPressed();
|
|
769
|
+
};
|
|
770
|
+
element.addEventListener("keydown", onKeyDown);
|
|
771
|
+
element.addEventListener("keyup", onKeyUp);
|
|
772
|
+
element.addEventListener("blur", onBlur);
|
|
773
|
+
window.addEventListener("blur", onBlur);
|
|
774
|
+
return () => {
|
|
775
|
+
element.removeEventListener("keydown", onKeyDown);
|
|
776
|
+
element.removeEventListener("keyup", onKeyUp);
|
|
777
|
+
element.removeEventListener("blur", onBlur);
|
|
778
|
+
window.removeEventListener("blur", onBlur);
|
|
779
|
+
clearPressed();
|
|
780
|
+
};
|
|
781
|
+
}, [controlsRef, enabled, gl, keyMap]);
|
|
782
|
+
(0, import_fiber3.useFrame)((_, deltaSeconds) => {
|
|
783
|
+
if (!enabled || pressedRef.current.size === 0) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
const controls = controlsRef.current;
|
|
787
|
+
if (!controls) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const multiplier = shiftHeldRef.current ? resolveSpeed(shiftMultiplier, DEFAULT_SHIFT_MULTIPLIER) : 1;
|
|
791
|
+
const rotation = resolveSpeed(rotateSpeed, DEFAULT_ROTATE_SPEED) * multiplier * deltaSeconds;
|
|
792
|
+
let zoom = resolveSpeed(zoomSpeed, DEFAULT_ZOOM_SPEED) * multiplier * deltaSeconds;
|
|
793
|
+
const fovCap = fovSpeedRef.current;
|
|
794
|
+
if (Number.isFinite(fovCap) && fovCap >= 0) {
|
|
795
|
+
zoom = Math.min(zoom, fovCap * deltaSeconds);
|
|
796
|
+
}
|
|
797
|
+
const pitchSign = invertRef.current ? -1 : 1;
|
|
798
|
+
let yaw = 0;
|
|
799
|
+
let pitch = 0;
|
|
800
|
+
let fov = 0;
|
|
801
|
+
for (const action of pressedRef.current) {
|
|
802
|
+
switch (action) {
|
|
803
|
+
case "left":
|
|
804
|
+
yaw -= rotation;
|
|
805
|
+
break;
|
|
806
|
+
case "right":
|
|
807
|
+
yaw += rotation;
|
|
808
|
+
break;
|
|
809
|
+
case "up":
|
|
810
|
+
pitch += pitchSign * rotation;
|
|
811
|
+
break;
|
|
812
|
+
case "down":
|
|
813
|
+
pitch -= pitchSign * rotation;
|
|
814
|
+
break;
|
|
815
|
+
case "zoomIn":
|
|
816
|
+
fov -= zoom;
|
|
817
|
+
break;
|
|
818
|
+
case "zoomOut":
|
|
819
|
+
fov += zoom;
|
|
820
|
+
break;
|
|
821
|
+
case "previousScene":
|
|
822
|
+
case "nextScene":
|
|
823
|
+
case "reset":
|
|
824
|
+
break;
|
|
825
|
+
default: {
|
|
826
|
+
const _exhaustive = action;
|
|
827
|
+
void _exhaustive;
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (yaw !== 0 || pitch !== 0 || fov !== 0) {
|
|
833
|
+
controls.applyViewDelta({ yaw, pitch, fov });
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/mouse-controls.tsx
|
|
840
|
+
var import_fiber4 = require("@react-three/fiber");
|
|
841
|
+
var import_react6 = require("react");
|
|
842
|
+
var DEFAULT_ROTATE_SPEED2 = 0.35;
|
|
843
|
+
var DEFAULT_ZOOM_SPEED2 = 0.08;
|
|
844
|
+
var BUTTON_FLAG = {
|
|
845
|
+
left: 0,
|
|
846
|
+
middle: 1,
|
|
847
|
+
right: 2
|
|
848
|
+
};
|
|
849
|
+
function resolveSpeed2(value, fallback) {
|
|
850
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
851
|
+
}
|
|
852
|
+
function resolveButtons(buttons) {
|
|
853
|
+
const list = buttons && buttons.length > 0 ? buttons : ["left"];
|
|
854
|
+
return new Set(list.map((button) => BUTTON_FLAG[button]));
|
|
855
|
+
}
|
|
856
|
+
function MouseControls({
|
|
857
|
+
enabled = true,
|
|
858
|
+
rotateSpeed = DEFAULT_ROTATE_SPEED2,
|
|
859
|
+
zoomSpeed = DEFAULT_ZOOM_SPEED2,
|
|
860
|
+
wheel = true,
|
|
861
|
+
invert = false,
|
|
862
|
+
buttons,
|
|
863
|
+
fovSpeed
|
|
864
|
+
}) {
|
|
865
|
+
const controlsRef = (0, import_react6.useContext)(PanoramaViewContext);
|
|
866
|
+
const { gl } = (0, import_fiber4.useThree)();
|
|
867
|
+
const pointersRef = (0, import_react6.useRef)(
|
|
868
|
+
/* @__PURE__ */ new Map()
|
|
869
|
+
);
|
|
870
|
+
const optionsRef = (0, import_react6.useRef)({
|
|
871
|
+
rotateSpeed,
|
|
872
|
+
zoomSpeed,
|
|
873
|
+
wheel,
|
|
874
|
+
invert,
|
|
875
|
+
fovSpeed
|
|
876
|
+
});
|
|
877
|
+
if (!controlsRef) {
|
|
878
|
+
throw new Error("<MouseControls> must be rendered inside <PanoView>.");
|
|
879
|
+
}
|
|
880
|
+
optionsRef.current = {
|
|
881
|
+
rotateSpeed,
|
|
882
|
+
zoomSpeed,
|
|
883
|
+
wheel,
|
|
884
|
+
invert,
|
|
885
|
+
fovSpeed
|
|
886
|
+
};
|
|
887
|
+
(0, import_react6.useEffect)(() => {
|
|
888
|
+
const element = gl.domElement;
|
|
889
|
+
const allowedButtons = resolveButtons(buttons);
|
|
890
|
+
const clearPointers = () => {
|
|
891
|
+
for (const pointerId of pointersRef.current.keys()) {
|
|
892
|
+
if (element.hasPointerCapture?.(pointerId)) {
|
|
893
|
+
element.releasePointerCapture?.(pointerId);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
pointersRef.current.clear();
|
|
897
|
+
controlsRef.current?.setPointerActive("mouse", false);
|
|
898
|
+
};
|
|
899
|
+
if (!enabled) {
|
|
900
|
+
clearPointers();
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const isMouseLike = (event) => event.pointerType === "mouse" || event.pointerType === "pen";
|
|
904
|
+
const syncActive = () => {
|
|
905
|
+
controlsRef.current?.setPointerActive(
|
|
906
|
+
"mouse",
|
|
907
|
+
pointersRef.current.size > 0
|
|
908
|
+
);
|
|
909
|
+
};
|
|
910
|
+
const onPointerDown = (event) => {
|
|
911
|
+
if (!isMouseLike(event)) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (!allowedButtons.has(event.button)) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
event.preventDefault();
|
|
921
|
+
pointersRef.current.set(event.pointerId, {
|
|
922
|
+
x: event.clientX,
|
|
923
|
+
y: event.clientY,
|
|
924
|
+
at: performance.now()
|
|
925
|
+
});
|
|
926
|
+
element.setPointerCapture?.(event.pointerId);
|
|
927
|
+
syncActive();
|
|
928
|
+
};
|
|
929
|
+
const onPointerMove = (event) => {
|
|
930
|
+
if (!isMouseLike(event)) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
const previous = pointersRef.current.get(event.pointerId);
|
|
937
|
+
if (!previous) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
event.preventDefault();
|
|
941
|
+
const next = {
|
|
942
|
+
x: event.clientX,
|
|
943
|
+
y: event.clientY,
|
|
944
|
+
at: performance.now()
|
|
945
|
+
};
|
|
946
|
+
pointersRef.current.set(event.pointerId, next);
|
|
947
|
+
const rect = element.getBoundingClientRect();
|
|
948
|
+
const speed = resolveSpeed2(
|
|
949
|
+
optionsRef.current.rotateSpeed,
|
|
950
|
+
DEFAULT_ROTATE_SPEED2
|
|
951
|
+
);
|
|
952
|
+
const sign = optionsRef.current.invert ? -1 : 1;
|
|
953
|
+
const deltaYaw = sign * -(next.x - previous.x) * 360 * speed / Math.max(rect.width, 1);
|
|
954
|
+
const deltaPitch = sign * (next.y - previous.y) * 180 * speed / Math.max(rect.height, 1);
|
|
955
|
+
controlsRef.current?.applyViewDelta(
|
|
956
|
+
{ yaw: deltaYaw, pitch: deltaPitch },
|
|
957
|
+
{ recordVelocity: true }
|
|
958
|
+
);
|
|
959
|
+
};
|
|
960
|
+
const releasePointer = (event) => {
|
|
961
|
+
if (!pointersRef.current.has(event.pointerId)) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
pointersRef.current.delete(event.pointerId);
|
|
965
|
+
if (element.hasPointerCapture?.(event.pointerId)) {
|
|
966
|
+
element.releasePointerCapture?.(event.pointerId);
|
|
967
|
+
}
|
|
968
|
+
syncActive();
|
|
969
|
+
};
|
|
970
|
+
const onWheel = (event) => {
|
|
971
|
+
if (!optionsRef.current.wheel) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
975
|
+
event.preventDefault();
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
event.preventDefault();
|
|
979
|
+
const speed = resolveSpeed2(
|
|
980
|
+
optionsRef.current.zoomSpeed,
|
|
981
|
+
DEFAULT_ZOOM_SPEED2
|
|
982
|
+
);
|
|
983
|
+
let delta = event.deltaY * speed;
|
|
984
|
+
const cap = optionsRef.current.fovSpeed;
|
|
985
|
+
if (Number.isFinite(cap) && cap > 0) {
|
|
986
|
+
delta = Math.max(-cap, Math.min(cap, delta));
|
|
987
|
+
}
|
|
988
|
+
controlsRef.current?.applyViewDelta(
|
|
989
|
+
{ fov: delta },
|
|
990
|
+
{ recordVelocity: true }
|
|
991
|
+
);
|
|
992
|
+
};
|
|
993
|
+
element.addEventListener("pointerdown", onPointerDown);
|
|
994
|
+
element.addEventListener("pointermove", onPointerMove);
|
|
995
|
+
element.addEventListener("pointerup", releasePointer);
|
|
996
|
+
element.addEventListener("pointercancel", releasePointer);
|
|
997
|
+
element.addEventListener("wheel", onWheel, { passive: false });
|
|
998
|
+
return () => {
|
|
999
|
+
element.removeEventListener("pointerdown", onPointerDown);
|
|
1000
|
+
element.removeEventListener("pointermove", onPointerMove);
|
|
1001
|
+
element.removeEventListener("pointerup", releasePointer);
|
|
1002
|
+
element.removeEventListener("pointercancel", releasePointer);
|
|
1003
|
+
element.removeEventListener("wheel", onWheel);
|
|
1004
|
+
clearPointers();
|
|
1005
|
+
};
|
|
1006
|
+
}, [buttons, controlsRef, enabled, gl]);
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/pano-context-menu.tsx
|
|
1011
|
+
var import_react7 = require("react");
|
|
1012
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1013
|
+
var DEFAULT_ICON_SIZE = 16;
|
|
1014
|
+
var MENU_EDGE_PADDING = 8;
|
|
1015
|
+
var MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY = 0.4;
|
|
1016
|
+
var PanoContextMenuOverlayContext = (0, import_react7.createContext)(null);
|
|
1017
|
+
var DEFAULT_APPEARANCE = {
|
|
1018
|
+
background: "#161616",
|
|
1019
|
+
color: "#f4f4f4",
|
|
1020
|
+
border: "1px solid #2e2e2e",
|
|
1021
|
+
borderRadius: 8,
|
|
1022
|
+
shadow: "0 8px 24px rgba(0, 0, 0, 0.45)",
|
|
1023
|
+
opacity: 1,
|
|
1024
|
+
fontSize: 13,
|
|
1025
|
+
minWidth: 180,
|
|
1026
|
+
padding: 4,
|
|
1027
|
+
iconSize: DEFAULT_ICON_SIZE,
|
|
1028
|
+
itemPadding: "6px 10px",
|
|
1029
|
+
itemHoverBackground: "#2a2a2a",
|
|
1030
|
+
itemDisabledColor: "#777777",
|
|
1031
|
+
separatorColor: "#333333",
|
|
1032
|
+
separatorMargin: "4px 6px"
|
|
1033
|
+
};
|
|
1034
|
+
function clampBackgroundOpacity(value) {
|
|
1035
|
+
if (!Number.isFinite(value)) {
|
|
1036
|
+
return DEFAULT_APPEARANCE.opacity;
|
|
1037
|
+
}
|
|
1038
|
+
return Math.min(
|
|
1039
|
+
1,
|
|
1040
|
+
Math.max(MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY, value)
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
function isActionItem(item) {
|
|
1044
|
+
return item.type !== "separator";
|
|
1045
|
+
}
|
|
1046
|
+
function DefaultContextMenuPanel({
|
|
1047
|
+
items,
|
|
1048
|
+
appearance,
|
|
1049
|
+
className,
|
|
1050
|
+
style,
|
|
1051
|
+
itemClassName,
|
|
1052
|
+
separatorClassName,
|
|
1053
|
+
selectContext,
|
|
1054
|
+
onRequestClose
|
|
1055
|
+
}) {
|
|
1056
|
+
const menuRef = (0, import_react7.useRef)(null);
|
|
1057
|
+
const [activeIndex, setActiveIndex] = (0, import_react7.useState)(0);
|
|
1058
|
+
const resolved = { ...DEFAULT_APPEARANCE, ...appearance };
|
|
1059
|
+
const iconSize = Number.isFinite(resolved.iconSize) ? Math.max(0, resolved.iconSize) : DEFAULT_ICON_SIZE;
|
|
1060
|
+
const visibleItems = (0, import_react7.useMemo)(
|
|
1061
|
+
() => items.filter((item) => {
|
|
1062
|
+
if (!isActionItem(item)) {
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
return !item.hidden;
|
|
1066
|
+
}),
|
|
1067
|
+
[items]
|
|
1068
|
+
);
|
|
1069
|
+
const focusableIndices = (0, import_react7.useMemo)(() => {
|
|
1070
|
+
const indices = [];
|
|
1071
|
+
visibleItems.forEach((item, index) => {
|
|
1072
|
+
if (isActionItem(item) && !item.disabled) {
|
|
1073
|
+
indices.push(index);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
return indices;
|
|
1077
|
+
}, [visibleItems]);
|
|
1078
|
+
(0, import_react7.useEffect)(() => {
|
|
1079
|
+
const first = focusableIndices[0];
|
|
1080
|
+
setActiveIndex(first ?? 0);
|
|
1081
|
+
const frame = requestAnimationFrame(() => {
|
|
1082
|
+
const buttons = menuRef.current?.querySelectorAll(
|
|
1083
|
+
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
1084
|
+
);
|
|
1085
|
+
buttons?.[0]?.focus();
|
|
1086
|
+
});
|
|
1087
|
+
return () => cancelAnimationFrame(frame);
|
|
1088
|
+
}, [focusableIndices]);
|
|
1089
|
+
const activateItem = (0, import_react7.useCallback)(
|
|
1090
|
+
(item) => {
|
|
1091
|
+
if (item.disabled) {
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
item.onSelect(selectContext);
|
|
1095
|
+
onRequestClose();
|
|
1096
|
+
},
|
|
1097
|
+
[onRequestClose, selectContext]
|
|
1098
|
+
);
|
|
1099
|
+
const moveFocus = (direction) => {
|
|
1100
|
+
if (focusableIndices.length === 0) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const currentSlot = focusableIndices.indexOf(activeIndex);
|
|
1104
|
+
const nextSlot = currentSlot === -1 ? 0 : (currentSlot + direction + focusableIndices.length) % focusableIndices.length;
|
|
1105
|
+
const nextIndex = focusableIndices[nextSlot];
|
|
1106
|
+
setActiveIndex(nextIndex);
|
|
1107
|
+
const buttons = menuRef.current?.querySelectorAll(
|
|
1108
|
+
'[role="menuitem"]'
|
|
1109
|
+
);
|
|
1110
|
+
buttons?.[nextIndex]?.focus();
|
|
1111
|
+
};
|
|
1112
|
+
const handleKeyDown = (event) => {
|
|
1113
|
+
switch (event.key) {
|
|
1114
|
+
case "ArrowDown":
|
|
1115
|
+
event.preventDefault();
|
|
1116
|
+
moveFocus(1);
|
|
1117
|
+
break;
|
|
1118
|
+
case "ArrowUp":
|
|
1119
|
+
event.preventDefault();
|
|
1120
|
+
moveFocus(-1);
|
|
1121
|
+
break;
|
|
1122
|
+
case "Home": {
|
|
1123
|
+
event.preventDefault();
|
|
1124
|
+
const first = focusableIndices[0];
|
|
1125
|
+
if (first !== void 0) {
|
|
1126
|
+
setActiveIndex(first);
|
|
1127
|
+
const buttons = menuRef.current?.querySelectorAll(
|
|
1128
|
+
'[role="menuitem"]'
|
|
1129
|
+
);
|
|
1130
|
+
buttons?.[first]?.focus();
|
|
1131
|
+
}
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
case "End": {
|
|
1135
|
+
event.preventDefault();
|
|
1136
|
+
const last = focusableIndices[focusableIndices.length - 1];
|
|
1137
|
+
if (last !== void 0) {
|
|
1138
|
+
setActiveIndex(last);
|
|
1139
|
+
const buttons = menuRef.current?.querySelectorAll(
|
|
1140
|
+
'[role="menuitem"]'
|
|
1141
|
+
);
|
|
1142
|
+
buttons?.[last]?.focus();
|
|
1143
|
+
}
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
case "Escape":
|
|
1147
|
+
event.preventDefault();
|
|
1148
|
+
onRequestClose();
|
|
1149
|
+
break;
|
|
1150
|
+
case "Enter":
|
|
1151
|
+
case " ": {
|
|
1152
|
+
event.preventDefault();
|
|
1153
|
+
const item = visibleItems[activeIndex];
|
|
1154
|
+
if (item && isActionItem(item)) {
|
|
1155
|
+
activateItem(item);
|
|
1156
|
+
}
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
default: {
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
const backgroundOpacity = clampBackgroundOpacity(resolved.opacity);
|
|
1165
|
+
const panelStyle = {
|
|
1166
|
+
color: resolved.color,
|
|
1167
|
+
border: resolved.border,
|
|
1168
|
+
borderRadius: resolved.borderRadius,
|
|
1169
|
+
boxShadow: resolved.shadow,
|
|
1170
|
+
fontSize: resolved.fontSize,
|
|
1171
|
+
minWidth: resolved.minWidth,
|
|
1172
|
+
padding: resolved.padding,
|
|
1173
|
+
pointerEvents: "auto",
|
|
1174
|
+
position: "relative",
|
|
1175
|
+
userSelect: "none",
|
|
1176
|
+
...style
|
|
1177
|
+
};
|
|
1178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1179
|
+
"div",
|
|
1180
|
+
{
|
|
1181
|
+
ref: menuRef,
|
|
1182
|
+
"aria-label": "Panorama context menu",
|
|
1183
|
+
className,
|
|
1184
|
+
onKeyDown: handleKeyDown,
|
|
1185
|
+
role: "menu",
|
|
1186
|
+
style: panelStyle,
|
|
1187
|
+
tabIndex: -1,
|
|
1188
|
+
children: [
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1190
|
+
"div",
|
|
1191
|
+
{
|
|
1192
|
+
"aria-hidden": true,
|
|
1193
|
+
style: {
|
|
1194
|
+
background: resolved.background,
|
|
1195
|
+
borderRadius: resolved.borderRadius,
|
|
1196
|
+
inset: 0,
|
|
1197
|
+
opacity: backgroundOpacity,
|
|
1198
|
+
pointerEvents: "none",
|
|
1199
|
+
position: "absolute",
|
|
1200
|
+
zIndex: 0
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
),
|
|
1204
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "relative", zIndex: 1 }, children: visibleItems.map((item, index) => {
|
|
1205
|
+
if (!isActionItem(item)) {
|
|
1206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1207
|
+
"div",
|
|
1208
|
+
{
|
|
1209
|
+
"aria-hidden": true,
|
|
1210
|
+
className: [separatorClassName, item.className].filter(Boolean).join(" ") || void 0,
|
|
1211
|
+
role: "separator",
|
|
1212
|
+
style: {
|
|
1213
|
+
background: resolved.separatorColor,
|
|
1214
|
+
height: 1,
|
|
1215
|
+
margin: resolved.separatorMargin,
|
|
1216
|
+
...item.style
|
|
1217
|
+
}
|
|
1218
|
+
},
|
|
1219
|
+
item.id ?? `separator-${index}`
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
const disabled = item.disabled === true;
|
|
1223
|
+
const iconNode = item.icon ?? (item.image ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1224
|
+
"img",
|
|
1225
|
+
{
|
|
1226
|
+
alt: item.imageAlt ?? "",
|
|
1227
|
+
draggable: false,
|
|
1228
|
+
src: item.image,
|
|
1229
|
+
style: {
|
|
1230
|
+
display: "block",
|
|
1231
|
+
height: iconSize,
|
|
1232
|
+
objectFit: "contain",
|
|
1233
|
+
width: iconSize
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
) : null);
|
|
1237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1238
|
+
"button",
|
|
1239
|
+
{
|
|
1240
|
+
"aria-disabled": disabled || void 0,
|
|
1241
|
+
className: [itemClassName, item.className].filter(Boolean).join(" ") || void 0,
|
|
1242
|
+
disabled,
|
|
1243
|
+
onClick: (event) => {
|
|
1244
|
+
event.preventDefault();
|
|
1245
|
+
event.stopPropagation();
|
|
1246
|
+
activateItem(item);
|
|
1247
|
+
},
|
|
1248
|
+
onMouseEnter: (event) => {
|
|
1249
|
+
if (!disabled) {
|
|
1250
|
+
setActiveIndex(index);
|
|
1251
|
+
event.currentTarget.focus();
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
role: "menuitem",
|
|
1255
|
+
style: {
|
|
1256
|
+
alignItems: "center",
|
|
1257
|
+
background: !disabled && activeIndex === index ? resolved.itemHoverBackground : "transparent",
|
|
1258
|
+
border: "none",
|
|
1259
|
+
borderRadius: 4,
|
|
1260
|
+
color: disabled ? resolved.itemDisabledColor : "inherit",
|
|
1261
|
+
cursor: disabled ? "default" : "pointer",
|
|
1262
|
+
display: "flex",
|
|
1263
|
+
font: "inherit",
|
|
1264
|
+
gap: 8,
|
|
1265
|
+
padding: resolved.itemPadding,
|
|
1266
|
+
textAlign: "left",
|
|
1267
|
+
width: "100%",
|
|
1268
|
+
...item.style
|
|
1269
|
+
},
|
|
1270
|
+
tabIndex: disabled ? -1 : activeIndex === index ? 0 : -1,
|
|
1271
|
+
type: "button",
|
|
1272
|
+
children: [
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1274
|
+
"span",
|
|
1275
|
+
{
|
|
1276
|
+
"aria-hidden": true,
|
|
1277
|
+
style: {
|
|
1278
|
+
alignItems: "center",
|
|
1279
|
+
display: "inline-flex",
|
|
1280
|
+
flexShrink: 0,
|
|
1281
|
+
height: iconSize,
|
|
1282
|
+
justifyContent: "center",
|
|
1283
|
+
width: iconSize
|
|
1284
|
+
},
|
|
1285
|
+
children: iconNode
|
|
1286
|
+
}
|
|
1287
|
+
),
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { flex: 1 }, children: item.label })
|
|
1289
|
+
]
|
|
1290
|
+
},
|
|
1291
|
+
item.id
|
|
1292
|
+
);
|
|
1293
|
+
}) })
|
|
1294
|
+
]
|
|
1295
|
+
}
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
function PositionedMenuShell({
|
|
1299
|
+
clientX,
|
|
1300
|
+
clientY,
|
|
1301
|
+
overlayElement,
|
|
1302
|
+
children
|
|
1303
|
+
}) {
|
|
1304
|
+
const shellRef = (0, import_react7.useRef)(null);
|
|
1305
|
+
const [offset, setOffset] = (0, import_react7.useState)({ left: 0, top: 0 });
|
|
1306
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
1307
|
+
const shell = shellRef.current;
|
|
1308
|
+
if (!overlayElement || !shell) {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const overlayRect = overlayElement.getBoundingClientRect();
|
|
1312
|
+
const menuRect = shell.getBoundingClientRect();
|
|
1313
|
+
const rawLeft = clientX - overlayRect.left;
|
|
1314
|
+
const rawTop = clientY - overlayRect.top;
|
|
1315
|
+
const maxLeft = Math.max(
|
|
1316
|
+
MENU_EDGE_PADDING,
|
|
1317
|
+
overlayRect.width - menuRect.width - MENU_EDGE_PADDING
|
|
1318
|
+
);
|
|
1319
|
+
const maxTop = Math.max(
|
|
1320
|
+
MENU_EDGE_PADDING,
|
|
1321
|
+
overlayRect.height - menuRect.height - MENU_EDGE_PADDING
|
|
1322
|
+
);
|
|
1323
|
+
setOffset({
|
|
1324
|
+
left: Math.min(Math.max(rawLeft, MENU_EDGE_PADDING), maxLeft),
|
|
1325
|
+
top: Math.min(Math.max(rawTop, MENU_EDGE_PADDING), maxTop)
|
|
1326
|
+
});
|
|
1327
|
+
}, [clientX, clientY, overlayElement, children]);
|
|
1328
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1329
|
+
"div",
|
|
1330
|
+
{
|
|
1331
|
+
ref: shellRef,
|
|
1332
|
+
style: {
|
|
1333
|
+
left: offset.left,
|
|
1334
|
+
position: "absolute",
|
|
1335
|
+
top: offset.top,
|
|
1336
|
+
zIndex: 20
|
|
1337
|
+
},
|
|
1338
|
+
children
|
|
1339
|
+
}
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
function usePanoContextMenuOverlay() {
|
|
1343
|
+
const [session, setSession] = (0, import_react7.useState)(null);
|
|
1344
|
+
const overlayRef = (0, import_react7.useRef)(null);
|
|
1345
|
+
const [overlayElement, setOverlayElement] = (0, import_react7.useState)(
|
|
1346
|
+
null
|
|
1347
|
+
);
|
|
1348
|
+
const api = (0, import_react7.useMemo)(
|
|
1349
|
+
() => ({ setSession, overlayRef }),
|
|
1350
|
+
[]
|
|
1351
|
+
);
|
|
1352
|
+
const setOverlayNode = (0, import_react7.useCallback)((node) => {
|
|
1353
|
+
overlayRef.current = node;
|
|
1354
|
+
setOverlayElement(node);
|
|
1355
|
+
}, []);
|
|
1356
|
+
(0, import_react7.useEffect)(() => {
|
|
1357
|
+
if (!session) {
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const handlePointerDown = (event) => {
|
|
1361
|
+
const overlay2 = overlayRef.current;
|
|
1362
|
+
if (!overlay2) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
const target = event.target;
|
|
1366
|
+
if (target instanceof Node && overlay2.contains(target)) {
|
|
1367
|
+
const menuRoot = overlay2.querySelector(
|
|
1368
|
+
'[role="menu"], [data-pano-context-menu]'
|
|
1369
|
+
);
|
|
1370
|
+
if (menuRoot && menuRoot.contains(target)) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
setSession(null);
|
|
1375
|
+
};
|
|
1376
|
+
const handleKeyDown = (event) => {
|
|
1377
|
+
if (event.key === "Escape") {
|
|
1378
|
+
setSession(null);
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
1382
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1383
|
+
return () => {
|
|
1384
|
+
document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
1385
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1386
|
+
};
|
|
1387
|
+
}, [session]);
|
|
1388
|
+
const overlay = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1389
|
+
"div",
|
|
1390
|
+
{
|
|
1391
|
+
ref: setOverlayNode,
|
|
1392
|
+
"aria-hidden": session ? void 0 : true,
|
|
1393
|
+
style: {
|
|
1394
|
+
inset: 0,
|
|
1395
|
+
pointerEvents: "none",
|
|
1396
|
+
position: "absolute",
|
|
1397
|
+
zIndex: 20
|
|
1398
|
+
},
|
|
1399
|
+
children: session ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1400
|
+
PositionedMenuShell,
|
|
1401
|
+
{
|
|
1402
|
+
clientX: session.event.clientX,
|
|
1403
|
+
clientY: session.event.clientY,
|
|
1404
|
+
overlayElement,
|
|
1405
|
+
children: session.render()
|
|
1406
|
+
},
|
|
1407
|
+
session.key
|
|
1408
|
+
) : null
|
|
1409
|
+
}
|
|
1410
|
+
);
|
|
1411
|
+
return { api, overlay };
|
|
1412
|
+
}
|
|
1413
|
+
function PanoContextMenu({
|
|
1414
|
+
items,
|
|
1415
|
+
prepend,
|
|
1416
|
+
append,
|
|
1417
|
+
appearance,
|
|
1418
|
+
className,
|
|
1419
|
+
style,
|
|
1420
|
+
itemClassName,
|
|
1421
|
+
separatorClassName,
|
|
1422
|
+
children
|
|
1423
|
+
}) {
|
|
1424
|
+
const eventBus = (0, import_react7.useContext)(PanoEventBusContext);
|
|
1425
|
+
const controlsRef = (0, import_react7.useContext)(PanoramaViewContext);
|
|
1426
|
+
const overlayApi = (0, import_react7.useContext)(PanoContextMenuOverlayContext);
|
|
1427
|
+
const actions = (0, import_react7.useContext)(PanoContextMenuActionsContext);
|
|
1428
|
+
const instanceId = (0, import_react7.useId)();
|
|
1429
|
+
const sessionKeyRef = (0, import_react7.useRef)(0);
|
|
1430
|
+
const propsRef = (0, import_react7.useRef)({
|
|
1431
|
+
items,
|
|
1432
|
+
prepend,
|
|
1433
|
+
append,
|
|
1434
|
+
appearance,
|
|
1435
|
+
className,
|
|
1436
|
+
style,
|
|
1437
|
+
itemClassName,
|
|
1438
|
+
separatorClassName,
|
|
1439
|
+
children
|
|
1440
|
+
});
|
|
1441
|
+
propsRef.current = {
|
|
1442
|
+
items,
|
|
1443
|
+
prepend,
|
|
1444
|
+
append,
|
|
1445
|
+
appearance,
|
|
1446
|
+
className,
|
|
1447
|
+
style,
|
|
1448
|
+
itemClassName,
|
|
1449
|
+
separatorClassName,
|
|
1450
|
+
children
|
|
1451
|
+
};
|
|
1452
|
+
if (!eventBus || !controlsRef) {
|
|
1453
|
+
throw new Error("<PanoContextMenu> must be rendered inside <PanoView>.");
|
|
1454
|
+
}
|
|
1455
|
+
if (!overlayApi) {
|
|
1456
|
+
throw new Error(
|
|
1457
|
+
"<PanoContextMenu> requires the PanoView context-menu overlay host."
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
const close = (0, import_react7.useCallback)(() => {
|
|
1461
|
+
overlayApi.setSession(null);
|
|
1462
|
+
}, [overlayApi]);
|
|
1463
|
+
const openMenu = (0, import_react7.useCallback)(
|
|
1464
|
+
(event) => {
|
|
1465
|
+
event.nativeEvent.preventDefault();
|
|
1466
|
+
const view = controlsRef.current?.getView() ?? {
|
|
1467
|
+
yaw: 0,
|
|
1468
|
+
pitch: 0,
|
|
1469
|
+
fov: 75
|
|
1470
|
+
};
|
|
1471
|
+
const selectContext = {
|
|
1472
|
+
position: event.position,
|
|
1473
|
+
view,
|
|
1474
|
+
nativeEvent: event.nativeEvent,
|
|
1475
|
+
close
|
|
1476
|
+
};
|
|
1477
|
+
const current = propsRef.current;
|
|
1478
|
+
sessionKeyRef.current += 1;
|
|
1479
|
+
const key = `${instanceId}-${sessionKeyRef.current}`;
|
|
1480
|
+
const hasCustomChildren = typeof current.children === "function";
|
|
1481
|
+
const presets = actions ? createPanoContextMenuPresets(actions) : null;
|
|
1482
|
+
const resolvedItems = presets ? composePanoContextMenuItems(current, presets) : current.items?.filter(isActionEntry) ?? [];
|
|
1483
|
+
if (!hasCustomChildren && resolvedItems.length === 0) {
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
overlayApi.setSession({
|
|
1487
|
+
key,
|
|
1488
|
+
event,
|
|
1489
|
+
view,
|
|
1490
|
+
render: () => {
|
|
1491
|
+
if (hasCustomChildren && current.children) {
|
|
1492
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-pano-context-menu": true, style: { pointerEvents: "auto" }, children: current.children({ event, view, close }) });
|
|
1493
|
+
}
|
|
1494
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1495
|
+
DefaultContextMenuPanel,
|
|
1496
|
+
{
|
|
1497
|
+
appearance: current.appearance,
|
|
1498
|
+
className: current.className,
|
|
1499
|
+
itemClassName: current.itemClassName,
|
|
1500
|
+
items: resolvedItems,
|
|
1501
|
+
onRequestClose: close,
|
|
1502
|
+
selectContext,
|
|
1503
|
+
separatorClassName: current.separatorClassName,
|
|
1504
|
+
style: current.style
|
|
1505
|
+
}
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
},
|
|
1510
|
+
[actions, close, controlsRef, instanceId, overlayApi]
|
|
1511
|
+
);
|
|
1512
|
+
(0, import_react7.useEffect)(() => {
|
|
1513
|
+
const unsubscribeContextMenu = eventBus.subscribe(
|
|
1514
|
+
"contextmenu",
|
|
1515
|
+
openMenu
|
|
1516
|
+
);
|
|
1517
|
+
const unsubscribeInteraction = eventBus.subscribe(
|
|
1518
|
+
"viewinteractionstart",
|
|
1519
|
+
() => {
|
|
1520
|
+
close();
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1523
|
+
return () => {
|
|
1524
|
+
unsubscribeContextMenu();
|
|
1525
|
+
unsubscribeInteraction();
|
|
1526
|
+
close();
|
|
1527
|
+
};
|
|
1528
|
+
}, [close, eventBus, openMenu]);
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
function ResetViewIcon() {
|
|
1532
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1533
|
+
"svg",
|
|
1534
|
+
{
|
|
1535
|
+
"aria-hidden": true,
|
|
1536
|
+
fill: "none",
|
|
1537
|
+
height: "100%",
|
|
1538
|
+
stroke: "currentColor",
|
|
1539
|
+
strokeLinecap: "round",
|
|
1540
|
+
strokeLinejoin: "round",
|
|
1541
|
+
strokeWidth: "1.75",
|
|
1542
|
+
viewBox: "0 0 16 16",
|
|
1543
|
+
width: "100%",
|
|
1544
|
+
children: [
|
|
1545
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 6.5A4.5 4.5 0 1 1 4 10" }),
|
|
1546
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 3.5v3h3" })
|
|
1547
|
+
]
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
function FullscreenIcon() {
|
|
1552
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1553
|
+
"svg",
|
|
1554
|
+
{
|
|
1555
|
+
"aria-hidden": true,
|
|
1556
|
+
fill: "none",
|
|
1557
|
+
height: "100%",
|
|
1558
|
+
stroke: "currentColor",
|
|
1559
|
+
strokeLinecap: "round",
|
|
1560
|
+
strokeLinejoin: "round",
|
|
1561
|
+
strokeWidth: "1.75",
|
|
1562
|
+
viewBox: "0 0 16 16",
|
|
1563
|
+
width: "100%",
|
|
1564
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 6V3h3M10 3h3v3M13 10v3h-3M6 13H3v-3" })
|
|
1565
|
+
}
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
function ExitFullscreenIcon() {
|
|
1569
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1570
|
+
"svg",
|
|
1571
|
+
{
|
|
1572
|
+
"aria-hidden": true,
|
|
1573
|
+
fill: "none",
|
|
1574
|
+
height: "100%",
|
|
1575
|
+
stroke: "currentColor",
|
|
1576
|
+
strokeLinecap: "round",
|
|
1577
|
+
strokeLinejoin: "round",
|
|
1578
|
+
strokeWidth: "1.75",
|
|
1579
|
+
viewBox: "0 0 16 16",
|
|
1580
|
+
width: "100%",
|
|
1581
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 3v3H3M10 3v3h3M3 10h3v3M10 10h3v3" })
|
|
1582
|
+
}
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
function createPanoContextMenuPresets(actions) {
|
|
1586
|
+
const isFullscreen = actions.isFullscreen === true;
|
|
1587
|
+
const resetView = {
|
|
1588
|
+
id: "pano-reset-view",
|
|
1589
|
+
label: "Reset view",
|
|
1590
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ResetViewIcon, {}),
|
|
1591
|
+
onSelect: () => {
|
|
1592
|
+
actions.reset();
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
const fullscreen = {
|
|
1596
|
+
id: "pano-fullscreen",
|
|
1597
|
+
label: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
1598
|
+
icon: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ExitFullscreenIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FullscreenIcon, {}),
|
|
1599
|
+
onSelect: () => {
|
|
1600
|
+
actions.toggleFullscreen();
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
const separator = (id = "pano-separator") => ({
|
|
1604
|
+
type: "separator",
|
|
1605
|
+
id
|
|
1606
|
+
});
|
|
1607
|
+
return {
|
|
1608
|
+
resetView,
|
|
1609
|
+
fullscreen,
|
|
1610
|
+
separator,
|
|
1611
|
+
defaults: [resetView, separator("pano-default-separator"), fullscreen]
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
function createDefaultPanoContextMenuItems(actions) {
|
|
1615
|
+
return createPanoContextMenuPresets(actions).defaults;
|
|
1616
|
+
}
|
|
1617
|
+
function isPresetId(value) {
|
|
1618
|
+
return value === "resetView" || value === "fullscreen" || value === "separator";
|
|
1619
|
+
}
|
|
1620
|
+
function isPresetRef(value) {
|
|
1621
|
+
return typeof value === "object" && value !== null && "preset" in value && isPresetId(value.preset);
|
|
1622
|
+
}
|
|
1623
|
+
function isActionEntry(value) {
|
|
1624
|
+
if (typeof value === "string") {
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
if (isPresetRef(value)) {
|
|
1628
|
+
return false;
|
|
1629
|
+
}
|
|
1630
|
+
return true;
|
|
1631
|
+
}
|
|
1632
|
+
function applyPresetOverrides(item, overrides) {
|
|
1633
|
+
if (item.type === "separator") {
|
|
1634
|
+
return {
|
|
1635
|
+
...item,
|
|
1636
|
+
id: overrides.id ?? item.id,
|
|
1637
|
+
className: overrides.className ?? item.className,
|
|
1638
|
+
style: overrides.style ?? item.style
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
return {
|
|
1642
|
+
...item,
|
|
1643
|
+
id: overrides.id ?? item.id,
|
|
1644
|
+
label: overrides.label ?? item.label,
|
|
1645
|
+
className: overrides.className ?? item.className,
|
|
1646
|
+
style: overrides.style ?? item.style,
|
|
1647
|
+
disabled: overrides.disabled ?? item.disabled,
|
|
1648
|
+
hidden: overrides.hidden ?? item.hidden
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function resolvePresetEntry(entry, presets) {
|
|
1652
|
+
const presetId = typeof entry === "string" ? entry : entry.preset;
|
|
1653
|
+
const overrides = typeof entry === "string" ? {} : entry;
|
|
1654
|
+
switch (presetId) {
|
|
1655
|
+
case "resetView":
|
|
1656
|
+
return applyPresetOverrides(presets.resetView, overrides);
|
|
1657
|
+
case "fullscreen":
|
|
1658
|
+
return applyPresetOverrides(presets.fullscreen, overrides);
|
|
1659
|
+
case "separator":
|
|
1660
|
+
return applyPresetOverrides(
|
|
1661
|
+
presets.separator(overrides.id ?? "pano-separator"),
|
|
1662
|
+
overrides
|
|
1663
|
+
);
|
|
1664
|
+
default: {
|
|
1665
|
+
const _exhaustive = presetId;
|
|
1666
|
+
return _exhaustive;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function resolvePanoContextMenuEntries(entries, presets) {
|
|
1671
|
+
const resolved = [];
|
|
1672
|
+
for (const entry of entries) {
|
|
1673
|
+
if (isActionEntry(entry)) {
|
|
1674
|
+
resolved.push(entry);
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1677
|
+
resolved.push(resolvePresetEntry(entry, presets));
|
|
1678
|
+
}
|
|
1679
|
+
return resolved;
|
|
1680
|
+
}
|
|
1681
|
+
function composePanoContextMenuItems(props, presets) {
|
|
1682
|
+
if (props.items) {
|
|
1683
|
+
return resolvePanoContextMenuEntries(props.items, presets);
|
|
1684
|
+
}
|
|
1685
|
+
return [
|
|
1686
|
+
...resolvePanoContextMenuEntries(props.prepend ?? [], presets),
|
|
1687
|
+
...presets.defaults,
|
|
1688
|
+
...resolvePanoContextMenuEntries(props.append ?? [], presets)
|
|
1689
|
+
];
|
|
1690
|
+
}
|
|
1691
|
+
var PanoContextMenuActionsContext = (0, import_react7.createContext)(null);
|
|
1692
|
+
|
|
1693
|
+
// src/pano-events.tsx
|
|
1694
|
+
var import_fiber5 = require("@react-three/fiber");
|
|
1695
|
+
var import_react8 = require("react");
|
|
1696
|
+
var DEFAULT_IDLE_TIME_MS = 2e3;
|
|
1697
|
+
function resolveIdleTime(value) {
|
|
1698
|
+
return Number.isFinite(value) && value >= 0 ? value : DEFAULT_IDLE_TIME_MS;
|
|
1699
|
+
}
|
|
1700
|
+
function usePanoEvents({
|
|
1701
|
+
idleTime = DEFAULT_IDLE_TIME_MS,
|
|
1702
|
+
onViewChange,
|
|
1703
|
+
onViewSettled,
|
|
1704
|
+
onViewInteractionStart,
|
|
1705
|
+
onViewInteractionEnd,
|
|
1706
|
+
onClick,
|
|
1707
|
+
onDoubleClick,
|
|
1708
|
+
onPointerDown,
|
|
1709
|
+
onPointerUp,
|
|
1710
|
+
onPointerMove,
|
|
1711
|
+
onContextMenu,
|
|
1712
|
+
onWheel,
|
|
1713
|
+
onIdle,
|
|
1714
|
+
onIdleEnd,
|
|
1715
|
+
onEnterFullscreen,
|
|
1716
|
+
onExitFullscreen,
|
|
1717
|
+
onResize,
|
|
1718
|
+
onAutoRotateStart,
|
|
1719
|
+
onAutoRotateStop,
|
|
1720
|
+
onAutoRotateOneRound
|
|
1721
|
+
}) {
|
|
1722
|
+
const eventBus = (0, import_react8.useContext)(PanoEventBusContext);
|
|
1723
|
+
const controlsRef = (0, import_react8.useContext)(PanoramaViewContext);
|
|
1724
|
+
const { gl } = (0, import_fiber5.useThree)();
|
|
1725
|
+
if (!eventBus || !controlsRef) {
|
|
1726
|
+
throw new Error("usePanoEvents must be used inside <PanoView>.");
|
|
1727
|
+
}
|
|
1728
|
+
const callbacksRef = (0, import_react8.useRef)({
|
|
1729
|
+
onViewChange,
|
|
1730
|
+
onViewSettled,
|
|
1731
|
+
onViewInteractionStart,
|
|
1732
|
+
onViewInteractionEnd,
|
|
1733
|
+
onClick,
|
|
1734
|
+
onDoubleClick,
|
|
1735
|
+
onPointerDown,
|
|
1736
|
+
onPointerUp,
|
|
1737
|
+
onPointerMove,
|
|
1738
|
+
onContextMenu,
|
|
1739
|
+
onWheel,
|
|
1740
|
+
onIdle,
|
|
1741
|
+
onIdleEnd,
|
|
1742
|
+
onEnterFullscreen,
|
|
1743
|
+
onExitFullscreen,
|
|
1744
|
+
onResize,
|
|
1745
|
+
onAutoRotateStart,
|
|
1746
|
+
onAutoRotateStop,
|
|
1747
|
+
onAutoRotateOneRound
|
|
1748
|
+
});
|
|
1749
|
+
callbacksRef.current = {
|
|
1750
|
+
onViewChange,
|
|
1751
|
+
onViewSettled,
|
|
1752
|
+
onViewInteractionStart,
|
|
1753
|
+
onViewInteractionEnd,
|
|
1754
|
+
onClick,
|
|
1755
|
+
onDoubleClick,
|
|
1756
|
+
onPointerDown,
|
|
1757
|
+
onPointerUp,
|
|
1758
|
+
onPointerMove,
|
|
1759
|
+
onContextMenu,
|
|
1760
|
+
onWheel,
|
|
1761
|
+
onIdle,
|
|
1762
|
+
onIdleEnd,
|
|
1763
|
+
onEnterFullscreen,
|
|
1764
|
+
onExitFullscreen,
|
|
1765
|
+
onResize,
|
|
1766
|
+
onAutoRotateStart,
|
|
1767
|
+
onAutoRotateStop,
|
|
1768
|
+
onAutoRotateOneRound
|
|
1769
|
+
};
|
|
1770
|
+
const idleElapsedRef = (0, import_react8.useRef)(0);
|
|
1771
|
+
const isIdleRef = (0, import_react8.useRef)(false);
|
|
1772
|
+
const idleTimeRef = (0, import_react8.useRef)(resolveIdleTime(idleTime));
|
|
1773
|
+
idleTimeRef.current = resolveIdleTime(idleTime);
|
|
1774
|
+
(0, import_react8.useEffect)(() => {
|
|
1775
|
+
idleElapsedRef.current = 0;
|
|
1776
|
+
isIdleRef.current = false;
|
|
1777
|
+
}, [idleTime]);
|
|
1778
|
+
(0, import_react8.useEffect)(() => {
|
|
1779
|
+
const unsubscribers = [
|
|
1780
|
+
eventBus.subscribe("viewchange", (view) => {
|
|
1781
|
+
callbacksRef.current.onViewChange?.(view);
|
|
1782
|
+
}),
|
|
1783
|
+
eventBus.subscribe("viewsettled", (view) => {
|
|
1784
|
+
callbacksRef.current.onViewSettled?.(view);
|
|
1785
|
+
}),
|
|
1786
|
+
eventBus.subscribe("viewinteractionstart", (event) => {
|
|
1787
|
+
callbacksRef.current.onViewInteractionStart?.(event);
|
|
1788
|
+
}),
|
|
1789
|
+
eventBus.subscribe("viewinteractionend", (event) => {
|
|
1790
|
+
callbacksRef.current.onViewInteractionEnd?.(event);
|
|
1791
|
+
}),
|
|
1792
|
+
eventBus.subscribe("click", (event) => {
|
|
1793
|
+
callbacksRef.current.onClick?.(event);
|
|
1794
|
+
}),
|
|
1795
|
+
eventBus.subscribe("doubleclick", (event) => {
|
|
1796
|
+
callbacksRef.current.onDoubleClick?.(event);
|
|
1797
|
+
}),
|
|
1798
|
+
eventBus.subscribe("pointerdown", (event) => {
|
|
1799
|
+
callbacksRef.current.onPointerDown?.(event);
|
|
1800
|
+
}),
|
|
1801
|
+
eventBus.subscribe("pointerup", (event) => {
|
|
1802
|
+
callbacksRef.current.onPointerUp?.(event);
|
|
1803
|
+
}),
|
|
1804
|
+
eventBus.subscribe("pointermove", (event) => {
|
|
1805
|
+
callbacksRef.current.onPointerMove?.(event);
|
|
1806
|
+
}),
|
|
1807
|
+
eventBus.subscribe("contextmenu", (event) => {
|
|
1808
|
+
callbacksRef.current.onContextMenu?.(event);
|
|
1809
|
+
}),
|
|
1810
|
+
eventBus.subscribe("autorotatestart", () => {
|
|
1811
|
+
callbacksRef.current.onAutoRotateStart?.();
|
|
1812
|
+
}),
|
|
1813
|
+
eventBus.subscribe("autorotatestop", () => {
|
|
1814
|
+
callbacksRef.current.onAutoRotateStop?.();
|
|
1815
|
+
}),
|
|
1816
|
+
eventBus.subscribe("autorotateoneround", () => {
|
|
1817
|
+
callbacksRef.current.onAutoRotateOneRound?.();
|
|
1818
|
+
})
|
|
1819
|
+
];
|
|
1820
|
+
return () => {
|
|
1821
|
+
for (const unsubscribe of unsubscribers) {
|
|
1822
|
+
unsubscribe();
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
}, [eventBus]);
|
|
1826
|
+
(0, import_react8.useEffect)(() => {
|
|
1827
|
+
const element = gl.domElement;
|
|
1828
|
+
const handleWheel = (nativeEvent) => {
|
|
1829
|
+
callbacksRef.current.onWheel?.({
|
|
1830
|
+
delta: nativeEvent.deltaY,
|
|
1831
|
+
nativeEvent
|
|
1832
|
+
});
|
|
1833
|
+
};
|
|
1834
|
+
element.addEventListener("wheel", handleWheel, { passive: true });
|
|
1835
|
+
return () => {
|
|
1836
|
+
element.removeEventListener("wheel", handleWheel);
|
|
1837
|
+
};
|
|
1838
|
+
}, [gl]);
|
|
1839
|
+
(0, import_react8.useEffect)(() => {
|
|
1840
|
+
if (typeof document === "undefined") {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
const wasFullscreenRef = { current: false };
|
|
1844
|
+
const isViewerFullscreen = () => {
|
|
1845
|
+
const fullscreenElement = document.fullscreenElement;
|
|
1846
|
+
if (!fullscreenElement) {
|
|
1847
|
+
return false;
|
|
1848
|
+
}
|
|
1849
|
+
const canvas = gl.domElement;
|
|
1850
|
+
return fullscreenElement === canvas || fullscreenElement.contains(canvas) || canvas.contains(fullscreenElement);
|
|
1851
|
+
};
|
|
1852
|
+
const handleFullscreenChange = () => {
|
|
1853
|
+
const isOurs = isViewerFullscreen();
|
|
1854
|
+
if (isOurs && !wasFullscreenRef.current) {
|
|
1855
|
+
wasFullscreenRef.current = true;
|
|
1856
|
+
callbacksRef.current.onEnterFullscreen?.();
|
|
1857
|
+
} else if (!isOurs && wasFullscreenRef.current) {
|
|
1858
|
+
wasFullscreenRef.current = false;
|
|
1859
|
+
callbacksRef.current.onExitFullscreen?.();
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
wasFullscreenRef.current = isViewerFullscreen();
|
|
1863
|
+
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
1864
|
+
return () => {
|
|
1865
|
+
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
1866
|
+
};
|
|
1867
|
+
}, [gl]);
|
|
1868
|
+
(0, import_react8.useEffect)(() => {
|
|
1869
|
+
const element = gl.domElement;
|
|
1870
|
+
if (typeof ResizeObserver === "undefined") {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
const observer = new ResizeObserver((entries) => {
|
|
1874
|
+
const entry = entries[0];
|
|
1875
|
+
if (!entry) {
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const { width, height } = entry.contentRect;
|
|
1879
|
+
callbacksRef.current.onResize?.({ width, height });
|
|
1880
|
+
});
|
|
1881
|
+
observer.observe(element);
|
|
1882
|
+
return () => {
|
|
1883
|
+
observer.disconnect();
|
|
1884
|
+
};
|
|
1885
|
+
}, [gl]);
|
|
1886
|
+
(0, import_fiber5.useFrame)((_, deltaSeconds) => {
|
|
1887
|
+
const interacting = controlsRef.current?.isUserInteracting() ?? false;
|
|
1888
|
+
if (interacting) {
|
|
1889
|
+
idleElapsedRef.current = 0;
|
|
1890
|
+
if (isIdleRef.current) {
|
|
1891
|
+
isIdleRef.current = false;
|
|
1892
|
+
callbacksRef.current.onIdleEnd?.();
|
|
1893
|
+
}
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
idleElapsedRef.current += deltaSeconds * 1e3;
|
|
1897
|
+
if (!isIdleRef.current && idleElapsedRef.current >= idleTimeRef.current) {
|
|
1898
|
+
isIdleRef.current = true;
|
|
1899
|
+
callbacksRef.current.onIdle?.();
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
function PanoEvents(props) {
|
|
1904
|
+
usePanoEvents(props);
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
// src/panorama-event-surface.tsx
|
|
1909
|
+
var import_fiber6 = require("@react-three/fiber");
|
|
1910
|
+
var import_react9 = require("react");
|
|
1911
|
+
var import_three3 = require("three");
|
|
1912
|
+
|
|
1913
|
+
// src/hotspot/coordinates.ts
|
|
1914
|
+
var import_three2 = require("three");
|
|
1915
|
+
|
|
1916
|
+
// src/hotspot/types.ts
|
|
1917
|
+
var MAX_HOTSPOT_PITCH = 90;
|
|
1918
|
+
|
|
1919
|
+
// src/hotspot/coordinates.ts
|
|
1920
|
+
var POLE_EPSILON_DEGREES = 1e-6;
|
|
1921
|
+
function normalizePanoYaw(yaw) {
|
|
1922
|
+
if (!Number.isFinite(yaw)) {
|
|
1923
|
+
return 0;
|
|
1924
|
+
}
|
|
1925
|
+
return import_three2.MathUtils.euclideanModulo(yaw + 180, 360) - 180;
|
|
1926
|
+
}
|
|
1927
|
+
function clampPanoPitch(pitch) {
|
|
1928
|
+
if (!Number.isFinite(pitch)) {
|
|
1929
|
+
return 0;
|
|
1930
|
+
}
|
|
1931
|
+
const clamped = import_three2.MathUtils.clamp(
|
|
1932
|
+
pitch,
|
|
1933
|
+
-MAX_HOTSPOT_PITCH,
|
|
1934
|
+
MAX_HOTSPOT_PITCH
|
|
1935
|
+
);
|
|
1936
|
+
return Math.abs(clamped) >= MAX_HOTSPOT_PITCH - POLE_EPSILON_DEGREES ? Math.sign(clamped) * MAX_HOTSPOT_PITCH : clamped;
|
|
1937
|
+
}
|
|
1938
|
+
function normalizePanoPosition(position) {
|
|
1939
|
+
const pitch = clampPanoPitch(position.pitch);
|
|
1940
|
+
return {
|
|
1941
|
+
yaw: Math.abs(pitch) === MAX_HOTSPOT_PITCH ? 0 : normalizePanoYaw(position.yaw),
|
|
1942
|
+
pitch
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
function panoPositionToVector3(position, radius = 1) {
|
|
1946
|
+
const normalized = normalizePanoPosition(position);
|
|
1947
|
+
const safeRadius = Number.isFinite(radius) ? Math.max(0, radius) : 1;
|
|
1948
|
+
const yaw = import_three2.MathUtils.degToRad(normalized.yaw);
|
|
1949
|
+
const pitch = import_three2.MathUtils.degToRad(normalized.pitch);
|
|
1950
|
+
const cosPitch = Math.cos(pitch);
|
|
1951
|
+
return new import_three2.Vector3(
|
|
1952
|
+
cosPitch * Math.sin(yaw) * safeRadius,
|
|
1953
|
+
Math.sin(pitch) * safeRadius,
|
|
1954
|
+
-cosPitch * Math.cos(yaw) * safeRadius
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
function vector3ToPanoPosition(vector) {
|
|
1958
|
+
const length = Math.hypot(vector.x, vector.y, vector.z);
|
|
1959
|
+
if (!Number.isFinite(length) || length === 0) {
|
|
1960
|
+
return { yaw: 0, pitch: 0 };
|
|
1961
|
+
}
|
|
1962
|
+
return normalizePanoPosition({
|
|
1963
|
+
yaw: import_three2.MathUtils.radToDeg(Math.atan2(vector.x, -vector.z)),
|
|
1964
|
+
pitch: import_three2.MathUtils.radToDeg(
|
|
1965
|
+
Math.asin(import_three2.MathUtils.clamp(vector.y / length, -1, 1))
|
|
1966
|
+
)
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// src/panorama-radius.ts
|
|
1971
|
+
var DEFAULT_PANORAMA_RADIUS = 1e3;
|
|
1972
|
+
var DEFAULT_PANORAMA_CAMERA_NEAR = 0.1;
|
|
1973
|
+
var DEFAULT_PANORAMA_CAMERA_FAR = DEFAULT_PANORAMA_RADIUS * 2;
|
|
1974
|
+
|
|
1975
|
+
// src/panorama-event-surface.tsx
|
|
1976
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1977
|
+
var EVENT_SURFACE_RADIUS = DEFAULT_PANORAMA_RADIUS - 1;
|
|
1978
|
+
function makePanoramaPointerEvent(event) {
|
|
1979
|
+
const nativeEvent = event.nativeEvent;
|
|
1980
|
+
return {
|
|
1981
|
+
position: vector3ToPanoPosition(event.ray.direction),
|
|
1982
|
+
nativeEvent,
|
|
1983
|
+
pointerId: "pointerId" in nativeEvent ? nativeEvent.pointerId : void 0,
|
|
1984
|
+
button: nativeEvent.button,
|
|
1985
|
+
buttons: nativeEvent.buttons,
|
|
1986
|
+
clientX: nativeEvent.clientX,
|
|
1987
|
+
clientY: nativeEvent.clientY
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function PanoramaEventSurface() {
|
|
1991
|
+
const eventBus = (0, import_react9.useContext)(PanoEventBusContext);
|
|
1992
|
+
const { camera, gl } = (0, import_fiber6.useThree)();
|
|
1993
|
+
(0, import_react9.useEffect)(() => {
|
|
1994
|
+
if (!eventBus) {
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
const element = gl.domElement;
|
|
1998
|
+
const ndc = new import_three3.Vector2();
|
|
1999
|
+
const raycaster = new import_three3.Raycaster();
|
|
2000
|
+
const direction = new import_three3.Vector3();
|
|
2001
|
+
const handleContextMenu = (nativeEvent) => {
|
|
2002
|
+
const rect = element.getBoundingClientRect();
|
|
2003
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
ndc.set(
|
|
2007
|
+
(nativeEvent.clientX - rect.left) / rect.width * 2 - 1,
|
|
2008
|
+
-((nativeEvent.clientY - rect.top) / rect.height) * 2 + 1
|
|
2009
|
+
);
|
|
2010
|
+
raycaster.setFromCamera(ndc, camera);
|
|
2011
|
+
direction.copy(raycaster.ray.direction);
|
|
2012
|
+
eventBus.emit("contextmenu", {
|
|
2013
|
+
position: vector3ToPanoPosition(direction),
|
|
2014
|
+
nativeEvent,
|
|
2015
|
+
pointerId: "pointerId" in nativeEvent ? nativeEvent.pointerId : void 0,
|
|
2016
|
+
button: nativeEvent.button,
|
|
2017
|
+
buttons: nativeEvent.buttons,
|
|
2018
|
+
clientX: nativeEvent.clientX,
|
|
2019
|
+
clientY: nativeEvent.clientY
|
|
2020
|
+
});
|
|
2021
|
+
};
|
|
2022
|
+
element.addEventListener("contextmenu", handleContextMenu);
|
|
2023
|
+
return () => {
|
|
2024
|
+
element.removeEventListener("contextmenu", handleContextMenu);
|
|
2025
|
+
};
|
|
2026
|
+
}, [camera, eventBus, gl]);
|
|
2027
|
+
if (!eventBus) {
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
2030
|
+
const emit = (type, event) => {
|
|
2031
|
+
event.stopPropagation();
|
|
2032
|
+
eventBus.emit(type, makePanoramaPointerEvent(event));
|
|
2033
|
+
};
|
|
2034
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
670
2035
|
"mesh",
|
|
671
2036
|
{
|
|
672
2037
|
renderOrder: -200,
|
|
673
|
-
onClick: (event) =>
|
|
674
|
-
onDoubleClick: (event) =>
|
|
675
|
-
|
|
2038
|
+
onClick: (event) => emit("click", event),
|
|
2039
|
+
onDoubleClick: (event) => emit("doubleclick", event),
|
|
2040
|
+
onPointerDown: (event) => emit("pointerdown", event),
|
|
2041
|
+
onPointerUp: (event) => emit("pointerup", event),
|
|
2042
|
+
onPointerMove: (event) => emit("pointermove", event),
|
|
676
2043
|
children: [
|
|
677
|
-
/* @__PURE__ */ (0,
|
|
678
|
-
/* @__PURE__ */ (0,
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("sphereGeometry", { args: [EVENT_SURFACE_RADIUS, 64, 32] }),
|
|
2045
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
679
2046
|
"meshBasicMaterial",
|
|
680
2047
|
{
|
|
681
2048
|
colorWrite: false,
|
|
@@ -685,31 +2052,205 @@ function PanoramaEventSurface({
|
|
|
685
2052
|
side: import_three3.BackSide,
|
|
686
2053
|
transparent: true
|
|
687
2054
|
}
|
|
688
|
-
)
|
|
689
|
-
]
|
|
690
|
-
}
|
|
691
|
-
);
|
|
2055
|
+
)
|
|
2056
|
+
]
|
|
2057
|
+
}
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
// src/touch-controls.tsx
|
|
2062
|
+
var import_fiber7 = require("@react-three/fiber");
|
|
2063
|
+
var import_react10 = require("react");
|
|
2064
|
+
var DEFAULT_ROTATE_SPEED3 = 0.35;
|
|
2065
|
+
function resolveSpeed3(value, fallback) {
|
|
2066
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
2067
|
+
}
|
|
2068
|
+
function pointerDistance(a, b) {
|
|
2069
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
2070
|
+
}
|
|
2071
|
+
function TouchControls({
|
|
2072
|
+
enabled = true,
|
|
2073
|
+
rotateSpeed = DEFAULT_ROTATE_SPEED3,
|
|
2074
|
+
invert = false,
|
|
2075
|
+
pinchZoom = true
|
|
2076
|
+
}) {
|
|
2077
|
+
const controlsRef = (0, import_react10.useContext)(PanoramaViewContext);
|
|
2078
|
+
const { gl } = (0, import_fiber7.useThree)();
|
|
2079
|
+
const pointersRef = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
|
|
2080
|
+
const pinchDistanceRef = (0, import_react10.useRef)(null);
|
|
2081
|
+
const optionsRef = (0, import_react10.useRef)({ rotateSpeed, invert, pinchZoom });
|
|
2082
|
+
if (!controlsRef) {
|
|
2083
|
+
throw new Error("<TouchControls> must be rendered inside <PanoView>.");
|
|
2084
|
+
}
|
|
2085
|
+
optionsRef.current = { rotateSpeed, invert, pinchZoom };
|
|
2086
|
+
(0, import_react10.useEffect)(() => {
|
|
2087
|
+
const element = gl.domElement;
|
|
2088
|
+
const clearPointers = () => {
|
|
2089
|
+
for (const pointerId of pointersRef.current.keys()) {
|
|
2090
|
+
if (element.hasPointerCapture?.(pointerId)) {
|
|
2091
|
+
element.releasePointerCapture?.(pointerId);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
pointersRef.current.clear();
|
|
2095
|
+
pinchDistanceRef.current = null;
|
|
2096
|
+
controlsRef.current?.setPointerActive("touch", false);
|
|
2097
|
+
};
|
|
2098
|
+
if (!enabled) {
|
|
2099
|
+
clearPointers();
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
const syncActive = () => {
|
|
2103
|
+
controlsRef.current?.setPointerActive(
|
|
2104
|
+
"touch",
|
|
2105
|
+
pointersRef.current.size > 0
|
|
2106
|
+
);
|
|
2107
|
+
};
|
|
2108
|
+
const onPointerDown = (event) => {
|
|
2109
|
+
if (event.pointerType !== "touch") {
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
event.preventDefault();
|
|
2116
|
+
pointersRef.current.set(event.pointerId, {
|
|
2117
|
+
x: event.clientX,
|
|
2118
|
+
y: event.clientY,
|
|
2119
|
+
at: performance.now()
|
|
2120
|
+
});
|
|
2121
|
+
element.setPointerCapture?.(event.pointerId);
|
|
2122
|
+
syncActive();
|
|
2123
|
+
if (pointersRef.current.size === 2) {
|
|
2124
|
+
const [first, second] = Array.from(pointersRef.current.values());
|
|
2125
|
+
pinchDistanceRef.current = pointerDistance(first, second);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
const onPointerMove = (event) => {
|
|
2129
|
+
if (event.pointerType !== "touch") {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
const previous = pointersRef.current.get(event.pointerId);
|
|
2136
|
+
if (!previous) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
event.preventDefault();
|
|
2140
|
+
const next = {
|
|
2141
|
+
x: event.clientX,
|
|
2142
|
+
y: event.clientY,
|
|
2143
|
+
at: performance.now()
|
|
2144
|
+
};
|
|
2145
|
+
pointersRef.current.set(event.pointerId, next);
|
|
2146
|
+
const pointers = Array.from(pointersRef.current.values());
|
|
2147
|
+
if (pointers.length >= 2) {
|
|
2148
|
+
if (!optionsRef.current.pinchZoom) {
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
const distance = pointerDistance(pointers[0], pointers[1]);
|
|
2152
|
+
const previousDistance = pinchDistanceRef.current;
|
|
2153
|
+
pinchDistanceRef.current = distance;
|
|
2154
|
+
if (previousDistance && distance > 0) {
|
|
2155
|
+
const view = controlsRef.current?.getTargetView();
|
|
2156
|
+
if (view) {
|
|
2157
|
+
const nextFov = view.fov * (previousDistance / distance);
|
|
2158
|
+
controlsRef.current?.applyViewDelta(
|
|
2159
|
+
{ fov: nextFov - view.fov },
|
|
2160
|
+
{ recordVelocity: true }
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
const rect = element.getBoundingClientRect();
|
|
2167
|
+
const speed = resolveSpeed3(
|
|
2168
|
+
optionsRef.current.rotateSpeed,
|
|
2169
|
+
DEFAULT_ROTATE_SPEED3
|
|
2170
|
+
);
|
|
2171
|
+
const sign = optionsRef.current.invert ? -1 : 1;
|
|
2172
|
+
const deltaYaw = sign * -(next.x - previous.x) * 360 * speed / Math.max(rect.width, 1);
|
|
2173
|
+
const deltaPitch = sign * (next.y - previous.y) * 180 * speed / Math.max(rect.height, 1);
|
|
2174
|
+
controlsRef.current?.applyViewDelta(
|
|
2175
|
+
{ yaw: deltaYaw, pitch: deltaPitch },
|
|
2176
|
+
{ recordVelocity: true }
|
|
2177
|
+
);
|
|
2178
|
+
};
|
|
2179
|
+
const releasePointer = (event) => {
|
|
2180
|
+
if (!pointersRef.current.has(event.pointerId)) {
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
pointersRef.current.delete(event.pointerId);
|
|
2184
|
+
if (element.hasPointerCapture?.(event.pointerId)) {
|
|
2185
|
+
element.releasePointerCapture?.(event.pointerId);
|
|
2186
|
+
}
|
|
2187
|
+
if (pointersRef.current.size < 2) {
|
|
2188
|
+
pinchDistanceRef.current = null;
|
|
2189
|
+
}
|
|
2190
|
+
syncActive();
|
|
2191
|
+
};
|
|
2192
|
+
element.addEventListener("pointerdown", onPointerDown);
|
|
2193
|
+
element.addEventListener("pointermove", onPointerMove);
|
|
2194
|
+
element.addEventListener("pointerup", releasePointer);
|
|
2195
|
+
element.addEventListener("pointercancel", releasePointer);
|
|
2196
|
+
return () => {
|
|
2197
|
+
element.removeEventListener("pointerdown", onPointerDown);
|
|
2198
|
+
element.removeEventListener("pointermove", onPointerMove);
|
|
2199
|
+
element.removeEventListener("pointerup", releasePointer);
|
|
2200
|
+
element.removeEventListener("pointercancel", releasePointer);
|
|
2201
|
+
clearPointers();
|
|
2202
|
+
};
|
|
2203
|
+
}, [controlsRef, enabled, gl]);
|
|
2204
|
+
return null;
|
|
692
2205
|
}
|
|
693
2206
|
|
|
694
2207
|
// src/pano-view.tsx
|
|
695
|
-
var
|
|
2208
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
696
2209
|
var DEFAULT_VIEW = {
|
|
697
2210
|
yaw: 0,
|
|
698
2211
|
pitch: 0,
|
|
699
2212
|
fov: 75
|
|
700
2213
|
};
|
|
2214
|
+
var DEFAULT_POINTER_ROTATE_SPEED = 0.35;
|
|
2215
|
+
var DEFAULT_MOUSE_ZOOM_SPEED = 0.08;
|
|
701
2216
|
var DEFAULT_CANVAS_STYLE = {
|
|
702
2217
|
display: "block",
|
|
703
2218
|
height: "100%",
|
|
704
2219
|
width: "100%"
|
|
705
2220
|
};
|
|
706
|
-
|
|
2221
|
+
function resolveChannel(value, defaults) {
|
|
2222
|
+
if (value === false) {
|
|
2223
|
+
return { mount: false };
|
|
2224
|
+
}
|
|
2225
|
+
if (value === true || value === void 0) {
|
|
2226
|
+
return { mount: true, props: { ...defaults } };
|
|
2227
|
+
}
|
|
2228
|
+
return {
|
|
2229
|
+
mount: true,
|
|
2230
|
+
props: {
|
|
2231
|
+
...defaults,
|
|
2232
|
+
...value,
|
|
2233
|
+
enabled: value.enabled !== false
|
|
2234
|
+
}
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
function resolveContextMenuChannel(value) {
|
|
2238
|
+
if (value === false) {
|
|
2239
|
+
return { mount: false };
|
|
2240
|
+
}
|
|
2241
|
+
if (value === true || value === void 0) {
|
|
2242
|
+
return { mount: true, props: {} };
|
|
2243
|
+
}
|
|
2244
|
+
return { mount: true, props: { ...value } };
|
|
2245
|
+
}
|
|
2246
|
+
var PanoView = (0, import_react11.forwardRef)(
|
|
707
2247
|
function PanoView2({
|
|
708
2248
|
children,
|
|
709
2249
|
initialView,
|
|
710
2250
|
minFov = 30,
|
|
711
2251
|
maxFov = 100,
|
|
712
2252
|
controls = true,
|
|
2253
|
+
contextMenu = true,
|
|
713
2254
|
onViewChange,
|
|
714
2255
|
onPanoramaClick,
|
|
715
2256
|
onPanoramaDoubleClick,
|
|
@@ -719,16 +2260,18 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
719
2260
|
"aria-label": ariaLabel = "Panorama viewer",
|
|
720
2261
|
...divProps
|
|
721
2262
|
}, ref) {
|
|
722
|
-
const rootRef = (0,
|
|
723
|
-
const controlsRef = (0,
|
|
2263
|
+
const rootRef = (0, import_react11.useRef)(null);
|
|
2264
|
+
const controlsRef = (0, import_react11.useRef)(null);
|
|
2265
|
+
const eventBus = (0, import_react11.useMemo)(() => createPanoEventBus(), []);
|
|
724
2266
|
const { controls: hotspotAccessibilityControls, registry } = useHotspotAccessibilityLayer();
|
|
725
|
-
const
|
|
2267
|
+
const { api: contextMenuOverlayApi, overlay: contextMenuOverlay } = usePanoContextMenuOverlay();
|
|
2268
|
+
const fallbackViewRef = (0, import_react11.useRef)(DEFAULT_VIEW);
|
|
726
2269
|
const normalizedMinFov = Math.max(1, Math.min(minFov, maxFov - 1));
|
|
727
2270
|
const normalizedMaxFov = Math.min(
|
|
728
2271
|
179,
|
|
729
2272
|
Math.max(maxFov, normalizedMinFov + 1)
|
|
730
2273
|
);
|
|
731
|
-
const normalizedInitialView = (0,
|
|
2274
|
+
const normalizedInitialView = (0, import_react11.useMemo)(
|
|
732
2275
|
() => ({
|
|
733
2276
|
yaw: initialView?.yaw ?? DEFAULT_VIEW.yaw,
|
|
734
2277
|
pitch: clampPanoPitch(initialView?.pitch ?? DEFAULT_VIEW.pitch),
|
|
@@ -747,18 +2290,101 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
747
2290
|
);
|
|
748
2291
|
fallbackViewRef.current = normalizedInitialView;
|
|
749
2292
|
const controlsEnabled = controls !== false;
|
|
750
|
-
const controlOptions = (0,
|
|
2293
|
+
const controlOptions = (0, import_react11.useMemo)(
|
|
751
2294
|
() => typeof controls === "object" ? controls : {},
|
|
752
2295
|
[controls]
|
|
753
2296
|
);
|
|
2297
|
+
const userControlsEnabled = controlsEnabled && controlOptions.enabled !== false;
|
|
2298
|
+
const allModeInvert = controlOptions.invert === true;
|
|
2299
|
+
const topRotateSpeed = controlOptions.rotateSpeed ?? DEFAULT_POINTER_ROTATE_SPEED;
|
|
2300
|
+
const topZoomSpeed = controlOptions.zoomSpeed ?? DEFAULT_MOUSE_ZOOM_SPEED;
|
|
2301
|
+
const mouseChannel = (0, import_react11.useMemo)(
|
|
2302
|
+
() => resolveChannel(controlOptions.mouse, {
|
|
2303
|
+
enabled: true,
|
|
2304
|
+
rotateSpeed: topRotateSpeed,
|
|
2305
|
+
zoomSpeed: topZoomSpeed,
|
|
2306
|
+
wheel: true,
|
|
2307
|
+
invert: allModeInvert,
|
|
2308
|
+
buttons: ["left"]
|
|
2309
|
+
}),
|
|
2310
|
+
[
|
|
2311
|
+
allModeInvert,
|
|
2312
|
+
controlOptions.mouse,
|
|
2313
|
+
topRotateSpeed,
|
|
2314
|
+
topZoomSpeed
|
|
2315
|
+
]
|
|
2316
|
+
);
|
|
2317
|
+
const touchChannel = (0, import_react11.useMemo)(
|
|
2318
|
+
() => resolveChannel(controlOptions.touch, {
|
|
2319
|
+
enabled: true,
|
|
2320
|
+
rotateSpeed: topRotateSpeed,
|
|
2321
|
+
invert: allModeInvert,
|
|
2322
|
+
pinchZoom: true
|
|
2323
|
+
}),
|
|
2324
|
+
[allModeInvert, controlOptions.touch, topRotateSpeed]
|
|
2325
|
+
);
|
|
2326
|
+
const keyboardChannel = (0, import_react11.useMemo)(
|
|
2327
|
+
() => resolveChannel(controlOptions.keyboard, {
|
|
2328
|
+
enabled: true
|
|
2329
|
+
}),
|
|
2330
|
+
[controlOptions.keyboard]
|
|
2331
|
+
);
|
|
2332
|
+
const toggleFullscreen = (0, import_react11.useCallback)(async () => {
|
|
2333
|
+
if (typeof document === "undefined") {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (document.fullscreenElement) {
|
|
2337
|
+
await document.exitFullscreen?.();
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
await rootRef.current?.requestFullscreen?.();
|
|
2341
|
+
}, []);
|
|
2342
|
+
const [isViewerFullscreen, setIsViewerFullscreen] = (0, import_react11.useState)(false);
|
|
2343
|
+
(0, import_react11.useEffect)(() => {
|
|
2344
|
+
if (typeof document === "undefined") {
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
const syncFullscreen = () => {
|
|
2348
|
+
const fullscreenElement = document.fullscreenElement;
|
|
2349
|
+
const root = rootRef.current;
|
|
2350
|
+
if (!fullscreenElement || !root) {
|
|
2351
|
+
setIsViewerFullscreen(false);
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
setIsViewerFullscreen(
|
|
2355
|
+
fullscreenElement === root || fullscreenElement.contains(root) || root.contains(fullscreenElement)
|
|
2356
|
+
);
|
|
2357
|
+
};
|
|
2358
|
+
syncFullscreen();
|
|
2359
|
+
document.addEventListener("fullscreenchange", syncFullscreen);
|
|
2360
|
+
return () => {
|
|
2361
|
+
document.removeEventListener("fullscreenchange", syncFullscreen);
|
|
2362
|
+
};
|
|
2363
|
+
}, []);
|
|
2364
|
+
const contextMenuActions = (0, import_react11.useMemo)(
|
|
2365
|
+
() => ({
|
|
2366
|
+
reset: () => {
|
|
2367
|
+
controlsRef.current?.reset();
|
|
2368
|
+
},
|
|
2369
|
+
toggleFullscreen: () => {
|
|
2370
|
+
void toggleFullscreen();
|
|
2371
|
+
},
|
|
2372
|
+
isFullscreen: isViewerFullscreen
|
|
2373
|
+
}),
|
|
2374
|
+
[isViewerFullscreen, toggleFullscreen]
|
|
2375
|
+
);
|
|
2376
|
+
const contextMenuChannel = (0, import_react11.useMemo)(
|
|
2377
|
+
() => resolveContextMenuChannel(contextMenu),
|
|
2378
|
+
[contextMenu]
|
|
2379
|
+
);
|
|
754
2380
|
const legacyAutoRotateOptions = controlOptions;
|
|
755
|
-
const [legacyAutoRotate, setLegacyAutoRotate] = (0,
|
|
2381
|
+
const [legacyAutoRotate, setLegacyAutoRotate] = (0, import_react11.useState)(
|
|
756
2382
|
legacyAutoRotateOptions.autoRotate ?? false
|
|
757
2383
|
);
|
|
758
|
-
(0,
|
|
2384
|
+
(0, import_react11.useEffect)(() => {
|
|
759
2385
|
setLegacyAutoRotate(legacyAutoRotateOptions.autoRotate ?? false);
|
|
760
2386
|
}, [legacyAutoRotateOptions.autoRotate]);
|
|
761
|
-
(0,
|
|
2387
|
+
(0, import_react11.useImperativeHandle)(
|
|
762
2388
|
ref,
|
|
763
2389
|
() => ({
|
|
764
2390
|
getView: () => controlsRef.current?.getView() ?? { ...fallbackViewRef.current },
|
|
@@ -774,20 +2400,11 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
774
2400
|
stopAutoRotate: () => {
|
|
775
2401
|
setLegacyAutoRotate(false);
|
|
776
2402
|
},
|
|
777
|
-
toggleFullscreen
|
|
778
|
-
if (typeof document === "undefined") {
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
if (document.fullscreenElement) {
|
|
782
|
-
await document.exitFullscreen?.();
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
await rootRef.current?.requestFullscreen?.();
|
|
786
|
-
}
|
|
2403
|
+
toggleFullscreen
|
|
787
2404
|
}),
|
|
788
|
-
[]
|
|
2405
|
+
[toggleFullscreen]
|
|
789
2406
|
);
|
|
790
|
-
const rootStyle = (0,
|
|
2407
|
+
const rootStyle = (0, import_react11.useMemo)(
|
|
791
2408
|
() => ({
|
|
792
2409
|
overflow: "hidden",
|
|
793
2410
|
position: "relative",
|
|
@@ -795,7 +2412,7 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
795
2412
|
}),
|
|
796
2413
|
[style]
|
|
797
2414
|
);
|
|
798
|
-
return /* @__PURE__ */ (0,
|
|
2415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
799
2416
|
"div",
|
|
800
2417
|
{
|
|
801
2418
|
...divProps,
|
|
@@ -803,8 +2420,8 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
803
2420
|
"aria-label": ariaLabel,
|
|
804
2421
|
style: rootStyle,
|
|
805
2422
|
children: [
|
|
806
|
-
/* @__PURE__ */ (0,
|
|
807
|
-
|
|
2423
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2424
|
+
import_fiber8.Canvas,
|
|
808
2425
|
{
|
|
809
2426
|
"aria-label": `${ariaLabel} canvas`,
|
|
810
2427
|
camera: {
|
|
@@ -823,41 +2440,71 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
823
2440
|
},
|
|
824
2441
|
style: {
|
|
825
2442
|
...DEFAULT_CANVAS_STYLE,
|
|
826
|
-
touchAction:
|
|
2443
|
+
touchAction: userControlsEnabled ? "none" : "auto"
|
|
827
2444
|
},
|
|
828
|
-
tabIndex:
|
|
829
|
-
children: /* @__PURE__ */ (0,
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
2445
|
+
tabIndex: userControlsEnabled ? 0 : void 0,
|
|
2446
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HotspotAccessibilityContext.Provider, { value: registry, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanoEventBusContext.Provider, { value: eventBus, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanoramaViewContext.Provider, { value: controlsRef, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2447
|
+
PanoContextMenuOverlayContext.Provider,
|
|
2448
|
+
{
|
|
2449
|
+
value: contextMenuOverlayApi,
|
|
2450
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2451
|
+
PanoContextMenuActionsContext.Provider,
|
|
2452
|
+
{
|
|
2453
|
+
value: contextMenuActions,
|
|
2454
|
+
children: [
|
|
2455
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2456
|
+
PanoramaViewRuntime,
|
|
2457
|
+
{
|
|
2458
|
+
ref: controlsRef,
|
|
2459
|
+
eventBus,
|
|
2460
|
+
initialView: normalizedInitialView,
|
|
2461
|
+
maxFov: normalizedMaxFov,
|
|
2462
|
+
minFov: normalizedMinFov,
|
|
2463
|
+
options: controlOptions
|
|
2464
|
+
}
|
|
2465
|
+
),
|
|
2466
|
+
(onViewChange || onPanoramaClick || onPanoramaDoubleClick || onPanoramaPointerMove) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2467
|
+
PanoEvents,
|
|
2468
|
+
{
|
|
2469
|
+
onViewChange,
|
|
2470
|
+
onClick: onPanoramaClick,
|
|
2471
|
+
onDoubleClick: onPanoramaDoubleClick,
|
|
2472
|
+
onPointerMove: onPanoramaPointerMove
|
|
2473
|
+
}
|
|
2474
|
+
),
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2476
|
+
AutoRotate,
|
|
2477
|
+
{
|
|
2478
|
+
enabled: legacyAutoRotate,
|
|
2479
|
+
speed: legacyAutoRotateOptions.autoRotateSpeed
|
|
2480
|
+
}
|
|
2481
|
+
),
|
|
2482
|
+
userControlsEnabled && mouseChannel.mount ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2483
|
+
MouseControls,
|
|
2484
|
+
{
|
|
2485
|
+
...mouseChannel.props,
|
|
2486
|
+
fovSpeed: controlOptions.fovSpeed
|
|
2487
|
+
}
|
|
2488
|
+
) : null,
|
|
2489
|
+
userControlsEnabled && touchChannel.mount ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TouchControls, { ...touchChannel.props }) : null,
|
|
2490
|
+
userControlsEnabled && keyboardChannel.mount ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2491
|
+
KeyboardControls,
|
|
2492
|
+
{
|
|
2493
|
+
...keyboardChannel.props,
|
|
2494
|
+
fovSpeed: keyboardChannel.props.fovSpeed ?? controlOptions.fovSpeed
|
|
2495
|
+
}
|
|
2496
|
+
) : null,
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanoramaEventSurface, {}),
|
|
2498
|
+
contextMenuChannel.mount ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanoContextMenu, { ...contextMenuChannel.props }) : null,
|
|
2499
|
+
children
|
|
2500
|
+
]
|
|
2501
|
+
}
|
|
2502
|
+
)
|
|
2503
|
+
}
|
|
2504
|
+
) }) }) })
|
|
859
2505
|
}
|
|
860
2506
|
),
|
|
2507
|
+
contextMenuOverlay,
|
|
861
2508
|
hotspotAccessibilityControls
|
|
862
2509
|
]
|
|
863
2510
|
}
|
|
@@ -866,10 +2513,10 @@ var PanoView = (0, import_react4.forwardRef)(
|
|
|
866
2513
|
);
|
|
867
2514
|
|
|
868
2515
|
// src/sphere.tsx
|
|
869
|
-
var
|
|
870
|
-
var
|
|
2516
|
+
var import_fiber9 = require("@react-three/fiber");
|
|
2517
|
+
var import_react12 = require("react");
|
|
871
2518
|
var import_three4 = require("three");
|
|
872
|
-
var
|
|
2519
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
873
2520
|
function Sphere({
|
|
874
2521
|
src,
|
|
875
2522
|
yawOffset = 0,
|
|
@@ -877,13 +2524,13 @@ function Sphere({
|
|
|
877
2524
|
onError,
|
|
878
2525
|
visible = true
|
|
879
2526
|
}) {
|
|
880
|
-
const gl = (0,
|
|
881
|
-
const [texture, setTexture] = (0,
|
|
882
|
-
const onLoadRef = (0,
|
|
883
|
-
const onErrorRef = (0,
|
|
2527
|
+
const gl = (0, import_fiber9.useThree)((state) => state.gl);
|
|
2528
|
+
const [texture, setTexture] = (0, import_react12.useState)(null);
|
|
2529
|
+
const onLoadRef = (0, import_react12.useRef)(onLoad);
|
|
2530
|
+
const onErrorRef = (0, import_react12.useRef)(onError);
|
|
884
2531
|
onLoadRef.current = onLoad;
|
|
885
2532
|
onErrorRef.current = onError;
|
|
886
|
-
(0,
|
|
2533
|
+
(0, import_react12.useEffect)(() => {
|
|
887
2534
|
let active = true;
|
|
888
2535
|
const loader = new import_three4.TextureLoader();
|
|
889
2536
|
setTexture(null);
|
|
@@ -917,7 +2564,7 @@ function Sphere({
|
|
|
917
2564
|
active = false;
|
|
918
2565
|
};
|
|
919
2566
|
}, [gl, src]);
|
|
920
|
-
(0,
|
|
2567
|
+
(0, import_react12.useEffect)(
|
|
921
2568
|
() => () => {
|
|
922
2569
|
texture?.dispose();
|
|
923
2570
|
},
|
|
@@ -926,14 +2573,14 @@ function Sphere({
|
|
|
926
2573
|
if (!texture) {
|
|
927
2574
|
return null;
|
|
928
2575
|
}
|
|
929
|
-
return /* @__PURE__ */ (0,
|
|
2576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
930
2577
|
"mesh",
|
|
931
2578
|
{
|
|
932
2579
|
rotation: [0, import_three4.MathUtils.degToRad(90 + yawOffset), 0],
|
|
933
2580
|
visible,
|
|
934
2581
|
children: [
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
2582
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
|
|
2583
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
937
2584
|
"meshBasicMaterial",
|
|
938
2585
|
{
|
|
939
2586
|
map: texture,
|
|
@@ -947,13 +2594,13 @@ function Sphere({
|
|
|
947
2594
|
}
|
|
948
2595
|
|
|
949
2596
|
// src/panorama-scenes.tsx
|
|
950
|
-
var
|
|
951
|
-
var
|
|
2597
|
+
var import_fiber12 = require("@react-three/fiber");
|
|
2598
|
+
var import_react15 = require("react");
|
|
952
2599
|
var import_three8 = require("three");
|
|
953
2600
|
|
|
954
2601
|
// src/tile/texture-manager-context.tsx
|
|
955
|
-
var
|
|
956
|
-
var
|
|
2602
|
+
var import_fiber10 = require("@react-three/fiber");
|
|
2603
|
+
var import_react13 = require("react");
|
|
957
2604
|
|
|
958
2605
|
// src/tile/texture-manager.ts
|
|
959
2606
|
var import_three5 = require("three");
|
|
@@ -1177,8 +2824,8 @@ var TileTextureManager = class {
|
|
|
1177
2824
|
};
|
|
1178
2825
|
|
|
1179
2826
|
// src/tile/texture-manager-context.tsx
|
|
1180
|
-
var
|
|
1181
|
-
var TileTextureManagerContext = (0,
|
|
2827
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2828
|
+
var TileTextureManagerContext = (0, import_react13.createContext)(
|
|
1182
2829
|
null
|
|
1183
2830
|
);
|
|
1184
2831
|
function TileTextureManagerProvider({
|
|
@@ -1186,9 +2833,9 @@ function TileTextureManagerProvider({
|
|
|
1186
2833
|
maxTextureMemoryMb = 128,
|
|
1187
2834
|
maxConcurrentLoads = 4
|
|
1188
2835
|
}) {
|
|
1189
|
-
const gl = (0,
|
|
2836
|
+
const gl = (0, import_fiber10.useThree)((state) => state.gl);
|
|
1190
2837
|
const anisotropy = gl.capabilities.getMaxAnisotropy();
|
|
1191
|
-
const manager = (0,
|
|
2838
|
+
const manager = (0, import_react13.useMemo)(
|
|
1192
2839
|
() => new TileTextureManager({
|
|
1193
2840
|
anisotropy,
|
|
1194
2841
|
maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
|
|
@@ -1197,19 +2844,19 @@ function TileTextureManagerProvider({
|
|
|
1197
2844
|
}),
|
|
1198
2845
|
[anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
|
|
1199
2846
|
);
|
|
1200
|
-
(0,
|
|
2847
|
+
(0, import_react13.useEffect)(() => {
|
|
1201
2848
|
manager.resume();
|
|
1202
2849
|
return () => manager.dispose();
|
|
1203
2850
|
}, [manager]);
|
|
1204
|
-
return /* @__PURE__ */ (0,
|
|
2851
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TileTextureManagerContext.Provider, { value: manager, children });
|
|
1205
2852
|
}
|
|
1206
2853
|
function useSharedTileTextureManager() {
|
|
1207
|
-
return (0,
|
|
2854
|
+
return (0, import_react13.useContext)(TileTextureManagerContext);
|
|
1208
2855
|
}
|
|
1209
2856
|
|
|
1210
2857
|
// src/tile/tile.tsx
|
|
1211
|
-
var
|
|
1212
|
-
var
|
|
2858
|
+
var import_fiber11 = require("@react-three/fiber");
|
|
2859
|
+
var import_react14 = require("react");
|
|
1213
2860
|
var import_three7 = require("three");
|
|
1214
2861
|
|
|
1215
2862
|
// src/tile/face-layout.ts
|
|
@@ -1390,7 +3037,7 @@ function resolveTemplateUrl(template, address) {
|
|
|
1390
3037
|
}
|
|
1391
3038
|
|
|
1392
3039
|
// src/tile/tile.tsx
|
|
1393
|
-
var
|
|
3040
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1394
3041
|
var MAX_LOD_UPDATE_INTERVAL_MS = 100;
|
|
1395
3042
|
var FAST_ROTATION_THRESHOLD_RADIANS = Math.PI / 180;
|
|
1396
3043
|
var FAST_FOV_THRESHOLD_DEGREES = 0.5;
|
|
@@ -1399,10 +3046,10 @@ var VISIBILITY_FOV_PADDING_DEGREES = 8;
|
|
|
1399
3046
|
var PREVIEW_RADIUS = CUBE_RADIUS + 0.05;
|
|
1400
3047
|
var SHARED_TILE_GEOMETRY = new import_three7.PlaneGeometry(1, 1);
|
|
1401
3048
|
function usePreviewTexture(url, anisotropy, onError) {
|
|
1402
|
-
const [texture, setTexture] = (0,
|
|
1403
|
-
const onErrorRef = (0,
|
|
3049
|
+
const [texture, setTexture] = (0, import_react14.useState)(null);
|
|
3050
|
+
const onErrorRef = (0, import_react14.useRef)(onError);
|
|
1404
3051
|
onErrorRef.current = onError;
|
|
1405
|
-
(0,
|
|
3052
|
+
(0, import_react14.useEffect)(() => {
|
|
1406
3053
|
if (!url) {
|
|
1407
3054
|
setTexture(null);
|
|
1408
3055
|
return;
|
|
@@ -1439,7 +3086,7 @@ function usePreviewTexture(url, anisotropy, onError) {
|
|
|
1439
3086
|
active = false;
|
|
1440
3087
|
};
|
|
1441
3088
|
}, [anisotropy, url]);
|
|
1442
|
-
(0,
|
|
3089
|
+
(0, import_react14.useEffect)(
|
|
1443
3090
|
() => () => {
|
|
1444
3091
|
texture?.dispose();
|
|
1445
3092
|
},
|
|
@@ -1452,7 +3099,7 @@ function PreviewFace({
|
|
|
1452
3099
|
texture,
|
|
1453
3100
|
faceOrder
|
|
1454
3101
|
}) {
|
|
1455
|
-
const geometry = (0,
|
|
3102
|
+
const geometry = (0, import_react14.useMemo)(() => {
|
|
1456
3103
|
const nextGeometry = new import_three7.PlaneGeometry(
|
|
1457
3104
|
PREVIEW_RADIUS * 2,
|
|
1458
3105
|
PREVIEW_RADIUS * 2
|
|
@@ -1470,21 +3117,21 @@ function PreviewFace({
|
|
|
1470
3117
|
uv.needsUpdate = true;
|
|
1471
3118
|
return nextGeometry;
|
|
1472
3119
|
}, [face, faceOrder]);
|
|
1473
|
-
(0,
|
|
3120
|
+
(0, import_react14.useEffect)(
|
|
1474
3121
|
() => () => {
|
|
1475
3122
|
geometry.dispose();
|
|
1476
3123
|
},
|
|
1477
3124
|
[geometry]
|
|
1478
3125
|
);
|
|
1479
3126
|
const transform = getFaceTransform(face, PREVIEW_RADIUS);
|
|
1480
|
-
return /* @__PURE__ */ (0,
|
|
3127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1481
3128
|
"mesh",
|
|
1482
3129
|
{
|
|
1483
3130
|
geometry,
|
|
1484
3131
|
position: transform.position,
|
|
1485
3132
|
renderOrder: -100,
|
|
1486
3133
|
rotation: transform.rotation,
|
|
1487
|
-
children: /* @__PURE__ */ (0,
|
|
3134
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1488
3135
|
"meshBasicMaterial",
|
|
1489
3136
|
{
|
|
1490
3137
|
depthTest: false,
|
|
@@ -1500,7 +3147,7 @@ function PreviewCube({
|
|
|
1500
3147
|
texture,
|
|
1501
3148
|
faceOrder
|
|
1502
3149
|
}) {
|
|
1503
|
-
return CUBE_FACES.map((face) => /* @__PURE__ */ (0,
|
|
3150
|
+
return CUBE_FACES.map((face) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1504
3151
|
PreviewFace,
|
|
1505
3152
|
{
|
|
1506
3153
|
face,
|
|
@@ -1511,14 +3158,14 @@ function PreviewCube({
|
|
|
1511
3158
|
));
|
|
1512
3159
|
}
|
|
1513
3160
|
function useManagedTexture(manager, key, url, address) {
|
|
1514
|
-
const [managedState, setManagedState] = (0,
|
|
3161
|
+
const [managedState, setManagedState] = (0, import_react14.useState)(() => ({
|
|
1515
3162
|
manager,
|
|
1516
3163
|
key,
|
|
1517
3164
|
url,
|
|
1518
3165
|
snapshot: manager.getSnapshot(key)
|
|
1519
3166
|
}));
|
|
1520
3167
|
const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
|
|
1521
|
-
(0,
|
|
3168
|
+
(0, import_react14.useEffect)(
|
|
1522
3169
|
() => manager.acquire(key, url, address, (nextSnapshot) => {
|
|
1523
3170
|
setManagedState({
|
|
1524
3171
|
manager,
|
|
@@ -1531,7 +3178,7 @@ function useManagedTexture(manager, key, url, address) {
|
|
|
1531
3178
|
);
|
|
1532
3179
|
return snapshot;
|
|
1533
3180
|
}
|
|
1534
|
-
var TileMesh = (0,
|
|
3181
|
+
var TileMesh = (0, import_react14.memo)(function TileMesh2({
|
|
1535
3182
|
address,
|
|
1536
3183
|
manager,
|
|
1537
3184
|
renderOrder,
|
|
@@ -1545,14 +3192,14 @@ var TileMesh = (0, import_react7.memo)(function TileMesh2({
|
|
|
1545
3192
|
if (!snapshot.texture) {
|
|
1546
3193
|
return null;
|
|
1547
3194
|
}
|
|
1548
|
-
return /* @__PURE__ */ (0,
|
|
3195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("group", { position: transform.position, rotation: transform.rotation, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1549
3196
|
"mesh",
|
|
1550
3197
|
{
|
|
1551
3198
|
geometry: SHARED_TILE_GEOMETRY,
|
|
1552
3199
|
position: [layout.x, layout.y, 0],
|
|
1553
3200
|
renderOrder,
|
|
1554
3201
|
scale: [layout.width, layout.height, 1],
|
|
1555
|
-
children: /* @__PURE__ */ (0,
|
|
3202
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1556
3203
|
"meshBasicMaterial",
|
|
1557
3204
|
{
|
|
1558
3205
|
depthTest: false,
|
|
@@ -1564,12 +3211,12 @@ var TileMesh = (0, import_react7.memo)(function TileMesh2({
|
|
|
1564
3211
|
}
|
|
1565
3212
|
) });
|
|
1566
3213
|
});
|
|
1567
|
-
var TileLayerMeshes = (0,
|
|
3214
|
+
var TileLayerMeshes = (0, import_react14.memo)(function TileLayerMeshes2({
|
|
1568
3215
|
layer,
|
|
1569
3216
|
manager,
|
|
1570
3217
|
resolveUrl
|
|
1571
3218
|
}) {
|
|
1572
|
-
return layer.addresses.map((address) => /* @__PURE__ */ (0,
|
|
3219
|
+
return layer.addresses.map((address) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1573
3220
|
TileMesh,
|
|
1574
3221
|
{
|
|
1575
3222
|
address,
|
|
@@ -1737,11 +3384,11 @@ function Tile({
|
|
|
1737
3384
|
onTileError,
|
|
1738
3385
|
onLevelChange
|
|
1739
3386
|
}) {
|
|
1740
|
-
const { camera, gl, size } = (0,
|
|
1741
|
-
const multiresConfig = (0,
|
|
3387
|
+
const { camera, gl, size } = (0, import_fiber11.useThree)();
|
|
3388
|
+
const multiresConfig = (0, import_react14.useMemo)(() => parseMultires(multires), [multires]);
|
|
1742
3389
|
const { levels, tileSize } = multiresConfig;
|
|
1743
3390
|
const multiresToken = `${tileSize}:${levels.join(",")}`;
|
|
1744
|
-
const candidatesByLevel = (0,
|
|
3391
|
+
const candidatesByLevel = (0, import_react14.useMemo)(
|
|
1745
3392
|
() => buildTileCandidates(levels, tileSize),
|
|
1746
3393
|
[levels, tileSize]
|
|
1747
3394
|
);
|
|
@@ -1753,13 +3400,13 @@ function Tile({
|
|
|
1753
3400
|
onPreviewError
|
|
1754
3401
|
);
|
|
1755
3402
|
const sharedManager = useSharedTileTextureManager();
|
|
1756
|
-
const progressRef = (0,
|
|
1757
|
-
const errorRef = (0,
|
|
1758
|
-
const levelChangeRef = (0,
|
|
3403
|
+
const progressRef = (0, import_react14.useRef)(onLoadProgress);
|
|
3404
|
+
const errorRef = (0, import_react14.useRef)(onTileError);
|
|
3405
|
+
const levelChangeRef = (0, import_react14.useRef)(onLevelChange);
|
|
1759
3406
|
progressRef.current = onLoadProgress;
|
|
1760
3407
|
errorRef.current = onTileError;
|
|
1761
3408
|
levelChangeRef.current = onLevelChange;
|
|
1762
|
-
const ownedManager = (0,
|
|
3409
|
+
const ownedManager = (0, import_react14.useMemo)(
|
|
1763
3410
|
() => new TileTextureManager({
|
|
1764
3411
|
anisotropy,
|
|
1765
3412
|
maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
|
|
@@ -1780,7 +3427,7 @@ function Tile({
|
|
|
1780
3427
|
]
|
|
1781
3428
|
);
|
|
1782
3429
|
const manager = sharedManager ?? ownedManager;
|
|
1783
|
-
(0,
|
|
3430
|
+
(0, import_react14.useEffect)(() => {
|
|
1784
3431
|
manager.resume();
|
|
1785
3432
|
return () => {
|
|
1786
3433
|
if (manager === ownedManager) {
|
|
@@ -1790,11 +3437,11 @@ function Tile({
|
|
|
1790
3437
|
queueMicrotask(() => manager.releaseUnused());
|
|
1791
3438
|
};
|
|
1792
3439
|
}, [manager, ownedManager]);
|
|
1793
|
-
const readyRef = (0,
|
|
1794
|
-
(0,
|
|
3440
|
+
const readyRef = (0, import_react14.useRef)(false);
|
|
3441
|
+
(0, import_react14.useEffect)(() => {
|
|
1795
3442
|
readyRef.current = false;
|
|
1796
3443
|
}, [effectivePreviewUrl]);
|
|
1797
|
-
(0,
|
|
3444
|
+
(0, import_react14.useEffect)(() => {
|
|
1798
3445
|
if (!previewTexture || readyRef.current) {
|
|
1799
3446
|
return;
|
|
1800
3447
|
}
|
|
@@ -1802,31 +3449,31 @@ function Tile({
|
|
|
1802
3449
|
onReady?.();
|
|
1803
3450
|
}, [onReady, previewTexture]);
|
|
1804
3451
|
const template = urlTemplate ?? buildDefaultTileUrlTemplate();
|
|
1805
|
-
const resolveUrl = (0,
|
|
3452
|
+
const resolveUrl = (0, import_react14.useMemo)(
|
|
1806
3453
|
() => (address) => {
|
|
1807
3454
|
const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
|
|
1808
3455
|
return resolveRelativeTileUrl(baseUrl, relativePath);
|
|
1809
3456
|
},
|
|
1810
3457
|
[baseUrl, resolveTileUrl, template]
|
|
1811
3458
|
);
|
|
1812
|
-
const [layers, setLayers] = (0,
|
|
1813
|
-
const layerTokenRef = (0,
|
|
1814
|
-
const currentLevelRef = (0,
|
|
1815
|
-
const lastUpdateAtRef = (0,
|
|
1816
|
-
const visibilityDirtyRef = (0,
|
|
1817
|
-
const lastVisibilityQuaternionRef = (0,
|
|
1818
|
-
const lastVisibilityFovRef = (0,
|
|
1819
|
-
const lastViewportRef = (0,
|
|
1820
|
-
const frustumRef = (0,
|
|
1821
|
-
const projectionMatrixRef = (0,
|
|
1822
|
-
const visibilityCameraRef = (0,
|
|
1823
|
-
(0,
|
|
3459
|
+
const [layers, setLayers] = (0, import_react14.useState)([]);
|
|
3460
|
+
const layerTokenRef = (0, import_react14.useRef)("");
|
|
3461
|
+
const currentLevelRef = (0, import_react14.useRef)(1);
|
|
3462
|
+
const lastUpdateAtRef = (0, import_react14.useRef)(-Infinity);
|
|
3463
|
+
const visibilityDirtyRef = (0, import_react14.useRef)(true);
|
|
3464
|
+
const lastVisibilityQuaternionRef = (0, import_react14.useRef)(camera.quaternion.clone());
|
|
3465
|
+
const lastVisibilityFovRef = (0, import_react14.useRef)(Number.NaN);
|
|
3466
|
+
const lastViewportRef = (0, import_react14.useRef)({ width: 0, height: 0, dpr: 0 });
|
|
3467
|
+
const frustumRef = (0, import_react14.useRef)(new import_three7.Frustum());
|
|
3468
|
+
const projectionMatrixRef = (0, import_react14.useRef)(new import_three7.Matrix4());
|
|
3469
|
+
const visibilityCameraRef = (0, import_react14.useRef)(new import_three7.PerspectiveCamera());
|
|
3470
|
+
(0, import_react14.useEffect)(() => {
|
|
1824
3471
|
currentLevelRef.current = 1;
|
|
1825
3472
|
layerTokenRef.current = "";
|
|
1826
3473
|
visibilityDirtyRef.current = true;
|
|
1827
3474
|
setLayers([]);
|
|
1828
3475
|
}, [manager, multiresToken]);
|
|
1829
|
-
(0,
|
|
3476
|
+
(0, import_react14.useEffect)(() => {
|
|
1830
3477
|
if (loadMode !== "preview") {
|
|
1831
3478
|
return;
|
|
1832
3479
|
}
|
|
@@ -1840,7 +3487,7 @@ function Tile({
|
|
|
1840
3487
|
setLayers(nextLayers);
|
|
1841
3488
|
}
|
|
1842
3489
|
};
|
|
1843
|
-
(0,
|
|
3490
|
+
(0, import_fiber11.useFrame)(() => {
|
|
1844
3491
|
if (loadMode === "preview") {
|
|
1845
3492
|
return;
|
|
1846
3493
|
}
|
|
@@ -1906,15 +3553,15 @@ function Tile({
|
|
|
1906
3553
|
};
|
|
1907
3554
|
commitLayers(nextLayers);
|
|
1908
3555
|
});
|
|
1909
|
-
return /* @__PURE__ */ (0,
|
|
1910
|
-
previewTexture ? /* @__PURE__ */ (0,
|
|
3556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("group", { visible, children: [
|
|
3557
|
+
previewTexture ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1911
3558
|
PreviewCube,
|
|
1912
3559
|
{
|
|
1913
3560
|
texture: previewTexture,
|
|
1914
3561
|
faceOrder: previewFaceOrder
|
|
1915
3562
|
}
|
|
1916
3563
|
) : null,
|
|
1917
|
-
layers.map((layer) => /* @__PURE__ */ (0,
|
|
3564
|
+
layers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1918
3565
|
TileLayerMeshes,
|
|
1919
3566
|
{
|
|
1920
3567
|
layer,
|
|
@@ -1927,7 +3574,7 @@ function Tile({
|
|
|
1927
3574
|
}
|
|
1928
3575
|
|
|
1929
3576
|
// src/panorama-scenes.tsx
|
|
1930
|
-
var
|
|
3577
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1931
3578
|
var TRANSITION_DEFAULTS = {
|
|
1932
3579
|
none: { duration: 0, krpanoBlend: "NOBLEND" },
|
|
1933
3580
|
crossfade: { duration: 1, krpanoBlend: "BLEND(1.0, easeInCubic)" },
|
|
@@ -1977,7 +3624,7 @@ function SceneSource({
|
|
|
1977
3624
|
return null;
|
|
1978
3625
|
}
|
|
1979
3626
|
if (scene.type === "sphere") {
|
|
1980
|
-
return /* @__PURE__ */ (0,
|
|
3627
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1981
3628
|
Sphere,
|
|
1982
3629
|
{
|
|
1983
3630
|
src: scene.src,
|
|
@@ -1989,7 +3636,7 @@ function SceneSource({
|
|
|
1989
3636
|
);
|
|
1990
3637
|
}
|
|
1991
3638
|
const { id: _id, type: _type, ...tileProps } = scene;
|
|
1992
|
-
return /* @__PURE__ */ (0,
|
|
3639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1993
3640
|
Tile,
|
|
1994
3641
|
{
|
|
1995
3642
|
...tileProps,
|
|
@@ -2005,17 +3652,17 @@ function SnapshotCapture({
|
|
|
2005
3652
|
maxPixels,
|
|
2006
3653
|
onCaptured
|
|
2007
3654
|
}) {
|
|
2008
|
-
const { camera, gl, scene, size } = (0,
|
|
2009
|
-
const capturedRequestRef = (0,
|
|
2010
|
-
const renderTargetRef = (0,
|
|
2011
|
-
(0,
|
|
3655
|
+
const { camera, gl, scene, size } = (0, import_fiber12.useThree)();
|
|
3656
|
+
const capturedRequestRef = (0, import_react15.useRef)(0);
|
|
3657
|
+
const renderTargetRef = (0, import_react15.useRef)(null);
|
|
3658
|
+
(0, import_react15.useEffect)(
|
|
2012
3659
|
() => () => {
|
|
2013
3660
|
renderTargetRef.current?.dispose();
|
|
2014
3661
|
renderTargetRef.current = null;
|
|
2015
3662
|
},
|
|
2016
3663
|
[]
|
|
2017
3664
|
);
|
|
2018
|
-
(0,
|
|
3665
|
+
(0, import_fiber12.useFrame)(() => {
|
|
2019
3666
|
if (requestId === 0 || requestId === capturedRequestRef.current) {
|
|
2020
3667
|
return;
|
|
2021
3668
|
}
|
|
@@ -2061,12 +3708,12 @@ function SnapshotOverlay({
|
|
|
2061
3708
|
runId,
|
|
2062
3709
|
onFinish
|
|
2063
3710
|
}) {
|
|
2064
|
-
const { camera, size } = (0,
|
|
2065
|
-
const meshRef = (0,
|
|
2066
|
-
const startRef = (0,
|
|
2067
|
-
const finishedRef = (0,
|
|
2068
|
-
const directionRef = (0,
|
|
2069
|
-
const material = (0,
|
|
3711
|
+
const { camera, size } = (0, import_fiber12.useThree)();
|
|
3712
|
+
const meshRef = (0, import_react15.useRef)(null);
|
|
3713
|
+
const startRef = (0, import_react15.useRef)(null);
|
|
3714
|
+
const finishedRef = (0, import_react15.useRef)(false);
|
|
3715
|
+
const directionRef = (0, import_react15.useRef)(new import_three8.Vector3());
|
|
3716
|
+
const material = (0, import_react15.useMemo)(
|
|
2070
3717
|
() => new import_three8.ShaderMaterial({
|
|
2071
3718
|
transparent: true,
|
|
2072
3719
|
depthTest: false,
|
|
@@ -2145,13 +3792,13 @@ function SnapshotOverlay({
|
|
|
2145
3792
|
}),
|
|
2146
3793
|
[snapshot.texture, transition.preset]
|
|
2147
3794
|
);
|
|
2148
|
-
(0,
|
|
2149
|
-
(0,
|
|
3795
|
+
(0, import_react15.useEffect)(() => () => material.dispose(), [material]);
|
|
3796
|
+
(0, import_react15.useEffect)(() => {
|
|
2150
3797
|
startRef.current = null;
|
|
2151
3798
|
finishedRef.current = false;
|
|
2152
3799
|
material.uniforms.progress.value = 0;
|
|
2153
3800
|
}, [material, runId]);
|
|
2154
|
-
(0,
|
|
3801
|
+
(0, import_fiber12.useFrame)((state) => {
|
|
2155
3802
|
const mesh = meshRef.current;
|
|
2156
3803
|
if (!mesh || !camera.isPerspectiveCamera) {
|
|
2157
3804
|
return;
|
|
@@ -2176,9 +3823,9 @@ function SnapshotOverlay({
|
|
|
2176
3823
|
onFinish();
|
|
2177
3824
|
}
|
|
2178
3825
|
});
|
|
2179
|
-
return /* @__PURE__ */ (0,
|
|
2180
|
-
/* @__PURE__ */ (0,
|
|
2181
|
-
/* @__PURE__ */ (0,
|
|
3826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("mesh", { ref: meshRef, renderOrder: 1e4, frustumCulled: false, children: [
|
|
3827
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
|
|
3828
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("primitive", { attach: "material", object: material })
|
|
2182
3829
|
] });
|
|
2183
3830
|
}
|
|
2184
3831
|
var FULLSCREEN_PLANE = new import_three8.PlaneGeometry(1, 1);
|
|
@@ -2191,47 +3838,47 @@ function PanoramaScenesController({
|
|
|
2191
3838
|
onTransitionError,
|
|
2192
3839
|
snapshotMaxPixels = 3686400
|
|
2193
3840
|
}) {
|
|
2194
|
-
const controlsRef = (0,
|
|
3841
|
+
const controlsRef = (0, import_react15.useContext)(PanoramaViewContext);
|
|
2195
3842
|
const manager = useSharedTileTextureManager();
|
|
2196
|
-
const transition = (0,
|
|
3843
|
+
const transition = (0, import_react15.useMemo)(
|
|
2197
3844
|
() => resolveTransition(transitionInput),
|
|
2198
3845
|
[transitionInput]
|
|
2199
3846
|
);
|
|
2200
|
-
const sceneMap = (0,
|
|
3847
|
+
const sceneMap = (0, import_react15.useMemo)(
|
|
2201
3848
|
() => new Map(scenes.map((scene) => [scene.id, scene])),
|
|
2202
3849
|
[scenes]
|
|
2203
3850
|
);
|
|
2204
3851
|
const initialScene = sceneMap.get(activeSceneId) ?? null;
|
|
2205
|
-
const [slots, setSlots] = (0,
|
|
3852
|
+
const [slots, setSlots] = (0, import_react15.useState)([
|
|
2206
3853
|
initialScene,
|
|
2207
3854
|
null
|
|
2208
3855
|
]);
|
|
2209
|
-
const [activeSlot, setActiveSlot] = (0,
|
|
2210
|
-
const [liveSlot, setLiveSlot] = (0,
|
|
2211
|
-
const [pendingSlot, setPendingSlot] = (0,
|
|
2212
|
-
const [phase, setPhase] = (0,
|
|
2213
|
-
const [captureRequestId, setCaptureRequestId] = (0,
|
|
2214
|
-
const [transitionRunId, setTransitionRunId] = (0,
|
|
2215
|
-
const [snapshot, setSnapshot] = (0,
|
|
2216
|
-
const [recoveryKey, setRecoveryKey] = (0,
|
|
2217
|
-
const requestedSceneIdRef = (0,
|
|
2218
|
-
const captureReasonRef = (0,
|
|
2219
|
-
const interactionReleaseRef = (0,
|
|
2220
|
-
const slotsRef = (0,
|
|
2221
|
-
const phaseRef = (0,
|
|
2222
|
-
const liveSlotRef = (0,
|
|
2223
|
-
const transitionFromRef = (0,
|
|
3856
|
+
const [activeSlot, setActiveSlot] = (0, import_react15.useState)(0);
|
|
3857
|
+
const [liveSlot, setLiveSlot] = (0, import_react15.useState)(0);
|
|
3858
|
+
const [pendingSlot, setPendingSlot] = (0, import_react15.useState)(null);
|
|
3859
|
+
const [phase, setPhase] = (0, import_react15.useState)(initialScene ? "idle" : "preloading");
|
|
3860
|
+
const [captureRequestId, setCaptureRequestId] = (0, import_react15.useState)(0);
|
|
3861
|
+
const [transitionRunId, setTransitionRunId] = (0, import_react15.useState)(0);
|
|
3862
|
+
const [snapshot, setSnapshot] = (0, import_react15.useState)(null);
|
|
3863
|
+
const [recoveryKey, setRecoveryKey] = (0, import_react15.useState)(0);
|
|
3864
|
+
const requestedSceneIdRef = (0, import_react15.useRef)(activeSceneId);
|
|
3865
|
+
const captureReasonRef = (0, import_react15.useRef)("start");
|
|
3866
|
+
const interactionReleaseRef = (0, import_react15.useRef)(null);
|
|
3867
|
+
const slotsRef = (0, import_react15.useRef)(slots);
|
|
3868
|
+
const phaseRef = (0, import_react15.useRef)(phase);
|
|
3869
|
+
const liveSlotRef = (0, import_react15.useRef)(liveSlot);
|
|
3870
|
+
const transitionFromRef = (0, import_react15.useRef)(initialScene);
|
|
2224
3871
|
slotsRef.current = slots;
|
|
2225
3872
|
phaseRef.current = phase;
|
|
2226
3873
|
liveSlotRef.current = liveSlot;
|
|
2227
|
-
const releaseInteraction = (0,
|
|
3874
|
+
const releaseInteraction = (0, import_react15.useCallback)(() => {
|
|
2228
3875
|
interactionReleaseRef.current?.();
|
|
2229
3876
|
interactionReleaseRef.current = null;
|
|
2230
3877
|
}, []);
|
|
2231
|
-
const reportError = (0,
|
|
3878
|
+
const reportError = (0, import_react15.useCallback)((sceneId, error) => {
|
|
2232
3879
|
onTransitionError?.({ sceneId, error });
|
|
2233
3880
|
}, [onTransitionError]);
|
|
2234
|
-
const startPreload = (0,
|
|
3881
|
+
const startPreload = (0, import_react15.useCallback)((scene) => {
|
|
2235
3882
|
const nextSlot = 1 - liveSlotRef.current;
|
|
2236
3883
|
requestedSceneIdRef.current = scene.id;
|
|
2237
3884
|
setSlots((current) => {
|
|
@@ -2242,7 +3889,7 @@ function PanoramaScenesController({
|
|
|
2242
3889
|
setPendingSlot(nextSlot);
|
|
2243
3890
|
setPhase("preloading");
|
|
2244
3891
|
}, []);
|
|
2245
|
-
(0,
|
|
3892
|
+
(0, import_react15.useEffect)(() => {
|
|
2246
3893
|
const requested = sceneMap.get(activeSceneId);
|
|
2247
3894
|
if (!requested) {
|
|
2248
3895
|
reportError(activeSceneId, new Error(`Unknown panorama scene: ${activeSceneId}`));
|
|
@@ -2266,7 +3913,7 @@ function PanoramaScenesController({
|
|
|
2266
3913
|
}
|
|
2267
3914
|
startPreload(requested);
|
|
2268
3915
|
}, [activeSceneId, pendingSlot, reportError, sceneMap, startPreload]);
|
|
2269
|
-
const handleReady = (0,
|
|
3916
|
+
const handleReady = (0, import_react15.useCallback)((slot) => {
|
|
2270
3917
|
if (slot !== pendingSlot || phaseRef.current !== "preloading") {
|
|
2271
3918
|
return;
|
|
2272
3919
|
}
|
|
@@ -2297,7 +3944,7 @@ function PanoramaScenesController({
|
|
|
2297
3944
|
setPhase("capturing");
|
|
2298
3945
|
setCaptureRequestId((current) => current + 1);
|
|
2299
3946
|
}, [controlsRef, onTransitionEnd, pendingSlot, transition.preset]);
|
|
2300
|
-
const handleCaptured = (0,
|
|
3947
|
+
const handleCaptured = (0, import_react15.useCallback)((nextSnapshot) => {
|
|
2301
3948
|
setSnapshot((current) => {
|
|
2302
3949
|
current?.dispose();
|
|
2303
3950
|
return nextSnapshot;
|
|
@@ -2319,7 +3966,7 @@ function PanoramaScenesController({
|
|
|
2319
3966
|
setPhase("transitioning");
|
|
2320
3967
|
setTransitionRunId((current) => current + 1);
|
|
2321
3968
|
}, [manager, pendingSlot, sceneMap, startPreload]);
|
|
2322
|
-
const finishTransition = (0,
|
|
3969
|
+
const finishTransition = (0, import_react15.useCallback)(() => {
|
|
2323
3970
|
const nextSlot = pendingSlot;
|
|
2324
3971
|
const previousScene = transitionFromRef.current;
|
|
2325
3972
|
const nextScene = nextSlot === null ? null : slotsRef.current[nextSlot];
|
|
@@ -2344,12 +3991,12 @@ function PanoramaScenesController({
|
|
|
2344
3991
|
});
|
|
2345
3992
|
}
|
|
2346
3993
|
}, [onTransitionEnd, pendingSlot, releaseInteraction, snapshot, transition.preset]);
|
|
2347
|
-
(0,
|
|
3994
|
+
(0, import_react15.useEffect)(() => () => {
|
|
2348
3995
|
releaseInteraction();
|
|
2349
3996
|
snapshot?.dispose();
|
|
2350
3997
|
}, [releaseInteraction, snapshot]);
|
|
2351
|
-
const { gl } = (0,
|
|
2352
|
-
(0,
|
|
3998
|
+
const { gl } = (0, import_fiber12.useThree)();
|
|
3999
|
+
(0, import_react15.useEffect)(() => {
|
|
2353
4000
|
const canvas = gl.domElement;
|
|
2354
4001
|
const onContextLost = (event) => {
|
|
2355
4002
|
event.preventDefault();
|
|
@@ -2373,8 +4020,8 @@ function PanoramaScenesController({
|
|
|
2373
4020
|
}, [gl, manager, releaseInteraction, snapshot]);
|
|
2374
4021
|
const renderedScene = slots[liveSlot];
|
|
2375
4022
|
const snapshotVisible = snapshot !== null && (phase === "transitioning" || phase === "recapturing" || phase === "preloading");
|
|
2376
|
-
return /* @__PURE__ */ (0,
|
|
2377
|
-
/* @__PURE__ */ (0,
|
|
4023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react15.Fragment, { children: [
|
|
4024
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2378
4025
|
SnapshotCapture,
|
|
2379
4026
|
{
|
|
2380
4027
|
requestId: captureRequestId,
|
|
@@ -2386,7 +4033,7 @@ function PanoramaScenesController({
|
|
|
2386
4033
|
const source = slots[slot];
|
|
2387
4034
|
const isLive = slot === liveSlot;
|
|
2388
4035
|
const isPending = slot === pendingSlot;
|
|
2389
|
-
return /* @__PURE__ */ (0,
|
|
4036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2390
4037
|
SceneSource,
|
|
2391
4038
|
{
|
|
2392
4039
|
scene: source,
|
|
@@ -2402,7 +4049,7 @@ function PanoramaScenesController({
|
|
|
2402
4049
|
`${slot}:${source?.id ?? "empty"}:${recoveryKey}`
|
|
2403
4050
|
);
|
|
2404
4051
|
}),
|
|
2405
|
-
snapshotVisible ? /* @__PURE__ */ (0,
|
|
4052
|
+
snapshotVisible ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2406
4053
|
SnapshotOverlay,
|
|
2407
4054
|
{
|
|
2408
4055
|
snapshot,
|
|
@@ -2420,26 +4067,26 @@ function PanoramaScenes({
|
|
|
2420
4067
|
maxConcurrentTileLoads,
|
|
2421
4068
|
...props
|
|
2422
4069
|
}) {
|
|
2423
|
-
return /* @__PURE__ */ (0,
|
|
4070
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2424
4071
|
TileTextureManagerProvider,
|
|
2425
4072
|
{
|
|
2426
4073
|
maxTextureMemoryMb,
|
|
2427
4074
|
maxConcurrentLoads: maxConcurrentTileLoads,
|
|
2428
|
-
children: /* @__PURE__ */ (0,
|
|
4075
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PanoramaScenesController, { ...props })
|
|
2429
4076
|
}
|
|
2430
4077
|
);
|
|
2431
4078
|
}
|
|
2432
4079
|
|
|
2433
4080
|
// src/hotspot/image-hotspot.tsx
|
|
2434
|
-
var
|
|
2435
|
-
var
|
|
4081
|
+
var import_fiber14 = require("@react-three/fiber");
|
|
4082
|
+
var import_react17 = require("react");
|
|
2436
4083
|
var import_three10 = require("three");
|
|
2437
4084
|
|
|
2438
4085
|
// src/hotspot/hotspot-anchor.tsx
|
|
2439
|
-
var
|
|
2440
|
-
var
|
|
4086
|
+
var import_fiber13 = require("@react-three/fiber");
|
|
4087
|
+
var import_react16 = require("react");
|
|
2441
4088
|
var import_three9 = require("three");
|
|
2442
|
-
var
|
|
4089
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2443
4090
|
var DEFAULT_FLOATING_DISTANCE = 10;
|
|
2444
4091
|
var PANORAMA_RADIUS = DEFAULT_PANORAMA_RADIUS;
|
|
2445
4092
|
var PANORAMA_SURFACE_INSET = 4;
|
|
@@ -2545,10 +4192,10 @@ function HotspotAnchor({
|
|
|
2545
4192
|
onPositionChange,
|
|
2546
4193
|
onDragEnd
|
|
2547
4194
|
}) {
|
|
2548
|
-
const controlsRef = (0,
|
|
2549
|
-
const groupRef = (0,
|
|
2550
|
-
const dragStateRef = (0,
|
|
2551
|
-
const suppressNextClickRef = (0,
|
|
4195
|
+
const controlsRef = (0, import_react16.useContext)(PanoramaViewContext);
|
|
4196
|
+
const groupRef = (0, import_react16.useRef)(null);
|
|
4197
|
+
const dragStateRef = (0, import_react16.useRef)(null);
|
|
4198
|
+
const suppressNextClickRef = (0, import_react16.useRef)(false);
|
|
2552
4199
|
const normalizedPosition = normalizePanoPosition(position);
|
|
2553
4200
|
const rendering = internalOrientation && internalPlacement ? { orientation: internalOrientation, placement: internalPlacement } : HOTSPOT_MODE_RENDERING[mode];
|
|
2554
4201
|
const { orientation: orientation2, placement } = rendering;
|
|
@@ -2561,17 +4208,17 @@ function HotspotAnchor({
|
|
|
2561
4208
|
scaledWidth,
|
|
2562
4209
|
scaledHeight
|
|
2563
4210
|
);
|
|
2564
|
-
const worldPosition = (0,
|
|
4211
|
+
const worldPosition = (0, import_react16.useMemo)(
|
|
2565
4212
|
() => panoPositionToVector3(normalizedPosition, resolvedDistance),
|
|
2566
4213
|
[normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
|
|
2567
4214
|
);
|
|
2568
|
-
const surfaceQuaternion3 = (0,
|
|
4215
|
+
const surfaceQuaternion3 = (0, import_react16.useMemo)(
|
|
2569
4216
|
() => createSurfaceQuaternion(normalizedPosition),
|
|
2570
4217
|
[normalizedPosition.pitch, normalizedPosition.yaw]
|
|
2571
4218
|
);
|
|
2572
4219
|
const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
|
|
2573
4220
|
const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
|
|
2574
|
-
const focusGeometry = (0,
|
|
4221
|
+
const focusGeometry = (0, import_react16.useMemo)(() => {
|
|
2575
4222
|
const planeGeometry = new import_three9.PlaneGeometry(1, 1);
|
|
2576
4223
|
const geometry = new import_three9.EdgesGeometry(planeGeometry);
|
|
2577
4224
|
planeGeometry.dispose();
|
|
@@ -2589,15 +4236,15 @@ function HotspotAnchor({
|
|
|
2589
4236
|
});
|
|
2590
4237
|
} : void 0
|
|
2591
4238
|
});
|
|
2592
|
-
(0,
|
|
4239
|
+
(0, import_react16.useEffect)(
|
|
2593
4240
|
() => () => {
|
|
2594
4241
|
dragStateRef.current?.releaseInteractionLock();
|
|
2595
4242
|
dragStateRef.current = null;
|
|
2596
4243
|
},
|
|
2597
4244
|
[]
|
|
2598
4245
|
);
|
|
2599
|
-
(0,
|
|
2600
|
-
(0,
|
|
4246
|
+
(0, import_react16.useEffect)(() => () => focusGeometry.dispose(), [focusGeometry]);
|
|
4247
|
+
(0, import_fiber13.useFrame)(({ camera }) => {
|
|
2601
4248
|
if (!groupRef.current) {
|
|
2602
4249
|
return;
|
|
2603
4250
|
}
|
|
@@ -2608,7 +4255,7 @@ function HotspotAnchor({
|
|
|
2608
4255
|
}
|
|
2609
4256
|
groupRef.current.rotateOnAxis(
|
|
2610
4257
|
LOCAL_FORWARD,
|
|
2611
|
-
import_three9.MathUtils.degToRad(rotation)
|
|
4258
|
+
import_three9.MathUtils.degToRad(-rotation)
|
|
2612
4259
|
);
|
|
2613
4260
|
const scaleFactor = scaleMode === "fixed" && camera instanceof import_three9.PerspectiveCamera ? fixedScaleFactor(camera.fov, referenceFov) : 1;
|
|
2614
4261
|
groupRef.current.scale.set(
|
|
@@ -2643,7 +4290,7 @@ function HotspotAnchor({
|
|
|
2643
4290
|
dragState.releaseInteractionLock();
|
|
2644
4291
|
dragStateRef.current = null;
|
|
2645
4292
|
};
|
|
2646
|
-
return /* @__PURE__ */ (0,
|
|
4293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2647
4294
|
"group",
|
|
2648
4295
|
{
|
|
2649
4296
|
ref: groupRef,
|
|
@@ -2713,14 +4360,14 @@ function HotspotAnchor({
|
|
|
2713
4360
|
} : void 0,
|
|
2714
4361
|
onPointerUp: interactive ? (event) => releasePointer(event, true) : void 0,
|
|
2715
4362
|
children: [
|
|
2716
|
-
focused ? focusContent ?? /* @__PURE__ */ (0,
|
|
4363
|
+
focused ? focusContent ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2717
4364
|
"lineSegments",
|
|
2718
4365
|
{
|
|
2719
4366
|
geometry: focusGeometry,
|
|
2720
4367
|
position: [0, 0, 0.01],
|
|
2721
4368
|
renderOrder: renderOrder + 1,
|
|
2722
4369
|
scale: [1.12, 1.12, 1],
|
|
2723
|
-
children: /* @__PURE__ */ (0,
|
|
4370
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("lineBasicMaterial", { color: "#f5fbfc", depthTest: false })
|
|
2724
4371
|
}
|
|
2725
4372
|
) : null,
|
|
2726
4373
|
children
|
|
@@ -2730,7 +4377,7 @@ function HotspotAnchor({
|
|
|
2730
4377
|
}
|
|
2731
4378
|
|
|
2732
4379
|
// src/hotspot/image-hotspot.tsx
|
|
2733
|
-
var
|
|
4380
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2734
4381
|
function clampOpacity(opacity) {
|
|
2735
4382
|
if (!Number.isFinite(opacity)) {
|
|
2736
4383
|
return 1;
|
|
@@ -2738,13 +4385,13 @@ function clampOpacity(opacity) {
|
|
|
2738
4385
|
return Math.max(0, Math.min(opacity, 1));
|
|
2739
4386
|
}
|
|
2740
4387
|
function useHotspotImageTexture(src, onLoad, onError) {
|
|
2741
|
-
const gl = (0,
|
|
2742
|
-
const [texture, setTexture] = (0,
|
|
2743
|
-
const onLoadRef = (0,
|
|
2744
|
-
const onErrorRef = (0,
|
|
4388
|
+
const gl = (0, import_fiber14.useThree)((state) => state.gl);
|
|
4389
|
+
const [texture, setTexture] = (0, import_react17.useState)(null);
|
|
4390
|
+
const onLoadRef = (0, import_react17.useRef)(onLoad);
|
|
4391
|
+
const onErrorRef = (0, import_react17.useRef)(onError);
|
|
2745
4392
|
onLoadRef.current = onLoad;
|
|
2746
4393
|
onErrorRef.current = onError;
|
|
2747
|
-
(0,
|
|
4394
|
+
(0, import_react17.useEffect)(() => {
|
|
2748
4395
|
let active = true;
|
|
2749
4396
|
setTexture(null);
|
|
2750
4397
|
if (!src) {
|
|
@@ -2782,7 +4429,7 @@ function useHotspotImageTexture(src, onLoad, onError) {
|
|
|
2782
4429
|
active = false;
|
|
2783
4430
|
};
|
|
2784
4431
|
}, [gl, src]);
|
|
2785
|
-
(0,
|
|
4432
|
+
(0, import_react17.useEffect)(
|
|
2786
4433
|
() => () => {
|
|
2787
4434
|
texture?.dispose();
|
|
2788
4435
|
},
|
|
@@ -2800,9 +4447,9 @@ function ImageHotspot({
|
|
|
2800
4447
|
...anchorProps
|
|
2801
4448
|
}) {
|
|
2802
4449
|
const texture = useHotspotImageTexture(src, onLoad, onError);
|
|
2803
|
-
return /* @__PURE__ */ (0,
|
|
2804
|
-
/* @__PURE__ */ (0,
|
|
2805
|
-
/* @__PURE__ */ (0,
|
|
4450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("mesh", { children: [
|
|
4451
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("planeGeometry", { args: [1, 1] }),
|
|
4452
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2806
4453
|
"meshBasicMaterial",
|
|
2807
4454
|
{
|
|
2808
4455
|
map: texture,
|
|
@@ -2816,13 +4463,13 @@ function ImageHotspot({
|
|
|
2816
4463
|
}
|
|
2817
4464
|
|
|
2818
4465
|
// src/hotspot/polygon-hotspot.tsx
|
|
2819
|
-
var
|
|
2820
|
-
var
|
|
4466
|
+
var import_fiber15 = require("@react-three/fiber");
|
|
4467
|
+
var import_react18 = require("react");
|
|
2821
4468
|
var import_three11 = require("three");
|
|
2822
4469
|
var import_Line2 = require("three/addons/lines/Line2.js");
|
|
2823
4470
|
var import_LineGeometry = require("three/addons/lines/LineGeometry.js");
|
|
2824
4471
|
var import_LineMaterial = require("three/addons/lines/LineMaterial.js");
|
|
2825
|
-
var
|
|
4472
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2826
4473
|
var POLYGON_RADIUS = 990;
|
|
2827
4474
|
var MAX_FILL_SEGMENT_DEGREES = 7;
|
|
2828
4475
|
function cloneVertices(vertices) {
|
|
@@ -3095,38 +4742,38 @@ function PolygonHotspot({
|
|
|
3095
4742
|
onInvalid,
|
|
3096
4743
|
...anchorProps
|
|
3097
4744
|
}) {
|
|
3098
|
-
const gl = (0,
|
|
3099
|
-
const size = (0,
|
|
3100
|
-
const issues = (0,
|
|
4745
|
+
const gl = (0, import_fiber15.useThree)((state) => state.gl);
|
|
4746
|
+
const size = (0, import_fiber15.useThree)((state) => state.size);
|
|
4747
|
+
const issues = (0, import_react18.useMemo)(() => validatePolygonVertices(vertices), [vertices]);
|
|
3101
4748
|
const valid = issues.length === 0;
|
|
3102
|
-
const center = (0,
|
|
3103
|
-
const fillGeometry = (0,
|
|
4749
|
+
const center = (0, import_react18.useMemo)(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
|
|
4750
|
+
const fillGeometry = (0, import_react18.useMemo)(
|
|
3104
4751
|
() => valid && center ? makeFillGeometry(vertices, center) : null,
|
|
3105
4752
|
[center, valid, vertices]
|
|
3106
4753
|
);
|
|
3107
|
-
const strokePositions = (0,
|
|
4754
|
+
const strokePositions = (0, import_react18.useMemo)(
|
|
3108
4755
|
() => valid && center ? makeStrokePositions(vertices, center) : null,
|
|
3109
4756
|
[center, valid, vertices]
|
|
3110
4757
|
);
|
|
3111
4758
|
const resolvedStrokeOpacity = Math.max(0, Math.min(strokeOpacity, 1));
|
|
3112
4759
|
const devicePixelRatio = gl.getPixelRatio();
|
|
3113
|
-
const strokeLine = (0,
|
|
4760
|
+
const strokeLine = (0, import_react18.useMemo)(
|
|
3114
4761
|
() => strokePositions ? makeStrokeLine(strokePositions, stroke, strokeWidth * devicePixelRatio, resolvedStrokeOpacity) : null,
|
|
3115
4762
|
[devicePixelRatio, resolvedStrokeOpacity, stroke, strokePositions, strokeWidth]
|
|
3116
4763
|
);
|
|
3117
|
-
const focusStrokeLine = (0,
|
|
4764
|
+
const focusStrokeLine = (0, import_react18.useMemo)(
|
|
3118
4765
|
() => strokePositions ? makeStrokeLine(strokePositions, "#75cbd3", (strokeWidth + 2) * devicePixelRatio, 1) : null,
|
|
3119
4766
|
[devicePixelRatio, strokePositions, strokeWidth]
|
|
3120
4767
|
);
|
|
3121
|
-
const startVerticesRef = (0,
|
|
4768
|
+
const startVerticesRef = (0, import_react18.useRef)(null);
|
|
3122
4769
|
const issuesKey = issues.map((issue) => issue.code).join(",");
|
|
3123
|
-
(0,
|
|
4770
|
+
(0, import_react18.useEffect)(() => {
|
|
3124
4771
|
if (!valid) onInvalid?.(issues);
|
|
3125
4772
|
}, [issues, issuesKey, onInvalid, valid]);
|
|
3126
|
-
(0,
|
|
3127
|
-
(0,
|
|
3128
|
-
(0,
|
|
3129
|
-
(0,
|
|
4773
|
+
(0, import_react18.useEffect)(() => () => fillGeometry?.dispose(), [fillGeometry]);
|
|
4774
|
+
(0, import_react18.useEffect)(() => () => disposeStrokeLine(strokeLine), [strokeLine]);
|
|
4775
|
+
(0, import_react18.useEffect)(() => () => disposeStrokeLine(focusStrokeLine), [focusStrokeLine]);
|
|
4776
|
+
(0, import_react18.useEffect)(() => {
|
|
3130
4777
|
const width = gl.domElement.width;
|
|
3131
4778
|
const height = gl.domElement.height;
|
|
3132
4779
|
for (const line of [strokeLine, focusStrokeLine]) {
|
|
@@ -3145,7 +4792,7 @@ function PolygonHotspot({
|
|
|
3145
4792
|
startPosition: event.startPosition
|
|
3146
4793
|
});
|
|
3147
4794
|
};
|
|
3148
|
-
return /* @__PURE__ */ (0,
|
|
4795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
3149
4796
|
HotspotAnchor,
|
|
3150
4797
|
{
|
|
3151
4798
|
...anchorProps,
|
|
@@ -3160,7 +4807,7 @@ function PolygonHotspot({
|
|
|
3160
4807
|
draggable,
|
|
3161
4808
|
interactive,
|
|
3162
4809
|
useAngularScale: false,
|
|
3163
|
-
focusContent: /* @__PURE__ */ (0,
|
|
4810
|
+
focusContent: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("primitive", { object: focusStrokeLine, renderOrder: renderOrder + 2 }),
|
|
3164
4811
|
onDragStart: (event) => {
|
|
3165
4812
|
startVerticesRef.current = cloneVertices(vertices);
|
|
3166
4813
|
emitVerticesChange(event, onDragStart);
|
|
@@ -3171,7 +4818,7 @@ function PolygonHotspot({
|
|
|
3171
4818
|
startVerticesRef.current = null;
|
|
3172
4819
|
},
|
|
3173
4820
|
children: [
|
|
3174
|
-
/* @__PURE__ */ (0,
|
|
4821
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mesh", { geometry: fillGeometry, renderOrder, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3175
4822
|
"meshBasicMaterial",
|
|
3176
4823
|
{
|
|
3177
4824
|
color: fill,
|
|
@@ -3181,20 +4828,20 @@ function PolygonHotspot({
|
|
|
3181
4828
|
transparent: true
|
|
3182
4829
|
}
|
|
3183
4830
|
) }),
|
|
3184
|
-
/* @__PURE__ */ (0,
|
|
4831
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("primitive", { object: strokeLine, renderOrder: renderOrder + 1 })
|
|
3185
4832
|
]
|
|
3186
4833
|
}
|
|
3187
4834
|
);
|
|
3188
4835
|
}
|
|
3189
4836
|
|
|
3190
4837
|
// src/hotspot/polyline-hotspot.tsx
|
|
3191
|
-
var
|
|
3192
|
-
var
|
|
4838
|
+
var import_fiber16 = require("@react-three/fiber");
|
|
4839
|
+
var import_react19 = require("react");
|
|
3193
4840
|
var import_three12 = require("three");
|
|
3194
4841
|
var import_Line22 = require("three/addons/lines/Line2.js");
|
|
3195
4842
|
var import_LineGeometry2 = require("three/addons/lines/LineGeometry.js");
|
|
3196
4843
|
var import_LineMaterial2 = require("three/addons/lines/LineMaterial.js");
|
|
3197
|
-
var
|
|
4844
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
3198
4845
|
var POLYLINE_RADIUS = 990;
|
|
3199
4846
|
var MAX_SEGMENT_DEGREES = 7;
|
|
3200
4847
|
function cloneVertices2(vertices) {
|
|
@@ -3331,32 +4978,32 @@ function PolylineHotspot({
|
|
|
3331
4978
|
onInvalid,
|
|
3332
4979
|
...anchorProps
|
|
3333
4980
|
}) {
|
|
3334
|
-
const gl = (0,
|
|
3335
|
-
const size = (0,
|
|
3336
|
-
const issues = (0,
|
|
4981
|
+
const gl = (0, import_fiber16.useThree)((state) => state.gl);
|
|
4982
|
+
const size = (0, import_fiber16.useThree)((state) => state.size);
|
|
4983
|
+
const issues = (0, import_react19.useMemo)(() => validatePolylineVertices(vertices), [vertices]);
|
|
3337
4984
|
const valid = issues.length === 0;
|
|
3338
|
-
const center = (0,
|
|
3339
|
-
const linePositions = (0,
|
|
4985
|
+
const center = (0, import_react19.useMemo)(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
|
|
4986
|
+
const linePositions = (0, import_react19.useMemo)(
|
|
3340
4987
|
() => valid && center ? makeLinePositions(vertices, center) : null,
|
|
3341
4988
|
[center, valid, vertices]
|
|
3342
4989
|
);
|
|
3343
4990
|
const devicePixelRatio = gl.getPixelRatio();
|
|
3344
4991
|
const opacity = Math.max(0, Math.min(strokeOpacity, 1));
|
|
3345
|
-
const line = (0,
|
|
4992
|
+
const line = (0, import_react19.useMemo)(
|
|
3346
4993
|
() => linePositions ? makeLine(linePositions, stroke, strokeWidth * devicePixelRatio, opacity) : null,
|
|
3347
4994
|
[devicePixelRatio, linePositions, opacity, stroke, strokeWidth]
|
|
3348
4995
|
);
|
|
3349
|
-
const focusLine = (0,
|
|
4996
|
+
const focusLine = (0, import_react19.useMemo)(
|
|
3350
4997
|
() => linePositions ? makeLine(linePositions, "#75cbd3", (strokeWidth + 2) * devicePixelRatio, 1) : null,
|
|
3351
4998
|
[devicePixelRatio, linePositions, strokeWidth]
|
|
3352
4999
|
);
|
|
3353
|
-
const startVerticesRef = (0,
|
|
3354
|
-
(0,
|
|
5000
|
+
const startVerticesRef = (0, import_react19.useRef)(null);
|
|
5001
|
+
(0, import_react19.useEffect)(() => {
|
|
3355
5002
|
if (!valid) onInvalid?.(issues);
|
|
3356
5003
|
}, [issues, onInvalid, valid]);
|
|
3357
|
-
(0,
|
|
3358
|
-
(0,
|
|
3359
|
-
(0,
|
|
5004
|
+
(0, import_react19.useEffect)(() => () => disposeLine(line), [line]);
|
|
5005
|
+
(0, import_react19.useEffect)(() => () => disposeLine(focusLine), [focusLine]);
|
|
5006
|
+
(0, import_react19.useEffect)(() => {
|
|
3360
5007
|
const width = gl.domElement.width;
|
|
3361
5008
|
const height = gl.domElement.height;
|
|
3362
5009
|
for (const nextLine of [line, focusLine]) {
|
|
@@ -3374,7 +5021,7 @@ function PolylineHotspot({
|
|
|
3374
5021
|
startPosition: event.startPosition
|
|
3375
5022
|
});
|
|
3376
5023
|
};
|
|
3377
|
-
return /* @__PURE__ */ (0,
|
|
5024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3378
5025
|
HotspotAnchor,
|
|
3379
5026
|
{
|
|
3380
5027
|
...anchorProps,
|
|
@@ -3388,7 +5035,7 @@ function PolylineHotspot({
|
|
|
3388
5035
|
renderOrder,
|
|
3389
5036
|
draggable,
|
|
3390
5037
|
useAngularScale: false,
|
|
3391
|
-
focusContent: /* @__PURE__ */ (0,
|
|
5038
|
+
focusContent: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("primitive", { object: focusLine, renderOrder: renderOrder + 2 }),
|
|
3392
5039
|
onDragStart: (event) => {
|
|
3393
5040
|
startVerticesRef.current = cloneVertices2(vertices);
|
|
3394
5041
|
emitVerticesChange(event, onDragStart);
|
|
@@ -3398,15 +5045,15 @@ function PolylineHotspot({
|
|
|
3398
5045
|
emitVerticesChange(event, onDragEnd);
|
|
3399
5046
|
startVerticesRef.current = null;
|
|
3400
5047
|
},
|
|
3401
|
-
children: /* @__PURE__ */ (0,
|
|
5048
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("primitive", { object: line, renderOrder: renderOrder + 1 })
|
|
3402
5049
|
}
|
|
3403
5050
|
);
|
|
3404
5051
|
}
|
|
3405
5052
|
|
|
3406
5053
|
// src/hotspot/graphic-hotspot.tsx
|
|
3407
|
-
var
|
|
5054
|
+
var import_react20 = require("react");
|
|
3408
5055
|
var import_three13 = require("three");
|
|
3409
|
-
var
|
|
5056
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
3410
5057
|
var GRAPHIC_TEXTURE_SIZE = 512;
|
|
3411
5058
|
function clampOpacity2(opacity) {
|
|
3412
5059
|
if (!Number.isFinite(opacity)) {
|
|
@@ -3417,14 +5064,34 @@ function clampOpacity2(opacity) {
|
|
|
3417
5064
|
function resolveStrokeWidth(value) {
|
|
3418
5065
|
return Number.isFinite(value) ? Math.max(value, 0) : 8;
|
|
3419
5066
|
}
|
|
3420
|
-
function
|
|
3421
|
-
|
|
5067
|
+
function resolveCornerRadius(value) {
|
|
5068
|
+
return Number.isFinite(value) ? Math.max(0, Math.min(value, 0.5)) : 0;
|
|
5069
|
+
}
|
|
5070
|
+
function roundedRectanglePath(context, x, y, width, height, radiusRatio, aspectWidth, aspectHeight) {
|
|
5071
|
+
const safeAspectWidth = Number.isFinite(aspectWidth) && aspectWidth > 0 ? aspectWidth : 1;
|
|
5072
|
+
const safeAspectHeight = Number.isFinite(aspectHeight) && aspectHeight > 0 ? aspectHeight : 1;
|
|
5073
|
+
const shortSide = Math.min(safeAspectWidth, safeAspectHeight);
|
|
5074
|
+
const safeRadiusRatio = resolveCornerRadius(radiusRatio);
|
|
5075
|
+
const radiusX = Math.min(width / 2, width * safeRadiusRatio * shortSide / safeAspectWidth);
|
|
5076
|
+
const radiusY = Math.min(height / 2, height * safeRadiusRatio * shortSide / safeAspectHeight);
|
|
5077
|
+
context.beginPath();
|
|
5078
|
+
context.moveTo(x + radiusX, y);
|
|
5079
|
+
context.lineTo(x + width - radiusX, y);
|
|
5080
|
+
context.ellipse(x + width - radiusX, y + radiusY, radiusX, radiusY, 0, -Math.PI / 2, 0);
|
|
5081
|
+
context.lineTo(x + width, y + height - radiusY);
|
|
5082
|
+
context.ellipse(x + width - radiusX, y + height - radiusY, radiusX, radiusY, 0, 0, Math.PI / 2);
|
|
5083
|
+
context.lineTo(x + radiusX, y + height);
|
|
5084
|
+
context.ellipse(x + radiusX, y + height - radiusY, radiusX, radiusY, 0, Math.PI / 2, Math.PI);
|
|
5085
|
+
context.lineTo(x, y + radiusY);
|
|
5086
|
+
context.ellipse(x + radiusX, y + radiusY, radiusX, radiusY, 0, Math.PI, Math.PI * 1.5);
|
|
5087
|
+
context.closePath();
|
|
5088
|
+
}
|
|
5089
|
+
function polygonPath(context, points) {
|
|
3422
5090
|
context.beginPath();
|
|
3423
|
-
context.moveTo(
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
context.arcTo(x, y, x + width, y, safeRadius);
|
|
5091
|
+
context.moveTo(points[0][0], points[0][1]);
|
|
5092
|
+
for (const [x, y] of points.slice(1)) {
|
|
5093
|
+
context.lineTo(x, y);
|
|
5094
|
+
}
|
|
3428
5095
|
context.closePath();
|
|
3429
5096
|
}
|
|
3430
5097
|
function paintPath(context, graphic, path) {
|
|
@@ -3438,7 +5105,7 @@ function paintPath(context, graphic, path) {
|
|
|
3438
5105
|
path ? context.stroke(path) : context.stroke();
|
|
3439
5106
|
}
|
|
3440
5107
|
}
|
|
3441
|
-
function drawGraphic(context, graphic) {
|
|
5108
|
+
function drawGraphic(context, graphic, aspectWidth, aspectHeight) {
|
|
3442
5109
|
const padding = Math.max(resolveStrokeWidth(graphic.strokeWidth) / 2 + 2, 4);
|
|
3443
5110
|
const drawableSize = GRAPHIC_TEXTURE_SIZE - padding * 2;
|
|
3444
5111
|
context.clearRect(0, 0, GRAPHIC_TEXTURE_SIZE, GRAPHIC_TEXTURE_SIZE);
|
|
@@ -3456,6 +5123,63 @@ function drawGraphic(context, graphic) {
|
|
|
3456
5123
|
paintPath(context, graphic);
|
|
3457
5124
|
return;
|
|
3458
5125
|
}
|
|
5126
|
+
if (graphic.kind === "triangle") {
|
|
5127
|
+
const center = GRAPHIC_TEXTURE_SIZE / 2;
|
|
5128
|
+
polygonPath(context, [
|
|
5129
|
+
[center, padding],
|
|
5130
|
+
[padding + drawableSize, padding + drawableSize],
|
|
5131
|
+
[padding, padding + drawableSize]
|
|
5132
|
+
]);
|
|
5133
|
+
paintPath(context, graphic);
|
|
5134
|
+
return;
|
|
5135
|
+
}
|
|
5136
|
+
if (graphic.kind === "diamond") {
|
|
5137
|
+
const center = GRAPHIC_TEXTURE_SIZE / 2;
|
|
5138
|
+
polygonPath(context, [
|
|
5139
|
+
[center, padding],
|
|
5140
|
+
[padding + drawableSize, center],
|
|
5141
|
+
[center, padding + drawableSize],
|
|
5142
|
+
[padding, center]
|
|
5143
|
+
]);
|
|
5144
|
+
paintPath(context, graphic);
|
|
5145
|
+
return;
|
|
5146
|
+
}
|
|
5147
|
+
if (graphic.kind === "star") {
|
|
5148
|
+
const center = GRAPHIC_TEXTURE_SIZE / 2;
|
|
5149
|
+
const outerRadius = drawableSize / 2;
|
|
5150
|
+
const innerRadius = outerRadius * 0.45;
|
|
5151
|
+
const points = [];
|
|
5152
|
+
for (let index = 0; index < 10; index += 1) {
|
|
5153
|
+
const angle = -Math.PI / 2 + index * Math.PI / 5;
|
|
5154
|
+
const radius = index % 2 === 0 ? outerRadius : innerRadius;
|
|
5155
|
+
points.push([
|
|
5156
|
+
center + Math.cos(angle) * radius,
|
|
5157
|
+
center + Math.sin(angle) * radius
|
|
5158
|
+
]);
|
|
5159
|
+
}
|
|
5160
|
+
polygonPath(context, points);
|
|
5161
|
+
paintPath(context, graphic);
|
|
5162
|
+
return;
|
|
5163
|
+
}
|
|
5164
|
+
if (graphic.kind === "arrow") {
|
|
5165
|
+
const center = GRAPHIC_TEXTURE_SIZE / 2;
|
|
5166
|
+
const top = padding;
|
|
5167
|
+
const bottom = padding + drawableSize;
|
|
5168
|
+
const headBottom = top + drawableSize * 0.43;
|
|
5169
|
+
const headHalfWidth = drawableSize * 0.38;
|
|
5170
|
+
const stemHalfWidth = drawableSize * 0.14;
|
|
5171
|
+
polygonPath(context, [
|
|
5172
|
+
[center, top],
|
|
5173
|
+
[center + headHalfWidth, headBottom],
|
|
5174
|
+
[center + stemHalfWidth, headBottom],
|
|
5175
|
+
[center + stemHalfWidth, bottom],
|
|
5176
|
+
[center - stemHalfWidth, bottom],
|
|
5177
|
+
[center - stemHalfWidth, headBottom],
|
|
5178
|
+
[center - headHalfWidth, headBottom]
|
|
5179
|
+
]);
|
|
5180
|
+
paintPath(context, graphic);
|
|
5181
|
+
return;
|
|
5182
|
+
}
|
|
3459
5183
|
if (graphic.kind === "rectangle") {
|
|
3460
5184
|
roundedRectanglePath(
|
|
3461
5185
|
context,
|
|
@@ -3463,7 +5187,9 @@ function drawGraphic(context, graphic) {
|
|
|
3463
5187
|
padding,
|
|
3464
5188
|
drawableSize,
|
|
3465
5189
|
drawableSize,
|
|
3466
|
-
graphic.cornerRadius ?? 0
|
|
5190
|
+
graphic.cornerRadius ?? 0,
|
|
5191
|
+
aspectWidth,
|
|
5192
|
+
aspectHeight
|
|
3467
5193
|
);
|
|
3468
5194
|
paintPath(context, graphic);
|
|
3469
5195
|
return;
|
|
@@ -3502,7 +5228,7 @@ function drawGraphic(context, graphic) {
|
|
|
3502
5228
|
paintPath(context, graphic, path);
|
|
3503
5229
|
context.restore();
|
|
3504
5230
|
}
|
|
3505
|
-
function createGraphicTexture(graphic) {
|
|
5231
|
+
function createGraphicTexture(graphic, aspectWidth, aspectHeight) {
|
|
3506
5232
|
const canvas = document.createElement("canvas");
|
|
3507
5233
|
canvas.width = GRAPHIC_TEXTURE_SIZE;
|
|
3508
5234
|
canvas.height = GRAPHIC_TEXTURE_SIZE;
|
|
@@ -3510,7 +5236,7 @@ function createGraphicTexture(graphic) {
|
|
|
3510
5236
|
if (!context) {
|
|
3511
5237
|
throw new Error("Canvas 2D is unavailable for this graphic hotspot.");
|
|
3512
5238
|
}
|
|
3513
|
-
drawGraphic(context, graphic);
|
|
5239
|
+
drawGraphic(context, graphic, aspectWidth, aspectHeight);
|
|
3514
5240
|
const texture = new import_three13.CanvasTexture(canvas);
|
|
3515
5241
|
texture.colorSpace = import_three13.SRGBColorSpace;
|
|
3516
5242
|
texture.wrapS = import_three13.ClampToEdgeWrapping;
|
|
@@ -3521,16 +5247,16 @@ function createGraphicTexture(graphic) {
|
|
|
3521
5247
|
texture.needsUpdate = true;
|
|
3522
5248
|
return texture;
|
|
3523
5249
|
}
|
|
3524
|
-
function useGraphicTexture(graphic, onLoad, onError) {
|
|
3525
|
-
const [texture, setTexture] = (0,
|
|
3526
|
-
const onLoadRef = (0,
|
|
3527
|
-
const onErrorRef = (0,
|
|
5250
|
+
function useGraphicTexture(graphic, aspectWidth, aspectHeight, onLoad, onError) {
|
|
5251
|
+
const [texture, setTexture] = (0, import_react20.useState)(null);
|
|
5252
|
+
const onLoadRef = (0, import_react20.useRef)(onLoad);
|
|
5253
|
+
const onErrorRef = (0, import_react20.useRef)(onError);
|
|
3528
5254
|
onLoadRef.current = onLoad;
|
|
3529
5255
|
onErrorRef.current = onError;
|
|
3530
|
-
(0,
|
|
5256
|
+
(0, import_react20.useEffect)(() => {
|
|
3531
5257
|
let nextTexture = null;
|
|
3532
5258
|
try {
|
|
3533
|
-
nextTexture = createGraphicTexture(graphic);
|
|
5259
|
+
nextTexture = createGraphicTexture(graphic, aspectWidth, aspectHeight);
|
|
3534
5260
|
setTexture(nextTexture);
|
|
3535
5261
|
onLoadRef.current?.(nextTexture);
|
|
3536
5262
|
} catch (error) {
|
|
@@ -3540,7 +5266,7 @@ function useGraphicTexture(graphic, onLoad, onError) {
|
|
|
3540
5266
|
return () => {
|
|
3541
5267
|
nextTexture?.dispose();
|
|
3542
5268
|
};
|
|
3543
|
-
}, [graphic]);
|
|
5269
|
+
}, [aspectHeight, aspectWidth, graphic]);
|
|
3544
5270
|
return texture;
|
|
3545
5271
|
}
|
|
3546
5272
|
function CanvasGraphicHotspot({
|
|
@@ -3552,10 +5278,10 @@ function CanvasGraphicHotspot({
|
|
|
3552
5278
|
onError,
|
|
3553
5279
|
...anchorProps
|
|
3554
5280
|
}) {
|
|
3555
|
-
const texture = useGraphicTexture(graphic, onLoad, onError);
|
|
3556
|
-
return /* @__PURE__ */ (0,
|
|
3557
|
-
/* @__PURE__ */ (0,
|
|
3558
|
-
/* @__PURE__ */ (0,
|
|
5281
|
+
const texture = useGraphicTexture(graphic, width, height, onLoad, onError);
|
|
5282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("mesh", { children: [
|
|
5283
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("planeGeometry", { args: [1, 1] }),
|
|
5284
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3559
5285
|
"meshBasicMaterial",
|
|
3560
5286
|
{
|
|
3561
5287
|
map: texture,
|
|
@@ -3571,16 +5297,16 @@ function GraphicHotspot(props) {
|
|
|
3571
5297
|
const { graphic, ...restProps } = props;
|
|
3572
5298
|
if (graphic.kind === "svg") {
|
|
3573
5299
|
const imageProps = restProps;
|
|
3574
|
-
return /* @__PURE__ */ (0,
|
|
5300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ImageHotspot, { ...imageProps, src: graphic.src });
|
|
3575
5301
|
}
|
|
3576
|
-
return /* @__PURE__ */ (0,
|
|
5302
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CanvasGraphicHotspot, { ...restProps, graphic });
|
|
3577
5303
|
}
|
|
3578
5304
|
|
|
3579
5305
|
// src/hotspot/sequence-hotspot.tsx
|
|
3580
|
-
var
|
|
3581
|
-
var
|
|
5306
|
+
var import_fiber17 = require("@react-three/fiber");
|
|
5307
|
+
var import_react21 = require("react");
|
|
3582
5308
|
var import_three14 = require("three");
|
|
3583
|
-
var
|
|
5309
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
3584
5310
|
var DEFAULT_SEQUENCE_FPS = 12;
|
|
3585
5311
|
function clampOpacity3(opacity) {
|
|
3586
5312
|
if (!Number.isFinite(opacity)) {
|
|
@@ -3600,13 +5326,13 @@ function useSpriteTexture({
|
|
|
3600
5326
|
onError,
|
|
3601
5327
|
onLoadProgress
|
|
3602
5328
|
}) {
|
|
3603
|
-
const gl = (0,
|
|
3604
|
-
const [texture, setTexture] = (0,
|
|
3605
|
-
const onErrorRef = (0,
|
|
3606
|
-
const onLoadProgressRef = (0,
|
|
5329
|
+
const gl = (0, import_fiber17.useThree)((state) => state.gl);
|
|
5330
|
+
const [texture, setTexture] = (0, import_react21.useState)(null);
|
|
5331
|
+
const onErrorRef = (0, import_react21.useRef)(onError);
|
|
5332
|
+
const onLoadProgressRef = (0, import_react21.useRef)(onLoadProgress);
|
|
3607
5333
|
onErrorRef.current = onError;
|
|
3608
5334
|
onLoadProgressRef.current = onLoadProgress;
|
|
3609
|
-
(0,
|
|
5335
|
+
(0, import_react21.useEffect)(() => {
|
|
3610
5336
|
let active = true;
|
|
3611
5337
|
setTexture(null);
|
|
3612
5338
|
if (!src) {
|
|
@@ -3647,7 +5373,7 @@ function useSpriteTexture({
|
|
|
3647
5373
|
active = false;
|
|
3648
5374
|
};
|
|
3649
5375
|
}, [gl, id, src]);
|
|
3650
|
-
(0,
|
|
5376
|
+
(0, import_react21.useEffect)(
|
|
3651
5377
|
() => () => {
|
|
3652
5378
|
texture?.dispose();
|
|
3653
5379
|
},
|
|
@@ -3676,24 +5402,24 @@ function SequenceHotspot({
|
|
|
3676
5402
|
const texture = useSpriteTexture({ id, src, onError, onLoadProgress });
|
|
3677
5403
|
const frameCount = resolveFrameCount(requestedFrameCount);
|
|
3678
5404
|
const safeFps = resolveFps(fps);
|
|
3679
|
-
const [frameIndex, setFrameIndex] = (0,
|
|
3680
|
-
const elapsedRef = (0,
|
|
3681
|
-
const frameIndexRef = (0,
|
|
3682
|
-
const endedRef = (0,
|
|
3683
|
-
const lastPlaybackStateRef = (0,
|
|
3684
|
-
const onFrameChangeRef = (0,
|
|
3685
|
-
const onPlaybackStateChangeRef = (0,
|
|
3686
|
-
const onEndedRef = (0,
|
|
5405
|
+
const [frameIndex, setFrameIndex] = (0, import_react21.useState)(0);
|
|
5406
|
+
const elapsedRef = (0, import_react21.useRef)(0);
|
|
5407
|
+
const frameIndexRef = (0, import_react21.useRef)(0);
|
|
5408
|
+
const endedRef = (0, import_react21.useRef)(false);
|
|
5409
|
+
const lastPlaybackStateRef = (0, import_react21.useRef)(null);
|
|
5410
|
+
const onFrameChangeRef = (0, import_react21.useRef)(onFrameChange);
|
|
5411
|
+
const onPlaybackStateChangeRef = (0, import_react21.useRef)(onPlaybackStateChange);
|
|
5412
|
+
const onEndedRef = (0, import_react21.useRef)(onEnded);
|
|
3687
5413
|
onFrameChangeRef.current = onFrameChange;
|
|
3688
5414
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
3689
5415
|
onEndedRef.current = onEnded;
|
|
3690
|
-
(0,
|
|
5416
|
+
(0, import_react21.useEffect)(() => {
|
|
3691
5417
|
elapsedRef.current = 0;
|
|
3692
5418
|
frameIndexRef.current = 0;
|
|
3693
5419
|
endedRef.current = false;
|
|
3694
5420
|
setFrameIndex(0);
|
|
3695
5421
|
}, [frameCount, frameDirection, src]);
|
|
3696
|
-
(0,
|
|
5422
|
+
(0, import_react21.useEffect)(() => {
|
|
3697
5423
|
if (!texture) {
|
|
3698
5424
|
return;
|
|
3699
5425
|
}
|
|
@@ -3707,13 +5433,13 @@ function SequenceHotspot({
|
|
|
3707
5433
|
);
|
|
3708
5434
|
texture.needsUpdate = true;
|
|
3709
5435
|
}, [frameCount, frameDirection, frameIndex, texture]);
|
|
3710
|
-
(0,
|
|
5436
|
+
(0, import_react21.useEffect)(() => {
|
|
3711
5437
|
if (!texture) {
|
|
3712
5438
|
return;
|
|
3713
5439
|
}
|
|
3714
5440
|
onFrameChangeRef.current?.({ id, index: 0, src });
|
|
3715
5441
|
}, [id, src, texture]);
|
|
3716
|
-
(0,
|
|
5442
|
+
(0, import_react21.useEffect)(() => {
|
|
3717
5443
|
if (!texture) {
|
|
3718
5444
|
return;
|
|
3719
5445
|
}
|
|
@@ -3732,7 +5458,7 @@ function SequenceHotspot({
|
|
|
3732
5458
|
onPlaybackStateChangeRef.current?.(nextState);
|
|
3733
5459
|
}
|
|
3734
5460
|
}, [playing, texture]);
|
|
3735
|
-
(0,
|
|
5461
|
+
(0, import_fiber17.useFrame)((_, deltaSeconds) => {
|
|
3736
5462
|
if (!texture || !playing || endedRef.current) {
|
|
3737
5463
|
return;
|
|
3738
5464
|
}
|
|
@@ -3762,9 +5488,9 @@ function SequenceHotspot({
|
|
|
3762
5488
|
onFrameChangeRef.current?.({ id, index: nextIndex, src });
|
|
3763
5489
|
}
|
|
3764
5490
|
});
|
|
3765
|
-
return /* @__PURE__ */ (0,
|
|
3766
|
-
/* @__PURE__ */ (0,
|
|
3767
|
-
/* @__PURE__ */ (0,
|
|
5491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("mesh", { children: [
|
|
5492
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("planeGeometry", { args: [1, 1] }),
|
|
5493
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3768
5494
|
"meshBasicMaterial",
|
|
3769
5495
|
{
|
|
3770
5496
|
map: texture,
|
|
@@ -3778,10 +5504,10 @@ function SequenceHotspot({
|
|
|
3778
5504
|
}
|
|
3779
5505
|
|
|
3780
5506
|
// src/hotspot/video-hotspot.tsx
|
|
3781
|
-
var
|
|
3782
|
-
var
|
|
5507
|
+
var import_fiber18 = require("@react-three/fiber");
|
|
5508
|
+
var import_react22 = require("react");
|
|
3783
5509
|
var import_three15 = require("three");
|
|
3784
|
-
var
|
|
5510
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3785
5511
|
function clampOpacity4(opacity) {
|
|
3786
5512
|
if (!Number.isFinite(opacity)) {
|
|
3787
5513
|
return 1;
|
|
@@ -3795,11 +5521,11 @@ function clampVolume(volume) {
|
|
|
3795
5521
|
return Math.max(0, Math.min(volume, 1));
|
|
3796
5522
|
}
|
|
3797
5523
|
function usePosterTexture(id, poster, onError) {
|
|
3798
|
-
const gl = (0,
|
|
3799
|
-
const [texture, setTexture] = (0,
|
|
3800
|
-
const onErrorRef = (0,
|
|
5524
|
+
const gl = (0, import_fiber18.useThree)((state) => state.gl);
|
|
5525
|
+
const [texture, setTexture] = (0, import_react22.useState)(null);
|
|
5526
|
+
const onErrorRef = (0, import_react22.useRef)(onError);
|
|
3801
5527
|
onErrorRef.current = onError;
|
|
3802
|
-
(0,
|
|
5528
|
+
(0, import_react22.useEffect)(() => {
|
|
3803
5529
|
let active = true;
|
|
3804
5530
|
setTexture(null);
|
|
3805
5531
|
if (!poster) {
|
|
@@ -3836,7 +5562,7 @@ function usePosterTexture(id, poster, onError) {
|
|
|
3836
5562
|
active = false;
|
|
3837
5563
|
};
|
|
3838
5564
|
}, [gl, id, poster]);
|
|
3839
|
-
(0,
|
|
5565
|
+
(0, import_react22.useEffect)(
|
|
3840
5566
|
() => () => {
|
|
3841
5567
|
texture?.dispose();
|
|
3842
5568
|
},
|
|
@@ -3854,14 +5580,14 @@ function useVideoResource({
|
|
|
3854
5580
|
poster,
|
|
3855
5581
|
src
|
|
3856
5582
|
}) {
|
|
3857
|
-
const [resource, setResource] = (0,
|
|
3858
|
-
const onEndedRef = (0,
|
|
3859
|
-
const onErrorRef = (0,
|
|
3860
|
-
const onPlaybackStateChangeRef = (0,
|
|
5583
|
+
const [resource, setResource] = (0, import_react22.useState)(null);
|
|
5584
|
+
const onEndedRef = (0, import_react22.useRef)(onEnded);
|
|
5585
|
+
const onErrorRef = (0, import_react22.useRef)(onError);
|
|
5586
|
+
const onPlaybackStateChangeRef = (0, import_react22.useRef)(onPlaybackStateChange);
|
|
3861
5587
|
onEndedRef.current = onEnded;
|
|
3862
5588
|
onErrorRef.current = onError;
|
|
3863
5589
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
3864
|
-
(0,
|
|
5590
|
+
(0, import_react22.useEffect)(() => {
|
|
3865
5591
|
if (!src) {
|
|
3866
5592
|
setResource(null);
|
|
3867
5593
|
return;
|
|
@@ -3962,11 +5688,11 @@ function VideoHotspot({
|
|
|
3962
5688
|
src
|
|
3963
5689
|
});
|
|
3964
5690
|
const posterTexture = usePosterTexture(id, poster, onError);
|
|
3965
|
-
const onPlaybackErrorRef = (0,
|
|
3966
|
-
const onPlaybackStateChangeRef = (0,
|
|
5691
|
+
const onPlaybackErrorRef = (0, import_react22.useRef)(onPlaybackError);
|
|
5692
|
+
const onPlaybackStateChangeRef = (0, import_react22.useRef)(onPlaybackStateChange);
|
|
3967
5693
|
onPlaybackErrorRef.current = onPlaybackError;
|
|
3968
5694
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
3969
|
-
(0,
|
|
5695
|
+
(0, import_react22.useEffect)(() => {
|
|
3970
5696
|
if (!resource) {
|
|
3971
5697
|
return;
|
|
3972
5698
|
}
|
|
@@ -3976,7 +5702,7 @@ function VideoHotspot({
|
|
|
3976
5702
|
resource.video.playsInline = playsInline;
|
|
3977
5703
|
resource.video.preload = preload;
|
|
3978
5704
|
}, [loop, muted, playsInline, preload, resource, volume]);
|
|
3979
|
-
(0,
|
|
5705
|
+
(0, import_react22.useEffect)(() => {
|
|
3980
5706
|
if (!resource) {
|
|
3981
5707
|
return;
|
|
3982
5708
|
}
|
|
@@ -3999,9 +5725,9 @@ function VideoHotspot({
|
|
|
3999
5725
|
};
|
|
4000
5726
|
}, [playing, resource]);
|
|
4001
5727
|
const activeTexture = resource?.ready ? resource.texture : posterTexture;
|
|
4002
|
-
return /* @__PURE__ */ (0,
|
|
4003
|
-
/* @__PURE__ */ (0,
|
|
4004
|
-
/* @__PURE__ */ (0,
|
|
5728
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("mesh", { children: [
|
|
5729
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("planeGeometry", { args: [1, 1] }),
|
|
5730
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
4005
5731
|
"meshBasicMaterial",
|
|
4006
5732
|
{
|
|
4007
5733
|
map: activeTexture,
|
|
@@ -4018,7 +5744,12 @@ function VideoHotspot({
|
|
|
4018
5744
|
AutoRotate,
|
|
4019
5745
|
GraphicHotspot,
|
|
4020
5746
|
ImageHotspot,
|
|
5747
|
+
KeyboardControls,
|
|
4021
5748
|
MAX_HOTSPOT_PITCH,
|
|
5749
|
+
MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY,
|
|
5750
|
+
MouseControls,
|
|
5751
|
+
PanoContextMenu,
|
|
5752
|
+
PanoEvents,
|
|
4022
5753
|
PanoView,
|
|
4023
5754
|
PanoramaScenes,
|
|
4024
5755
|
PolygonHotspot,
|
|
@@ -4026,12 +5757,19 @@ function VideoHotspot({
|
|
|
4026
5757
|
SequenceHotspot,
|
|
4027
5758
|
Sphere,
|
|
4028
5759
|
Tile,
|
|
5760
|
+
TouchControls,
|
|
4029
5761
|
VideoHotspot,
|
|
4030
5762
|
clampPanoPitch,
|
|
5763
|
+
composePanoContextMenuItems,
|
|
5764
|
+
createDefaultPanoContextMenuItems,
|
|
5765
|
+
createPanoContextMenuPresets,
|
|
5766
|
+
cycleSceneId,
|
|
4031
5767
|
normalizePanoPosition,
|
|
4032
5768
|
normalizePanoYaw,
|
|
4033
5769
|
panoPositionToVector3,
|
|
5770
|
+
resolvePanoContextMenuEntries,
|
|
4034
5771
|
unwrapPolygonVertices,
|
|
5772
|
+
usePanoEvents,
|
|
4035
5773
|
validatePolygonVertices,
|
|
4036
5774
|
validatePolylineVertices,
|
|
4037
5775
|
vector3ToPanoPosition
|