@ericchen1990/pano-view 3.6.3 → 3.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3981,7 +3981,10 @@ function MouseControls({
3981
3981
  const element = gl.domElement;
3982
3982
  const drag = createPointerDrag(camera, element, () => controlsRef.current, "mouse");
3983
3983
  const allowedButtons = resolveButtons(buttons);
3984
+ const pending = /* @__PURE__ */ new Set();
3985
+ let disposed = false;
3984
3986
  const clearPointers = () => {
3987
+ pending.clear();
3985
3988
  for (const pointerId of pointersRef.current.keys()) {
3986
3989
  if (element.hasPointerCapture?.(pointerId)) {
3987
3990
  element.releasePointerCapture?.(pointerId);
@@ -4007,6 +4010,25 @@ function MouseControls({
4007
4010
  { cancelled }
4008
4011
  );
4009
4012
  };
4013
+ const beginPointer = (event) => {
4014
+ if (disposed || controlsRef.current?.isInteractionLocked()) {
4015
+ return;
4016
+ }
4017
+ if (pointersRef.current.has(event.pointerId)) {
4018
+ return;
4019
+ }
4020
+ pointersRef.current.set(event.pointerId, {
4021
+ x: event.clientX,
4022
+ y: event.clientY,
4023
+ at: performance.now()
4024
+ });
4025
+ try {
4026
+ element.setPointerCapture?.(event.pointerId);
4027
+ } catch {
4028
+ }
4029
+ syncActive();
4030
+ drag.start({ x: event.clientX, y: event.clientY }, pointerAt(event));
4031
+ };
4010
4032
  const onPointerDown = (event) => {
4011
4033
  if (!isMouseLike(event)) {
4012
4034
  return;
@@ -4018,14 +4040,14 @@ function MouseControls({
4018
4040
  return;
4019
4041
  }
4020
4042
  event.preventDefault();
4021
- pointersRef.current.set(event.pointerId, {
4022
- x: event.clientX,
4023
- y: event.clientY,
4024
- at: performance.now()
4043
+ const pointerId = event.pointerId;
4044
+ pending.add(pointerId);
4045
+ queueMicrotask(() => {
4046
+ if (!pending.delete(pointerId)) {
4047
+ return;
4048
+ }
4049
+ beginPointer(event);
4025
4050
  });
4026
- element.setPointerCapture?.(event.pointerId);
4027
- syncActive();
4028
- drag.start({ x: event.clientX, y: event.clientY }, pointerAt(event));
4029
4051
  };
4030
4052
  const onPointerMove = (event) => {
4031
4053
  if (!isMouseLike(event)) {
@@ -4049,6 +4071,7 @@ function MouseControls({
4049
4071
  drag.move(next, previous, speed, optionsRef.current.invert, pointerAt(event));
4050
4072
  };
4051
4073
  const releasePointer = (event) => {
4074
+ pending.delete(event.pointerId);
4052
4075
  if (!pointersRef.current.has(event.pointerId)) {
4053
4076
  return;
4054
4077
  }
@@ -4090,6 +4113,7 @@ function MouseControls({
4090
4113
  element.addEventListener("lostpointercapture", releasePointer);
4091
4114
  element.addEventListener("wheel", onWheel, { passive: false });
4092
4115
  return () => {
4116
+ disposed = true;
4093
4117
  validateDragRef.current = void 0;
4094
4118
  element.removeEventListener("pointerdown", onPointerDown);
4095
4119
  element.removeEventListener("pointermove", onPointerMove);
@@ -5607,6 +5631,12 @@ function vector3ToPanoPosition(vector) {
5607
5631
  )
5608
5632
  });
5609
5633
  }
5634
+ function translateHotspotPosition(origin, from, to) {
5635
+ return normalizePanoPosition({
5636
+ yaw: origin.yaw + normalizePanoYaw(to.yaw - from.yaw),
5637
+ pitch: origin.pitch + (to.pitch - from.pitch)
5638
+ });
5639
+ }
5610
5640
 
5611
5641
  // src/panorama-radius.ts
5612
5642
  var DEFAULT_PANORAMA_RADIUS = 1e3;
@@ -5750,7 +5780,10 @@ function TouchControls({
5750
5780
  useEffect11(() => {
5751
5781
  const element = gl.domElement;
5752
5782
  const drag = createPointerDrag(camera, element, () => controlsRef.current, "touch");
5783
+ const pending = /* @__PURE__ */ new Set();
5784
+ let disposed = false;
5753
5785
  const clearPointers = () => {
5786
+ pending.clear();
5754
5787
  for (const pointerId of pointersRef.current.keys()) {
5755
5788
  if (element.hasPointerCapture?.(pointerId)) {
5756
5789
  element.releasePointerCapture?.(pointerId);
@@ -5776,20 +5809,22 @@ function TouchControls({
5776
5809
  { cancelled }
5777
5810
  );
5778
5811
  };
5779
- const onPointerDown = (event) => {
5780
- if (event.pointerType !== "touch") {
5812
+ const beginPointer = (event) => {
5813
+ if (disposed || controlsRef.current?.isInteractionLocked()) {
5781
5814
  return;
5782
5815
  }
5783
- if (controlsRef.current?.isInteractionLocked()) {
5816
+ if (pointersRef.current.has(event.pointerId)) {
5784
5817
  return;
5785
5818
  }
5786
- event.preventDefault();
5787
5819
  pointersRef.current.set(event.pointerId, {
5788
5820
  x: event.clientX,
5789
5821
  y: event.clientY,
5790
5822
  at: performance.now()
5791
5823
  });
5792
- element.setPointerCapture?.(event.pointerId);
5824
+ try {
5825
+ element.setPointerCapture?.(event.pointerId);
5826
+ } catch {
5827
+ }
5793
5828
  syncActive();
5794
5829
  drag.start({ x: event.clientX, y: event.clientY }, pointerAt2(event));
5795
5830
  if (pointersRef.current.size >= 2) {
@@ -5798,6 +5833,23 @@ function TouchControls({
5798
5833
  pinchDistanceRef.current = pointerDistance(first, second);
5799
5834
  }
5800
5835
  };
5836
+ const onPointerDown = (event) => {
5837
+ if (event.pointerType !== "touch") {
5838
+ return;
5839
+ }
5840
+ if (controlsRef.current?.isInteractionLocked()) {
5841
+ return;
5842
+ }
5843
+ event.preventDefault();
5844
+ const pointerId = event.pointerId;
5845
+ pending.add(pointerId);
5846
+ queueMicrotask(() => {
5847
+ if (!pending.delete(pointerId)) {
5848
+ return;
5849
+ }
5850
+ beginPointer(event);
5851
+ });
5852
+ };
5801
5853
  const onPointerMove = (event) => {
5802
5854
  if (event.pointerType !== "touch") {
5803
5855
  return;
@@ -5841,6 +5893,7 @@ function TouchControls({
5841
5893
  drag.move(next, previous, speed, optionsRef.current.invert, pointerAt2(event));
5842
5894
  };
5843
5895
  const releasePointer = (event) => {
5896
+ pending.delete(event.pointerId);
5844
5897
  if (!pointersRef.current.has(event.pointerId)) {
5845
5898
  return;
5846
5899
  }
@@ -5864,6 +5917,7 @@ function TouchControls({
5864
5917
  element.addEventListener("pointercancel", releasePointer);
5865
5918
  element.addEventListener("lostpointercapture", releasePointer);
5866
5919
  return () => {
5920
+ disposed = true;
5867
5921
  validateDragRef.current = void 0;
5868
5922
  element.removeEventListener("pointerdown", onPointerDown);
5869
5923
  element.removeEventListener("pointermove", onPointerMove);
@@ -11505,13 +11559,104 @@ import {
11505
11559
  EdgesGeometry,
11506
11560
  MathUtils as MathUtils8,
11507
11561
  Matrix4 as Matrix44,
11562
+ PerspectiveCamera as PerspectiveCamera8,
11508
11563
  Plane,
11509
11564
  PlaneGeometry as PlaneGeometry4,
11510
11565
  Quaternion as Quaternion3,
11511
11566
  Ray,
11567
+ Raycaster as Raycaster2,
11568
+ Vector2 as Vector24,
11512
11569
  Vector3 as Vector316
11513
11570
  } from "three";
11514
11571
 
11572
+ // src/hotspot/drag-session.ts
11573
+ var CLICK_SUPPRESS_MS = 1e3;
11574
+ function trackPointerDrag(source, element, pointerId, onMove, onEnd) {
11575
+ let stopped = false;
11576
+ const releaseCapture = () => {
11577
+ if (!element.hasPointerCapture?.(pointerId)) {
11578
+ return;
11579
+ }
11580
+ try {
11581
+ element.releasePointerCapture?.(pointerId);
11582
+ } catch {
11583
+ }
11584
+ };
11585
+ const stop = () => {
11586
+ if (stopped) {
11587
+ return;
11588
+ }
11589
+ stopped = true;
11590
+ source.removeEventListener("pointermove", onPointerMove, true);
11591
+ source.removeEventListener("pointerup", onPointerUp, true);
11592
+ source.removeEventListener("pointercancel", onPointerCancel, true);
11593
+ element.removeEventListener("lostpointercapture", onLostCapture);
11594
+ releaseCapture();
11595
+ };
11596
+ const finish = (event) => {
11597
+ if (stopped || event.pointerId !== pointerId) {
11598
+ return;
11599
+ }
11600
+ stop();
11601
+ onEnd(event);
11602
+ };
11603
+ const onPointerMove = (event) => {
11604
+ if (stopped || event.pointerId !== pointerId) {
11605
+ return;
11606
+ }
11607
+ try {
11608
+ event.preventDefault?.();
11609
+ } catch {
11610
+ }
11611
+ onMove(event);
11612
+ };
11613
+ const onPointerUp = (event) => {
11614
+ finish(event);
11615
+ };
11616
+ const onPointerCancel = (event) => {
11617
+ finish(event);
11618
+ };
11619
+ const onLostCapture = (event) => {
11620
+ if ((event.buttons ?? 0) > 0) {
11621
+ return;
11622
+ }
11623
+ finish(event);
11624
+ };
11625
+ try {
11626
+ element.setPointerCapture?.(pointerId);
11627
+ } catch {
11628
+ }
11629
+ source.addEventListener("pointermove", onPointerMove, true);
11630
+ source.addEventListener("pointerup", onPointerUp, true);
11631
+ source.addEventListener("pointercancel", onPointerCancel, true);
11632
+ element.addEventListener("lostpointercapture", onLostCapture);
11633
+ return stop;
11634
+ }
11635
+ var CAPTURE = { capture: true };
11636
+ function suppressFollowingClick(target) {
11637
+ let cleaned = false;
11638
+ const cleanup = () => {
11639
+ if (cleaned) {
11640
+ return;
11641
+ }
11642
+ cleaned = true;
11643
+ target.removeEventListener("click", swallowClick, CAPTURE);
11644
+ target.removeEventListener("pointerdown", cancel, CAPTURE);
11645
+ };
11646
+ const swallowClick = (event) => {
11647
+ event.preventDefault();
11648
+ event.stopPropagation();
11649
+ event.stopImmediatePropagation();
11650
+ cleanup();
11651
+ };
11652
+ const cancel = () => {
11653
+ cleanup();
11654
+ };
11655
+ target.addEventListener("click", swallowClick, CAPTURE);
11656
+ target.addEventListener("pointerdown", cancel, CAPTURE);
11657
+ setTimeout(cleanup, CLICK_SUPPRESS_MS);
11658
+ }
11659
+
11515
11660
  // src/hotspot/hotspot-tooltip.tsx
11516
11661
  import { useEffect as useEffect23, useMemo as useMemo18, useState as useState12 } from "react";
11517
11662
 
@@ -11780,6 +11925,21 @@ function fixedScaleFactor(cameraFov, referenceFov) {
11780
11925
  const safeReferenceFov = MathUtils6.clamp(referenceFov, 1, 179);
11781
11926
  return Math.tan(MathUtils6.degToRad(cameraFov) / 2) / Math.tan(MathUtils6.degToRad(safeReferenceFov) / 2);
11782
11927
  }
11928
+ function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegrees, viewportHeight, outsetPixels) {
11929
+ const pixels = Number.isFinite(outsetPixels) && outsetPixels > 0 ? outsetPixels : 0;
11930
+ const depth = Number.isFinite(cameraSpaceDepth) ? Math.max(cameraSpaceDepth, 1e-4) : 1e-4;
11931
+ const height = Number.isFinite(viewportHeight) ? Math.max(viewportHeight, 1) : 1;
11932
+ const fov = Number.isFinite(fovDegrees) ? MathUtils6.clamp(fovDegrees, 1, 179) : 75;
11933
+ const pixelsPerWorldUnit = height / (2 * Math.tan(MathUtils6.degToRad(fov) * 0.5) * depth);
11934
+ const axis = (worldScale) => {
11935
+ const pixelsPerLocalUnit = pixelsPerWorldUnit * Math.abs(worldScale);
11936
+ if (pixelsPerLocalUnit < 1e-4) {
11937
+ return 1;
11938
+ }
11939
+ return 1 + 2 * pixels / pixelsPerLocalUnit;
11940
+ };
11941
+ return [axis(worldScaleX), axis(worldScaleY)];
11942
+ }
11783
11943
 
11784
11944
  // src/hotspot/hotspot-stroke-line.tsx
11785
11945
  import { Line } from "@react-three/drei";
@@ -11866,6 +12026,11 @@ var DEFAULT_REFERENCE_FOV = 75;
11866
12026
  var MIN_ANGULAR_SIZE = 0.01;
11867
12027
  var MAX_ANGULAR_SIZE = 179;
11868
12028
  var DRAG_EPSILON_DEGREES = 1e-3;
12029
+ var SELECTION_BORDER_WIDTH = 3;
12030
+ var SELECTION_BORDER_GAP = 3;
12031
+ var SELECTION_BORDER_OUTSET = SELECTION_BORDER_GAP + SELECTION_BORDER_WIDTH / 2;
12032
+ var SELECTION_CAMERA_POSITION = new Vector316();
12033
+ var SELECTION_WORLD_SCALE = new Vector316();
11869
12034
  var SELECTION_BORDER_POINTS = [
11870
12035
  [-0.5, -0.5, 1e-3],
11871
12036
  [0.5, -0.5, 1e-3],
@@ -11953,6 +12118,34 @@ function angularDistance(a, b) {
11953
12118
  const bVector = panoPositionToVector3(b);
11954
12119
  return MathUtils8.radToDeg(aVector.angleTo(bVector));
11955
12120
  }
12121
+ var DRAG_POINTER = new Vector24();
12122
+ var DRAG_RAYCASTER = new Raycaster2();
12123
+ function aimDragRaycaster(event, element, camera) {
12124
+ const rect = element.getBoundingClientRect();
12125
+ if (!(rect.width > 0) || !(rect.height > 0)) {
12126
+ return false;
12127
+ }
12128
+ DRAG_POINTER.set(
12129
+ (event.clientX - rect.left) / rect.width * 2 - 1,
12130
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
12131
+ );
12132
+ camera.updateMatrixWorld();
12133
+ DRAG_RAYCASTER.setFromCamera(DRAG_POINTER, camera);
12134
+ return true;
12135
+ }
12136
+ function pointerToPanoPosition(event, element, camera) {
12137
+ if (!aimDragRaycaster(event, element, camera)) {
12138
+ return null;
12139
+ }
12140
+ return vector3ToPanoPosition(DRAG_RAYCASTER.ray.direction);
12141
+ }
12142
+ function pointerHitsHotspot(event, element, camera, group) {
12143
+ if (!group || !aimDragRaycaster(event, element, camera)) {
12144
+ return false;
12145
+ }
12146
+ group.updateMatrixWorld(true);
12147
+ return DRAG_RAYCASTER.intersectObject(group, true).length > 0;
12148
+ }
11956
12149
  function isTooltipOpen(trigger, hovered, focused, pinned) {
11957
12150
  switch (trigger) {
11958
12151
  case "always":
@@ -12148,6 +12341,24 @@ function updateTooltipAnchor(anchor, group, placement, useAngularScale, camera,
12148
12341
  }
12149
12342
  setTooltipAnchorOnLocalBox(anchor, placement, -0.5, 0.5, -0.5, 0.5);
12150
12343
  }
12344
+ function placeSelectionFrame(frame, group, camera, viewportHeight) {
12345
+ if (!frame) {
12346
+ return;
12347
+ }
12348
+ group.getWorldScale(SELECTION_WORLD_SCALE);
12349
+ SELECTION_CAMERA_POSITION.setFromMatrixPosition(group.matrixWorld).applyMatrix4(
12350
+ camera.matrixWorldInverse
12351
+ );
12352
+ const [scaleX, scaleY] = selectionFrameScale(
12353
+ SELECTION_WORLD_SCALE.x,
12354
+ SELECTION_WORLD_SCALE.y,
12355
+ -SELECTION_CAMERA_POSITION.z,
12356
+ camera instanceof PerspectiveCamera8 ? camera.fov : DEFAULT_REFERENCE_FOV,
12357
+ viewportHeight,
12358
+ SELECTION_BORDER_OUTSET
12359
+ );
12360
+ frame.scale.set(scaleX, scaleY, 1);
12361
+ }
12151
12362
  function HotspotAnchor({
12152
12363
  id,
12153
12364
  position,
@@ -12187,10 +12398,13 @@ function HotspotAnchor({
12187
12398
  const hotspotTargetRegistry = useContext21(HotspotTargetRegistryContext);
12188
12399
  const eventBus = useContext21(PanoEventBusContext);
12189
12400
  const cursorApi = usePanoCursor();
12190
- const { camera, size } = useThree19();
12401
+ const { camera, gl, size } = useThree19();
12191
12402
  const groupRef = useRef25(null);
12403
+ const selectionFrameRef = useRef25(null);
12192
12404
  const tooltipAnchorRef = useRef25(null);
12193
12405
  const dragStateRef = useRef25(null);
12406
+ const updateDragRef = useRef25(() => void 0);
12407
+ const endDragRef = useRef25(() => void 0);
12194
12408
  const suppressNextClickRef = useRef25(false);
12195
12409
  const [hovered, setHovered] = useState13(false);
12196
12410
  const [dragging, setDragging] = useState13(false);
@@ -12260,7 +12474,9 @@ function HotspotAnchor({
12260
12474
  const cursorClaimId = hotspotCursorClaimId(id);
12261
12475
  useEffect24(
12262
12476
  () => () => {
12263
- dragStateRef.current?.releaseInteractionLock();
12477
+ const dragState = dragStateRef.current;
12478
+ dragState?.stopTracking();
12479
+ dragState?.releaseInteractionLock();
12264
12480
  dragStateRef.current = null;
12265
12481
  },
12266
12482
  []
@@ -12317,6 +12533,7 @@ function HotspotAnchor({
12317
12533
  if (!dragState) {
12318
12534
  return;
12319
12535
  }
12536
+ dragState.stopTracking();
12320
12537
  dragState.releaseInteractionLock();
12321
12538
  dragStateRef.current = null;
12322
12539
  setDragging(false);
@@ -12339,6 +12556,7 @@ function HotspotAnchor({
12339
12556
  worldHeight,
12340
12557
  resolvedDistance
12341
12558
  );
12559
+ placeSelectionFrame(selectionFrameRef.current, group, camera, size.height);
12342
12560
  if (tooltipOpen && tooltipAnchorRef.current) {
12343
12561
  updateTooltipAnchor(
12344
12562
  tooltipAnchorRef.current,
@@ -12357,6 +12575,7 @@ function HotspotAnchor({
12357
12575
  rotation,
12358
12576
  scaleMode,
12359
12577
  size,
12578
+ selected,
12360
12579
  surfaceQuaternion3,
12361
12580
  tooltipOpen,
12362
12581
  tooltipPlacement,
@@ -12382,6 +12601,12 @@ function HotspotAnchor({
12382
12601
  worldHeight,
12383
12602
  resolvedDistance
12384
12603
  );
12604
+ placeSelectionFrame(
12605
+ selectionFrameRef.current,
12606
+ group,
12607
+ state.camera,
12608
+ state.size.height
12609
+ );
12385
12610
  if (tooltipOpen && tooltipAnchorRef.current) {
12386
12611
  updateTooltipAnchor(
12387
12612
  tooltipAnchorRef.current,
@@ -12397,31 +12622,78 @@ function HotspotAnchor({
12397
12622
  event.stopPropagation();
12398
12623
  event.nativeEvent.stopImmediatePropagation?.();
12399
12624
  };
12400
- const releasePointer = (event, emitDragEnd) => {
12625
+ const dragPosition = (dragState, event) => {
12626
+ const pointerPosition = pointerToPanoPosition(event, gl.domElement, camera);
12627
+ if (!pointerPosition) {
12628
+ return dragState.lastPosition;
12629
+ }
12630
+ return translateHotspotPosition(
12631
+ dragState.startPosition,
12632
+ dragState.pointerOrigin,
12633
+ pointerPosition
12634
+ );
12635
+ };
12636
+ updateDragRef.current = (nativeEvent) => {
12401
12637
  const dragState = dragStateRef.current;
12402
- if (!dragState || dragState.pointerId !== event.pointerId) {
12638
+ if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
12403
12639
  return;
12404
12640
  }
12405
- stopPointerEvent(event);
12406
- const target = event.target;
12407
- if (target.hasPointerCapture?.(event.pointerId)) {
12408
- target.releasePointerCapture?.(event.pointerId);
12409
- }
12410
- const nextPosition = vector3ToPanoPosition(event.ray.direction);
12411
- if (emitDragEnd) {
12412
- onDragEnd?.({
12413
- ...makeInteractionEvent(id, nextPosition, event),
12414
- source: "pointer",
12415
- startPosition: dragState.startPosition
12416
- });
12641
+ const nextPosition = dragPosition(dragState, nativeEvent);
12642
+ dragState.lastPosition = nextPosition;
12643
+ dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
12644
+ onPositionChange?.({
12645
+ id,
12646
+ position: nextPosition,
12647
+ source: "pointer",
12648
+ nativeEvent,
12649
+ startPosition: dragState.startPosition
12650
+ });
12651
+ };
12652
+ endDragRef.current = (nativeEvent, emitDragEnd) => {
12653
+ const dragState = dragStateRef.current;
12654
+ if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
12655
+ return;
12417
12656
  }
12418
- suppressNextClickRef.current = dragState.moved;
12657
+ const nextPosition = dragPosition(dragState, nativeEvent);
12658
+ dragState.lastPosition = nextPosition;
12659
+ dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
12660
+ const moved = dragState.moved;
12661
+ const startPosition = dragState.startPosition;
12662
+ dragState.stopTracking();
12419
12663
  dragState.releaseInteractionLock();
12420
12664
  dragStateRef.current = null;
12665
+ suppressNextClickRef.current = moved;
12421
12666
  setDragging(false);
12422
- if (emitDragEnd) {
12423
- setHovered(true);
12667
+ if (!emitDragEnd) {
12668
+ return;
12424
12669
  }
12670
+ const overHotspot = pointerHitsHotspot(
12671
+ nativeEvent,
12672
+ gl.domElement,
12673
+ camera,
12674
+ groupRef.current
12675
+ );
12676
+ eventHovered.current = overHotspot;
12677
+ setHovered(overHotspot);
12678
+ if (moved && typeof window !== "undefined") {
12679
+ suppressFollowingClick(window);
12680
+ }
12681
+ if (moved) {
12682
+ onPositionChange?.({
12683
+ id,
12684
+ position: nextPosition,
12685
+ source: "pointer",
12686
+ nativeEvent,
12687
+ startPosition
12688
+ });
12689
+ }
12690
+ onDragEnd?.({
12691
+ id,
12692
+ position: nextPosition,
12693
+ source: "pointer",
12694
+ nativeEvent,
12695
+ startPosition
12696
+ });
12425
12697
  };
12426
12698
  return /* @__PURE__ */ jsxs15(
12427
12699
  "group",
@@ -12441,23 +12713,42 @@ function HotspotAnchor({
12441
12713
  }
12442
12714
  suppressNextClickRef.current = false;
12443
12715
  } : void 0,
12444
- onLostPointerCapture: acceptsPointer ? (event) => releasePointer(event, false) : void 0,
12445
- onPointerCancel: acceptsPointer ? (event) => releasePointer(event, false) : void 0,
12716
+ onLostPointerCapture: acceptsPointer ? (event) => {
12717
+ endDragRef.current(event.nativeEvent, true);
12718
+ } : void 0,
12719
+ onPointerCancel: acceptsPointer ? (event) => {
12720
+ endDragRef.current(event.nativeEvent, true);
12721
+ } : void 0,
12446
12722
  onPointerDown: acceptsPointer ? (event) => {
12447
- stopPointerEvent(event);
12723
+ const native = event.nativeEvent;
12448
12724
  if (!draggable || dragStateRef.current) {
12725
+ stopPointerEvent(event);
12449
12726
  return;
12450
12727
  }
12728
+ const pointerId = native.pointerId;
12729
+ stopPointerEvent(event);
12451
12730
  const releaseInteractionLock = controlsRef?.current?.acquireInteractionLock() ?? (() => void 0);
12452
- const target = event.target;
12453
- target.setPointerCapture?.(event.pointerId);
12731
+ const pointerOrigin = pointerToPanoPosition(native, gl.domElement, camera) ?? vector3ToPanoPosition(event.ray.direction);
12454
12732
  const startPosition = { ...normalizedPosition };
12733
+ const tracksOutsideMesh = typeof window !== "undefined";
12734
+ const stopTracking = tracksOutsideMesh ? trackPointerDrag(
12735
+ window,
12736
+ gl.domElement,
12737
+ pointerId,
12738
+ (pointerEvent) => updateDragRef.current(pointerEvent),
12739
+ (pointerEvent) => endDragRef.current(pointerEvent, true)
12740
+ ) : () => void 0;
12455
12741
  dragStateRef.current = {
12456
- pointerId: event.pointerId,
12742
+ pointerId,
12457
12743
  startPosition,
12744
+ pointerOrigin,
12745
+ lastPosition: startPosition,
12458
12746
  releaseInteractionLock,
12459
- moved: false
12747
+ moved: false,
12748
+ tracksOutsideMesh,
12749
+ stopTracking
12460
12750
  };
12751
+ eventHovered.current = false;
12461
12752
  setHovered(false);
12462
12753
  setDragging(true);
12463
12754
  onDragStart?.({
@@ -12468,17 +12759,13 @@ function HotspotAnchor({
12468
12759
  } : void 0,
12469
12760
  onPointerMove: acceptsPointer ? (event) => {
12470
12761
  const dragState = dragStateRef.current;
12471
- if (!dragState || dragState.pointerId !== event.pointerId) {
12762
+ if (!dragState || dragState.pointerId !== event.nativeEvent.pointerId) {
12472
12763
  return;
12473
12764
  }
12474
12765
  stopPointerEvent(event);
12475
- const nextPosition = vector3ToPanoPosition(event.ray.direction);
12476
- dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
12477
- onPositionChange?.({
12478
- ...makeInteractionEvent(id, nextPosition, event),
12479
- source: "pointer",
12480
- startPosition: dragState.startPosition
12481
- });
12766
+ if (!dragState.tracksOutsideMesh) {
12767
+ updateDragRef.current(event.nativeEvent);
12768
+ }
12482
12769
  } : void 0,
12483
12770
  onPointerOut: acceptsPointer ? (event) => {
12484
12771
  if (!dragStateRef.current) {
@@ -12507,7 +12794,10 @@ function HotspotAnchor({
12507
12794
  );
12508
12795
  }
12509
12796
  } : void 0,
12510
- onPointerUp: acceptsPointer ? (event) => releasePointer(event, true) : void 0,
12797
+ onPointerUp: acceptsPointer ? (event) => {
12798
+ stopPointerEvent(event);
12799
+ endDragRef.current(event.nativeEvent, true);
12800
+ } : void 0,
12511
12801
  children: [
12512
12802
  focused ? focusContent ?? /* @__PURE__ */ jsx26(
12513
12803
  "lineSegments",
@@ -12520,16 +12810,16 @@ function HotspotAnchor({
12520
12810
  }
12521
12811
  ) : null,
12522
12812
  children,
12523
- selected && useAngularScale ? /* @__PURE__ */ jsx26(
12813
+ selected && useAngularScale ? /* @__PURE__ */ jsx26("group", { ref: selectionFrameRef, children: /* @__PURE__ */ jsx26(
12524
12814
  HotspotStrokeLine,
12525
12815
  {
12526
12816
  points: SELECTION_BORDER_POINTS,
12527
12817
  color: "#ffb300",
12528
- lineWidth: 3,
12818
+ lineWidth: SELECTION_BORDER_WIDTH,
12529
12819
  opacity: 1,
12530
12820
  renderOrder: renderOrder + 1
12531
12821
  }
12532
- ) : null,
12822
+ ) }) : null,
12533
12823
  tooltipOpen && tooltipContent ? /* @__PURE__ */ jsx26("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ jsx26(
12534
12824
  HotspotTooltip,
12535
12825
  {
@@ -12549,11 +12839,11 @@ import {
12549
12839
  DoubleSide as DoubleSide3,
12550
12840
  Mesh as Mesh4,
12551
12841
  MeshBasicMaterial,
12552
- Vector2 as Vector24
12842
+ Vector2 as Vector25
12553
12843
  } from "three";
12554
12844
  import { jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
12555
12845
  var HOTSPOT_ALPHA_CUTOFF = 0.05;
12556
- var SAMPLE_UV = new Vector24();
12846
+ var SAMPLE_UV = new Vector25();
12557
12847
  var RAYCAST_HITS = [];
12558
12848
  var IMAGE_DATA_CACHE = /* @__PURE__ */ new WeakMap();
12559
12849
  function clampOpacity(opacity) {
@@ -13816,7 +14106,7 @@ import {
13816
14106
  ClampToEdgeWrapping as ClampToEdgeWrapping8,
13817
14107
  LinearFilter as LinearFilter10,
13818
14108
  MathUtils as MathUtils10,
13819
- PerspectiveCamera as PerspectiveCamera8,
14109
+ PerspectiveCamera as PerspectiveCamera9,
13820
14110
  SRGBColorSpace as SRGBColorSpace9
13821
14111
  } from "three";
13822
14112
  import { jsx as jsx33 } from "react/jsx-runtime";
@@ -14070,11 +14360,15 @@ function createTextTexture(text, width, height, style, rasterLong) {
14070
14360
  if (!context) {
14071
14361
  throw new Error("Canvas 2D is unavailable for this text hotspot.");
14072
14362
  }
14073
- drawTextHotspot(context, canvasWidth, canvasHeight, text, {
14074
- ...style,
14075
- fontSize: resolvePositive(style.fontSize, DEFAULT_FONT_SIZE) * scale,
14076
- strokeWidth: Number.isFinite(style.strokeWidth) ? style.strokeWidth * scale : style.strokeWidth
14077
- });
14363
+ context.setTransform(
14364
+ canvasWidth / layout.canvasWidth,
14365
+ 0,
14366
+ 0,
14367
+ canvasHeight / layout.canvasHeight,
14368
+ 0,
14369
+ 0
14370
+ );
14371
+ drawTextHotspot(context, layout.canvasWidth, layout.canvasHeight, text, style);
14078
14372
  const texture = new CanvasTexture3(canvas);
14079
14373
  texture.colorSpace = SRGBColorSpace9;
14080
14374
  texture.wrapS = ClampToEdgeWrapping8;
@@ -14092,7 +14386,7 @@ function useRasterLongSide(width, height, scale, scaleMode, referenceFov) {
14092
14386
  const measure = () => {
14093
14387
  const pixelRatio = getPixelRatio();
14094
14388
  const viewportHeight = size.height * (Number.isFinite(pixelRatio) && pixelRatio > 0 ? pixelRatio : 1);
14095
- const cameraFov = camera instanceof PerspectiveCamera8 ? camera.fov : DEFAULT_REFERENCE_FOV2;
14389
+ const cameraFov = camera instanceof PerspectiveCamera9 ? camera.fov : DEFAULT_REFERENCE_FOV2;
14096
14390
  const screenLong = Math.max(
14097
14391
  projectedDevicePixels(
14098
14392
  width * resolveScale2(scale),
@@ -14134,13 +14428,7 @@ function useTextTexture(text, width, height, style, scale, scaleMode, referenceF
14134
14428
  let nextTexture = null;
14135
14429
  const run = async () => {
14136
14430
  try {
14137
- const layout = textureSize(width, height);
14138
- const layoutLong = Math.max(layout.canvasWidth, layout.canvasHeight);
14139
- const drawScale = rasterLong / layoutLong;
14140
- const { font } = resolveTextFont({
14141
- ...style,
14142
- fontSize: resolvePositive(style.fontSize, DEFAULT_FONT_SIZE) * drawScale
14143
- });
14431
+ const { font } = resolveTextFont(style);
14144
14432
  try {
14145
14433
  await document.fonts?.load(font);
14146
14434
  } catch {
@@ -14369,7 +14657,7 @@ import {
14369
14657
  Matrix4 as Matrix45,
14370
14658
  Quaternion as Quaternion4,
14371
14659
  ShapeUtils,
14372
- Vector2 as Vector25,
14660
+ Vector2 as Vector26,
14373
14661
  Vector3 as Vector318
14374
14662
  } from "three";
14375
14663
  import { jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
@@ -14407,7 +14695,7 @@ function intersects(a, b, c, d) {
14407
14695
  }
14408
14696
  function hasSelfIntersection(vertices) {
14409
14697
  const points = vertices.map(
14410
- (vertex) => new Vector25(vertex.unwrappedYaw, vertex.pitch)
14698
+ (vertex) => new Vector26(vertex.unwrappedYaw, vertex.pitch)
14411
14699
  );
14412
14700
  for (let index = 0; index < points.length; index += 1) {
14413
14701
  const nextIndex = (index + 1) % points.length;
@@ -14556,7 +14844,7 @@ function addSubdividedTriangle(positions, indices, a, b, c, worldToLocal, steps)
14556
14844
  }
14557
14845
  function makeFillGeometry(vertices, center) {
14558
14846
  const unwrapped = unwrapPolygonVertices(vertices);
14559
- const contour = unwrapped.map((vertex) => new Vector25(vertex.unwrappedYaw, vertex.pitch));
14847
+ const contour = unwrapped.map((vertex) => new Vector26(vertex.unwrappedYaw, vertex.pitch));
14560
14848
  const triangles = ShapeUtils.triangulateShape(contour, []);
14561
14849
  const positions = [];
14562
14850
  const indices = [];
@@ -16054,12 +16342,12 @@ import { useFrame as useFrame21, useThree as useThree26 } from "@react-three/fib
16054
16342
  import { useRef as useRef36 } from "react";
16055
16343
  import {
16056
16344
  DoubleSide as DoubleSide6,
16057
- Raycaster as Raycaster3,
16058
- Vector2 as Vector26,
16345
+ Raycaster as Raycaster4,
16346
+ Vector2 as Vector27,
16059
16347
  Vector3 as Vector321
16060
16348
  } from "three";
16061
16349
  import { jsx as jsx38, jsxs as jsxs19 } from "react/jsx-runtime";
16062
- var CENTER_NDC = new Vector26(0, 0);
16350
+ var CENTER_NDC = new Vector27(0, 0);
16063
16351
  var HUD_DISTANCE = 2;
16064
16352
  var RETICLE_SCALE = 1;
16065
16353
  var RETICLE_RENDER_ORDER = 1e5;
@@ -16145,7 +16433,7 @@ function WebVRReticle({
16145
16433
  const groupRef = useRef36(null);
16146
16434
  const ringMeshRef = useRef36(null);
16147
16435
  const directionRef = useRef36(new Vector321());
16148
- const raycasterRef = useRef36(new Raycaster3());
16436
+ const raycasterRef = useRef36(new Raycaster4());
16149
16437
  const hoveredRef = useRef36(null);
16150
16438
  const dwellRef = useRef36(0);
16151
16439
  const clickedRef = useRef36(false);
@@ -16315,12 +16603,12 @@ import { useContext as useContext26, useEffect as useEffect36, useMemo as useMem
16315
16603
  import {
16316
16604
  Mesh as Mesh6,
16317
16605
  OrthographicCamera,
16318
- PerspectiveCamera as PerspectiveCamera9,
16606
+ PerspectiveCamera as PerspectiveCamera10,
16319
16607
  PlaneGeometry as PlaneGeometry5,
16320
16608
  Scene,
16321
16609
  ShaderMaterial as ShaderMaterial3,
16322
16610
  StereoCamera as StereoCamera2,
16323
- Vector2 as Vector27,
16611
+ Vector2 as Vector28,
16324
16612
  Vector4 as Vector42,
16325
16613
  WebGLRenderTarget as WebGLRenderTarget2
16326
16614
  } from "three";
@@ -16358,7 +16646,7 @@ function createDistortionResources() {
16358
16646
  depthWrite: false,
16359
16647
  fragmentShader,
16360
16648
  uniforms: {
16361
- distortion: { value: new Vector27() },
16649
+ distortion: { value: new Vector28() },
16362
16650
  sourceTexture: { value: target.texture }
16363
16651
  },
16364
16652
  vertexShader
@@ -16406,7 +16694,7 @@ function WebVRStereoRenderer({
16406
16694
  const stereoRef = useRef37(stereo);
16407
16695
  stereoRef.current = stereo;
16408
16696
  const resources = useMemo25(createDistortionResources, []);
16409
- const drawingSizeRef = useRef37(new Vector27());
16697
+ const drawingSizeRef = useRef37(new Vector28());
16410
16698
  const viewportRef = useRef37(new Vector42());
16411
16699
  const scissorRef = useRef37(new Vector42());
16412
16700
  const originalFovRef = useRef37(null);
@@ -16426,7 +16714,7 @@ function WebVRStereoRenderer({
16426
16714
  useEffect36(() => {
16427
16715
  const originalRender = gl.render.bind(gl);
16428
16716
  gl.render = (sceneToRender, cameraToRender) => {
16429
- if (!stereoActiveRef.current || !(cameraToRender instanceof PerspectiveCamera9)) {
16717
+ if (!stereoActiveRef.current || !(cameraToRender instanceof PerspectiveCamera10)) {
16430
16718
  originalRender(sceneToRender, cameraToRender);
16431
16719
  return;
16432
16720
  }
@@ -16501,7 +16789,7 @@ function WebVRStereoRenderer({
16501
16789
  if (runtime) {
16502
16790
  runtime.stereoView.active = fallback;
16503
16791
  }
16504
- if (fallback && camera instanceof PerspectiveCamera9) {
16792
+ if (fallback && camera instanceof PerspectiveCamera10) {
16505
16793
  syncStereoCameras(
16506
16794
  stereoRef.current,
16507
16795
  camera,
@@ -16512,7 +16800,7 @@ function WebVRStereoRenderer({
16512
16800
  );
16513
16801
  return;
16514
16802
  }
16515
- if (originalFovRef.current !== null && camera instanceof PerspectiveCamera9) {
16803
+ if (originalFovRef.current !== null && camera instanceof PerspectiveCamera10) {
16516
16804
  camera.fov = originalFovRef.current;
16517
16805
  camera.updateProjectionMatrix();
16518
16806
  originalFovRef.current = null;