@ericchen1990/pano-view 0.2.0 → 1.0.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 +304 -130
- package/dist/index.cjs +1587 -630
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +194 -62
- package/dist/index.d.ts +194 -62
- package/dist/index.mjs +1472 -502
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
// src/pano-
|
|
3
|
+
// src/pano-viewer.tsx
|
|
4
4
|
import { Canvas } from "@react-three/fiber";
|
|
5
5
|
import {
|
|
6
6
|
forwardRef as forwardRef2,
|
|
7
|
-
useCallback as
|
|
8
|
-
|
|
7
|
+
useCallback as useCallback4,
|
|
8
|
+
useContext as useContext11,
|
|
9
|
+
useEffect as useEffect11,
|
|
9
10
|
useImperativeHandle as useImperativeHandle2,
|
|
10
|
-
useMemo as
|
|
11
|
-
useRef as
|
|
11
|
+
useMemo as useMemo5,
|
|
12
|
+
useRef as useRef10,
|
|
12
13
|
useState as useState3
|
|
13
14
|
} from "react";
|
|
14
15
|
|
|
@@ -404,7 +405,7 @@ function AutoRotate({
|
|
|
404
405
|
const rotatingRef = useRef2(false);
|
|
405
406
|
const yawAccumulatedRef = useRef2(0);
|
|
406
407
|
if (!controlsRef) {
|
|
407
|
-
throw new Error("<AutoRotate> must be rendered inside <
|
|
408
|
+
throw new Error("<AutoRotate> must be rendered inside <PanoViewer>.");
|
|
408
409
|
}
|
|
409
410
|
useEffect2(() => {
|
|
410
411
|
elapsedRef.current = 0;
|
|
@@ -460,18 +461,54 @@ function AutoRotate({
|
|
|
460
461
|
return null;
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
// src/control-claims.ts
|
|
465
|
+
import { createContext as createContext3, useContext as useContext2 } from "react";
|
|
466
|
+
var ControlClaimsContext = createContext3(null);
|
|
467
|
+
var DefaultControlChannelContext = createContext3(false);
|
|
468
|
+
var CHANNEL_COMPONENT = {
|
|
469
|
+
mouse: "MouseControls",
|
|
470
|
+
touch: "TouchControls",
|
|
471
|
+
keyboard: "KeyboardControls"
|
|
472
|
+
};
|
|
473
|
+
function createControlClaims() {
|
|
474
|
+
return { mouse: 0, touch: 0, keyboard: 0 };
|
|
475
|
+
}
|
|
476
|
+
function resetControlClaims(claims) {
|
|
477
|
+
claims.mouse = 0;
|
|
478
|
+
claims.touch = 0;
|
|
479
|
+
claims.keyboard = 0;
|
|
480
|
+
}
|
|
481
|
+
function isProduction() {
|
|
482
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
483
|
+
return nodeEnv === "production";
|
|
484
|
+
}
|
|
485
|
+
function useClaimControlChannel(channel) {
|
|
486
|
+
const claims = useContext2(ControlClaimsContext);
|
|
487
|
+
const isDefault = useContext2(DefaultControlChannelContext);
|
|
488
|
+
if (!claims || isDefault) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
claims[channel] += 1;
|
|
492
|
+
if (!isProduction() && claims[channel] > 1) {
|
|
493
|
+
const name = CHANNEL_COMPONENT[channel];
|
|
494
|
+
console.warn(
|
|
495
|
+
`PanoViewer: multiple <${name}> instances are mounted. Render at most one per channel.`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
463
500
|
// src/hotspot/accessibility.tsx
|
|
464
501
|
import {
|
|
465
|
-
createContext as
|
|
502
|
+
createContext as createContext4,
|
|
466
503
|
useCallback,
|
|
467
|
-
useContext as
|
|
504
|
+
useContext as useContext3,
|
|
468
505
|
useEffect as useEffect3,
|
|
469
506
|
useMemo,
|
|
470
507
|
useRef as useRef3,
|
|
471
508
|
useState
|
|
472
509
|
} from "react";
|
|
473
510
|
import { jsx } from "react/jsx-runtime";
|
|
474
|
-
var HotspotAccessibilityContext =
|
|
511
|
+
var HotspotAccessibilityContext = createContext4(null);
|
|
475
512
|
var VISUALLY_HIDDEN_STYLE = {
|
|
476
513
|
border: 0,
|
|
477
514
|
clip: "rect(0 0 0 0)",
|
|
@@ -497,7 +534,7 @@ function useHotspotAccessibility({
|
|
|
497
534
|
ariaLabel,
|
|
498
535
|
onActivate
|
|
499
536
|
}) {
|
|
500
|
-
const registry =
|
|
537
|
+
const registry = useContext3(HotspotAccessibilityContext);
|
|
501
538
|
const [focused, setFocused] = useState(false);
|
|
502
539
|
const activateRef = useRef3(onActivate);
|
|
503
540
|
const enabled = Boolean(onActivate);
|
|
@@ -578,7 +615,7 @@ function useHotspotAccessibilityLayer() {
|
|
|
578
615
|
|
|
579
616
|
// src/keyboard-controls.tsx
|
|
580
617
|
import { useFrame as useFrame3, useThree as useThree2 } from "@react-three/fiber";
|
|
581
|
-
import { useContext as
|
|
618
|
+
import { useContext as useContext4, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4 } from "react";
|
|
582
619
|
var DEFAULT_ROTATE_SPEED = 60;
|
|
583
620
|
var DEFAULT_ZOOM_SPEED = 30;
|
|
584
621
|
var DEFAULT_SHIFT_MULTIPLIER = 3;
|
|
@@ -647,7 +684,8 @@ function KeyboardControls({
|
|
|
647
684
|
onPreviousScene,
|
|
648
685
|
onNextScene
|
|
649
686
|
}) {
|
|
650
|
-
|
|
687
|
+
useClaimControlChannel("keyboard");
|
|
688
|
+
const controlsRef = useContext4(PanoramaViewContext);
|
|
651
689
|
const { gl } = useThree2();
|
|
652
690
|
const pressedRef = useRef4(/* @__PURE__ */ new Set());
|
|
653
691
|
const shiftHeldRef = useRef4(false);
|
|
@@ -657,7 +695,7 @@ function KeyboardControls({
|
|
|
657
695
|
const onNextSceneRef = useRef4(onNextScene);
|
|
658
696
|
const keyMap = useMemo2(() => resolveKeyMap(keys), [keys]);
|
|
659
697
|
if (!controlsRef) {
|
|
660
|
-
throw new Error("<KeyboardControls> must be rendered inside <
|
|
698
|
+
throw new Error("<KeyboardControls> must be rendered inside <PanoViewer>.");
|
|
661
699
|
}
|
|
662
700
|
invertRef.current = invert;
|
|
663
701
|
fovSpeedRef.current = fovSpeed;
|
|
@@ -804,7 +842,7 @@ function KeyboardControls({
|
|
|
804
842
|
|
|
805
843
|
// src/mouse-controls.tsx
|
|
806
844
|
import { useThree as useThree3 } from "@react-three/fiber";
|
|
807
|
-
import { useContext as
|
|
845
|
+
import { useContext as useContext5, useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
808
846
|
var DEFAULT_ROTATE_SPEED2 = 0.35;
|
|
809
847
|
var DEFAULT_ZOOM_SPEED2 = 0.08;
|
|
810
848
|
var BUTTON_FLAG = {
|
|
@@ -828,7 +866,8 @@ function MouseControls({
|
|
|
828
866
|
buttons,
|
|
829
867
|
fovSpeed
|
|
830
868
|
}) {
|
|
831
|
-
|
|
869
|
+
useClaimControlChannel("mouse");
|
|
870
|
+
const controlsRef = useContext5(PanoramaViewContext);
|
|
832
871
|
const { gl } = useThree3();
|
|
833
872
|
const pointersRef = useRef5(
|
|
834
873
|
/* @__PURE__ */ new Map()
|
|
@@ -841,7 +880,7 @@ function MouseControls({
|
|
|
841
880
|
fovSpeed
|
|
842
881
|
});
|
|
843
882
|
if (!controlsRef) {
|
|
844
|
-
throw new Error("<MouseControls> must be rendered inside <
|
|
883
|
+
throw new Error("<MouseControls> must be rendered inside <PanoViewer>.");
|
|
845
884
|
}
|
|
846
885
|
optionsRef.current = {
|
|
847
886
|
rotateSpeed,
|
|
@@ -975,9 +1014,9 @@ function MouseControls({
|
|
|
975
1014
|
|
|
976
1015
|
// src/pano-context-menu.tsx
|
|
977
1016
|
import {
|
|
978
|
-
createContext as
|
|
1017
|
+
createContext as createContext5,
|
|
979
1018
|
useCallback as useCallback2,
|
|
980
|
-
useContext as
|
|
1019
|
+
useContext as useContext6,
|
|
981
1020
|
useEffect as useEffect6,
|
|
982
1021
|
useId,
|
|
983
1022
|
useLayoutEffect,
|
|
@@ -989,7 +1028,7 @@ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
|
989
1028
|
var DEFAULT_ICON_SIZE = 16;
|
|
990
1029
|
var MENU_EDGE_PADDING = 8;
|
|
991
1030
|
var MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY = 0.4;
|
|
992
|
-
var PanoContextMenuOverlayContext =
|
|
1031
|
+
var PanoContextMenuOverlayContext = createContext5(null);
|
|
993
1032
|
var DEFAULT_APPEARANCE = {
|
|
994
1033
|
background: "#161616",
|
|
995
1034
|
color: "#f4f4f4",
|
|
@@ -1397,10 +1436,10 @@ function PanoContextMenu({
|
|
|
1397
1436
|
separatorClassName,
|
|
1398
1437
|
children
|
|
1399
1438
|
}) {
|
|
1400
|
-
const eventBus =
|
|
1401
|
-
const controlsRef =
|
|
1402
|
-
const overlayApi =
|
|
1403
|
-
const actions =
|
|
1439
|
+
const eventBus = useContext6(PanoEventBusContext);
|
|
1440
|
+
const controlsRef = useContext6(PanoramaViewContext);
|
|
1441
|
+
const overlayApi = useContext6(PanoContextMenuOverlayContext);
|
|
1442
|
+
const actions = useContext6(PanoContextMenuActionsContext);
|
|
1404
1443
|
const instanceId = useId();
|
|
1405
1444
|
const sessionKeyRef = useRef6(0);
|
|
1406
1445
|
const propsRef = useRef6({
|
|
@@ -1426,11 +1465,11 @@ function PanoContextMenu({
|
|
|
1426
1465
|
children
|
|
1427
1466
|
};
|
|
1428
1467
|
if (!eventBus || !controlsRef) {
|
|
1429
|
-
throw new Error("<PanoContextMenu> must be rendered inside <
|
|
1468
|
+
throw new Error("<PanoContextMenu> must be rendered inside <PanoViewer>.");
|
|
1430
1469
|
}
|
|
1431
1470
|
if (!overlayApi) {
|
|
1432
1471
|
throw new Error(
|
|
1433
|
-
"<PanoContextMenu> requires the
|
|
1472
|
+
"<PanoContextMenu> requires the PanoViewer context-menu overlay host."
|
|
1434
1473
|
);
|
|
1435
1474
|
}
|
|
1436
1475
|
const close = useCallback2(() => {
|
|
@@ -1664,11 +1703,163 @@ function composePanoContextMenuItems(props, presets) {
|
|
|
1664
1703
|
...resolvePanoContextMenuEntries(props.append ?? [], presets)
|
|
1665
1704
|
];
|
|
1666
1705
|
}
|
|
1667
|
-
var PanoContextMenuActionsContext =
|
|
1706
|
+
var PanoContextMenuActionsContext = createContext5(null);
|
|
1707
|
+
|
|
1708
|
+
// src/pano-cursor.tsx
|
|
1709
|
+
import { useThree as useThree4 } from "@react-three/fiber";
|
|
1710
|
+
import {
|
|
1711
|
+
createContext as createContext6,
|
|
1712
|
+
useCallback as useCallback3,
|
|
1713
|
+
useContext as useContext7,
|
|
1714
|
+
useEffect as useEffect7,
|
|
1715
|
+
useMemo as useMemo4,
|
|
1716
|
+
useRef as useRef7
|
|
1717
|
+
} from "react";
|
|
1718
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1719
|
+
var DEFAULT_PANO_CURSORS = {
|
|
1720
|
+
default: "grab",
|
|
1721
|
+
dragging: "grabbing",
|
|
1722
|
+
hotspot: "pointer",
|
|
1723
|
+
hotspotDragging: "move"
|
|
1724
|
+
};
|
|
1725
|
+
var PANO_DRAG_CLAIM_ID = "pano-cursor:dragging";
|
|
1726
|
+
var LAYER_PRIORITY = {
|
|
1727
|
+
dragging: 1,
|
|
1728
|
+
hotspot: 2,
|
|
1729
|
+
hotspotDragging: 3
|
|
1730
|
+
};
|
|
1731
|
+
var PanoCursorContext = createContext6(null);
|
|
1732
|
+
function usePanoCursor() {
|
|
1733
|
+
return useContext7(PanoCursorContext);
|
|
1734
|
+
}
|
|
1735
|
+
function hotspotCursorClaimId(id) {
|
|
1736
|
+
return `pano-cursor:hotspot:${id}`;
|
|
1737
|
+
}
|
|
1738
|
+
function resolveClaimCursor(claim, cursors) {
|
|
1739
|
+
switch (claim.layer) {
|
|
1740
|
+
case "dragging":
|
|
1741
|
+
return cursors.dragging;
|
|
1742
|
+
case "hotspot":
|
|
1743
|
+
return claim.cursor ?? cursors.hotspot;
|
|
1744
|
+
case "hotspotDragging":
|
|
1745
|
+
return claim.cursor ?? cursors.hotspotDragging;
|
|
1746
|
+
default: {
|
|
1747
|
+
const exhaustive = claim.layer;
|
|
1748
|
+
return exhaustive;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
function resolveHighestCursor(claims, cursors) {
|
|
1753
|
+
let best = null;
|
|
1754
|
+
let bestPriority = 0;
|
|
1755
|
+
for (const claim of claims.values()) {
|
|
1756
|
+
const priority = LAYER_PRIORITY[claim.layer];
|
|
1757
|
+
if (priority >= bestPriority) {
|
|
1758
|
+
best = claim;
|
|
1759
|
+
bestPriority = priority;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
return best ? resolveClaimCursor(best, cursors) : cursors.default;
|
|
1763
|
+
}
|
|
1764
|
+
function resolvePanoCursors(value) {
|
|
1765
|
+
if (value === false) {
|
|
1766
|
+
return null;
|
|
1767
|
+
}
|
|
1768
|
+
if (value === true || value === void 0) {
|
|
1769
|
+
return DEFAULT_PANO_CURSORS;
|
|
1770
|
+
}
|
|
1771
|
+
return {
|
|
1772
|
+
default: value.default ?? DEFAULT_PANO_CURSORS.default,
|
|
1773
|
+
dragging: value.dragging ?? DEFAULT_PANO_CURSORS.dragging,
|
|
1774
|
+
hotspot: value.hotspot ?? DEFAULT_PANO_CURSORS.hotspot,
|
|
1775
|
+
hotspotDragging: value.hotspotDragging ?? DEFAULT_PANO_CURSORS.hotspotDragging
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
function PanoCursorController({
|
|
1779
|
+
cursors,
|
|
1780
|
+
children
|
|
1781
|
+
}) {
|
|
1782
|
+
const { gl } = useThree4();
|
|
1783
|
+
const eventBus = useContext7(PanoEventBusContext);
|
|
1784
|
+
const claimsRef = useRef7(/* @__PURE__ */ new Map());
|
|
1785
|
+
const cursorsRef = useRef7(cursors);
|
|
1786
|
+
const appliedCursorRef = useRef7(null);
|
|
1787
|
+
cursorsRef.current = cursors;
|
|
1788
|
+
const applyCursor = useCallback3(() => {
|
|
1789
|
+
const next = resolveHighestCursor(claimsRef.current, cursorsRef.current);
|
|
1790
|
+
const element = gl.domElement;
|
|
1791
|
+
if (appliedCursorRef.current === next && element.style.cursor === next) {
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
element.style.cursor = next;
|
|
1795
|
+
appliedCursorRef.current = next;
|
|
1796
|
+
}, [gl]);
|
|
1797
|
+
const claim = useCallback3(
|
|
1798
|
+
(id, layer, cursor) => {
|
|
1799
|
+
claimsRef.current.set(id, { layer, cursor });
|
|
1800
|
+
applyCursor();
|
|
1801
|
+
},
|
|
1802
|
+
[applyCursor]
|
|
1803
|
+
);
|
|
1804
|
+
const release = useCallback3(
|
|
1805
|
+
(id) => {
|
|
1806
|
+
if (claimsRef.current.delete(id)) {
|
|
1807
|
+
applyCursor();
|
|
1808
|
+
}
|
|
1809
|
+
},
|
|
1810
|
+
[applyCursor]
|
|
1811
|
+
);
|
|
1812
|
+
const api = useMemo4(
|
|
1813
|
+
() => ({
|
|
1814
|
+
cursors,
|
|
1815
|
+
claim,
|
|
1816
|
+
release
|
|
1817
|
+
}),
|
|
1818
|
+
[claim, cursors, release]
|
|
1819
|
+
);
|
|
1820
|
+
useEffect7(() => {
|
|
1821
|
+
const element = gl.domElement;
|
|
1822
|
+
const previous = element.style.cursor;
|
|
1823
|
+
return () => {
|
|
1824
|
+
claimsRef.current.clear();
|
|
1825
|
+
appliedCursorRef.current = null;
|
|
1826
|
+
element.style.cursor = previous;
|
|
1827
|
+
};
|
|
1828
|
+
}, [gl]);
|
|
1829
|
+
useEffect7(() => {
|
|
1830
|
+
applyCursor();
|
|
1831
|
+
}, [applyCursor, cursors]);
|
|
1832
|
+
useEffect7(() => {
|
|
1833
|
+
if (!eventBus) {
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
const unsubscribeStart = eventBus.subscribe(
|
|
1837
|
+
"viewinteractionstart",
|
|
1838
|
+
(event) => {
|
|
1839
|
+
if (event.source !== "mouse") {
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
claim(PANO_DRAG_CLAIM_ID, "dragging");
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1845
|
+
const unsubscribeEnd = eventBus.subscribe("viewinteractionend", (event) => {
|
|
1846
|
+
if (event.source !== "mouse") {
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
release(PANO_DRAG_CLAIM_ID);
|
|
1850
|
+
});
|
|
1851
|
+
return () => {
|
|
1852
|
+
unsubscribeStart();
|
|
1853
|
+
unsubscribeEnd();
|
|
1854
|
+
release(PANO_DRAG_CLAIM_ID);
|
|
1855
|
+
};
|
|
1856
|
+
}, [claim, eventBus, release]);
|
|
1857
|
+
return /* @__PURE__ */ jsx3(PanoCursorContext.Provider, { value: api, children });
|
|
1858
|
+
}
|
|
1668
1859
|
|
|
1669
1860
|
// src/pano-events.tsx
|
|
1670
|
-
import { useFrame as useFrame4, useThree as
|
|
1671
|
-
import { useContext as
|
|
1861
|
+
import { useFrame as useFrame4, useThree as useThree5 } from "@react-three/fiber";
|
|
1862
|
+
import { useContext as useContext8, useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
1672
1863
|
var DEFAULT_IDLE_TIME_MS = 2e3;
|
|
1673
1864
|
function resolveIdleTime(value) {
|
|
1674
1865
|
return Number.isFinite(value) && value >= 0 ? value : DEFAULT_IDLE_TIME_MS;
|
|
@@ -1695,13 +1886,13 @@ function usePanoEvents({
|
|
|
1695
1886
|
onAutoRotateStop,
|
|
1696
1887
|
onAutoRotateOneRound
|
|
1697
1888
|
}) {
|
|
1698
|
-
const eventBus =
|
|
1699
|
-
const controlsRef =
|
|
1700
|
-
const { gl } =
|
|
1889
|
+
const eventBus = useContext8(PanoEventBusContext);
|
|
1890
|
+
const controlsRef = useContext8(PanoramaViewContext);
|
|
1891
|
+
const { gl } = useThree5();
|
|
1701
1892
|
if (!eventBus || !controlsRef) {
|
|
1702
|
-
throw new Error("usePanoEvents must be used inside <
|
|
1893
|
+
throw new Error("usePanoEvents must be used inside <PanoViewer>.");
|
|
1703
1894
|
}
|
|
1704
|
-
const callbacksRef =
|
|
1895
|
+
const callbacksRef = useRef8({
|
|
1705
1896
|
onViewChange,
|
|
1706
1897
|
onViewSettled,
|
|
1707
1898
|
onViewInteractionStart,
|
|
@@ -1743,15 +1934,15 @@ function usePanoEvents({
|
|
|
1743
1934
|
onAutoRotateStop,
|
|
1744
1935
|
onAutoRotateOneRound
|
|
1745
1936
|
};
|
|
1746
|
-
const idleElapsedRef =
|
|
1747
|
-
const isIdleRef =
|
|
1748
|
-
const idleTimeRef =
|
|
1937
|
+
const idleElapsedRef = useRef8(0);
|
|
1938
|
+
const isIdleRef = useRef8(false);
|
|
1939
|
+
const idleTimeRef = useRef8(resolveIdleTime(idleTime));
|
|
1749
1940
|
idleTimeRef.current = resolveIdleTime(idleTime);
|
|
1750
|
-
|
|
1941
|
+
useEffect8(() => {
|
|
1751
1942
|
idleElapsedRef.current = 0;
|
|
1752
1943
|
isIdleRef.current = false;
|
|
1753
1944
|
}, [idleTime]);
|
|
1754
|
-
|
|
1945
|
+
useEffect8(() => {
|
|
1755
1946
|
const unsubscribers = [
|
|
1756
1947
|
eventBus.subscribe("viewchange", (view) => {
|
|
1757
1948
|
callbacksRef.current.onViewChange?.(view);
|
|
@@ -1799,7 +1990,7 @@ function usePanoEvents({
|
|
|
1799
1990
|
}
|
|
1800
1991
|
};
|
|
1801
1992
|
}, [eventBus]);
|
|
1802
|
-
|
|
1993
|
+
useEffect8(() => {
|
|
1803
1994
|
const element = gl.domElement;
|
|
1804
1995
|
const handleWheel = (nativeEvent) => {
|
|
1805
1996
|
callbacksRef.current.onWheel?.({
|
|
@@ -1812,7 +2003,7 @@ function usePanoEvents({
|
|
|
1812
2003
|
element.removeEventListener("wheel", handleWheel);
|
|
1813
2004
|
};
|
|
1814
2005
|
}, [gl]);
|
|
1815
|
-
|
|
2006
|
+
useEffect8(() => {
|
|
1816
2007
|
if (typeof document === "undefined") {
|
|
1817
2008
|
return;
|
|
1818
2009
|
}
|
|
@@ -1841,7 +2032,7 @@ function usePanoEvents({
|
|
|
1841
2032
|
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
1842
2033
|
};
|
|
1843
2034
|
}, [gl]);
|
|
1844
|
-
|
|
2035
|
+
useEffect8(() => {
|
|
1845
2036
|
const element = gl.domElement;
|
|
1846
2037
|
if (typeof ResizeObserver === "undefined") {
|
|
1847
2038
|
return;
|
|
@@ -1882,8 +2073,8 @@ function PanoEvents(props) {
|
|
|
1882
2073
|
}
|
|
1883
2074
|
|
|
1884
2075
|
// src/panorama-event-surface.tsx
|
|
1885
|
-
import { useThree as
|
|
1886
|
-
import { useContext as
|
|
2076
|
+
import { useThree as useThree6 } from "@react-three/fiber";
|
|
2077
|
+
import { useContext as useContext9, useEffect as useEffect9 } from "react";
|
|
1887
2078
|
import { BackSide, Raycaster, Vector2, Vector3 as Vector32 } from "three";
|
|
1888
2079
|
|
|
1889
2080
|
// src/hotspot/coordinates.ts
|
|
@@ -1949,7 +2140,7 @@ var DEFAULT_PANORAMA_CAMERA_NEAR = 0.1;
|
|
|
1949
2140
|
var DEFAULT_PANORAMA_CAMERA_FAR = DEFAULT_PANORAMA_RADIUS * 2;
|
|
1950
2141
|
|
|
1951
2142
|
// src/panorama-event-surface.tsx
|
|
1952
|
-
import { jsx as
|
|
2143
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1953
2144
|
var EVENT_SURFACE_RADIUS = DEFAULT_PANORAMA_RADIUS - 1;
|
|
1954
2145
|
function makePanoramaPointerEvent(event) {
|
|
1955
2146
|
const nativeEvent = event.nativeEvent;
|
|
@@ -1964,9 +2155,9 @@ function makePanoramaPointerEvent(event) {
|
|
|
1964
2155
|
};
|
|
1965
2156
|
}
|
|
1966
2157
|
function PanoramaEventSurface() {
|
|
1967
|
-
const eventBus =
|
|
1968
|
-
const { camera, gl } =
|
|
1969
|
-
|
|
2158
|
+
const eventBus = useContext9(PanoEventBusContext);
|
|
2159
|
+
const { camera, gl } = useThree6();
|
|
2160
|
+
useEffect9(() => {
|
|
1970
2161
|
if (!eventBus) {
|
|
1971
2162
|
return;
|
|
1972
2163
|
}
|
|
@@ -2017,8 +2208,8 @@ function PanoramaEventSurface() {
|
|
|
2017
2208
|
onPointerUp: (event) => emit("pointerup", event),
|
|
2018
2209
|
onPointerMove: (event) => emit("pointermove", event),
|
|
2019
2210
|
children: [
|
|
2020
|
-
/* @__PURE__ */
|
|
2021
|
-
/* @__PURE__ */
|
|
2211
|
+
/* @__PURE__ */ jsx4("sphereGeometry", { args: [EVENT_SURFACE_RADIUS, 64, 32] }),
|
|
2212
|
+
/* @__PURE__ */ jsx4(
|
|
2022
2213
|
"meshBasicMaterial",
|
|
2023
2214
|
{
|
|
2024
2215
|
colorWrite: false,
|
|
@@ -2035,8 +2226,8 @@ function PanoramaEventSurface() {
|
|
|
2035
2226
|
}
|
|
2036
2227
|
|
|
2037
2228
|
// src/touch-controls.tsx
|
|
2038
|
-
import { useThree as
|
|
2039
|
-
import { useContext as
|
|
2229
|
+
import { useThree as useThree7 } from "@react-three/fiber";
|
|
2230
|
+
import { useContext as useContext10, useEffect as useEffect10, useRef as useRef9 } from "react";
|
|
2040
2231
|
var DEFAULT_ROTATE_SPEED3 = 0.35;
|
|
2041
2232
|
function resolveSpeed3(value, fallback) {
|
|
2042
2233
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
@@ -2050,16 +2241,17 @@ function TouchControls({
|
|
|
2050
2241
|
invert = false,
|
|
2051
2242
|
pinchZoom = true
|
|
2052
2243
|
}) {
|
|
2053
|
-
|
|
2054
|
-
const
|
|
2055
|
-
const
|
|
2056
|
-
const
|
|
2057
|
-
const
|
|
2244
|
+
useClaimControlChannel("touch");
|
|
2245
|
+
const controlsRef = useContext10(PanoramaViewContext);
|
|
2246
|
+
const { gl } = useThree7();
|
|
2247
|
+
const pointersRef = useRef9(/* @__PURE__ */ new Map());
|
|
2248
|
+
const pinchDistanceRef = useRef9(null);
|
|
2249
|
+
const optionsRef = useRef9({ rotateSpeed, invert, pinchZoom });
|
|
2058
2250
|
if (!controlsRef) {
|
|
2059
|
-
throw new Error("<TouchControls> must be rendered inside <
|
|
2251
|
+
throw new Error("<TouchControls> must be rendered inside <PanoViewer>.");
|
|
2060
2252
|
}
|
|
2061
2253
|
optionsRef.current = { rotateSpeed, invert, pinchZoom };
|
|
2062
|
-
|
|
2254
|
+
useEffect10(() => {
|
|
2063
2255
|
const element = gl.domElement;
|
|
2064
2256
|
const clearPointers = () => {
|
|
2065
2257
|
for (const pointerId of pointersRef.current.keys()) {
|
|
@@ -2180,8 +2372,8 @@ function TouchControls({
|
|
|
2180
2372
|
return null;
|
|
2181
2373
|
}
|
|
2182
2374
|
|
|
2183
|
-
// src/pano-
|
|
2184
|
-
import { jsx as
|
|
2375
|
+
// src/pano-viewer.tsx
|
|
2376
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2185
2377
|
var DEFAULT_VIEW = {
|
|
2186
2378
|
yaw: 0,
|
|
2187
2379
|
pitch: 0,
|
|
@@ -2219,13 +2411,46 @@ function resolveContextMenuChannel(value) {
|
|
|
2219
2411
|
}
|
|
2220
2412
|
return { mount: true, props: { ...value } };
|
|
2221
2413
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2414
|
+
function PanoCursorTree({
|
|
2415
|
+
cursors,
|
|
2416
|
+
children
|
|
2417
|
+
}) {
|
|
2418
|
+
if (!cursors) {
|
|
2419
|
+
return children;
|
|
2420
|
+
}
|
|
2421
|
+
return /* @__PURE__ */ jsx5(PanoCursorController, { cursors, children });
|
|
2422
|
+
}
|
|
2423
|
+
function DefaultControlChannels({
|
|
2424
|
+
userControlsEnabled,
|
|
2425
|
+
mouseChannel,
|
|
2426
|
+
touchChannel,
|
|
2427
|
+
keyboardChannel,
|
|
2428
|
+
fovSpeed
|
|
2429
|
+
}) {
|
|
2430
|
+
const claims = useContext11(ControlClaimsContext);
|
|
2431
|
+
if (!claims) {
|
|
2432
|
+
return null;
|
|
2433
|
+
}
|
|
2434
|
+
return /* @__PURE__ */ jsxs3(DefaultControlChannelContext.Provider, { value: true, children: [
|
|
2435
|
+
userControlsEnabled && mouseChannel.mount && claims.mouse === 0 ? /* @__PURE__ */ jsx5(MouseControls, { ...mouseChannel.props, fovSpeed }) : null,
|
|
2436
|
+
userControlsEnabled && touchChannel.mount && claims.touch === 0 ? /* @__PURE__ */ jsx5(TouchControls, { ...touchChannel.props }) : null,
|
|
2437
|
+
userControlsEnabled && keyboardChannel.mount && claims.keyboard === 0 ? /* @__PURE__ */ jsx5(
|
|
2438
|
+
KeyboardControls,
|
|
2439
|
+
{
|
|
2440
|
+
...keyboardChannel.props,
|
|
2441
|
+
fovSpeed: keyboardChannel.props.fovSpeed ?? fovSpeed
|
|
2442
|
+
}
|
|
2443
|
+
) : null
|
|
2444
|
+
] });
|
|
2445
|
+
}
|
|
2446
|
+
var PanoViewer = forwardRef2(
|
|
2447
|
+
function PanoViewer2({
|
|
2224
2448
|
children,
|
|
2225
2449
|
initialView,
|
|
2226
2450
|
minFov = 30,
|
|
2227
2451
|
maxFov = 100,
|
|
2228
2452
|
controls = true,
|
|
2453
|
+
cursors,
|
|
2229
2454
|
contextMenu = true,
|
|
2230
2455
|
onViewChange,
|
|
2231
2456
|
onPanoramaClick,
|
|
@@ -2236,18 +2461,18 @@ var PanoView = forwardRef2(
|
|
|
2236
2461
|
"aria-label": ariaLabel = "Panorama viewer",
|
|
2237
2462
|
...divProps
|
|
2238
2463
|
}, ref) {
|
|
2239
|
-
const rootRef =
|
|
2240
|
-
const controlsRef =
|
|
2241
|
-
const eventBus =
|
|
2464
|
+
const rootRef = useRef10(null);
|
|
2465
|
+
const controlsRef = useRef10(null);
|
|
2466
|
+
const eventBus = useMemo5(() => createPanoEventBus(), []);
|
|
2242
2467
|
const { controls: hotspotAccessibilityControls, registry } = useHotspotAccessibilityLayer();
|
|
2243
2468
|
const { api: contextMenuOverlayApi, overlay: contextMenuOverlay } = usePanoContextMenuOverlay();
|
|
2244
|
-
const fallbackViewRef =
|
|
2469
|
+
const fallbackViewRef = useRef10(DEFAULT_VIEW);
|
|
2245
2470
|
const normalizedMinFov = Math.max(1, Math.min(minFov, maxFov - 1));
|
|
2246
2471
|
const normalizedMaxFov = Math.min(
|
|
2247
2472
|
179,
|
|
2248
2473
|
Math.max(maxFov, normalizedMinFov + 1)
|
|
2249
2474
|
);
|
|
2250
|
-
const normalizedInitialView =
|
|
2475
|
+
const normalizedInitialView = useMemo5(
|
|
2251
2476
|
() => ({
|
|
2252
2477
|
yaw: initialView?.yaw ?? DEFAULT_VIEW.yaw,
|
|
2253
2478
|
pitch: clampPanoPitch(initialView?.pitch ?? DEFAULT_VIEW.pitch),
|
|
@@ -2265,8 +2490,10 @@ var PanoView = forwardRef2(
|
|
|
2265
2490
|
]
|
|
2266
2491
|
);
|
|
2267
2492
|
fallbackViewRef.current = normalizedInitialView;
|
|
2493
|
+
const controlClaims = useMemo5(() => createControlClaims(), []);
|
|
2494
|
+
resetControlClaims(controlClaims);
|
|
2268
2495
|
const controlsEnabled = controls !== false;
|
|
2269
|
-
const controlOptions =
|
|
2496
|
+
const controlOptions = useMemo5(
|
|
2270
2497
|
() => typeof controls === "object" ? controls : {},
|
|
2271
2498
|
[controls]
|
|
2272
2499
|
);
|
|
@@ -2274,7 +2501,7 @@ var PanoView = forwardRef2(
|
|
|
2274
2501
|
const allModeInvert = controlOptions.invert === true;
|
|
2275
2502
|
const topRotateSpeed = controlOptions.rotateSpeed ?? DEFAULT_POINTER_ROTATE_SPEED;
|
|
2276
2503
|
const topZoomSpeed = controlOptions.zoomSpeed ?? DEFAULT_MOUSE_ZOOM_SPEED;
|
|
2277
|
-
const mouseChannel =
|
|
2504
|
+
const mouseChannel = useMemo5(
|
|
2278
2505
|
() => resolveChannel(controlOptions.mouse, {
|
|
2279
2506
|
enabled: true,
|
|
2280
2507
|
rotateSpeed: topRotateSpeed,
|
|
@@ -2290,7 +2517,7 @@ var PanoView = forwardRef2(
|
|
|
2290
2517
|
topZoomSpeed
|
|
2291
2518
|
]
|
|
2292
2519
|
);
|
|
2293
|
-
const touchChannel =
|
|
2520
|
+
const touchChannel = useMemo5(
|
|
2294
2521
|
() => resolveChannel(controlOptions.touch, {
|
|
2295
2522
|
enabled: true,
|
|
2296
2523
|
rotateSpeed: topRotateSpeed,
|
|
@@ -2299,13 +2526,13 @@ var PanoView = forwardRef2(
|
|
|
2299
2526
|
}),
|
|
2300
2527
|
[allModeInvert, controlOptions.touch, topRotateSpeed]
|
|
2301
2528
|
);
|
|
2302
|
-
const keyboardChannel =
|
|
2529
|
+
const keyboardChannel = useMemo5(
|
|
2303
2530
|
() => resolveChannel(controlOptions.keyboard, {
|
|
2304
2531
|
enabled: true
|
|
2305
2532
|
}),
|
|
2306
2533
|
[controlOptions.keyboard]
|
|
2307
2534
|
);
|
|
2308
|
-
const toggleFullscreen =
|
|
2535
|
+
const toggleFullscreen = useCallback4(async () => {
|
|
2309
2536
|
if (typeof document === "undefined") {
|
|
2310
2537
|
return;
|
|
2311
2538
|
}
|
|
@@ -2316,7 +2543,7 @@ var PanoView = forwardRef2(
|
|
|
2316
2543
|
await rootRef.current?.requestFullscreen?.();
|
|
2317
2544
|
}, []);
|
|
2318
2545
|
const [isViewerFullscreen, setIsViewerFullscreen] = useState3(false);
|
|
2319
|
-
|
|
2546
|
+
useEffect11(() => {
|
|
2320
2547
|
if (typeof document === "undefined") {
|
|
2321
2548
|
return;
|
|
2322
2549
|
}
|
|
@@ -2337,7 +2564,7 @@ var PanoView = forwardRef2(
|
|
|
2337
2564
|
document.removeEventListener("fullscreenchange", syncFullscreen);
|
|
2338
2565
|
};
|
|
2339
2566
|
}, []);
|
|
2340
|
-
const contextMenuActions =
|
|
2567
|
+
const contextMenuActions = useMemo5(
|
|
2341
2568
|
() => ({
|
|
2342
2569
|
reset: () => {
|
|
2343
2570
|
controlsRef.current?.reset();
|
|
@@ -2349,15 +2576,19 @@ var PanoView = forwardRef2(
|
|
|
2349
2576
|
}),
|
|
2350
2577
|
[isViewerFullscreen, toggleFullscreen]
|
|
2351
2578
|
);
|
|
2352
|
-
const contextMenuChannel =
|
|
2579
|
+
const contextMenuChannel = useMemo5(
|
|
2353
2580
|
() => resolveContextMenuChannel(contextMenu),
|
|
2354
2581
|
[contextMenu]
|
|
2355
2582
|
);
|
|
2583
|
+
const resolvedCursors = useMemo5(
|
|
2584
|
+
() => resolvePanoCursors(cursors),
|
|
2585
|
+
[cursors]
|
|
2586
|
+
);
|
|
2356
2587
|
const legacyAutoRotateOptions = controlOptions;
|
|
2357
2588
|
const [legacyAutoRotate, setLegacyAutoRotate] = useState3(
|
|
2358
2589
|
legacyAutoRotateOptions.autoRotate ?? false
|
|
2359
2590
|
);
|
|
2360
|
-
|
|
2591
|
+
useEffect11(() => {
|
|
2361
2592
|
setLegacyAutoRotate(legacyAutoRotateOptions.autoRotate ?? false);
|
|
2362
2593
|
}, [legacyAutoRotateOptions.autoRotate]);
|
|
2363
2594
|
useImperativeHandle2(
|
|
@@ -2380,7 +2611,7 @@ var PanoView = forwardRef2(
|
|
|
2380
2611
|
}),
|
|
2381
2612
|
[toggleFullscreen]
|
|
2382
2613
|
);
|
|
2383
|
-
const rootStyle =
|
|
2614
|
+
const rootStyle = useMemo5(
|
|
2384
2615
|
() => ({
|
|
2385
2616
|
overflow: "hidden",
|
|
2386
2617
|
position: "relative",
|
|
@@ -2396,7 +2627,7 @@ var PanoView = forwardRef2(
|
|
|
2396
2627
|
"aria-label": ariaLabel,
|
|
2397
2628
|
style: rootStyle,
|
|
2398
2629
|
children: [
|
|
2399
|
-
/* @__PURE__ */
|
|
2630
|
+
/* @__PURE__ */ jsx5(
|
|
2400
2631
|
Canvas,
|
|
2401
2632
|
{
|
|
2402
2633
|
"aria-label": `${ariaLabel} canvas`,
|
|
@@ -2419,7 +2650,7 @@ var PanoView = forwardRef2(
|
|
|
2419
2650
|
touchAction: userControlsEnabled ? "none" : "auto"
|
|
2420
2651
|
},
|
|
2421
2652
|
tabIndex: userControlsEnabled ? 0 : void 0,
|
|
2422
|
-
children: /* @__PURE__ */
|
|
2653
|
+
children: /* @__PURE__ */ jsx5(HotspotAccessibilityContext.Provider, { value: registry, children: /* @__PURE__ */ jsx5(PanoEventBusContext.Provider, { value: eventBus, children: /* @__PURE__ */ jsx5(PanoCursorTree, { cursors: resolvedCursors, children: /* @__PURE__ */ jsx5(PanoramaViewContext.Provider, { value: controlsRef, children: /* @__PURE__ */ jsx5(
|
|
2423
2654
|
PanoContextMenuOverlayContext.Provider,
|
|
2424
2655
|
{
|
|
2425
2656
|
value: contextMenuOverlayApi,
|
|
@@ -2428,7 +2659,7 @@ var PanoView = forwardRef2(
|
|
|
2428
2659
|
{
|
|
2429
2660
|
value: contextMenuActions,
|
|
2430
2661
|
children: [
|
|
2431
|
-
/* @__PURE__ */
|
|
2662
|
+
/* @__PURE__ */ jsx5(
|
|
2432
2663
|
PanoramaViewRuntime,
|
|
2433
2664
|
{
|
|
2434
2665
|
ref: controlsRef,
|
|
@@ -2439,7 +2670,7 @@ var PanoView = forwardRef2(
|
|
|
2439
2670
|
options: controlOptions
|
|
2440
2671
|
}
|
|
2441
2672
|
),
|
|
2442
|
-
(onViewChange || onPanoramaClick || onPanoramaDoubleClick || onPanoramaPointerMove) && /* @__PURE__ */
|
|
2673
|
+
(onViewChange || onPanoramaClick || onPanoramaDoubleClick || onPanoramaPointerMove) && /* @__PURE__ */ jsx5(
|
|
2443
2674
|
PanoEvents,
|
|
2444
2675
|
{
|
|
2445
2676
|
onViewChange,
|
|
@@ -2448,36 +2679,33 @@ var PanoView = forwardRef2(
|
|
|
2448
2679
|
onPointerMove: onPanoramaPointerMove
|
|
2449
2680
|
}
|
|
2450
2681
|
),
|
|
2451
|
-
/* @__PURE__ */
|
|
2682
|
+
/* @__PURE__ */ jsx5(
|
|
2452
2683
|
AutoRotate,
|
|
2453
2684
|
{
|
|
2454
2685
|
enabled: legacyAutoRotate,
|
|
2455
2686
|
speed: legacyAutoRotateOptions.autoRotateSpeed
|
|
2456
2687
|
}
|
|
2457
2688
|
),
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
)
|
|
2473
|
-
/* @__PURE__ */ jsx4(PanoramaEventSurface, {}),
|
|
2474
|
-
contextMenuChannel.mount ? /* @__PURE__ */ jsx4(PanoContextMenu, { ...contextMenuChannel.props }) : null,
|
|
2475
|
-
children
|
|
2689
|
+
/* @__PURE__ */ jsx5(PanoramaEventSurface, {}),
|
|
2690
|
+
contextMenuChannel.mount ? /* @__PURE__ */ jsx5(PanoContextMenu, { ...contextMenuChannel.props }) : null,
|
|
2691
|
+
/* @__PURE__ */ jsxs3(ControlClaimsContext.Provider, { value: controlClaims, children: [
|
|
2692
|
+
children,
|
|
2693
|
+
/* @__PURE__ */ jsx5(
|
|
2694
|
+
DefaultControlChannels,
|
|
2695
|
+
{
|
|
2696
|
+
fovSpeed: controlOptions.fovSpeed,
|
|
2697
|
+
keyboardChannel,
|
|
2698
|
+
mouseChannel,
|
|
2699
|
+
touchChannel,
|
|
2700
|
+
userControlsEnabled
|
|
2701
|
+
}
|
|
2702
|
+
)
|
|
2703
|
+
] })
|
|
2476
2704
|
]
|
|
2477
2705
|
}
|
|
2478
2706
|
)
|
|
2479
2707
|
}
|
|
2480
|
-
) }) }) })
|
|
2708
|
+
) }) }) }) })
|
|
2481
2709
|
}
|
|
2482
2710
|
),
|
|
2483
2711
|
contextMenuOverlay,
|
|
@@ -2489,8 +2717,8 @@ var PanoView = forwardRef2(
|
|
|
2489
2717
|
);
|
|
2490
2718
|
|
|
2491
2719
|
// src/sphere.tsx
|
|
2492
|
-
import { useThree as
|
|
2493
|
-
import { useEffect as
|
|
2720
|
+
import { useThree as useThree8 } from "@react-three/fiber";
|
|
2721
|
+
import { useEffect as useEffect12, useRef as useRef11, useState as useState4 } from "react";
|
|
2494
2722
|
import {
|
|
2495
2723
|
BackSide as BackSide2,
|
|
2496
2724
|
LinearFilter,
|
|
@@ -2500,7 +2728,7 @@ import {
|
|
|
2500
2728
|
SRGBColorSpace,
|
|
2501
2729
|
TextureLoader
|
|
2502
2730
|
} from "three";
|
|
2503
|
-
import { jsx as
|
|
2731
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2504
2732
|
function Sphere({
|
|
2505
2733
|
src,
|
|
2506
2734
|
yawOffset = 0,
|
|
@@ -2508,13 +2736,13 @@ function Sphere({
|
|
|
2508
2736
|
onError,
|
|
2509
2737
|
visible = true
|
|
2510
2738
|
}) {
|
|
2511
|
-
const gl =
|
|
2739
|
+
const gl = useThree8((state) => state.gl);
|
|
2512
2740
|
const [texture, setTexture] = useState4(null);
|
|
2513
|
-
const onLoadRef =
|
|
2514
|
-
const onErrorRef =
|
|
2741
|
+
const onLoadRef = useRef11(onLoad);
|
|
2742
|
+
const onErrorRef = useRef11(onError);
|
|
2515
2743
|
onLoadRef.current = onLoad;
|
|
2516
2744
|
onErrorRef.current = onError;
|
|
2517
|
-
|
|
2745
|
+
useEffect12(() => {
|
|
2518
2746
|
let active = true;
|
|
2519
2747
|
const loader = new TextureLoader();
|
|
2520
2748
|
setTexture(null);
|
|
@@ -2548,7 +2776,7 @@ function Sphere({
|
|
|
2548
2776
|
active = false;
|
|
2549
2777
|
};
|
|
2550
2778
|
}, [gl, src]);
|
|
2551
|
-
|
|
2779
|
+
useEffect12(
|
|
2552
2780
|
() => () => {
|
|
2553
2781
|
texture?.dispose();
|
|
2554
2782
|
},
|
|
@@ -2563,8 +2791,8 @@ function Sphere({
|
|
|
2563
2791
|
rotation: [0, MathUtils3.degToRad(90 + yawOffset), 0],
|
|
2564
2792
|
visible,
|
|
2565
2793
|
children: [
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
/* @__PURE__ */
|
|
2794
|
+
/* @__PURE__ */ jsx6("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
|
|
2795
|
+
/* @__PURE__ */ jsx6(
|
|
2568
2796
|
"meshBasicMaterial",
|
|
2569
2797
|
{
|
|
2570
2798
|
map: texture,
|
|
@@ -2577,15 +2805,15 @@ function Sphere({
|
|
|
2577
2805
|
);
|
|
2578
2806
|
}
|
|
2579
2807
|
|
|
2580
|
-
// src/
|
|
2581
|
-
import { useFrame as useFrame6, useThree as
|
|
2808
|
+
// src/scenes.tsx
|
|
2809
|
+
import { useFrame as useFrame6, useThree as useThree11 } from "@react-three/fiber";
|
|
2582
2810
|
import {
|
|
2583
2811
|
Fragment,
|
|
2584
|
-
useCallback as
|
|
2585
|
-
useContext as
|
|
2586
|
-
useEffect as
|
|
2587
|
-
useMemo as
|
|
2588
|
-
useRef as
|
|
2812
|
+
useCallback as useCallback5,
|
|
2813
|
+
useContext as useContext13,
|
|
2814
|
+
useEffect as useEffect15,
|
|
2815
|
+
useMemo as useMemo8,
|
|
2816
|
+
useRef as useRef13,
|
|
2589
2817
|
useState as useState6
|
|
2590
2818
|
} from "react";
|
|
2591
2819
|
import {
|
|
@@ -2599,12 +2827,12 @@ import {
|
|
|
2599
2827
|
} from "three";
|
|
2600
2828
|
|
|
2601
2829
|
// src/tile/texture-manager-context.tsx
|
|
2602
|
-
import { useThree as
|
|
2830
|
+
import { useThree as useThree9 } from "@react-three/fiber";
|
|
2603
2831
|
import {
|
|
2604
|
-
createContext as
|
|
2605
|
-
useContext as
|
|
2606
|
-
useEffect as
|
|
2607
|
-
useMemo as
|
|
2832
|
+
createContext as createContext7,
|
|
2833
|
+
useContext as useContext12,
|
|
2834
|
+
useEffect as useEffect13,
|
|
2835
|
+
useMemo as useMemo6
|
|
2608
2836
|
} from "react";
|
|
2609
2837
|
|
|
2610
2838
|
// src/tile/texture-manager.ts
|
|
@@ -2834,8 +3062,8 @@ var TileTextureManager = class {
|
|
|
2834
3062
|
};
|
|
2835
3063
|
|
|
2836
3064
|
// src/tile/texture-manager-context.tsx
|
|
2837
|
-
import { jsx as
|
|
2838
|
-
var TileTextureManagerContext =
|
|
3065
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
3066
|
+
var TileTextureManagerContext = createContext7(
|
|
2839
3067
|
null
|
|
2840
3068
|
);
|
|
2841
3069
|
function TileTextureManagerProvider({
|
|
@@ -2843,9 +3071,9 @@ function TileTextureManagerProvider({
|
|
|
2843
3071
|
maxTextureMemoryMb = 128,
|
|
2844
3072
|
maxConcurrentLoads = 4
|
|
2845
3073
|
}) {
|
|
2846
|
-
const gl =
|
|
3074
|
+
const gl = useThree9((state) => state.gl);
|
|
2847
3075
|
const anisotropy = gl.capabilities.getMaxAnisotropy();
|
|
2848
|
-
const manager =
|
|
3076
|
+
const manager = useMemo6(
|
|
2849
3077
|
() => new TileTextureManager({
|
|
2850
3078
|
anisotropy,
|
|
2851
3079
|
maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
|
|
@@ -2854,23 +3082,23 @@ function TileTextureManagerProvider({
|
|
|
2854
3082
|
}),
|
|
2855
3083
|
[anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
|
|
2856
3084
|
);
|
|
2857
|
-
|
|
3085
|
+
useEffect13(() => {
|
|
2858
3086
|
manager.resume();
|
|
2859
3087
|
return () => manager.dispose();
|
|
2860
3088
|
}, [manager]);
|
|
2861
|
-
return /* @__PURE__ */
|
|
3089
|
+
return /* @__PURE__ */ jsx7(TileTextureManagerContext.Provider, { value: manager, children });
|
|
2862
3090
|
}
|
|
2863
3091
|
function useSharedTileTextureManager() {
|
|
2864
|
-
return
|
|
3092
|
+
return useContext12(TileTextureManagerContext);
|
|
2865
3093
|
}
|
|
2866
3094
|
|
|
2867
3095
|
// src/tile/tile.tsx
|
|
2868
|
-
import { useFrame as useFrame5, useThree as
|
|
3096
|
+
import { useFrame as useFrame5, useThree as useThree10 } from "@react-three/fiber";
|
|
2869
3097
|
import {
|
|
2870
3098
|
memo,
|
|
2871
|
-
useEffect as
|
|
2872
|
-
useMemo as
|
|
2873
|
-
useRef as
|
|
3099
|
+
useEffect as useEffect14,
|
|
3100
|
+
useMemo as useMemo7,
|
|
3101
|
+
useRef as useRef12,
|
|
2874
3102
|
useState as useState5
|
|
2875
3103
|
} from "react";
|
|
2876
3104
|
import {
|
|
@@ -3068,7 +3296,7 @@ function resolveTemplateUrl(template, address) {
|
|
|
3068
3296
|
}
|
|
3069
3297
|
|
|
3070
3298
|
// src/tile/tile.tsx
|
|
3071
|
-
import { jsx as
|
|
3299
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3072
3300
|
var MAX_LOD_UPDATE_INTERVAL_MS = 100;
|
|
3073
3301
|
var FAST_ROTATION_THRESHOLD_RADIANS = Math.PI / 180;
|
|
3074
3302
|
var FAST_FOV_THRESHOLD_DEGREES = 0.5;
|
|
@@ -3078,9 +3306,9 @@ var PREVIEW_RADIUS = CUBE_RADIUS + 0.05;
|
|
|
3078
3306
|
var SHARED_TILE_GEOMETRY = new PlaneGeometry(1, 1);
|
|
3079
3307
|
function usePreviewTexture(url, anisotropy, onError) {
|
|
3080
3308
|
const [texture, setTexture] = useState5(null);
|
|
3081
|
-
const onErrorRef =
|
|
3309
|
+
const onErrorRef = useRef12(onError);
|
|
3082
3310
|
onErrorRef.current = onError;
|
|
3083
|
-
|
|
3311
|
+
useEffect14(() => {
|
|
3084
3312
|
if (!url) {
|
|
3085
3313
|
setTexture(null);
|
|
3086
3314
|
return;
|
|
@@ -3117,7 +3345,7 @@ function usePreviewTexture(url, anisotropy, onError) {
|
|
|
3117
3345
|
active = false;
|
|
3118
3346
|
};
|
|
3119
3347
|
}, [anisotropy, url]);
|
|
3120
|
-
|
|
3348
|
+
useEffect14(
|
|
3121
3349
|
() => () => {
|
|
3122
3350
|
texture?.dispose();
|
|
3123
3351
|
},
|
|
@@ -3130,7 +3358,7 @@ function PreviewFace({
|
|
|
3130
3358
|
texture,
|
|
3131
3359
|
faceOrder
|
|
3132
3360
|
}) {
|
|
3133
|
-
const geometry =
|
|
3361
|
+
const geometry = useMemo7(() => {
|
|
3134
3362
|
const nextGeometry = new PlaneGeometry(
|
|
3135
3363
|
PREVIEW_RADIUS * 2,
|
|
3136
3364
|
PREVIEW_RADIUS * 2
|
|
@@ -3148,21 +3376,21 @@ function PreviewFace({
|
|
|
3148
3376
|
uv.needsUpdate = true;
|
|
3149
3377
|
return nextGeometry;
|
|
3150
3378
|
}, [face, faceOrder]);
|
|
3151
|
-
|
|
3379
|
+
useEffect14(
|
|
3152
3380
|
() => () => {
|
|
3153
3381
|
geometry.dispose();
|
|
3154
3382
|
},
|
|
3155
3383
|
[geometry]
|
|
3156
3384
|
);
|
|
3157
3385
|
const transform = getFaceTransform(face, PREVIEW_RADIUS);
|
|
3158
|
-
return /* @__PURE__ */
|
|
3386
|
+
return /* @__PURE__ */ jsx8(
|
|
3159
3387
|
"mesh",
|
|
3160
3388
|
{
|
|
3161
3389
|
geometry,
|
|
3162
3390
|
position: transform.position,
|
|
3163
3391
|
renderOrder: -100,
|
|
3164
3392
|
rotation: transform.rotation,
|
|
3165
|
-
children: /* @__PURE__ */
|
|
3393
|
+
children: /* @__PURE__ */ jsx8(
|
|
3166
3394
|
"meshBasicMaterial",
|
|
3167
3395
|
{
|
|
3168
3396
|
depthTest: false,
|
|
@@ -3178,7 +3406,7 @@ function PreviewCube({
|
|
|
3178
3406
|
texture,
|
|
3179
3407
|
faceOrder
|
|
3180
3408
|
}) {
|
|
3181
|
-
return CUBE_FACES.map((face) => /* @__PURE__ */
|
|
3409
|
+
return CUBE_FACES.map((face) => /* @__PURE__ */ jsx8(
|
|
3182
3410
|
PreviewFace,
|
|
3183
3411
|
{
|
|
3184
3412
|
face,
|
|
@@ -3196,7 +3424,7 @@ function useManagedTexture(manager, key, url, address) {
|
|
|
3196
3424
|
snapshot: manager.getSnapshot(key)
|
|
3197
3425
|
}));
|
|
3198
3426
|
const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
|
|
3199
|
-
|
|
3427
|
+
useEffect14(
|
|
3200
3428
|
() => manager.acquire(key, url, address, (nextSnapshot) => {
|
|
3201
3429
|
setManagedState({
|
|
3202
3430
|
manager,
|
|
@@ -3223,14 +3451,14 @@ var TileMesh = memo(function TileMesh2({
|
|
|
3223
3451
|
if (!snapshot.texture) {
|
|
3224
3452
|
return null;
|
|
3225
3453
|
}
|
|
3226
|
-
return /* @__PURE__ */
|
|
3454
|
+
return /* @__PURE__ */ jsx8("group", { position: transform.position, rotation: transform.rotation, children: /* @__PURE__ */ jsx8(
|
|
3227
3455
|
"mesh",
|
|
3228
3456
|
{
|
|
3229
3457
|
geometry: SHARED_TILE_GEOMETRY,
|
|
3230
3458
|
position: [layout.x, layout.y, 0],
|
|
3231
3459
|
renderOrder,
|
|
3232
3460
|
scale: [layout.width, layout.height, 1],
|
|
3233
|
-
children: /* @__PURE__ */
|
|
3461
|
+
children: /* @__PURE__ */ jsx8(
|
|
3234
3462
|
"meshBasicMaterial",
|
|
3235
3463
|
{
|
|
3236
3464
|
depthTest: false,
|
|
@@ -3247,7 +3475,7 @@ var TileLayerMeshes = memo(function TileLayerMeshes2({
|
|
|
3247
3475
|
manager,
|
|
3248
3476
|
resolveUrl
|
|
3249
3477
|
}) {
|
|
3250
|
-
return layer.addresses.map((address) => /* @__PURE__ */
|
|
3478
|
+
return layer.addresses.map((address) => /* @__PURE__ */ jsx8(
|
|
3251
3479
|
TileMesh,
|
|
3252
3480
|
{
|
|
3253
3481
|
address,
|
|
@@ -3415,11 +3643,11 @@ function Tile({
|
|
|
3415
3643
|
onTileError,
|
|
3416
3644
|
onLevelChange
|
|
3417
3645
|
}) {
|
|
3418
|
-
const { camera, gl, size } =
|
|
3419
|
-
const multiresConfig =
|
|
3646
|
+
const { camera, gl, size } = useThree10();
|
|
3647
|
+
const multiresConfig = useMemo7(() => parseMultires(multires), [multires]);
|
|
3420
3648
|
const { levels, tileSize } = multiresConfig;
|
|
3421
3649
|
const multiresToken = `${tileSize}:${levels.join(",")}`;
|
|
3422
|
-
const candidatesByLevel =
|
|
3650
|
+
const candidatesByLevel = useMemo7(
|
|
3423
3651
|
() => buildTileCandidates(levels, tileSize),
|
|
3424
3652
|
[levels, tileSize]
|
|
3425
3653
|
);
|
|
@@ -3431,13 +3659,13 @@ function Tile({
|
|
|
3431
3659
|
onPreviewError
|
|
3432
3660
|
);
|
|
3433
3661
|
const sharedManager = useSharedTileTextureManager();
|
|
3434
|
-
const progressRef =
|
|
3435
|
-
const errorRef =
|
|
3436
|
-
const levelChangeRef =
|
|
3662
|
+
const progressRef = useRef12(onLoadProgress);
|
|
3663
|
+
const errorRef = useRef12(onTileError);
|
|
3664
|
+
const levelChangeRef = useRef12(onLevelChange);
|
|
3437
3665
|
progressRef.current = onLoadProgress;
|
|
3438
3666
|
errorRef.current = onTileError;
|
|
3439
3667
|
levelChangeRef.current = onLevelChange;
|
|
3440
|
-
const ownedManager =
|
|
3668
|
+
const ownedManager = useMemo7(
|
|
3441
3669
|
() => new TileTextureManager({
|
|
3442
3670
|
anisotropy,
|
|
3443
3671
|
maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
|
|
@@ -3458,7 +3686,7 @@ function Tile({
|
|
|
3458
3686
|
]
|
|
3459
3687
|
);
|
|
3460
3688
|
const manager = sharedManager ?? ownedManager;
|
|
3461
|
-
|
|
3689
|
+
useEffect14(() => {
|
|
3462
3690
|
manager.resume();
|
|
3463
3691
|
return () => {
|
|
3464
3692
|
if (manager === ownedManager) {
|
|
@@ -3468,11 +3696,11 @@ function Tile({
|
|
|
3468
3696
|
queueMicrotask(() => manager.releaseUnused());
|
|
3469
3697
|
};
|
|
3470
3698
|
}, [manager, ownedManager]);
|
|
3471
|
-
const readyRef =
|
|
3472
|
-
|
|
3699
|
+
const readyRef = useRef12(false);
|
|
3700
|
+
useEffect14(() => {
|
|
3473
3701
|
readyRef.current = false;
|
|
3474
3702
|
}, [effectivePreviewUrl]);
|
|
3475
|
-
|
|
3703
|
+
useEffect14(() => {
|
|
3476
3704
|
if (!previewTexture || readyRef.current) {
|
|
3477
3705
|
return;
|
|
3478
3706
|
}
|
|
@@ -3480,7 +3708,7 @@ function Tile({
|
|
|
3480
3708
|
onReady?.();
|
|
3481
3709
|
}, [onReady, previewTexture]);
|
|
3482
3710
|
const template = urlTemplate ?? buildDefaultTileUrlTemplate();
|
|
3483
|
-
const resolveUrl =
|
|
3711
|
+
const resolveUrl = useMemo7(
|
|
3484
3712
|
() => (address) => {
|
|
3485
3713
|
const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
|
|
3486
3714
|
return resolveRelativeTileUrl(baseUrl, relativePath);
|
|
@@ -3488,23 +3716,23 @@ function Tile({
|
|
|
3488
3716
|
[baseUrl, resolveTileUrl, template]
|
|
3489
3717
|
);
|
|
3490
3718
|
const [layers, setLayers] = useState5([]);
|
|
3491
|
-
const layerTokenRef =
|
|
3492
|
-
const currentLevelRef =
|
|
3493
|
-
const lastUpdateAtRef =
|
|
3494
|
-
const visibilityDirtyRef =
|
|
3495
|
-
const lastVisibilityQuaternionRef =
|
|
3496
|
-
const lastVisibilityFovRef =
|
|
3497
|
-
const lastViewportRef =
|
|
3498
|
-
const frustumRef =
|
|
3499
|
-
const projectionMatrixRef =
|
|
3500
|
-
const visibilityCameraRef =
|
|
3501
|
-
|
|
3719
|
+
const layerTokenRef = useRef12("");
|
|
3720
|
+
const currentLevelRef = useRef12(1);
|
|
3721
|
+
const lastUpdateAtRef = useRef12(-Infinity);
|
|
3722
|
+
const visibilityDirtyRef = useRef12(true);
|
|
3723
|
+
const lastVisibilityQuaternionRef = useRef12(camera.quaternion.clone());
|
|
3724
|
+
const lastVisibilityFovRef = useRef12(Number.NaN);
|
|
3725
|
+
const lastViewportRef = useRef12({ width: 0, height: 0, dpr: 0 });
|
|
3726
|
+
const frustumRef = useRef12(new Frustum());
|
|
3727
|
+
const projectionMatrixRef = useRef12(new Matrix4());
|
|
3728
|
+
const visibilityCameraRef = useRef12(new PerspectiveCamera2());
|
|
3729
|
+
useEffect14(() => {
|
|
3502
3730
|
currentLevelRef.current = 1;
|
|
3503
3731
|
layerTokenRef.current = "";
|
|
3504
3732
|
visibilityDirtyRef.current = true;
|
|
3505
3733
|
setLayers([]);
|
|
3506
3734
|
}, [manager, multiresToken]);
|
|
3507
|
-
|
|
3735
|
+
useEffect14(() => {
|
|
3508
3736
|
if (loadMode !== "preview") {
|
|
3509
3737
|
return;
|
|
3510
3738
|
}
|
|
@@ -3585,14 +3813,14 @@ function Tile({
|
|
|
3585
3813
|
commitLayers(nextLayers);
|
|
3586
3814
|
});
|
|
3587
3815
|
return /* @__PURE__ */ jsxs5("group", { visible, children: [
|
|
3588
|
-
previewTexture ? /* @__PURE__ */
|
|
3816
|
+
previewTexture ? /* @__PURE__ */ jsx8(
|
|
3589
3817
|
PreviewCube,
|
|
3590
3818
|
{
|
|
3591
3819
|
texture: previewTexture,
|
|
3592
3820
|
faceOrder: previewFaceOrder
|
|
3593
3821
|
}
|
|
3594
3822
|
) : null,
|
|
3595
|
-
layers.map((layer) => /* @__PURE__ */
|
|
3823
|
+
layers.map((layer) => /* @__PURE__ */ jsx8(
|
|
3596
3824
|
TileLayerMeshes,
|
|
3597
3825
|
{
|
|
3598
3826
|
layer,
|
|
@@ -3604,8 +3832,8 @@ function Tile({
|
|
|
3604
3832
|
] });
|
|
3605
3833
|
}
|
|
3606
3834
|
|
|
3607
|
-
// src/
|
|
3608
|
-
import { jsx as
|
|
3835
|
+
// src/scenes.tsx
|
|
3836
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3609
3837
|
var TRANSITION_DEFAULTS = {
|
|
3610
3838
|
none: { duration: 0, krpanoBlend: "NOBLEND" },
|
|
3611
3839
|
crossfade: { duration: 1, krpanoBlend: "BLEND(1.0, easeInCubic)" },
|
|
@@ -3655,7 +3883,7 @@ function SceneSource({
|
|
|
3655
3883
|
return null;
|
|
3656
3884
|
}
|
|
3657
3885
|
if (scene.type === "sphere") {
|
|
3658
|
-
return /* @__PURE__ */
|
|
3886
|
+
return /* @__PURE__ */ jsx9(
|
|
3659
3887
|
Sphere,
|
|
3660
3888
|
{
|
|
3661
3889
|
src: scene.src,
|
|
@@ -3667,7 +3895,7 @@ function SceneSource({
|
|
|
3667
3895
|
);
|
|
3668
3896
|
}
|
|
3669
3897
|
const { id: _id, type: _type, ...tileProps } = scene;
|
|
3670
|
-
return /* @__PURE__ */
|
|
3898
|
+
return /* @__PURE__ */ jsx9(
|
|
3671
3899
|
Tile,
|
|
3672
3900
|
{
|
|
3673
3901
|
...tileProps,
|
|
@@ -3683,10 +3911,10 @@ function SnapshotCapture({
|
|
|
3683
3911
|
maxPixels,
|
|
3684
3912
|
onCaptured
|
|
3685
3913
|
}) {
|
|
3686
|
-
const { camera, gl, scene, size } =
|
|
3687
|
-
const capturedRequestRef =
|
|
3688
|
-
const renderTargetRef =
|
|
3689
|
-
|
|
3914
|
+
const { camera, gl, scene, size } = useThree11();
|
|
3915
|
+
const capturedRequestRef = useRef13(0);
|
|
3916
|
+
const renderTargetRef = useRef13(null);
|
|
3917
|
+
useEffect15(
|
|
3690
3918
|
() => () => {
|
|
3691
3919
|
renderTargetRef.current?.dispose();
|
|
3692
3920
|
renderTargetRef.current = null;
|
|
@@ -3739,12 +3967,12 @@ function SnapshotOverlay({
|
|
|
3739
3967
|
runId,
|
|
3740
3968
|
onFinish
|
|
3741
3969
|
}) {
|
|
3742
|
-
const { camera, size } =
|
|
3743
|
-
const meshRef =
|
|
3744
|
-
const startRef =
|
|
3745
|
-
const finishedRef =
|
|
3746
|
-
const directionRef =
|
|
3747
|
-
const material =
|
|
3970
|
+
const { camera, size } = useThree11();
|
|
3971
|
+
const meshRef = useRef13(null);
|
|
3972
|
+
const startRef = useRef13(null);
|
|
3973
|
+
const finishedRef = useRef13(false);
|
|
3974
|
+
const directionRef = useRef13(new Vector35());
|
|
3975
|
+
const material = useMemo8(
|
|
3748
3976
|
() => new ShaderMaterial({
|
|
3749
3977
|
transparent: true,
|
|
3750
3978
|
depthTest: false,
|
|
@@ -3823,8 +4051,8 @@ function SnapshotOverlay({
|
|
|
3823
4051
|
}),
|
|
3824
4052
|
[snapshot.texture, transition.preset]
|
|
3825
4053
|
);
|
|
3826
|
-
|
|
3827
|
-
|
|
4054
|
+
useEffect15(() => () => material.dispose(), [material]);
|
|
4055
|
+
useEffect15(() => {
|
|
3828
4056
|
startRef.current = null;
|
|
3829
4057
|
finishedRef.current = false;
|
|
3830
4058
|
material.uniforms.progress.value = 0;
|
|
@@ -3855,12 +4083,12 @@ function SnapshotOverlay({
|
|
|
3855
4083
|
}
|
|
3856
4084
|
});
|
|
3857
4085
|
return /* @__PURE__ */ jsxs6("mesh", { ref: meshRef, renderOrder: 1e4, frustumCulled: false, children: [
|
|
3858
|
-
/* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
4086
|
+
/* @__PURE__ */ jsx9("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
|
|
4087
|
+
/* @__PURE__ */ jsx9("primitive", { attach: "material", object: material })
|
|
3860
4088
|
] });
|
|
3861
4089
|
}
|
|
3862
4090
|
var FULLSCREEN_PLANE = new PlaneGeometry2(1, 1);
|
|
3863
|
-
function
|
|
4091
|
+
function ScenesController({
|
|
3864
4092
|
scenes,
|
|
3865
4093
|
activeSceneId,
|
|
3866
4094
|
transition: transitionInput,
|
|
@@ -3869,13 +4097,13 @@ function PanoramaScenesController({
|
|
|
3869
4097
|
onTransitionError,
|
|
3870
4098
|
snapshotMaxPixels = 3686400
|
|
3871
4099
|
}) {
|
|
3872
|
-
const controlsRef =
|
|
4100
|
+
const controlsRef = useContext13(PanoramaViewContext);
|
|
3873
4101
|
const manager = useSharedTileTextureManager();
|
|
3874
|
-
const transition =
|
|
4102
|
+
const transition = useMemo8(
|
|
3875
4103
|
() => resolveTransition(transitionInput),
|
|
3876
4104
|
[transitionInput]
|
|
3877
4105
|
);
|
|
3878
|
-
const sceneMap =
|
|
4106
|
+
const sceneMap = useMemo8(
|
|
3879
4107
|
() => new Map(scenes.map((scene) => [scene.id, scene])),
|
|
3880
4108
|
[scenes]
|
|
3881
4109
|
);
|
|
@@ -3892,24 +4120,24 @@ function PanoramaScenesController({
|
|
|
3892
4120
|
const [transitionRunId, setTransitionRunId] = useState6(0);
|
|
3893
4121
|
const [snapshot, setSnapshot] = useState6(null);
|
|
3894
4122
|
const [recoveryKey, setRecoveryKey] = useState6(0);
|
|
3895
|
-
const requestedSceneIdRef =
|
|
3896
|
-
const captureReasonRef =
|
|
3897
|
-
const interactionReleaseRef =
|
|
3898
|
-
const slotsRef =
|
|
3899
|
-
const phaseRef =
|
|
3900
|
-
const liveSlotRef =
|
|
3901
|
-
const transitionFromRef =
|
|
4123
|
+
const requestedSceneIdRef = useRef13(activeSceneId);
|
|
4124
|
+
const captureReasonRef = useRef13("start");
|
|
4125
|
+
const interactionReleaseRef = useRef13(null);
|
|
4126
|
+
const slotsRef = useRef13(slots);
|
|
4127
|
+
const phaseRef = useRef13(phase);
|
|
4128
|
+
const liveSlotRef = useRef13(liveSlot);
|
|
4129
|
+
const transitionFromRef = useRef13(initialScene);
|
|
3902
4130
|
slotsRef.current = slots;
|
|
3903
4131
|
phaseRef.current = phase;
|
|
3904
4132
|
liveSlotRef.current = liveSlot;
|
|
3905
|
-
const releaseInteraction =
|
|
4133
|
+
const releaseInteraction = useCallback5(() => {
|
|
3906
4134
|
interactionReleaseRef.current?.();
|
|
3907
4135
|
interactionReleaseRef.current = null;
|
|
3908
4136
|
}, []);
|
|
3909
|
-
const reportError =
|
|
4137
|
+
const reportError = useCallback5((sceneId, error) => {
|
|
3910
4138
|
onTransitionError?.({ sceneId, error });
|
|
3911
4139
|
}, [onTransitionError]);
|
|
3912
|
-
const startPreload =
|
|
4140
|
+
const startPreload = useCallback5((scene) => {
|
|
3913
4141
|
const nextSlot = 1 - liveSlotRef.current;
|
|
3914
4142
|
requestedSceneIdRef.current = scene.id;
|
|
3915
4143
|
setSlots((current) => {
|
|
@@ -3920,7 +4148,7 @@ function PanoramaScenesController({
|
|
|
3920
4148
|
setPendingSlot(nextSlot);
|
|
3921
4149
|
setPhase("preloading");
|
|
3922
4150
|
}, []);
|
|
3923
|
-
|
|
4151
|
+
useEffect15(() => {
|
|
3924
4152
|
const requested = sceneMap.get(activeSceneId);
|
|
3925
4153
|
if (!requested) {
|
|
3926
4154
|
reportError(activeSceneId, new Error(`Unknown panorama scene: ${activeSceneId}`));
|
|
@@ -3944,7 +4172,7 @@ function PanoramaScenesController({
|
|
|
3944
4172
|
}
|
|
3945
4173
|
startPreload(requested);
|
|
3946
4174
|
}, [activeSceneId, pendingSlot, reportError, sceneMap, startPreload]);
|
|
3947
|
-
const handleReady =
|
|
4175
|
+
const handleReady = useCallback5((slot) => {
|
|
3948
4176
|
if (slot !== pendingSlot || phaseRef.current !== "preloading") {
|
|
3949
4177
|
return;
|
|
3950
4178
|
}
|
|
@@ -3975,7 +4203,7 @@ function PanoramaScenesController({
|
|
|
3975
4203
|
setPhase("capturing");
|
|
3976
4204
|
setCaptureRequestId((current) => current + 1);
|
|
3977
4205
|
}, [controlsRef, onTransitionEnd, pendingSlot, transition.preset]);
|
|
3978
|
-
const handleCaptured =
|
|
4206
|
+
const handleCaptured = useCallback5((nextSnapshot) => {
|
|
3979
4207
|
setSnapshot((current) => {
|
|
3980
4208
|
current?.dispose();
|
|
3981
4209
|
return nextSnapshot;
|
|
@@ -3997,7 +4225,7 @@ function PanoramaScenesController({
|
|
|
3997
4225
|
setPhase("transitioning");
|
|
3998
4226
|
setTransitionRunId((current) => current + 1);
|
|
3999
4227
|
}, [manager, pendingSlot, sceneMap, startPreload]);
|
|
4000
|
-
const finishTransition =
|
|
4228
|
+
const finishTransition = useCallback5(() => {
|
|
4001
4229
|
const nextSlot = pendingSlot;
|
|
4002
4230
|
const previousScene = transitionFromRef.current;
|
|
4003
4231
|
const nextScene = nextSlot === null ? null : slotsRef.current[nextSlot];
|
|
@@ -4022,12 +4250,12 @@ function PanoramaScenesController({
|
|
|
4022
4250
|
});
|
|
4023
4251
|
}
|
|
4024
4252
|
}, [onTransitionEnd, pendingSlot, releaseInteraction, snapshot, transition.preset]);
|
|
4025
|
-
|
|
4253
|
+
useEffect15(() => () => {
|
|
4026
4254
|
releaseInteraction();
|
|
4027
4255
|
snapshot?.dispose();
|
|
4028
4256
|
}, [releaseInteraction, snapshot]);
|
|
4029
|
-
const { gl } =
|
|
4030
|
-
|
|
4257
|
+
const { gl } = useThree11();
|
|
4258
|
+
useEffect15(() => {
|
|
4031
4259
|
const canvas = gl.domElement;
|
|
4032
4260
|
const onContextLost = (event) => {
|
|
4033
4261
|
event.preventDefault();
|
|
@@ -4052,7 +4280,7 @@ function PanoramaScenesController({
|
|
|
4052
4280
|
const renderedScene = slots[liveSlot];
|
|
4053
4281
|
const snapshotVisible = snapshot !== null && (phase === "transitioning" || phase === "recapturing" || phase === "preloading");
|
|
4054
4282
|
return /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
4055
|
-
/* @__PURE__ */
|
|
4283
|
+
/* @__PURE__ */ jsx9(
|
|
4056
4284
|
SnapshotCapture,
|
|
4057
4285
|
{
|
|
4058
4286
|
requestId: captureRequestId,
|
|
@@ -4064,7 +4292,7 @@ function PanoramaScenesController({
|
|
|
4064
4292
|
const source = slots[slot];
|
|
4065
4293
|
const isLive = slot === liveSlot;
|
|
4066
4294
|
const isPending = slot === pendingSlot;
|
|
4067
|
-
return /* @__PURE__ */
|
|
4295
|
+
return /* @__PURE__ */ jsx9(
|
|
4068
4296
|
SceneSource,
|
|
4069
4297
|
{
|
|
4070
4298
|
scene: source,
|
|
@@ -4080,7 +4308,7 @@ function PanoramaScenesController({
|
|
|
4080
4308
|
`${slot}:${source?.id ?? "empty"}:${recoveryKey}`
|
|
4081
4309
|
);
|
|
4082
4310
|
}),
|
|
4083
|
-
snapshotVisible ? /* @__PURE__ */
|
|
4311
|
+
snapshotVisible ? /* @__PURE__ */ jsx9(
|
|
4084
4312
|
SnapshotOverlay,
|
|
4085
4313
|
{
|
|
4086
4314
|
snapshot,
|
|
@@ -4093,27 +4321,26 @@ function PanoramaScenesController({
|
|
|
4093
4321
|
phase === "idle" && renderedScene ? renderHotspots?.(renderedScene) : null
|
|
4094
4322
|
] });
|
|
4095
4323
|
}
|
|
4096
|
-
function
|
|
4324
|
+
function Scenes({
|
|
4097
4325
|
maxTextureMemoryMb,
|
|
4098
4326
|
maxConcurrentTileLoads,
|
|
4099
4327
|
...props
|
|
4100
4328
|
}) {
|
|
4101
|
-
return /* @__PURE__ */
|
|
4329
|
+
return /* @__PURE__ */ jsx9(
|
|
4102
4330
|
TileTextureManagerProvider,
|
|
4103
4331
|
{
|
|
4104
4332
|
maxTextureMemoryMb,
|
|
4105
4333
|
maxConcurrentLoads: maxConcurrentTileLoads,
|
|
4106
|
-
children: /* @__PURE__ */
|
|
4334
|
+
children: /* @__PURE__ */ jsx9(ScenesController, { ...props })
|
|
4107
4335
|
}
|
|
4108
4336
|
);
|
|
4109
4337
|
}
|
|
4110
4338
|
|
|
4111
4339
|
// src/hotspot/image-hotspot.tsx
|
|
4112
|
-
import { useThree as
|
|
4113
|
-
import { useEffect as
|
|
4340
|
+
import { useThree as useThree12 } from "@react-three/fiber";
|
|
4341
|
+
import { useEffect as useEffect18, useRef as useRef15, useState as useState9 } from "react";
|
|
4114
4342
|
import {
|
|
4115
4343
|
ClampToEdgeWrapping as ClampToEdgeWrapping3,
|
|
4116
|
-
DoubleSide as DoubleSide2,
|
|
4117
4344
|
LinearFilter as LinearFilter5,
|
|
4118
4345
|
LinearMipmapLinearFilter as LinearMipmapLinearFilter2,
|
|
4119
4346
|
SRGBColorSpace as SRGBColorSpace4,
|
|
@@ -4123,12 +4350,14 @@ import {
|
|
|
4123
4350
|
// src/hotspot/hotspot-anchor.tsx
|
|
4124
4351
|
import { useFrame as useFrame7 } from "@react-three/fiber";
|
|
4125
4352
|
import {
|
|
4126
|
-
useContext as
|
|
4127
|
-
useEffect as
|
|
4128
|
-
useMemo as
|
|
4129
|
-
useRef as
|
|
4353
|
+
useContext as useContext14,
|
|
4354
|
+
useEffect as useEffect17,
|
|
4355
|
+
useMemo as useMemo10,
|
|
4356
|
+
useRef as useRef14,
|
|
4357
|
+
useState as useState8
|
|
4130
4358
|
} from "react";
|
|
4131
4359
|
import {
|
|
4360
|
+
Box3 as Box33,
|
|
4132
4361
|
EdgesGeometry,
|
|
4133
4362
|
MathUtils as MathUtils4,
|
|
4134
4363
|
Matrix4 as Matrix42,
|
|
@@ -4137,7 +4366,129 @@ import {
|
|
|
4137
4366
|
Quaternion as Quaternion2,
|
|
4138
4367
|
Vector3 as Vector36
|
|
4139
4368
|
} from "three";
|
|
4140
|
-
|
|
4369
|
+
|
|
4370
|
+
// src/hotspot/hotspot-tooltip.tsx
|
|
4371
|
+
import { Html } from "@react-three/drei";
|
|
4372
|
+
import { useEffect as useEffect16, useMemo as useMemo9, useState as useState7 } from "react";
|
|
4373
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4374
|
+
var TOOLTIP_MAX_WIDTH = 220;
|
|
4375
|
+
var TOOLTIP_IMAGE_MAX_HEIGHT = 120;
|
|
4376
|
+
var DEFAULT_HOTSPOT_TOOLTIP_OFFSET = 12;
|
|
4377
|
+
var WRAPPER_STYLE = {
|
|
4378
|
+
pointerEvents: "none"
|
|
4379
|
+
};
|
|
4380
|
+
var BUBBLE_STYLE = {
|
|
4381
|
+
background: "rgba(22, 22, 22, 0.72)",
|
|
4382
|
+
border: "1px solid rgba(46, 46, 46, 0.7)",
|
|
4383
|
+
borderRadius: 8,
|
|
4384
|
+
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.35)",
|
|
4385
|
+
boxSizing: "border-box",
|
|
4386
|
+
color: "#f5fbfc",
|
|
4387
|
+
display: "flex",
|
|
4388
|
+
flexDirection: "column",
|
|
4389
|
+
gap: 8,
|
|
4390
|
+
padding: 8,
|
|
4391
|
+
pointerEvents: "none",
|
|
4392
|
+
whiteSpace: "nowrap"
|
|
4393
|
+
};
|
|
4394
|
+
var IMAGE_STYLE = {
|
|
4395
|
+
borderRadius: 4,
|
|
4396
|
+
display: "block",
|
|
4397
|
+
height: "auto",
|
|
4398
|
+
maxHeight: TOOLTIP_IMAGE_MAX_HEIGHT,
|
|
4399
|
+
maxWidth: TOOLTIP_MAX_WIDTH,
|
|
4400
|
+
objectFit: "contain"
|
|
4401
|
+
};
|
|
4402
|
+
var TEXT_STYLE = {
|
|
4403
|
+
fontFamily: "system-ui, sans-serif",
|
|
4404
|
+
fontSize: 12,
|
|
4405
|
+
lineHeight: 1.4,
|
|
4406
|
+
margin: 0,
|
|
4407
|
+
overflow: "visible",
|
|
4408
|
+
whiteSpace: "nowrap"
|
|
4409
|
+
};
|
|
4410
|
+
function resolveHotspotTooltipContent(tooltip) {
|
|
4411
|
+
if (tooltip == null) {
|
|
4412
|
+
return null;
|
|
4413
|
+
}
|
|
4414
|
+
if (typeof tooltip === "string") {
|
|
4415
|
+
const text2 = tooltip.trim();
|
|
4416
|
+
return text2 ? { text: text2 } : null;
|
|
4417
|
+
}
|
|
4418
|
+
const text = tooltip.text?.trim();
|
|
4419
|
+
const image = tooltip.image?.trim();
|
|
4420
|
+
if (!text && !image) {
|
|
4421
|
+
return null;
|
|
4422
|
+
}
|
|
4423
|
+
const imageAlt = tooltip.imageAlt?.trim();
|
|
4424
|
+
return {
|
|
4425
|
+
image: image || void 0,
|
|
4426
|
+
imageAlt: imageAlt || void 0,
|
|
4427
|
+
text: text || void 0
|
|
4428
|
+
};
|
|
4429
|
+
}
|
|
4430
|
+
function resolveHotspotTooltipOffset(offset) {
|
|
4431
|
+
if (!Number.isFinite(offset)) {
|
|
4432
|
+
return DEFAULT_HOTSPOT_TOOLTIP_OFFSET;
|
|
4433
|
+
}
|
|
4434
|
+
return Math.max(0, offset);
|
|
4435
|
+
}
|
|
4436
|
+
function tooltipBubbleTransform(placement, offset) {
|
|
4437
|
+
const gap = `${offset}px`;
|
|
4438
|
+
switch (placement) {
|
|
4439
|
+
case "top":
|
|
4440
|
+
return `translate(-50%, calc(-100% - ${gap}))`;
|
|
4441
|
+
case "bottom":
|
|
4442
|
+
return `translate(-50%, ${gap})`;
|
|
4443
|
+
case "left":
|
|
4444
|
+
return `translate(calc(-100% - ${gap}), -50%)`;
|
|
4445
|
+
case "right":
|
|
4446
|
+
return `translate(${gap}, -50%)`;
|
|
4447
|
+
default: {
|
|
4448
|
+
const exhaustive = placement;
|
|
4449
|
+
return exhaustive;
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
function HotspotTooltip({
|
|
4454
|
+
content,
|
|
4455
|
+
placement = "top",
|
|
4456
|
+
offset = DEFAULT_HOTSPOT_TOOLTIP_OFFSET
|
|
4457
|
+
}) {
|
|
4458
|
+
const [imageFailed, setImageFailed] = useState7(false);
|
|
4459
|
+
useEffect16(() => {
|
|
4460
|
+
setImageFailed(false);
|
|
4461
|
+
}, [content.image]);
|
|
4462
|
+
const showImage = Boolean(content.image) && !imageFailed;
|
|
4463
|
+
const bubbleStyle = useMemo9(
|
|
4464
|
+
() => ({
|
|
4465
|
+
...BUBBLE_STYLE,
|
|
4466
|
+
transform: tooltipBubbleTransform(
|
|
4467
|
+
placement,
|
|
4468
|
+
resolveHotspotTooltipOffset(offset)
|
|
4469
|
+
)
|
|
4470
|
+
}),
|
|
4471
|
+
[offset, placement]
|
|
4472
|
+
);
|
|
4473
|
+
if (!showImage && !content.text) {
|
|
4474
|
+
return null;
|
|
4475
|
+
}
|
|
4476
|
+
return /* @__PURE__ */ jsx10(Html, { pointerEvents: "none", style: WRAPPER_STYLE, zIndexRange: [20, 10], children: /* @__PURE__ */ jsxs7("div", { role: "tooltip", style: bubbleStyle, children: [
|
|
4477
|
+
showImage ? /* @__PURE__ */ jsx10(
|
|
4478
|
+
"img",
|
|
4479
|
+
{
|
|
4480
|
+
alt: content.imageAlt ?? content.text ?? "",
|
|
4481
|
+
onError: () => setImageFailed(true),
|
|
4482
|
+
src: content.image,
|
|
4483
|
+
style: IMAGE_STYLE
|
|
4484
|
+
}
|
|
4485
|
+
) : null,
|
|
4486
|
+
content.text ? /* @__PURE__ */ jsx10("p", { style: TEXT_STYLE, children: content.text }) : null
|
|
4487
|
+
] }) });
|
|
4488
|
+
}
|
|
4489
|
+
|
|
4490
|
+
// src/hotspot/hotspot-anchor.tsx
|
|
4491
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4141
4492
|
var DEFAULT_FLOATING_DISTANCE = 10;
|
|
4142
4493
|
var PANORAMA_RADIUS = DEFAULT_PANORAMA_RADIUS;
|
|
4143
4494
|
var PANORAMA_SURFACE_INSET = 4;
|
|
@@ -4146,6 +4497,10 @@ var MIN_ANGULAR_SIZE = 0.01;
|
|
|
4146
4497
|
var MAX_ANGULAR_SIZE = 179;
|
|
4147
4498
|
var DRAG_EPSILON_DEGREES = 1e-3;
|
|
4148
4499
|
var LOCAL_FORWARD = new Vector36(0, 0, 1);
|
|
4500
|
+
var TOOLTIP_WORLD_BOX = new Box33();
|
|
4501
|
+
var TOOLTIP_LOCAL_BOX = new Box33();
|
|
4502
|
+
var TOOLTIP_CORNER = new Vector36();
|
|
4503
|
+
var TOOLTIP_INVERSE = new Matrix42();
|
|
4149
4504
|
var HOTSPOT_MODE_RENDERING = {
|
|
4150
4505
|
surface: { orientation: "surface", placement: "surface" },
|
|
4151
4506
|
billboard: { orientation: "billboard", placement: "floating" }
|
|
@@ -4216,6 +4571,100 @@ function angularDistance(a, b) {
|
|
|
4216
4571
|
const bVector = panoPositionToVector3(b);
|
|
4217
4572
|
return MathUtils4.radToDeg(aVector.angleTo(bVector));
|
|
4218
4573
|
}
|
|
4574
|
+
function isTooltipOpen(trigger, hovered, focused, pinned) {
|
|
4575
|
+
switch (trigger) {
|
|
4576
|
+
case "always":
|
|
4577
|
+
return true;
|
|
4578
|
+
case "hover":
|
|
4579
|
+
return hovered || focused;
|
|
4580
|
+
case "click":
|
|
4581
|
+
return pinned;
|
|
4582
|
+
default: {
|
|
4583
|
+
const exhaustive = trigger;
|
|
4584
|
+
return exhaustive;
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
function setTooltipAnchorOnUnitPlane(anchor, placement) {
|
|
4589
|
+
switch (placement) {
|
|
4590
|
+
case "top":
|
|
4591
|
+
anchor.position.set(0, 0.5, 0);
|
|
4592
|
+
return;
|
|
4593
|
+
case "bottom":
|
|
4594
|
+
anchor.position.set(0, -0.5, 0);
|
|
4595
|
+
return;
|
|
4596
|
+
case "left":
|
|
4597
|
+
anchor.position.set(-0.5, 0, 0);
|
|
4598
|
+
return;
|
|
4599
|
+
case "right":
|
|
4600
|
+
anchor.position.set(0.5, 0, 0);
|
|
4601
|
+
return;
|
|
4602
|
+
default: {
|
|
4603
|
+
const exhaustive = placement;
|
|
4604
|
+
return exhaustive;
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
function setTooltipAnchorOnLocalBox(anchor, placement, minX, maxX, minY, maxY) {
|
|
4609
|
+
switch (placement) {
|
|
4610
|
+
case "top":
|
|
4611
|
+
anchor.position.set((minX + maxX) / 2, maxY, 0);
|
|
4612
|
+
return;
|
|
4613
|
+
case "bottom":
|
|
4614
|
+
anchor.position.set((minX + maxX) / 2, minY, 0);
|
|
4615
|
+
return;
|
|
4616
|
+
case "left":
|
|
4617
|
+
anchor.position.set(minX, (minY + maxY) / 2, 0);
|
|
4618
|
+
return;
|
|
4619
|
+
case "right":
|
|
4620
|
+
anchor.position.set(maxX, (minY + maxY) / 2, 0);
|
|
4621
|
+
return;
|
|
4622
|
+
default: {
|
|
4623
|
+
const exhaustive = placement;
|
|
4624
|
+
return exhaustive;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
function updateTooltipAnchor(anchor, group, placement, useAngularScale) {
|
|
4629
|
+
if (useAngularScale) {
|
|
4630
|
+
setTooltipAnchorOnUnitPlane(anchor, placement);
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
group.updateMatrixWorld(true);
|
|
4634
|
+
TOOLTIP_WORLD_BOX.makeEmpty();
|
|
4635
|
+
for (const child of group.children) {
|
|
4636
|
+
if (child === anchor) {
|
|
4637
|
+
continue;
|
|
4638
|
+
}
|
|
4639
|
+
TOOLTIP_WORLD_BOX.expandByObject(child);
|
|
4640
|
+
}
|
|
4641
|
+
if (TOOLTIP_WORLD_BOX.isEmpty()) {
|
|
4642
|
+
setTooltipAnchorOnUnitPlane(anchor, placement);
|
|
4643
|
+
return;
|
|
4644
|
+
}
|
|
4645
|
+
TOOLTIP_INVERSE.copy(group.matrixWorld).invert();
|
|
4646
|
+
TOOLTIP_LOCAL_BOX.makeEmpty();
|
|
4647
|
+
for (let x = 0; x <= 1; x += 1) {
|
|
4648
|
+
for (let y = 0; y <= 1; y += 1) {
|
|
4649
|
+
for (let z = 0; z <= 1; z += 1) {
|
|
4650
|
+
TOOLTIP_CORNER.set(
|
|
4651
|
+
x === 0 ? TOOLTIP_WORLD_BOX.min.x : TOOLTIP_WORLD_BOX.max.x,
|
|
4652
|
+
y === 0 ? TOOLTIP_WORLD_BOX.min.y : TOOLTIP_WORLD_BOX.max.y,
|
|
4653
|
+
z === 0 ? TOOLTIP_WORLD_BOX.min.z : TOOLTIP_WORLD_BOX.max.z
|
|
4654
|
+
).applyMatrix4(TOOLTIP_INVERSE);
|
|
4655
|
+
TOOLTIP_LOCAL_BOX.expandByPoint(TOOLTIP_CORNER);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
setTooltipAnchorOnLocalBox(
|
|
4660
|
+
anchor,
|
|
4661
|
+
placement,
|
|
4662
|
+
TOOLTIP_LOCAL_BOX.min.x,
|
|
4663
|
+
TOOLTIP_LOCAL_BOX.max.x,
|
|
4664
|
+
TOOLTIP_LOCAL_BOX.min.y,
|
|
4665
|
+
TOOLTIP_LOCAL_BOX.max.y
|
|
4666
|
+
);
|
|
4667
|
+
}
|
|
4219
4668
|
function HotspotAnchor({
|
|
4220
4669
|
id,
|
|
4221
4670
|
position,
|
|
@@ -4235,7 +4684,12 @@ function HotspotAnchor({
|
|
|
4235
4684
|
visible = true,
|
|
4236
4685
|
draggable = false,
|
|
4237
4686
|
interactive = true,
|
|
4687
|
+
cursor,
|
|
4238
4688
|
ariaLabel,
|
|
4689
|
+
tooltip,
|
|
4690
|
+
tooltipTrigger = "always",
|
|
4691
|
+
tooltipPlacement = "top",
|
|
4692
|
+
tooltipOffset,
|
|
4239
4693
|
children,
|
|
4240
4694
|
onClick,
|
|
4241
4695
|
onHoverChange,
|
|
@@ -4243,10 +4697,20 @@ function HotspotAnchor({
|
|
|
4243
4697
|
onPositionChange,
|
|
4244
4698
|
onDragEnd
|
|
4245
4699
|
}) {
|
|
4246
|
-
const controlsRef =
|
|
4247
|
-
const
|
|
4248
|
-
const
|
|
4249
|
-
const
|
|
4700
|
+
const controlsRef = useContext14(PanoramaViewContext);
|
|
4701
|
+
const eventBus = useContext14(PanoEventBusContext);
|
|
4702
|
+
const cursorApi = usePanoCursor();
|
|
4703
|
+
const groupRef = useRef14(null);
|
|
4704
|
+
const tooltipAnchorRef = useRef14(null);
|
|
4705
|
+
const dragStateRef = useRef14(null);
|
|
4706
|
+
const suppressNextClickRef = useRef14(false);
|
|
4707
|
+
const [hovered, setHovered] = useState8(false);
|
|
4708
|
+
const [dragging, setDragging] = useState8(false);
|
|
4709
|
+
const [pinned, setPinned] = useState8(false);
|
|
4710
|
+
const tooltipContent = useMemo10(
|
|
4711
|
+
() => resolveHotspotTooltipContent(tooltip),
|
|
4712
|
+
[tooltip]
|
|
4713
|
+
);
|
|
4250
4714
|
const normalizedPosition = normalizePanoPosition(position);
|
|
4251
4715
|
const rendering = internalOrientation && internalPlacement ? { orientation: internalOrientation, placement: internalPlacement } : HOTSPOT_MODE_RENDERING[mode];
|
|
4252
4716
|
const { orientation: orientation2, placement } = rendering;
|
|
@@ -4259,27 +4723,33 @@ function HotspotAnchor({
|
|
|
4259
4723
|
scaledWidth,
|
|
4260
4724
|
scaledHeight
|
|
4261
4725
|
);
|
|
4262
|
-
const worldPosition =
|
|
4726
|
+
const worldPosition = useMemo10(
|
|
4263
4727
|
() => panoPositionToVector3(normalizedPosition, resolvedDistance),
|
|
4264
4728
|
[normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
|
|
4265
4729
|
);
|
|
4266
|
-
const surfaceQuaternion3 =
|
|
4730
|
+
const surfaceQuaternion3 = useMemo10(
|
|
4267
4731
|
() => createSurfaceQuaternion(normalizedPosition),
|
|
4268
4732
|
[normalizedPosition.pitch, normalizedPosition.yaw]
|
|
4269
4733
|
);
|
|
4270
4734
|
const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
|
|
4271
4735
|
const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
|
|
4272
|
-
const focusGeometry =
|
|
4736
|
+
const focusGeometry = useMemo10(() => {
|
|
4273
4737
|
const planeGeometry = new PlaneGeometry3(1, 1);
|
|
4274
4738
|
const geometry = new EdgesGeometry(planeGeometry);
|
|
4275
4739
|
planeGeometry.dispose();
|
|
4276
4740
|
return geometry;
|
|
4277
4741
|
}, []);
|
|
4742
|
+
const canActivateFromKeyboard = Boolean(
|
|
4743
|
+
visible && interactive && (onClick || tooltipTrigger === "click")
|
|
4744
|
+
);
|
|
4278
4745
|
const focused = useHotspotAccessibility({
|
|
4279
4746
|
id,
|
|
4280
4747
|
ariaLabel,
|
|
4281
|
-
onActivate:
|
|
4282
|
-
|
|
4748
|
+
onActivate: canActivateFromKeyboard ? (event) => {
|
|
4749
|
+
if (tooltipTrigger === "click") {
|
|
4750
|
+
setPinned((current) => !current);
|
|
4751
|
+
}
|
|
4752
|
+
onClick?.({
|
|
4283
4753
|
id,
|
|
4284
4754
|
position: normalizedPosition,
|
|
4285
4755
|
source: "keyboard",
|
|
@@ -4287,24 +4757,68 @@ function HotspotAnchor({
|
|
|
4287
4757
|
});
|
|
4288
4758
|
} : void 0
|
|
4289
4759
|
});
|
|
4290
|
-
|
|
4760
|
+
const tooltipOpen = visible && tooltipContent !== null && isTooltipOpen(tooltipTrigger, hovered, focused, pinned);
|
|
4761
|
+
const cursorClaimId = hotspotCursorClaimId(id);
|
|
4762
|
+
useEffect17(
|
|
4291
4763
|
() => () => {
|
|
4292
4764
|
dragStateRef.current?.releaseInteractionLock();
|
|
4293
4765
|
dragStateRef.current = null;
|
|
4294
4766
|
},
|
|
4295
4767
|
[]
|
|
4296
4768
|
);
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
if (!groupRef.current) {
|
|
4769
|
+
useEffect17(() => {
|
|
4770
|
+
if (!cursorApi) {
|
|
4300
4771
|
return;
|
|
4301
4772
|
}
|
|
4302
|
-
if (
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
groupRef.current.quaternion.copy(surfaceQuaternion3);
|
|
4773
|
+
if (interactive && visible && dragging) {
|
|
4774
|
+
cursorApi.claim(cursorClaimId, "hotspotDragging");
|
|
4775
|
+
return;
|
|
4306
4776
|
}
|
|
4307
|
-
|
|
4777
|
+
if (interactive && visible && hovered) {
|
|
4778
|
+
cursorApi.claim(cursorClaimId, "hotspot", cursor);
|
|
4779
|
+
return;
|
|
4780
|
+
}
|
|
4781
|
+
cursorApi.release(cursorClaimId);
|
|
4782
|
+
}, [
|
|
4783
|
+
cursor,
|
|
4784
|
+
cursorApi,
|
|
4785
|
+
cursorClaimId,
|
|
4786
|
+
dragging,
|
|
4787
|
+
hovered,
|
|
4788
|
+
interactive,
|
|
4789
|
+
visible
|
|
4790
|
+
]);
|
|
4791
|
+
useEffect17(
|
|
4792
|
+
() => () => {
|
|
4793
|
+
cursorApi?.release(cursorClaimId);
|
|
4794
|
+
},
|
|
4795
|
+
[cursorApi, cursorClaimId]
|
|
4796
|
+
);
|
|
4797
|
+
useEffect17(() => () => focusGeometry.dispose(), [focusGeometry]);
|
|
4798
|
+
useEffect17(() => {
|
|
4799
|
+
if (tooltipTrigger !== "click") {
|
|
4800
|
+
setPinned(false);
|
|
4801
|
+
return;
|
|
4802
|
+
}
|
|
4803
|
+
return eventBus?.subscribe("click", () => {
|
|
4804
|
+
setPinned(false);
|
|
4805
|
+
});
|
|
4806
|
+
}, [eventBus, tooltipTrigger]);
|
|
4807
|
+
useEffect17(() => {
|
|
4808
|
+
if (tooltipTrigger !== "hover") {
|
|
4809
|
+
setHovered(false);
|
|
4810
|
+
}
|
|
4811
|
+
}, [tooltipTrigger]);
|
|
4812
|
+
useFrame7(({ camera }) => {
|
|
4813
|
+
if (!groupRef.current) {
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
if (orientation2 === "billboard") {
|
|
4817
|
+
groupRef.current.quaternion.copy(camera.quaternion);
|
|
4818
|
+
} else {
|
|
4819
|
+
groupRef.current.quaternion.copy(surfaceQuaternion3);
|
|
4820
|
+
}
|
|
4821
|
+
groupRef.current.rotateOnAxis(
|
|
4308
4822
|
LOCAL_FORWARD,
|
|
4309
4823
|
MathUtils4.degToRad(-rotation)
|
|
4310
4824
|
);
|
|
@@ -4314,6 +4828,14 @@ function HotspotAnchor({
|
|
|
4314
4828
|
(useAngularScale ? worldHeight : 1) * scaleFactor,
|
|
4315
4829
|
1
|
|
4316
4830
|
);
|
|
4831
|
+
if (tooltipOpen && tooltipAnchorRef.current) {
|
|
4832
|
+
updateTooltipAnchor(
|
|
4833
|
+
tooltipAnchorRef.current,
|
|
4834
|
+
groupRef.current,
|
|
4835
|
+
tooltipPlacement,
|
|
4836
|
+
useAngularScale
|
|
4837
|
+
);
|
|
4838
|
+
}
|
|
4317
4839
|
});
|
|
4318
4840
|
const stopPointerEvent = (event) => {
|
|
4319
4841
|
event.stopPropagation();
|
|
@@ -4340,8 +4862,12 @@ function HotspotAnchor({
|
|
|
4340
4862
|
suppressNextClickRef.current = dragState.moved;
|
|
4341
4863
|
dragState.releaseInteractionLock();
|
|
4342
4864
|
dragStateRef.current = null;
|
|
4865
|
+
setDragging(false);
|
|
4866
|
+
if (emitDragEnd) {
|
|
4867
|
+
setHovered(true);
|
|
4868
|
+
}
|
|
4343
4869
|
};
|
|
4344
|
-
return /* @__PURE__ */
|
|
4870
|
+
return /* @__PURE__ */ jsxs8(
|
|
4345
4871
|
"group",
|
|
4346
4872
|
{
|
|
4347
4873
|
ref: groupRef,
|
|
@@ -4352,6 +4878,9 @@ function HotspotAnchor({
|
|
|
4352
4878
|
onClick: interactive ? (event) => {
|
|
4353
4879
|
stopPointerEvent(event);
|
|
4354
4880
|
if (!suppressNextClickRef.current) {
|
|
4881
|
+
if (tooltipTrigger === "click") {
|
|
4882
|
+
setPinned((current) => !current);
|
|
4883
|
+
}
|
|
4355
4884
|
onClick?.(makeInteractionEvent(id, normalizedPosition, event));
|
|
4356
4885
|
}
|
|
4357
4886
|
suppressNextClickRef.current = false;
|
|
@@ -4373,6 +4902,8 @@ function HotspotAnchor({
|
|
|
4373
4902
|
releaseInteractionLock,
|
|
4374
4903
|
moved: false
|
|
4375
4904
|
};
|
|
4905
|
+
setHovered(false);
|
|
4906
|
+
setDragging(true);
|
|
4376
4907
|
onDragStart?.({
|
|
4377
4908
|
...makeInteractionEvent(id, startPosition, event),
|
|
4378
4909
|
source: "pointer",
|
|
@@ -4395,6 +4926,7 @@ function HotspotAnchor({
|
|
|
4395
4926
|
} : void 0,
|
|
4396
4927
|
onPointerOut: interactive ? (event) => {
|
|
4397
4928
|
if (!dragStateRef.current) {
|
|
4929
|
+
setHovered(false);
|
|
4398
4930
|
onHoverChange?.(
|
|
4399
4931
|
false,
|
|
4400
4932
|
makeInteractionEvent(id, normalizedPosition, event)
|
|
@@ -4403,6 +4935,7 @@ function HotspotAnchor({
|
|
|
4403
4935
|
} : void 0,
|
|
4404
4936
|
onPointerOver: interactive ? (event) => {
|
|
4405
4937
|
if (!dragStateRef.current) {
|
|
4938
|
+
setHovered(true);
|
|
4406
4939
|
onHoverChange?.(
|
|
4407
4940
|
true,
|
|
4408
4941
|
makeInteractionEvent(id, normalizedPosition, event)
|
|
@@ -4411,38 +4944,166 @@ function HotspotAnchor({
|
|
|
4411
4944
|
} : void 0,
|
|
4412
4945
|
onPointerUp: interactive ? (event) => releasePointer(event, true) : void 0,
|
|
4413
4946
|
children: [
|
|
4414
|
-
focused ? focusContent ?? /* @__PURE__ */
|
|
4947
|
+
focused ? focusContent ?? /* @__PURE__ */ jsx11(
|
|
4415
4948
|
"lineSegments",
|
|
4416
4949
|
{
|
|
4417
4950
|
geometry: focusGeometry,
|
|
4418
4951
|
position: [0, 0, 0.01],
|
|
4419
4952
|
renderOrder: renderOrder + 1,
|
|
4420
4953
|
scale: [1.12, 1.12, 1],
|
|
4421
|
-
children: /* @__PURE__ */
|
|
4954
|
+
children: /* @__PURE__ */ jsx11("lineBasicMaterial", { color: "#f5fbfc", depthTest: false })
|
|
4422
4955
|
}
|
|
4423
4956
|
) : null,
|
|
4424
|
-
children
|
|
4957
|
+
children,
|
|
4958
|
+
tooltipOpen && tooltipContent ? /* @__PURE__ */ jsx11("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ jsx11(
|
|
4959
|
+
HotspotTooltip,
|
|
4960
|
+
{
|
|
4961
|
+
content: tooltipContent,
|
|
4962
|
+
offset: resolveHotspotTooltipOffset(tooltipOffset),
|
|
4963
|
+
placement: tooltipPlacement
|
|
4964
|
+
}
|
|
4965
|
+
) }) : null
|
|
4425
4966
|
]
|
|
4426
4967
|
}
|
|
4427
4968
|
);
|
|
4428
4969
|
}
|
|
4429
4970
|
|
|
4430
|
-
// src/hotspot/
|
|
4431
|
-
import {
|
|
4971
|
+
// src/hotspot/hotspot-plane.tsx
|
|
4972
|
+
import {
|
|
4973
|
+
DoubleSide as DoubleSide2,
|
|
4974
|
+
Mesh as Mesh2,
|
|
4975
|
+
MeshBasicMaterial,
|
|
4976
|
+
Vector2 as Vector23
|
|
4977
|
+
} from "three";
|
|
4978
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4979
|
+
var HOTSPOT_ALPHA_CUTOFF = 0.05;
|
|
4980
|
+
var SAMPLE_UV = new Vector23();
|
|
4981
|
+
var RAYCAST_HITS = [];
|
|
4982
|
+
var IMAGE_DATA_CACHE = /* @__PURE__ */ new WeakMap();
|
|
4432
4983
|
function clampOpacity(opacity) {
|
|
4433
4984
|
if (!Number.isFinite(opacity)) {
|
|
4434
4985
|
return 1;
|
|
4435
4986
|
}
|
|
4436
4987
|
return Math.max(0, Math.min(opacity, 1));
|
|
4437
4988
|
}
|
|
4989
|
+
function isSampleableImage(image) {
|
|
4990
|
+
if (image instanceof HTMLImageElement) {
|
|
4991
|
+
return image.naturalWidth > 0 && image.naturalHeight > 0;
|
|
4992
|
+
}
|
|
4993
|
+
if (image instanceof HTMLCanvasElement) {
|
|
4994
|
+
return image.width > 0 && image.height > 0;
|
|
4995
|
+
}
|
|
4996
|
+
if (typeof OffscreenCanvas !== "undefined" && image instanceof OffscreenCanvas) {
|
|
4997
|
+
return image.width > 0 && image.height > 0;
|
|
4998
|
+
}
|
|
4999
|
+
if (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
|
|
5000
|
+
return image.width > 0 && image.height > 0;
|
|
5001
|
+
}
|
|
5002
|
+
return false;
|
|
5003
|
+
}
|
|
5004
|
+
function imageSize(image) {
|
|
5005
|
+
if (image instanceof HTMLImageElement) {
|
|
5006
|
+
return { width: image.naturalWidth, height: image.naturalHeight };
|
|
5007
|
+
}
|
|
5008
|
+
return { width: image.width, height: image.height };
|
|
5009
|
+
}
|
|
5010
|
+
function readImageData(image) {
|
|
5011
|
+
const cached = IMAGE_DATA_CACHE.get(image);
|
|
5012
|
+
if (cached) {
|
|
5013
|
+
return cached;
|
|
5014
|
+
}
|
|
5015
|
+
const { width, height } = imageSize(image);
|
|
5016
|
+
const canvas = document.createElement("canvas");
|
|
5017
|
+
canvas.width = width;
|
|
5018
|
+
canvas.height = height;
|
|
5019
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
5020
|
+
if (!context) {
|
|
5021
|
+
return null;
|
|
5022
|
+
}
|
|
5023
|
+
try {
|
|
5024
|
+
context.drawImage(image, 0, 0);
|
|
5025
|
+
const data = context.getImageData(0, 0, width, height);
|
|
5026
|
+
IMAGE_DATA_CACHE.set(image, data);
|
|
5027
|
+
return data;
|
|
5028
|
+
} catch {
|
|
5029
|
+
return null;
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
function sampleTextureAlpha(texture, u, v) {
|
|
5033
|
+
if (!isSampleableImage(texture.image)) {
|
|
5034
|
+
return null;
|
|
5035
|
+
}
|
|
5036
|
+
const data = readImageData(texture.image);
|
|
5037
|
+
if (!data) {
|
|
5038
|
+
return null;
|
|
5039
|
+
}
|
|
5040
|
+
SAMPLE_UV.set(u, v);
|
|
5041
|
+
texture.transformUv(SAMPLE_UV);
|
|
5042
|
+
const pixelX = Math.min(
|
|
5043
|
+
data.width - 1,
|
|
5044
|
+
Math.max(0, Math.floor(SAMPLE_UV.x * data.width))
|
|
5045
|
+
);
|
|
5046
|
+
const pixelY = Math.min(
|
|
5047
|
+
data.height - 1,
|
|
5048
|
+
Math.max(0, Math.floor(SAMPLE_UV.y * data.height))
|
|
5049
|
+
);
|
|
5050
|
+
return data.data[(pixelY * data.width + pixelX) * 4 + 3] / 255;
|
|
5051
|
+
}
|
|
5052
|
+
function hotspotPlaneRaycast(raycaster, intersects2) {
|
|
5053
|
+
const material = Array.isArray(this.material) ? this.material[0] : this.material;
|
|
5054
|
+
if (!(material instanceof MeshBasicMaterial)) {
|
|
5055
|
+
Mesh2.prototype.raycast.call(this, raycaster, intersects2);
|
|
5056
|
+
return;
|
|
5057
|
+
}
|
|
5058
|
+
const cutoff = Math.max(material.alphaTest, HOTSPOT_ALPHA_CUTOFF);
|
|
5059
|
+
if (material.opacity < cutoff) {
|
|
5060
|
+
return;
|
|
5061
|
+
}
|
|
5062
|
+
RAYCAST_HITS.length = 0;
|
|
5063
|
+
Mesh2.prototype.raycast.call(this, raycaster, RAYCAST_HITS);
|
|
5064
|
+
const map = material.map;
|
|
5065
|
+
for (const hit of RAYCAST_HITS) {
|
|
5066
|
+
if (!map || !hit.uv) {
|
|
5067
|
+
intersects2.push(hit);
|
|
5068
|
+
continue;
|
|
5069
|
+
}
|
|
5070
|
+
const texelAlpha = sampleTextureAlpha(map, hit.uv.x, hit.uv.y);
|
|
5071
|
+
if (texelAlpha === null || texelAlpha * material.opacity >= cutoff) {
|
|
5072
|
+
intersects2.push(hit);
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
function HotspotPlane({
|
|
5077
|
+
map,
|
|
5078
|
+
opacity
|
|
5079
|
+
}) {
|
|
5080
|
+
return /* @__PURE__ */ jsxs9("mesh", { raycast: hotspotPlaneRaycast, children: [
|
|
5081
|
+
/* @__PURE__ */ jsx12("planeGeometry", { args: [1, 1] }),
|
|
5082
|
+
/* @__PURE__ */ jsx12(
|
|
5083
|
+
"meshBasicMaterial",
|
|
5084
|
+
{
|
|
5085
|
+
alphaTest: HOTSPOT_ALPHA_CUTOFF,
|
|
5086
|
+
alphaToCoverage: true,
|
|
5087
|
+
map,
|
|
5088
|
+
opacity: map ? clampOpacity(opacity) : 0,
|
|
5089
|
+
side: DoubleSide2,
|
|
5090
|
+
toneMapped: false,
|
|
5091
|
+
transparent: true
|
|
5092
|
+
}
|
|
5093
|
+
)
|
|
5094
|
+
] });
|
|
5095
|
+
}
|
|
5096
|
+
|
|
5097
|
+
// src/hotspot/image-hotspot.tsx
|
|
5098
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
4438
5099
|
function useHotspotImageTexture(src, onLoad, onError) {
|
|
4439
|
-
const gl =
|
|
4440
|
-
const [texture, setTexture] =
|
|
4441
|
-
const onLoadRef =
|
|
4442
|
-
const onErrorRef =
|
|
5100
|
+
const gl = useThree12((state) => state.gl);
|
|
5101
|
+
const [texture, setTexture] = useState9(null);
|
|
5102
|
+
const onLoadRef = useRef15(onLoad);
|
|
5103
|
+
const onErrorRef = useRef15(onError);
|
|
4443
5104
|
onLoadRef.current = onLoad;
|
|
4444
5105
|
onErrorRef.current = onError;
|
|
4445
|
-
|
|
5106
|
+
useEffect18(() => {
|
|
4446
5107
|
let active = true;
|
|
4447
5108
|
setTexture(null);
|
|
4448
5109
|
if (!src) {
|
|
@@ -4480,7 +5141,7 @@ function useHotspotImageTexture(src, onLoad, onError) {
|
|
|
4480
5141
|
active = false;
|
|
4481
5142
|
};
|
|
4482
5143
|
}, [gl, src]);
|
|
4483
|
-
|
|
5144
|
+
useEffect18(
|
|
4484
5145
|
() => () => {
|
|
4485
5146
|
texture?.dispose();
|
|
4486
5147
|
},
|
|
@@ -4498,24 +5159,11 @@ function ImageHotspot({
|
|
|
4498
5159
|
...anchorProps
|
|
4499
5160
|
}) {
|
|
4500
5161
|
const texture = useHotspotImageTexture(src, onLoad, onError);
|
|
4501
|
-
return /* @__PURE__ */
|
|
4502
|
-
/* @__PURE__ */ jsx10("planeGeometry", { args: [1, 1] }),
|
|
4503
|
-
/* @__PURE__ */ jsx10(
|
|
4504
|
-
"meshBasicMaterial",
|
|
4505
|
-
{
|
|
4506
|
-
map: texture,
|
|
4507
|
-
opacity: texture ? clampOpacity(opacity) : 0,
|
|
4508
|
-
side: DoubleSide2,
|
|
4509
|
-
toneMapped: false,
|
|
4510
|
-
transparent: true
|
|
4511
|
-
}
|
|
4512
|
-
)
|
|
4513
|
-
] }) });
|
|
5162
|
+
return /* @__PURE__ */ jsx13(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx13(HotspotPlane, { map: texture, opacity }) });
|
|
4514
5163
|
}
|
|
4515
5164
|
|
|
4516
5165
|
// src/hotspot/polygon-hotspot.tsx
|
|
4517
|
-
import {
|
|
4518
|
-
import { useEffect as useEffect17, useMemo as useMemo9, useRef as useRef15 } from "react";
|
|
5166
|
+
import { useEffect as useEffect19, useMemo as useMemo11, useRef as useRef16 } from "react";
|
|
4519
5167
|
import {
|
|
4520
5168
|
BufferGeometry,
|
|
4521
5169
|
DoubleSide as DoubleSide3,
|
|
@@ -4523,13 +5171,39 @@ import {
|
|
|
4523
5171
|
Matrix4 as Matrix43,
|
|
4524
5172
|
Quaternion as Quaternion3,
|
|
4525
5173
|
ShapeUtils,
|
|
4526
|
-
Vector2 as
|
|
5174
|
+
Vector2 as Vector24,
|
|
4527
5175
|
Vector3 as Vector37
|
|
4528
5176
|
} from "three";
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
import {
|
|
4532
|
-
import { jsx as
|
|
5177
|
+
|
|
5178
|
+
// src/hotspot/hotspot-stroke-line.tsx
|
|
5179
|
+
import { Line } from "@react-three/drei";
|
|
5180
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
5181
|
+
function HotspotStrokeLine({
|
|
5182
|
+
points,
|
|
5183
|
+
color,
|
|
5184
|
+
lineWidth,
|
|
5185
|
+
opacity,
|
|
5186
|
+
renderOrder
|
|
5187
|
+
}) {
|
|
5188
|
+
return /* @__PURE__ */ jsx14(
|
|
5189
|
+
Line,
|
|
5190
|
+
{
|
|
5191
|
+
alphaToCoverage: true,
|
|
5192
|
+
color,
|
|
5193
|
+
depthTest: true,
|
|
5194
|
+
depthWrite: false,
|
|
5195
|
+
lineWidth: Math.max(0.5, lineWidth),
|
|
5196
|
+
opacity,
|
|
5197
|
+
points,
|
|
5198
|
+
renderOrder,
|
|
5199
|
+
transparent: true,
|
|
5200
|
+
worldUnits: false
|
|
5201
|
+
}
|
|
5202
|
+
);
|
|
5203
|
+
}
|
|
5204
|
+
|
|
5205
|
+
// src/hotspot/polygon-hotspot.tsx
|
|
5206
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4533
5207
|
var POLYGON_RADIUS = 990;
|
|
4534
5208
|
var MAX_FILL_SEGMENT_DEGREES = 7;
|
|
4535
5209
|
function cloneVertices(vertices) {
|
|
@@ -4564,7 +5238,7 @@ function intersects(a, b, c, d) {
|
|
|
4564
5238
|
}
|
|
4565
5239
|
function hasSelfIntersection(vertices) {
|
|
4566
5240
|
const points = vertices.map(
|
|
4567
|
-
(vertex) => new
|
|
5241
|
+
(vertex) => new Vector24(vertex.unwrappedYaw, vertex.pitch)
|
|
4568
5242
|
);
|
|
4569
5243
|
for (let index = 0; index < points.length; index += 1) {
|
|
4570
5244
|
const nextIndex = (index + 1) % points.length;
|
|
@@ -4713,7 +5387,7 @@ function addSubdividedTriangle(positions, indices, a, b, c, worldToLocal, steps)
|
|
|
4713
5387
|
}
|
|
4714
5388
|
function makeFillGeometry(vertices, center) {
|
|
4715
5389
|
const unwrapped = unwrapPolygonVertices(vertices);
|
|
4716
|
-
const contour = unwrapped.map((vertex) => new
|
|
5390
|
+
const contour = unwrapped.map((vertex) => new Vector24(vertex.unwrappedYaw, vertex.pitch));
|
|
4717
5391
|
const triangles = ShapeUtils.triangulateShape(contour, []);
|
|
4718
5392
|
const positions = [];
|
|
4719
5393
|
const indices = [];
|
|
@@ -4733,7 +5407,7 @@ function makeFillGeometry(vertices, center) {
|
|
|
4733
5407
|
return new BufferGeometry().setAttribute("position", new Float32BufferAttribute(positions, 3)).setIndex(indices);
|
|
4734
5408
|
}
|
|
4735
5409
|
function makeStrokePositions(vertices, center) {
|
|
4736
|
-
const
|
|
5410
|
+
const points = [];
|
|
4737
5411
|
const worldToLocal = makeWorldToLocal(center);
|
|
4738
5412
|
const unwrapped = unwrapPolygonVertices(vertices);
|
|
4739
5413
|
const steps = polygonSubdivisionSteps(vertices);
|
|
@@ -4749,33 +5423,14 @@ function makeStrokePositions(vertices, center) {
|
|
|
4749
5423
|
},
|
|
4750
5424
|
POLYGON_RADIUS
|
|
4751
5425
|
).applyMatrix4(worldToLocal);
|
|
4752
|
-
|
|
5426
|
+
points.push([point.x, point.y, point.z]);
|
|
4753
5427
|
}
|
|
4754
5428
|
}
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
geometry.setPositions(positions);
|
|
4761
|
-
const material = new LineMaterial({
|
|
4762
|
-
color,
|
|
4763
|
-
depthTest: true,
|
|
4764
|
-
depthWrite: false,
|
|
4765
|
-
linewidth: Math.max(0.5, width),
|
|
4766
|
-
opacity,
|
|
4767
|
-
transparent: true,
|
|
4768
|
-
worldUnits: false,
|
|
4769
|
-
alphaToCoverage: true
|
|
4770
|
-
});
|
|
4771
|
-
const line = new Line2(geometry, material);
|
|
4772
|
-
line.computeLineDistances();
|
|
4773
|
-
return line;
|
|
4774
|
-
}
|
|
4775
|
-
function disposeStrokeLine(line) {
|
|
4776
|
-
if (!line) return;
|
|
4777
|
-
line.geometry.dispose();
|
|
4778
|
-
line.material.dispose();
|
|
5429
|
+
const first = points[0];
|
|
5430
|
+
if (first) {
|
|
5431
|
+
points.push(first);
|
|
5432
|
+
}
|
|
5433
|
+
return points;
|
|
4779
5434
|
}
|
|
4780
5435
|
function translateVertices(vertices, from, to) {
|
|
4781
5436
|
const yawOffset = normalizePanoYaw(to.yaw - from.yaw);
|
|
@@ -4802,45 +5457,27 @@ function PolygonHotspot({
|
|
|
4802
5457
|
onInvalid,
|
|
4803
5458
|
...anchorProps
|
|
4804
5459
|
}) {
|
|
4805
|
-
const
|
|
4806
|
-
const size = useThree12((state) => state.size);
|
|
4807
|
-
const issues = useMemo9(() => validatePolygonVertices(vertices), [vertices]);
|
|
5460
|
+
const issues = useMemo11(() => validatePolygonVertices(vertices), [vertices]);
|
|
4808
5461
|
const valid = issues.length === 0;
|
|
4809
|
-
const center =
|
|
4810
|
-
const fillGeometry =
|
|
5462
|
+
const center = useMemo11(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
|
|
5463
|
+
const fillGeometry = useMemo11(
|
|
4811
5464
|
() => valid && center ? makeFillGeometry(vertices, center) : null,
|
|
4812
5465
|
[center, valid, vertices]
|
|
4813
5466
|
);
|
|
4814
|
-
const
|
|
5467
|
+
const strokePoints = useMemo11(
|
|
4815
5468
|
() => valid && center ? makeStrokePositions(vertices, center) : null,
|
|
4816
5469
|
[center, valid, vertices]
|
|
4817
5470
|
);
|
|
4818
5471
|
const resolvedStrokeOpacity = Math.max(0, Math.min(strokeOpacity, 1));
|
|
4819
|
-
const
|
|
4820
|
-
const strokeLine = useMemo9(
|
|
4821
|
-
() => strokePositions ? makeStrokeLine(strokePositions, stroke, strokeWidth * devicePixelRatio, resolvedStrokeOpacity) : null,
|
|
4822
|
-
[devicePixelRatio, resolvedStrokeOpacity, stroke, strokePositions, strokeWidth]
|
|
4823
|
-
);
|
|
4824
|
-
const focusStrokeLine = useMemo9(
|
|
4825
|
-
() => strokePositions ? makeStrokeLine(strokePositions, "#75cbd3", (strokeWidth + 2) * devicePixelRatio, 1) : null,
|
|
4826
|
-
[devicePixelRatio, strokePositions, strokeWidth]
|
|
4827
|
-
);
|
|
4828
|
-
const startVerticesRef = useRef15(null);
|
|
5472
|
+
const startVerticesRef = useRef16(null);
|
|
4829
5473
|
const issuesKey = issues.map((issue) => issue.code).join(",");
|
|
4830
|
-
|
|
5474
|
+
useEffect19(() => {
|
|
4831
5475
|
if (!valid) onInvalid?.(issues);
|
|
4832
5476
|
}, [issues, issuesKey, onInvalid, valid]);
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
const width = gl.domElement.width;
|
|
4838
|
-
const height = gl.domElement.height;
|
|
4839
|
-
for (const line of [strokeLine, focusStrokeLine]) {
|
|
4840
|
-
if (line) line.material.resolution.set(width, height);
|
|
4841
|
-
}
|
|
4842
|
-
}, [focusStrokeLine, gl, size.height, size.width, strokeLine]);
|
|
4843
|
-
if (!valid || !center || !fillGeometry || !strokeLine || !focusStrokeLine) return null;
|
|
5477
|
+
useEffect19(() => () => fillGeometry?.dispose(), [fillGeometry]);
|
|
5478
|
+
if (!valid || !center || !fillGeometry || !strokePoints || strokePoints.length < 2) {
|
|
5479
|
+
return null;
|
|
5480
|
+
}
|
|
4844
5481
|
const emitVerticesChange = (event, callback) => {
|
|
4845
5482
|
const startVertices = startVerticesRef.current ?? cloneVertices(vertices);
|
|
4846
5483
|
const nextVertices = translateVertices(startVertices, event.startPosition, event.position);
|
|
@@ -4852,7 +5489,7 @@ function PolygonHotspot({
|
|
|
4852
5489
|
startPosition: event.startPosition
|
|
4853
5490
|
});
|
|
4854
5491
|
};
|
|
4855
|
-
return /* @__PURE__ */
|
|
5492
|
+
return /* @__PURE__ */ jsxs10(
|
|
4856
5493
|
HotspotAnchor,
|
|
4857
5494
|
{
|
|
4858
5495
|
...anchorProps,
|
|
@@ -4867,7 +5504,16 @@ function PolygonHotspot({
|
|
|
4867
5504
|
draggable,
|
|
4868
5505
|
interactive,
|
|
4869
5506
|
useAngularScale: false,
|
|
4870
|
-
focusContent: /* @__PURE__ */
|
|
5507
|
+
focusContent: /* @__PURE__ */ jsx15(
|
|
5508
|
+
HotspotStrokeLine,
|
|
5509
|
+
{
|
|
5510
|
+
color: "#75cbd3",
|
|
5511
|
+
lineWidth: strokeWidth + 2,
|
|
5512
|
+
opacity: 1,
|
|
5513
|
+
points: strokePoints,
|
|
5514
|
+
renderOrder: renderOrder + 2
|
|
5515
|
+
}
|
|
5516
|
+
),
|
|
4871
5517
|
onDragStart: (event) => {
|
|
4872
5518
|
startVerticesRef.current = cloneVertices(vertices);
|
|
4873
5519
|
emitVerticesChange(event, onDragStart);
|
|
@@ -4878,7 +5524,7 @@ function PolygonHotspot({
|
|
|
4878
5524
|
startVerticesRef.current = null;
|
|
4879
5525
|
},
|
|
4880
5526
|
children: [
|
|
4881
|
-
/* @__PURE__ */
|
|
5527
|
+
/* @__PURE__ */ jsx15("mesh", { geometry: fillGeometry, renderOrder, children: /* @__PURE__ */ jsx15(
|
|
4882
5528
|
"meshBasicMaterial",
|
|
4883
5529
|
{
|
|
4884
5530
|
color: fill,
|
|
@@ -4888,25 +5534,30 @@ function PolygonHotspot({
|
|
|
4888
5534
|
transparent: true
|
|
4889
5535
|
}
|
|
4890
5536
|
) }),
|
|
4891
|
-
/* @__PURE__ */
|
|
5537
|
+
/* @__PURE__ */ jsx15(
|
|
5538
|
+
HotspotStrokeLine,
|
|
5539
|
+
{
|
|
5540
|
+
color: stroke,
|
|
5541
|
+
lineWidth: strokeWidth,
|
|
5542
|
+
opacity: resolvedStrokeOpacity,
|
|
5543
|
+
points: strokePoints,
|
|
5544
|
+
renderOrder: renderOrder + 1
|
|
5545
|
+
}
|
|
5546
|
+
)
|
|
4892
5547
|
]
|
|
4893
5548
|
}
|
|
4894
5549
|
);
|
|
4895
5550
|
}
|
|
4896
5551
|
|
|
4897
5552
|
// src/hotspot/polyline-hotspot.tsx
|
|
4898
|
-
import {
|
|
4899
|
-
import { useEffect as useEffect18, useMemo as useMemo10, useRef as useRef16 } from "react";
|
|
5553
|
+
import { useEffect as useEffect20, useMemo as useMemo12, useRef as useRef17 } from "react";
|
|
4900
5554
|
import {
|
|
4901
5555
|
MathUtils as MathUtils5,
|
|
4902
5556
|
Matrix4 as Matrix44,
|
|
4903
5557
|
Quaternion as Quaternion4,
|
|
4904
5558
|
Vector3 as Vector38
|
|
4905
5559
|
} from "three";
|
|
4906
|
-
import {
|
|
4907
|
-
import { LineGeometry as LineGeometry2 } from "three/addons/lines/LineGeometry.js";
|
|
4908
|
-
import { LineMaterial as LineMaterial2 } from "three/addons/lines/LineMaterial.js";
|
|
4909
|
-
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
5560
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
4910
5561
|
var POLYLINE_RADIUS = 990;
|
|
4911
5562
|
var MAX_SEGMENT_DEGREES = 7;
|
|
4912
5563
|
function cloneVertices2(vertices) {
|
|
@@ -4972,7 +5623,7 @@ function subdivisionSteps(vertices) {
|
|
|
4972
5623
|
);
|
|
4973
5624
|
}
|
|
4974
5625
|
function makeLinePositions(vertices, center) {
|
|
4975
|
-
const
|
|
5626
|
+
const points = [];
|
|
4976
5627
|
const worldToLocal = makeWorldToLocal2(center);
|
|
4977
5628
|
const unwrapped = unwrapVertices(vertices);
|
|
4978
5629
|
const steps = subdivisionSteps(vertices);
|
|
@@ -4988,35 +5639,13 @@ function makeLinePositions(vertices, center) {
|
|
|
4988
5639
|
},
|
|
4989
5640
|
POLYLINE_RADIUS
|
|
4990
5641
|
).applyMatrix4(worldToLocal);
|
|
4991
|
-
|
|
5642
|
+
points.push([point.x, point.y, point.z]);
|
|
4992
5643
|
}
|
|
4993
5644
|
}
|
|
4994
5645
|
const last = unwrapped[unwrapped.length - 1];
|
|
4995
5646
|
const lastPoint = panoPositionToVector3(last, POLYLINE_RADIUS).applyMatrix4(worldToLocal);
|
|
4996
|
-
|
|
4997
|
-
return
|
|
4998
|
-
}
|
|
4999
|
-
function makeLine(positions, color, width, opacity) {
|
|
5000
|
-
const geometry = new LineGeometry2();
|
|
5001
|
-
geometry.setPositions(positions);
|
|
5002
|
-
const material = new LineMaterial2({
|
|
5003
|
-
color,
|
|
5004
|
-
depthTest: true,
|
|
5005
|
-
depthWrite: false,
|
|
5006
|
-
linewidth: Math.max(0.5, width),
|
|
5007
|
-
opacity,
|
|
5008
|
-
transparent: true,
|
|
5009
|
-
worldUnits: false,
|
|
5010
|
-
alphaToCoverage: true
|
|
5011
|
-
});
|
|
5012
|
-
const line = new Line22(geometry, material);
|
|
5013
|
-
line.computeLineDistances();
|
|
5014
|
-
return line;
|
|
5015
|
-
}
|
|
5016
|
-
function disposeLine(line) {
|
|
5017
|
-
if (!line) return;
|
|
5018
|
-
line.geometry.dispose();
|
|
5019
|
-
line.material.dispose();
|
|
5647
|
+
points.push([lastPoint.x, lastPoint.y, lastPoint.z]);
|
|
5648
|
+
return points;
|
|
5020
5649
|
}
|
|
5021
5650
|
function translateVertices2(vertices, from, to) {
|
|
5022
5651
|
const yawOffset = normalizePanoYaw(to.yaw - from.yaw);
|
|
@@ -5043,39 +5672,19 @@ function PolylineHotspot({
|
|
|
5043
5672
|
onInvalid,
|
|
5044
5673
|
...anchorProps
|
|
5045
5674
|
}) {
|
|
5046
|
-
const
|
|
5047
|
-
const size = useThree13((state) => state.size);
|
|
5048
|
-
const issues = useMemo10(() => validatePolylineVertices(vertices), [vertices]);
|
|
5675
|
+
const issues = useMemo12(() => validatePolylineVertices(vertices), [vertices]);
|
|
5049
5676
|
const valid = issues.length === 0;
|
|
5050
|
-
const center =
|
|
5051
|
-
const
|
|
5677
|
+
const center = useMemo12(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
|
|
5678
|
+
const linePoints = useMemo12(
|
|
5052
5679
|
() => valid && center ? makeLinePositions(vertices, center) : null,
|
|
5053
5680
|
[center, valid, vertices]
|
|
5054
5681
|
);
|
|
5055
|
-
const devicePixelRatio = gl.getPixelRatio();
|
|
5056
5682
|
const opacity = Math.max(0, Math.min(strokeOpacity, 1));
|
|
5057
|
-
const
|
|
5058
|
-
|
|
5059
|
-
[devicePixelRatio, linePositions, opacity, stroke, strokeWidth]
|
|
5060
|
-
);
|
|
5061
|
-
const focusLine = useMemo10(
|
|
5062
|
-
() => linePositions ? makeLine(linePositions, "#75cbd3", (strokeWidth + 2) * devicePixelRatio, 1) : null,
|
|
5063
|
-
[devicePixelRatio, linePositions, strokeWidth]
|
|
5064
|
-
);
|
|
5065
|
-
const startVerticesRef = useRef16(null);
|
|
5066
|
-
useEffect18(() => {
|
|
5683
|
+
const startVerticesRef = useRef17(null);
|
|
5684
|
+
useEffect20(() => {
|
|
5067
5685
|
if (!valid) onInvalid?.(issues);
|
|
5068
5686
|
}, [issues, onInvalid, valid]);
|
|
5069
|
-
|
|
5070
|
-
useEffect18(() => () => disposeLine(focusLine), [focusLine]);
|
|
5071
|
-
useEffect18(() => {
|
|
5072
|
-
const width = gl.domElement.width;
|
|
5073
|
-
const height = gl.domElement.height;
|
|
5074
|
-
for (const nextLine of [line, focusLine]) {
|
|
5075
|
-
if (nextLine) nextLine.material.resolution.set(width, height);
|
|
5076
|
-
}
|
|
5077
|
-
}, [focusLine, gl, line, size.height, size.width]);
|
|
5078
|
-
if (!valid || !center || !line || !focusLine) return null;
|
|
5687
|
+
if (!valid || !center || !linePoints || linePoints.length < 2) return null;
|
|
5079
5688
|
const emitVerticesChange = (event, callback) => {
|
|
5080
5689
|
const startVertices = startVerticesRef.current ?? cloneVertices2(vertices);
|
|
5081
5690
|
callback?.({
|
|
@@ -5086,7 +5695,7 @@ function PolylineHotspot({
|
|
|
5086
5695
|
startPosition: event.startPosition
|
|
5087
5696
|
});
|
|
5088
5697
|
};
|
|
5089
|
-
return /* @__PURE__ */
|
|
5698
|
+
return /* @__PURE__ */ jsx16(
|
|
5090
5699
|
HotspotAnchor,
|
|
5091
5700
|
{
|
|
5092
5701
|
...anchorProps,
|
|
@@ -5100,7 +5709,16 @@ function PolylineHotspot({
|
|
|
5100
5709
|
renderOrder,
|
|
5101
5710
|
draggable,
|
|
5102
5711
|
useAngularScale: false,
|
|
5103
|
-
focusContent: /* @__PURE__ */
|
|
5712
|
+
focusContent: /* @__PURE__ */ jsx16(
|
|
5713
|
+
HotspotStrokeLine,
|
|
5714
|
+
{
|
|
5715
|
+
color: "#75cbd3",
|
|
5716
|
+
lineWidth: strokeWidth + 2,
|
|
5717
|
+
opacity: 1,
|
|
5718
|
+
points: linePoints,
|
|
5719
|
+
renderOrder: renderOrder + 2
|
|
5720
|
+
}
|
|
5721
|
+
),
|
|
5104
5722
|
onDragStart: (event) => {
|
|
5105
5723
|
startVerticesRef.current = cloneVertices2(vertices);
|
|
5106
5724
|
emitVerticesChange(event, onDragStart);
|
|
@@ -5110,29 +5728,31 @@ function PolylineHotspot({
|
|
|
5110
5728
|
emitVerticesChange(event, onDragEnd);
|
|
5111
5729
|
startVerticesRef.current = null;
|
|
5112
5730
|
},
|
|
5113
|
-
children: /* @__PURE__ */
|
|
5731
|
+
children: /* @__PURE__ */ jsx16(
|
|
5732
|
+
HotspotStrokeLine,
|
|
5733
|
+
{
|
|
5734
|
+
color: stroke,
|
|
5735
|
+
lineWidth: strokeWidth,
|
|
5736
|
+
opacity,
|
|
5737
|
+
points: linePoints,
|
|
5738
|
+
renderOrder: renderOrder + 1
|
|
5739
|
+
}
|
|
5740
|
+
)
|
|
5114
5741
|
}
|
|
5115
5742
|
);
|
|
5116
5743
|
}
|
|
5117
5744
|
|
|
5118
5745
|
// src/hotspot/graphic-hotspot.tsx
|
|
5119
|
-
import { useEffect as
|
|
5746
|
+
import { useEffect as useEffect21, useRef as useRef18, useState as useState10 } from "react";
|
|
5120
5747
|
import {
|
|
5121
5748
|
CanvasTexture,
|
|
5122
5749
|
ClampToEdgeWrapping as ClampToEdgeWrapping4,
|
|
5123
|
-
DoubleSide as DoubleSide4,
|
|
5124
5750
|
LinearFilter as LinearFilter6,
|
|
5125
5751
|
LinearMipmapLinearFilter as LinearMipmapLinearFilter3,
|
|
5126
5752
|
SRGBColorSpace as SRGBColorSpace5
|
|
5127
5753
|
} from "three";
|
|
5128
|
-
import { jsx as
|
|
5754
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
5129
5755
|
var GRAPHIC_TEXTURE_SIZE = 512;
|
|
5130
|
-
function clampOpacity2(opacity) {
|
|
5131
|
-
if (!Number.isFinite(opacity)) {
|
|
5132
|
-
return 1;
|
|
5133
|
-
}
|
|
5134
|
-
return Math.max(0, Math.min(opacity, 1));
|
|
5135
|
-
}
|
|
5136
5756
|
function resolveStrokeWidth(value) {
|
|
5137
5757
|
return Number.isFinite(value) ? Math.max(value, 0) : 8;
|
|
5138
5758
|
}
|
|
@@ -5320,12 +5940,12 @@ function createGraphicTexture(graphic, aspectWidth, aspectHeight) {
|
|
|
5320
5940
|
return texture;
|
|
5321
5941
|
}
|
|
5322
5942
|
function useGraphicTexture(graphic, aspectWidth, aspectHeight, onLoad, onError) {
|
|
5323
|
-
const [texture, setTexture] =
|
|
5324
|
-
const onLoadRef =
|
|
5325
|
-
const onErrorRef =
|
|
5943
|
+
const [texture, setTexture] = useState10(null);
|
|
5944
|
+
const onLoadRef = useRef18(onLoad);
|
|
5945
|
+
const onErrorRef = useRef18(onError);
|
|
5326
5946
|
onLoadRef.current = onLoad;
|
|
5327
5947
|
onErrorRef.current = onError;
|
|
5328
|
-
|
|
5948
|
+
useEffect21(() => {
|
|
5329
5949
|
let nextTexture = null;
|
|
5330
5950
|
try {
|
|
5331
5951
|
nextTexture = createGraphicTexture(graphic, aspectWidth, aspectHeight);
|
|
@@ -5351,48 +5971,29 @@ function CanvasGraphicHotspot({
|
|
|
5351
5971
|
...anchorProps
|
|
5352
5972
|
}) {
|
|
5353
5973
|
const texture = useGraphicTexture(graphic, width, height, onLoad, onError);
|
|
5354
|
-
return /* @__PURE__ */
|
|
5355
|
-
/* @__PURE__ */ jsx13("planeGeometry", { args: [1, 1] }),
|
|
5356
|
-
/* @__PURE__ */ jsx13(
|
|
5357
|
-
"meshBasicMaterial",
|
|
5358
|
-
{
|
|
5359
|
-
map: texture,
|
|
5360
|
-
opacity: texture ? clampOpacity2(opacity) : 0,
|
|
5361
|
-
side: DoubleSide4,
|
|
5362
|
-
toneMapped: false,
|
|
5363
|
-
transparent: true
|
|
5364
|
-
}
|
|
5365
|
-
)
|
|
5366
|
-
] }) });
|
|
5974
|
+
return /* @__PURE__ */ jsx17(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx17(HotspotPlane, { map: texture, opacity }) });
|
|
5367
5975
|
}
|
|
5368
5976
|
function GraphicHotspot(props) {
|
|
5369
5977
|
const { graphic, ...restProps } = props;
|
|
5370
5978
|
if (graphic.kind === "svg") {
|
|
5371
5979
|
const imageProps = restProps;
|
|
5372
|
-
return /* @__PURE__ */
|
|
5980
|
+
return /* @__PURE__ */ jsx17(ImageHotspot, { ...imageProps, src: graphic.src });
|
|
5373
5981
|
}
|
|
5374
|
-
return /* @__PURE__ */
|
|
5982
|
+
return /* @__PURE__ */ jsx17(CanvasGraphicHotspot, { ...restProps, graphic });
|
|
5375
5983
|
}
|
|
5376
5984
|
|
|
5377
5985
|
// src/hotspot/sequence-hotspot.tsx
|
|
5378
|
-
import { useFrame as useFrame8, useThree as
|
|
5379
|
-
import { useEffect as
|
|
5986
|
+
import { useFrame as useFrame8, useThree as useThree13 } from "@react-three/fiber";
|
|
5987
|
+
import { useEffect as useEffect22, useRef as useRef19, useState as useState11 } from "react";
|
|
5380
5988
|
import {
|
|
5381
5989
|
ClampToEdgeWrapping as ClampToEdgeWrapping5,
|
|
5382
|
-
DoubleSide as DoubleSide5,
|
|
5383
5990
|
LinearFilter as LinearFilter7,
|
|
5384
5991
|
LinearMipmapLinearFilter as LinearMipmapLinearFilter4,
|
|
5385
5992
|
SRGBColorSpace as SRGBColorSpace6,
|
|
5386
5993
|
TextureLoader as TextureLoader5
|
|
5387
5994
|
} from "three";
|
|
5388
|
-
import { jsx as
|
|
5995
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
5389
5996
|
var DEFAULT_SEQUENCE_FPS = 12;
|
|
5390
|
-
function clampOpacity3(opacity) {
|
|
5391
|
-
if (!Number.isFinite(opacity)) {
|
|
5392
|
-
return 1;
|
|
5393
|
-
}
|
|
5394
|
-
return Math.max(0, Math.min(opacity, 1));
|
|
5395
|
-
}
|
|
5396
5997
|
function resolveFps(value) {
|
|
5397
5998
|
return Number.isFinite(value) && value > 0 ? value : DEFAULT_SEQUENCE_FPS;
|
|
5398
5999
|
}
|
|
@@ -5405,13 +6006,13 @@ function useSpriteTexture({
|
|
|
5405
6006
|
onError,
|
|
5406
6007
|
onLoadProgress
|
|
5407
6008
|
}) {
|
|
5408
|
-
const gl =
|
|
5409
|
-
const [texture, setTexture] =
|
|
5410
|
-
const onErrorRef =
|
|
5411
|
-
const onLoadProgressRef =
|
|
6009
|
+
const gl = useThree13((state) => state.gl);
|
|
6010
|
+
const [texture, setTexture] = useState11(null);
|
|
6011
|
+
const onErrorRef = useRef19(onError);
|
|
6012
|
+
const onLoadProgressRef = useRef19(onLoadProgress);
|
|
5412
6013
|
onErrorRef.current = onError;
|
|
5413
6014
|
onLoadProgressRef.current = onLoadProgress;
|
|
5414
|
-
|
|
6015
|
+
useEffect22(() => {
|
|
5415
6016
|
let active = true;
|
|
5416
6017
|
setTexture(null);
|
|
5417
6018
|
if (!src) {
|
|
@@ -5452,7 +6053,7 @@ function useSpriteTexture({
|
|
|
5452
6053
|
active = false;
|
|
5453
6054
|
};
|
|
5454
6055
|
}, [gl, id, src]);
|
|
5455
|
-
|
|
6056
|
+
useEffect22(
|
|
5456
6057
|
() => () => {
|
|
5457
6058
|
texture?.dispose();
|
|
5458
6059
|
},
|
|
@@ -5481,24 +6082,24 @@ function SequenceHotspot({
|
|
|
5481
6082
|
const texture = useSpriteTexture({ id, src, onError, onLoadProgress });
|
|
5482
6083
|
const frameCount = resolveFrameCount(requestedFrameCount);
|
|
5483
6084
|
const safeFps = resolveFps(fps);
|
|
5484
|
-
const [frameIndex, setFrameIndex] =
|
|
5485
|
-
const elapsedRef =
|
|
5486
|
-
const frameIndexRef =
|
|
5487
|
-
const endedRef =
|
|
5488
|
-
const lastPlaybackStateRef =
|
|
5489
|
-
const onFrameChangeRef =
|
|
5490
|
-
const onPlaybackStateChangeRef =
|
|
5491
|
-
const onEndedRef =
|
|
6085
|
+
const [frameIndex, setFrameIndex] = useState11(0);
|
|
6086
|
+
const elapsedRef = useRef19(0);
|
|
6087
|
+
const frameIndexRef = useRef19(0);
|
|
6088
|
+
const endedRef = useRef19(false);
|
|
6089
|
+
const lastPlaybackStateRef = useRef19(null);
|
|
6090
|
+
const onFrameChangeRef = useRef19(onFrameChange);
|
|
6091
|
+
const onPlaybackStateChangeRef = useRef19(onPlaybackStateChange);
|
|
6092
|
+
const onEndedRef = useRef19(onEnded);
|
|
5492
6093
|
onFrameChangeRef.current = onFrameChange;
|
|
5493
6094
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
5494
6095
|
onEndedRef.current = onEnded;
|
|
5495
|
-
|
|
6096
|
+
useEffect22(() => {
|
|
5496
6097
|
elapsedRef.current = 0;
|
|
5497
6098
|
frameIndexRef.current = 0;
|
|
5498
6099
|
endedRef.current = false;
|
|
5499
6100
|
setFrameIndex(0);
|
|
5500
6101
|
}, [frameCount, frameDirection, src]);
|
|
5501
|
-
|
|
6102
|
+
useEffect22(() => {
|
|
5502
6103
|
if (!texture) {
|
|
5503
6104
|
return;
|
|
5504
6105
|
}
|
|
@@ -5512,13 +6113,13 @@ function SequenceHotspot({
|
|
|
5512
6113
|
);
|
|
5513
6114
|
texture.needsUpdate = true;
|
|
5514
6115
|
}, [frameCount, frameDirection, frameIndex, texture]);
|
|
5515
|
-
|
|
6116
|
+
useEffect22(() => {
|
|
5516
6117
|
if (!texture) {
|
|
5517
6118
|
return;
|
|
5518
6119
|
}
|
|
5519
6120
|
onFrameChangeRef.current?.({ id, index: 0, src });
|
|
5520
6121
|
}, [id, src, texture]);
|
|
5521
|
-
|
|
6122
|
+
useEffect22(() => {
|
|
5522
6123
|
if (!texture) {
|
|
5523
6124
|
return;
|
|
5524
6125
|
}
|
|
@@ -5567,40 +6168,21 @@ function SequenceHotspot({
|
|
|
5567
6168
|
onFrameChangeRef.current?.({ id, index: nextIndex, src });
|
|
5568
6169
|
}
|
|
5569
6170
|
});
|
|
5570
|
-
return /* @__PURE__ */
|
|
5571
|
-
/* @__PURE__ */ jsx14("planeGeometry", { args: [1, 1] }),
|
|
5572
|
-
/* @__PURE__ */ jsx14(
|
|
5573
|
-
"meshBasicMaterial",
|
|
5574
|
-
{
|
|
5575
|
-
map: texture,
|
|
5576
|
-
opacity: texture ? clampOpacity3(opacity) : 0,
|
|
5577
|
-
side: DoubleSide5,
|
|
5578
|
-
toneMapped: false,
|
|
5579
|
-
transparent: true
|
|
5580
|
-
}
|
|
5581
|
-
)
|
|
5582
|
-
] }) });
|
|
6171
|
+
return /* @__PURE__ */ jsx18(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx18(HotspotPlane, { map: texture, opacity }) });
|
|
5583
6172
|
}
|
|
5584
6173
|
|
|
5585
6174
|
// src/hotspot/video-hotspot.tsx
|
|
5586
|
-
import { useThree as
|
|
5587
|
-
import { useEffect as
|
|
6175
|
+
import { useThree as useThree14 } from "@react-three/fiber";
|
|
6176
|
+
import { useEffect as useEffect23, useRef as useRef20, useState as useState12 } from "react";
|
|
5588
6177
|
import {
|
|
5589
6178
|
ClampToEdgeWrapping as ClampToEdgeWrapping6,
|
|
5590
|
-
DoubleSide as DoubleSide6,
|
|
5591
6179
|
LinearFilter as LinearFilter8,
|
|
5592
6180
|
LinearMipmapLinearFilter as LinearMipmapLinearFilter5,
|
|
5593
6181
|
SRGBColorSpace as SRGBColorSpace7,
|
|
5594
6182
|
TextureLoader as TextureLoader6,
|
|
5595
6183
|
VideoTexture
|
|
5596
6184
|
} from "three";
|
|
5597
|
-
import { jsx as
|
|
5598
|
-
function clampOpacity4(opacity) {
|
|
5599
|
-
if (!Number.isFinite(opacity)) {
|
|
5600
|
-
return 1;
|
|
5601
|
-
}
|
|
5602
|
-
return Math.max(0, Math.min(opacity, 1));
|
|
5603
|
-
}
|
|
6185
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
5604
6186
|
function clampVolume(volume) {
|
|
5605
6187
|
if (!Number.isFinite(volume)) {
|
|
5606
6188
|
return 1;
|
|
@@ -5608,11 +6190,11 @@ function clampVolume(volume) {
|
|
|
5608
6190
|
return Math.max(0, Math.min(volume, 1));
|
|
5609
6191
|
}
|
|
5610
6192
|
function usePosterTexture(id, poster, onError) {
|
|
5611
|
-
const gl =
|
|
5612
|
-
const [texture, setTexture] =
|
|
5613
|
-
const onErrorRef =
|
|
6193
|
+
const gl = useThree14((state) => state.gl);
|
|
6194
|
+
const [texture, setTexture] = useState12(null);
|
|
6195
|
+
const onErrorRef = useRef20(onError);
|
|
5614
6196
|
onErrorRef.current = onError;
|
|
5615
|
-
|
|
6197
|
+
useEffect23(() => {
|
|
5616
6198
|
let active = true;
|
|
5617
6199
|
setTexture(null);
|
|
5618
6200
|
if (!poster) {
|
|
@@ -5649,7 +6231,7 @@ function usePosterTexture(id, poster, onError) {
|
|
|
5649
6231
|
active = false;
|
|
5650
6232
|
};
|
|
5651
6233
|
}, [gl, id, poster]);
|
|
5652
|
-
|
|
6234
|
+
useEffect23(
|
|
5653
6235
|
() => () => {
|
|
5654
6236
|
texture?.dispose();
|
|
5655
6237
|
},
|
|
@@ -5667,14 +6249,14 @@ function useVideoResource({
|
|
|
5667
6249
|
poster,
|
|
5668
6250
|
src
|
|
5669
6251
|
}) {
|
|
5670
|
-
const [resource, setResource] =
|
|
5671
|
-
const onEndedRef =
|
|
5672
|
-
const onErrorRef =
|
|
5673
|
-
const onPlaybackStateChangeRef =
|
|
6252
|
+
const [resource, setResource] = useState12(null);
|
|
6253
|
+
const onEndedRef = useRef20(onEnded);
|
|
6254
|
+
const onErrorRef = useRef20(onError);
|
|
6255
|
+
const onPlaybackStateChangeRef = useRef20(onPlaybackStateChange);
|
|
5674
6256
|
onEndedRef.current = onEnded;
|
|
5675
6257
|
onErrorRef.current = onError;
|
|
5676
6258
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
5677
|
-
|
|
6259
|
+
useEffect23(() => {
|
|
5678
6260
|
if (!src) {
|
|
5679
6261
|
setResource(null);
|
|
5680
6262
|
return;
|
|
@@ -5775,11 +6357,11 @@ function VideoHotspot({
|
|
|
5775
6357
|
src
|
|
5776
6358
|
});
|
|
5777
6359
|
const posterTexture = usePosterTexture(id, poster, onError);
|
|
5778
|
-
const onPlaybackErrorRef =
|
|
5779
|
-
const onPlaybackStateChangeRef =
|
|
6360
|
+
const onPlaybackErrorRef = useRef20(onPlaybackError);
|
|
6361
|
+
const onPlaybackStateChangeRef = useRef20(onPlaybackStateChange);
|
|
5780
6362
|
onPlaybackErrorRef.current = onPlaybackError;
|
|
5781
6363
|
onPlaybackStateChangeRef.current = onPlaybackStateChange;
|
|
5782
|
-
|
|
6364
|
+
useEffect23(() => {
|
|
5783
6365
|
if (!resource) {
|
|
5784
6366
|
return;
|
|
5785
6367
|
}
|
|
@@ -5789,7 +6371,7 @@ function VideoHotspot({
|
|
|
5789
6371
|
resource.video.playsInline = playsInline;
|
|
5790
6372
|
resource.video.preload = preload;
|
|
5791
6373
|
}, [loop, muted, playsInline, preload, resource, volume]);
|
|
5792
|
-
|
|
6374
|
+
useEffect23(() => {
|
|
5793
6375
|
if (!resource) {
|
|
5794
6376
|
return;
|
|
5795
6377
|
}
|
|
@@ -5812,23 +6394,410 @@ function VideoHotspot({
|
|
|
5812
6394
|
};
|
|
5813
6395
|
}, [playing, resource]);
|
|
5814
6396
|
const activeTexture = resource?.ready ? resource.texture : posterTexture;
|
|
5815
|
-
return /* @__PURE__ */
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
6397
|
+
return /* @__PURE__ */ jsx19(HotspotAnchor, { ...anchorProps, id, height, width, children: /* @__PURE__ */ jsx19(HotspotPlane, { map: activeTexture, opacity }) });
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6400
|
+
// src/hotspot/text-hotspot.tsx
|
|
6401
|
+
import { useEffect as useEffect24, useRef as useRef21, useState as useState13 } from "react";
|
|
6402
|
+
import {
|
|
6403
|
+
CanvasTexture as CanvasTexture2,
|
|
6404
|
+
ClampToEdgeWrapping as ClampToEdgeWrapping7,
|
|
6405
|
+
LinearFilter as LinearFilter9,
|
|
6406
|
+
LinearMipmapLinearFilter as LinearMipmapLinearFilter6,
|
|
6407
|
+
SRGBColorSpace as SRGBColorSpace8
|
|
6408
|
+
} from "three";
|
|
6409
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
6410
|
+
var TEXT_TEXTURE_LONG_SIDE = 1024;
|
|
6411
|
+
var DEFAULT_FONT_FAMILY = "system-ui, sans-serif";
|
|
6412
|
+
var DEFAULT_COLOR = "#f8fafc";
|
|
6413
|
+
var DEFAULT_BACKGROUND = "#111827";
|
|
6414
|
+
function clamp01(value, fallback) {
|
|
6415
|
+
if (!Number.isFinite(value)) {
|
|
6416
|
+
return fallback;
|
|
6417
|
+
}
|
|
6418
|
+
return Math.max(0, Math.min(value, 1));
|
|
6419
|
+
}
|
|
6420
|
+
function resolvePositive(value, fallback) {
|
|
6421
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
6422
|
+
}
|
|
6423
|
+
function resolveCornerRadius2(value) {
|
|
6424
|
+
return Number.isFinite(value) ? Math.max(0, Math.min(value, 0.5)) : 0.08;
|
|
6425
|
+
}
|
|
6426
|
+
function textureSize(width, height) {
|
|
6427
|
+
const safeWidth = resolvePositive(width, 1);
|
|
6428
|
+
const safeHeight = resolvePositive(height, 1);
|
|
6429
|
+
if (safeWidth >= safeHeight) {
|
|
6430
|
+
return {
|
|
6431
|
+
canvasWidth: TEXT_TEXTURE_LONG_SIDE,
|
|
6432
|
+
canvasHeight: Math.max(
|
|
6433
|
+
1,
|
|
6434
|
+
Math.round(TEXT_TEXTURE_LONG_SIDE * (safeHeight / safeWidth))
|
|
6435
|
+
)
|
|
6436
|
+
};
|
|
6437
|
+
}
|
|
6438
|
+
return {
|
|
6439
|
+
canvasWidth: Math.max(
|
|
6440
|
+
1,
|
|
6441
|
+
Math.round(TEXT_TEXTURE_LONG_SIDE * (safeWidth / safeHeight))
|
|
6442
|
+
),
|
|
6443
|
+
canvasHeight: TEXT_TEXTURE_LONG_SIDE
|
|
6444
|
+
};
|
|
6445
|
+
}
|
|
6446
|
+
function roundedRectanglePath2(context, x, y, width, height, radius) {
|
|
6447
|
+
const safeRadius = Math.max(
|
|
6448
|
+
0,
|
|
6449
|
+
Math.min(radius, width / 2, height / 2)
|
|
6450
|
+
);
|
|
6451
|
+
context.beginPath();
|
|
6452
|
+
if (safeRadius <= 0) {
|
|
6453
|
+
context.rect(x, y, width, height);
|
|
6454
|
+
return;
|
|
6455
|
+
}
|
|
6456
|
+
context.moveTo(x + safeRadius, y);
|
|
6457
|
+
context.lineTo(x + width - safeRadius, y);
|
|
6458
|
+
context.quadraticCurveTo(x + width, y, x + width, y + safeRadius);
|
|
6459
|
+
context.lineTo(x + width, y + height - safeRadius);
|
|
6460
|
+
context.quadraticCurveTo(
|
|
6461
|
+
x + width,
|
|
6462
|
+
y + height,
|
|
6463
|
+
x + width - safeRadius,
|
|
6464
|
+
y + height
|
|
6465
|
+
);
|
|
6466
|
+
context.lineTo(x + safeRadius, y + height);
|
|
6467
|
+
context.quadraticCurveTo(x, y + height, x, y + height - safeRadius);
|
|
6468
|
+
context.lineTo(x, y + safeRadius);
|
|
6469
|
+
context.quadraticCurveTo(x, y, x + safeRadius, y);
|
|
6470
|
+
context.closePath();
|
|
6471
|
+
}
|
|
6472
|
+
function wrapLine(context, line, maxWidth) {
|
|
6473
|
+
if (maxWidth <= 0 || context.measureText(line).width <= maxWidth) {
|
|
6474
|
+
return [line];
|
|
6475
|
+
}
|
|
6476
|
+
const lines = [];
|
|
6477
|
+
let current = "";
|
|
6478
|
+
for (const word of line.split(" ")) {
|
|
6479
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
6480
|
+
if (context.measureText(candidate).width <= maxWidth) {
|
|
6481
|
+
current = candidate;
|
|
6482
|
+
continue;
|
|
6483
|
+
}
|
|
6484
|
+
if (current) {
|
|
6485
|
+
lines.push(current);
|
|
6486
|
+
}
|
|
6487
|
+
if (context.measureText(word).width <= maxWidth) {
|
|
6488
|
+
current = word;
|
|
6489
|
+
continue;
|
|
6490
|
+
}
|
|
6491
|
+
let chunk = "";
|
|
6492
|
+
for (const character of word) {
|
|
6493
|
+
const nextChunk = chunk + character;
|
|
6494
|
+
if (context.measureText(nextChunk).width <= maxWidth || chunk === "") {
|
|
6495
|
+
chunk = nextChunk;
|
|
6496
|
+
} else {
|
|
6497
|
+
lines.push(chunk);
|
|
6498
|
+
chunk = character;
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
current = chunk;
|
|
6502
|
+
}
|
|
6503
|
+
if (current) {
|
|
6504
|
+
lines.push(current);
|
|
6505
|
+
}
|
|
6506
|
+
return lines.length > 0 ? lines : [""];
|
|
6507
|
+
}
|
|
6508
|
+
function wrapText(context, text, maxWidth, nowrap) {
|
|
6509
|
+
const paragraphs = text.split("\n");
|
|
6510
|
+
if (nowrap) {
|
|
6511
|
+
return paragraphs;
|
|
6512
|
+
}
|
|
6513
|
+
return paragraphs.flatMap((paragraph) => wrapLine(context, paragraph, maxWidth));
|
|
6514
|
+
}
|
|
6515
|
+
function drawTextHotspot(context, canvasWidth, canvasHeight, text, style) {
|
|
6516
|
+
const shortSide = Math.min(canvasWidth, canvasHeight);
|
|
6517
|
+
const padding = clamp01(style.padding, 0.08) * shortSide;
|
|
6518
|
+
const fontSize = Math.max(1, clamp01(style.fontSize, 0.18) * canvasHeight);
|
|
6519
|
+
const lineHeight = resolvePositive(style.lineHeight, 1.25) * fontSize;
|
|
6520
|
+
const align = style.align ?? "center";
|
|
6521
|
+
const verticalAlign = style.verticalAlign ?? "middle";
|
|
6522
|
+
const fontFamily = style.fontFamily?.trim() || DEFAULT_FONT_FAMILY;
|
|
6523
|
+
const fontWeight = style.fontWeight ?? 600;
|
|
6524
|
+
const fontStyle = style.fontStyle ?? "normal";
|
|
6525
|
+
const contentWidth = Math.max(1, canvasWidth - padding * 2);
|
|
6526
|
+
const contentHeight = Math.max(1, canvasHeight - padding * 2);
|
|
6527
|
+
context.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
6528
|
+
const background = style.background ?? DEFAULT_BACKGROUND;
|
|
6529
|
+
if (background !== "") {
|
|
6530
|
+
roundedRectanglePath2(
|
|
6531
|
+
context,
|
|
6532
|
+
0,
|
|
6533
|
+
0,
|
|
6534
|
+
canvasWidth,
|
|
6535
|
+
canvasHeight,
|
|
6536
|
+
resolveCornerRadius2(style.borderRadius) * shortSide
|
|
6537
|
+
);
|
|
6538
|
+
context.save();
|
|
6539
|
+
context.globalAlpha = clamp01(style.backgroundOpacity, 0.72);
|
|
6540
|
+
context.fillStyle = background;
|
|
6541
|
+
context.fill();
|
|
6542
|
+
context.restore();
|
|
6543
|
+
}
|
|
6544
|
+
context.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
6545
|
+
context.textAlign = align;
|
|
6546
|
+
context.textBaseline = "top";
|
|
6547
|
+
const lines = wrapText(
|
|
6548
|
+
context,
|
|
6549
|
+
text,
|
|
6550
|
+
contentWidth,
|
|
6551
|
+
(style.whiteSpace ?? "normal") === "nowrap"
|
|
6552
|
+
);
|
|
6553
|
+
const maxLines = Math.max(1, Math.floor(contentHeight / lineHeight));
|
|
6554
|
+
const visibleLines = lines.slice(0, maxLines);
|
|
6555
|
+
const blockHeight = visibleLines.length * lineHeight;
|
|
6556
|
+
let startY = padding;
|
|
6557
|
+
if (verticalAlign === "middle") {
|
|
6558
|
+
startY = padding + Math.max(0, (contentHeight - blockHeight) / 2);
|
|
6559
|
+
} else if (verticalAlign === "bottom") {
|
|
6560
|
+
startY = padding + Math.max(0, contentHeight - blockHeight);
|
|
6561
|
+
}
|
|
6562
|
+
const startX = align === "left" ? padding : align === "right" ? canvasWidth - padding : canvasWidth / 2;
|
|
6563
|
+
const color = style.color?.trim() || DEFAULT_COLOR;
|
|
6564
|
+
const strokeWidth = Number.isFinite(style.strokeWidth) ? Math.max(style.strokeWidth, 0) : 0;
|
|
6565
|
+
context.save();
|
|
6566
|
+
context.beginPath();
|
|
6567
|
+
context.rect(padding, padding, contentWidth, contentHeight);
|
|
6568
|
+
context.clip();
|
|
6569
|
+
visibleLines.forEach((line, index) => {
|
|
6570
|
+
const y = startY + index * lineHeight;
|
|
6571
|
+
if (style.stroke && strokeWidth > 0) {
|
|
6572
|
+
context.lineWidth = strokeWidth;
|
|
6573
|
+
context.strokeStyle = style.stroke;
|
|
6574
|
+
context.lineJoin = "round";
|
|
6575
|
+
context.miterLimit = 2;
|
|
6576
|
+
context.strokeText(line, startX, y);
|
|
6577
|
+
}
|
|
6578
|
+
context.fillStyle = color;
|
|
6579
|
+
context.fillText(line, startX, y);
|
|
6580
|
+
});
|
|
6581
|
+
context.restore();
|
|
6582
|
+
}
|
|
6583
|
+
function createTextTexture(text, width, height, style) {
|
|
6584
|
+
const { canvasWidth, canvasHeight } = textureSize(width, height);
|
|
6585
|
+
const canvas = document.createElement("canvas");
|
|
6586
|
+
canvas.width = canvasWidth;
|
|
6587
|
+
canvas.height = canvasHeight;
|
|
6588
|
+
const context = canvas.getContext("2d");
|
|
6589
|
+
if (!context) {
|
|
6590
|
+
throw new Error("Canvas 2D is unavailable for this text hotspot.");
|
|
6591
|
+
}
|
|
6592
|
+
drawTextHotspot(context, canvasWidth, canvasHeight, text, style);
|
|
6593
|
+
const texture = new CanvasTexture2(canvas);
|
|
6594
|
+
texture.colorSpace = SRGBColorSpace8;
|
|
6595
|
+
texture.wrapS = ClampToEdgeWrapping7;
|
|
6596
|
+
texture.wrapT = ClampToEdgeWrapping7;
|
|
6597
|
+
texture.magFilter = LinearFilter9;
|
|
6598
|
+
texture.minFilter = LinearMipmapLinearFilter6;
|
|
6599
|
+
texture.generateMipmaps = true;
|
|
6600
|
+
texture.needsUpdate = true;
|
|
6601
|
+
return texture;
|
|
6602
|
+
}
|
|
6603
|
+
function useTextTexture(text, width, height, style, onLoad, onError) {
|
|
6604
|
+
const [texture, setTexture] = useState13(null);
|
|
6605
|
+
const onLoadRef = useRef21(onLoad);
|
|
6606
|
+
const onErrorRef = useRef21(onError);
|
|
6607
|
+
onLoadRef.current = onLoad;
|
|
6608
|
+
onErrorRef.current = onError;
|
|
6609
|
+
useEffect24(() => {
|
|
6610
|
+
let nextTexture = null;
|
|
6611
|
+
try {
|
|
6612
|
+
nextTexture = createTextTexture(text, width, height, style);
|
|
6613
|
+
setTexture(nextTexture);
|
|
6614
|
+
onLoadRef.current?.(nextTexture);
|
|
6615
|
+
} catch (error) {
|
|
6616
|
+
setTexture(null);
|
|
6617
|
+
onErrorRef.current?.(error);
|
|
6618
|
+
}
|
|
6619
|
+
return () => {
|
|
6620
|
+
nextTexture?.dispose();
|
|
6621
|
+
};
|
|
6622
|
+
}, [
|
|
6623
|
+
height,
|
|
6624
|
+
style.align,
|
|
6625
|
+
style.background,
|
|
6626
|
+
style.backgroundOpacity,
|
|
6627
|
+
style.borderRadius,
|
|
6628
|
+
style.color,
|
|
6629
|
+
style.fontFamily,
|
|
6630
|
+
style.fontSize,
|
|
6631
|
+
style.fontStyle,
|
|
6632
|
+
style.fontWeight,
|
|
6633
|
+
style.lineHeight,
|
|
6634
|
+
style.padding,
|
|
6635
|
+
style.stroke,
|
|
6636
|
+
style.strokeWidth,
|
|
6637
|
+
style.verticalAlign,
|
|
6638
|
+
style.whiteSpace,
|
|
6639
|
+
text,
|
|
6640
|
+
width
|
|
6641
|
+
]);
|
|
6642
|
+
return texture;
|
|
6643
|
+
}
|
|
6644
|
+
function TextHotspot({
|
|
6645
|
+
text,
|
|
6646
|
+
width = 16,
|
|
6647
|
+
height = 6,
|
|
6648
|
+
opacity,
|
|
6649
|
+
fontFamily,
|
|
6650
|
+
fontSize,
|
|
6651
|
+
fontWeight,
|
|
6652
|
+
fontStyle,
|
|
6653
|
+
color,
|
|
6654
|
+
background,
|
|
6655
|
+
backgroundOpacity,
|
|
6656
|
+
padding,
|
|
6657
|
+
align,
|
|
6658
|
+
verticalAlign,
|
|
6659
|
+
lineHeight,
|
|
6660
|
+
whiteSpace,
|
|
6661
|
+
borderRadius,
|
|
6662
|
+
stroke,
|
|
6663
|
+
strokeWidth,
|
|
6664
|
+
onLoad,
|
|
6665
|
+
onError,
|
|
6666
|
+
...anchorProps
|
|
6667
|
+
}) {
|
|
6668
|
+
const style = {
|
|
6669
|
+
align,
|
|
6670
|
+
background,
|
|
6671
|
+
backgroundOpacity,
|
|
6672
|
+
borderRadius,
|
|
6673
|
+
color,
|
|
6674
|
+
fontFamily,
|
|
6675
|
+
fontSize,
|
|
6676
|
+
fontStyle,
|
|
6677
|
+
fontWeight,
|
|
6678
|
+
lineHeight,
|
|
6679
|
+
padding,
|
|
6680
|
+
stroke,
|
|
6681
|
+
strokeWidth,
|
|
6682
|
+
verticalAlign,
|
|
6683
|
+
whiteSpace
|
|
6684
|
+
};
|
|
6685
|
+
const texture = useTextTexture(text, width, height, style, onLoad, onError);
|
|
6686
|
+
return /* @__PURE__ */ jsx20(HotspotAnchor, { ...anchorProps, height, width, children: /* @__PURE__ */ jsx20(HotspotPlane, { map: texture, opacity }) });
|
|
6687
|
+
}
|
|
6688
|
+
|
|
6689
|
+
// src/hotspot/iframe-hotspot.tsx
|
|
6690
|
+
import { Html as Html2 } from "@react-three/drei";
|
|
6691
|
+
import { DoubleSide as DoubleSide4 } from "three";
|
|
6692
|
+
import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
6693
|
+
var IFRAME_LONG_SIDE = 800;
|
|
6694
|
+
var CSS3D_DISTANCE_FACTOR = 400;
|
|
6695
|
+
var DEFAULT_SANDBOX = "allow-scripts allow-popups allow-forms";
|
|
6696
|
+
function clampOpacity2(opacity) {
|
|
6697
|
+
if (!Number.isFinite(opacity)) {
|
|
6698
|
+
return 1;
|
|
6699
|
+
}
|
|
6700
|
+
return Math.max(0, Math.min(opacity, 1));
|
|
6701
|
+
}
|
|
6702
|
+
function resolvePositive2(value, fallback) {
|
|
6703
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
6704
|
+
}
|
|
6705
|
+
function iframeCssSize(width, height) {
|
|
6706
|
+
const safeWidth = resolvePositive2(width, 1);
|
|
6707
|
+
const safeHeight = resolvePositive2(height, 1);
|
|
6708
|
+
if (safeWidth >= safeHeight) {
|
|
6709
|
+
return {
|
|
6710
|
+
cssWidth: IFRAME_LONG_SIDE,
|
|
6711
|
+
cssHeight: Math.max(
|
|
6712
|
+
1,
|
|
6713
|
+
Math.round(IFRAME_LONG_SIDE * (safeHeight / safeWidth))
|
|
6714
|
+
)
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6717
|
+
return {
|
|
6718
|
+
cssWidth: Math.max(
|
|
6719
|
+
1,
|
|
6720
|
+
Math.round(IFRAME_LONG_SIDE * (safeWidth / safeHeight))
|
|
6721
|
+
),
|
|
6722
|
+
cssHeight: IFRAME_LONG_SIDE
|
|
6723
|
+
};
|
|
6724
|
+
}
|
|
6725
|
+
function IframeHotspot({
|
|
6726
|
+
src,
|
|
6727
|
+
title,
|
|
6728
|
+
sandbox = DEFAULT_SANDBOX,
|
|
6729
|
+
allow,
|
|
6730
|
+
referrerPolicy = "strict-origin-when-cross-origin",
|
|
6731
|
+
loading,
|
|
6732
|
+
pointerPolicy = "hotspot",
|
|
6733
|
+
background,
|
|
6734
|
+
opacity,
|
|
6735
|
+
width = 18,
|
|
6736
|
+
height = 12,
|
|
6737
|
+
onLoad,
|
|
6738
|
+
onError,
|
|
6739
|
+
...anchorProps
|
|
6740
|
+
}) {
|
|
6741
|
+
const { cssWidth, cssHeight } = iframeCssSize(width, height);
|
|
6742
|
+
const iframeTitle = title ?? anchorProps.ariaLabel;
|
|
6743
|
+
return /* @__PURE__ */ jsxs11(HotspotAnchor, { ...anchorProps, height, width, children: [
|
|
6744
|
+
/* @__PURE__ */ jsxs11("mesh", { children: [
|
|
6745
|
+
/* @__PURE__ */ jsx21("planeGeometry", { args: [1, 1] }),
|
|
6746
|
+
/* @__PURE__ */ jsx21(
|
|
6747
|
+
"meshBasicMaterial",
|
|
6748
|
+
{
|
|
6749
|
+
depthWrite: false,
|
|
6750
|
+
opacity: 0,
|
|
6751
|
+
side: DoubleSide4,
|
|
6752
|
+
toneMapped: false,
|
|
6753
|
+
transparent: true
|
|
6754
|
+
}
|
|
6755
|
+
)
|
|
6756
|
+
] }),
|
|
6757
|
+
/* @__PURE__ */ jsx21(
|
|
6758
|
+
Html2,
|
|
5819
6759
|
{
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
6760
|
+
distanceFactor: CSS3D_DISTANCE_FACTOR,
|
|
6761
|
+
pointerEvents: pointerPolicy === "content" ? "auto" : "none",
|
|
6762
|
+
scale: [1 / cssWidth, 1 / cssHeight, 1],
|
|
6763
|
+
style: {
|
|
6764
|
+
background: background ?? "transparent",
|
|
6765
|
+
height: cssHeight,
|
|
6766
|
+
opacity: clampOpacity2(opacity),
|
|
6767
|
+
overflow: "hidden",
|
|
6768
|
+
width: cssWidth
|
|
6769
|
+
},
|
|
6770
|
+
transform: true,
|
|
6771
|
+
zIndexRange: [10, 0],
|
|
6772
|
+
children: /* @__PURE__ */ jsx21(
|
|
6773
|
+
"iframe",
|
|
6774
|
+
{
|
|
6775
|
+
allow,
|
|
6776
|
+
loading,
|
|
6777
|
+
onError: (event) => onError?.(event.nativeEvent),
|
|
6778
|
+
onLoad,
|
|
6779
|
+
referrerPolicy,
|
|
6780
|
+
sandbox,
|
|
6781
|
+
src,
|
|
6782
|
+
style: {
|
|
6783
|
+
border: 0,
|
|
6784
|
+
display: "block",
|
|
6785
|
+
height: "100%",
|
|
6786
|
+
width: "100%"
|
|
6787
|
+
},
|
|
6788
|
+
title: iframeTitle
|
|
6789
|
+
}
|
|
6790
|
+
)
|
|
5825
6791
|
}
|
|
5826
6792
|
)
|
|
5827
|
-
] })
|
|
6793
|
+
] });
|
|
5828
6794
|
}
|
|
5829
6795
|
export {
|
|
5830
6796
|
AutoRotate,
|
|
6797
|
+
DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
|
|
6798
|
+
DEFAULT_PANO_CURSORS,
|
|
5831
6799
|
GraphicHotspot,
|
|
6800
|
+
IframeHotspot,
|
|
5832
6801
|
ImageHotspot,
|
|
5833
6802
|
KeyboardControls,
|
|
5834
6803
|
MAX_HOTSPOT_PITCH,
|
|
@@ -5836,12 +6805,13 @@ export {
|
|
|
5836
6805
|
MouseControls,
|
|
5837
6806
|
PanoContextMenu,
|
|
5838
6807
|
PanoEvents,
|
|
5839
|
-
|
|
5840
|
-
PanoramaScenes,
|
|
6808
|
+
PanoViewer,
|
|
5841
6809
|
PolygonHotspot,
|
|
5842
6810
|
PolylineHotspot,
|
|
6811
|
+
Scenes,
|
|
5843
6812
|
SequenceHotspot,
|
|
5844
6813
|
Sphere,
|
|
6814
|
+
TextHotspot,
|
|
5845
6815
|
Tile,
|
|
5846
6816
|
TouchControls,
|
|
5847
6817
|
VideoHotspot,
|