3dtiles-inspector 0.2.2 → 0.2.4

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.
@@ -45136,10 +45136,10 @@ function createGeoCameraController({
45136
45136
  const right = new Vector3();
45137
45137
  const backward = new Vector3();
45138
45138
  const quaternion = new Quaternion();
45139
- function isCenterModePosition(value) {
45139
+ function isCenterModePosition2(value) {
45140
45140
  return value.lengthSq() <= centerModeDistanceSq;
45141
45141
  }
45142
- function getLocalFrame(referencePoint) {
45142
+ function getLocalFrame2(referencePoint) {
45143
45143
  const ellipsoid = getActiveEllipsoid();
45144
45144
  ellipsoid.getPositionToCartographic(referencePoint, cartographicTarget);
45145
45145
  ellipsoid.getEastNorthUpFrame(
@@ -45186,7 +45186,7 @@ function createGeoCameraController({
45186
45186
  if (referencePoint.lengthSq() < centerModeDistanceSq || !getActiveEllipsoid()) {
45187
45187
  return target.identity();
45188
45188
  }
45189
- getLocalFrame(referencePoint);
45189
+ getLocalFrame2(referencePoint);
45190
45190
  basis.makeBasis(east, north, up);
45191
45191
  return target.setFromRotationMatrix(basis);
45192
45192
  }
@@ -45201,7 +45201,7 @@ function createGeoCameraController({
45201
45201
  );
45202
45202
  return Math.max(radius / Math.sin(limitingHalfFov), 1);
45203
45203
  }
45204
- function getCenterModeHeadingPitchRollForward(heading, pitch) {
45204
+ function getCenterModeHeadingPitchRollForward2(heading, pitch) {
45205
45205
  const cosPitch = Math.cos(pitch);
45206
45206
  forward.set(
45207
45207
  Math.sin(heading) * cosPitch,
@@ -45210,14 +45210,14 @@ function createGeoCameraController({
45210
45210
  );
45211
45211
  return forward.normalize();
45212
45212
  }
45213
- function getHeadingPitchRollForward(referencePoint, heading, pitch) {
45214
- if (isCenterModePosition(referencePoint)) {
45215
- return getCenterModeHeadingPitchRollForward(heading, pitch);
45213
+ function getHeadingPitchRollForward2(referencePoint, heading, pitch) {
45214
+ if (isCenterModePosition2(referencePoint)) {
45215
+ return getCenterModeHeadingPitchRollForward2(heading, pitch);
45216
45216
  }
45217
45217
  if (referencePoint.lengthSq() < 1e-6) {
45218
45218
  return forward.set(0, 0, -1);
45219
45219
  }
45220
- getLocalFrame(referencePoint);
45220
+ getLocalFrame2(referencePoint);
45221
45221
  const cosPitch = Math.cos(pitch);
45222
45222
  const sinPitch = Math.sin(pitch);
45223
45223
  const cosHeading = Math.cos(heading);
@@ -45225,8 +45225,8 @@ function createGeoCameraController({
45225
45225
  forward.copy(north).multiplyScalar(cosHeading * cosPitch).addScaledVector(east, sinHeading * cosPitch).addScaledVector(up, sinPitch).normalize();
45226
45226
  return forward;
45227
45227
  }
45228
- function getCenterModeHeadingPitchRollBasis(heading, pitch, roll) {
45229
- getCenterModeHeadingPitchRollForward(heading, pitch);
45228
+ function getCenterModeHeadingPitchRollBasis2(heading, pitch, roll) {
45229
+ getCenterModeHeadingPitchRollForward2(heading, pitch);
45230
45230
  right.copy(WORLD_RIGHT).multiplyScalar(Math.cos(heading)).addScaledVector(CENTER_NORTH, -Math.sin(heading)).normalize();
45231
45231
  up.crossVectors(right, forward).normalize();
45232
45232
  if (roll !== 0) {
@@ -45235,14 +45235,14 @@ function createGeoCameraController({
45235
45235
  }
45236
45236
  backward.copy(forward).negate();
45237
45237
  }
45238
- function getHeadingPitchRollQuaternion(referencePoint, heading, pitch, roll) {
45239
- if (isCenterModePosition(referencePoint)) {
45240
- getCenterModeHeadingPitchRollBasis(heading, pitch, roll);
45238
+ function getHeadingPitchRollQuaternion2(referencePoint, heading, pitch, roll) {
45239
+ if (isCenterModePosition2(referencePoint)) {
45240
+ getCenterModeHeadingPitchRollBasis2(heading, pitch, roll);
45241
45241
  } else if (referencePoint.lengthSq() < 1e-6) {
45242
45242
  quaternion.identity();
45243
45243
  return quaternion;
45244
45244
  } else {
45245
- getHeadingPitchRollForward(referencePoint, heading, pitch);
45245
+ getHeadingPitchRollForward2(referencePoint, heading, pitch);
45246
45246
  right.copy(east).multiplyScalar(Math.cos(heading)).addScaledVector(north, -Math.sin(heading)).normalize();
45247
45247
  up.crossVectors(right, forward).normalize();
45248
45248
  if (roll !== 0) {
@@ -45254,7 +45254,7 @@ function createGeoCameraController({
45254
45254
  basis.makeBasis(right, up, backward);
45255
45255
  return quaternion.setFromRotationMatrix(basis);
45256
45256
  }
45257
- function getBoundingSphereFlyToPosition(target, range, options) {
45257
+ function getBoundingSphereFlyToPosition2(target, range, options) {
45258
45258
  const { heading, pitch } = options;
45259
45259
  if (heading === void 0 && pitch === void 0) {
45260
45260
  const direction = target.lengthSq() > 1e-6 ? position.copy(target).normalize() : camera.position.lengthSq() > 1e-6 ? position.copy(camera.position).normalize() : position.set(0, -1, 0);
@@ -45262,15 +45262,15 @@ function createGeoCameraController({
45262
45262
  }
45263
45263
  const resolvedHeading = heading ?? 0;
45264
45264
  const resolvedPitch = pitch ?? -Math.PI / 2;
45265
- const centerForward = getCenterModeHeadingPitchRollForward(
45265
+ const centerForward = getCenterModeHeadingPitchRollForward2(
45266
45266
  resolvedHeading,
45267
45267
  resolvedPitch
45268
45268
  );
45269
45269
  const centerPosition = position.copy(target).addScaledVector(centerForward, -range);
45270
- if (isCenterModePosition(centerPosition)) {
45270
+ if (isCenterModePosition2(centerPosition)) {
45271
45271
  return centerPosition;
45272
45272
  }
45273
- const resolvedForward = getHeadingPitchRollForward(
45273
+ const resolvedForward = getHeadingPitchRollForward2(
45274
45274
  target,
45275
45275
  resolvedHeading,
45276
45276
  resolvedPitch
@@ -45291,13 +45291,13 @@ function createGeoCameraController({
45291
45291
  } else {
45292
45292
  offsetDistance = getCameraDistanceForBoundingSphere(safeRadius);
45293
45293
  }
45294
- const nextPosition = getBoundingSphereFlyToPosition(
45294
+ const nextPosition = getBoundingSphereFlyToPosition2(
45295
45295
  target,
45296
45296
  offsetDistance,
45297
45297
  options
45298
45298
  );
45299
- const nextQuaternion = getHeadingPitchRollQuaternion(
45300
- isCenterModePosition(nextPosition) ? nextPosition : target,
45299
+ const nextQuaternion = getHeadingPitchRollQuaternion2(
45300
+ isCenterModePosition2(nextPosition) ? nextPosition : target,
45301
45301
  options.heading ?? 0,
45302
45302
  options.pitch ?? -Math.PI / 2,
45303
45303
  options.roll ?? 0
@@ -65854,7 +65854,7 @@ var init_cameraController = __esm({
65854
65854
  const minScale = 0;
65855
65855
  metrics.distanceScale = baseScale;
65856
65856
  metrics.transitionWeight = 0;
65857
- if (!__privateGet(this, _ellipsoid)) {
65857
+ if (!__privateGet(this, _ellipsoid) || __privateMethod(this, _CameraController_instances, isCameraCenterMode_fn).call(this)) {
65858
65858
  return metrics;
65859
65859
  }
65860
65860
  const taperStartRadius = __privateGet(this, _ellipsoidMaxRadius) * 1.5;
@@ -65903,7 +65903,7 @@ var init_cameraController = __esm({
65903
65903
  const source = _vec4.copy(__privateGet(this, _camera3).position);
65904
65904
  let distanceScale = 1;
65905
65905
  let transitionWeight = 0;
65906
- if (zoomAmount < 0 && __privateGet(this, _ellipsoid)) {
65906
+ if (zoomAmount < 0 && __privateGet(this, _ellipsoid) && !__privateMethod(this, _CameraController_instances, isCameraCenterMode_fn).call(this)) {
65907
65907
  const metrics = __privateMethod(this, _CameraController_instances, getZoomOutMetrics_fn).call(this, source, hit.virtual ? null : hit);
65908
65908
  distanceScale = metrics.distanceScale;
65909
65909
  transitionWeight = metrics.transitionWeight;
@@ -66096,9 +66096,9 @@ function createViewerScene({
66096
66096
  60,
66097
66097
  window.innerWidth / window.innerHeight,
66098
66098
  1,
66099
- 2e7
66099
+ 12e6
66100
66100
  );
66101
- camera.position.set(0, 0, 175e5);
66101
+ camera.position.set(0, 0, 12e6);
66102
66102
  camera.updateMatrixWorld(true);
66103
66103
  const contentGroup = new Group();
66104
66104
  scene.add(contentGroup);
@@ -67650,6 +67650,505 @@ var init_setPositionController = __esm({
67650
67650
  }
67651
67651
  });
67652
67652
 
67653
+ // src/viewer/navigation/cameraFlyTo.js
67654
+ function eastNorthUpToFixedFrame(origin) {
67655
+ _geoUp.copy(origin).multiply(_oneOverRadiiSquared).normalize();
67656
+ _geoEast.set(0, 0, 1).cross(_geoUp).normalize();
67657
+ _geoNorth.copy(_geoUp).cross(_geoEast).normalize();
67658
+ return _matrix3.set(
67659
+ _geoEast.x,
67660
+ _geoNorth.x,
67661
+ _geoUp.x,
67662
+ origin.x,
67663
+ _geoEast.y,
67664
+ _geoNorth.y,
67665
+ _geoUp.y,
67666
+ origin.y,
67667
+ _geoEast.z,
67668
+ _geoNorth.z,
67669
+ _geoUp.z,
67670
+ origin.z,
67671
+ 0,
67672
+ 0,
67673
+ 0,
67674
+ 1
67675
+ );
67676
+ }
67677
+ function createCameraFlight(camera, position, target, options = {}) {
67678
+ const duration = options.duration ?? 2500;
67679
+ const endPosition = position.clone();
67680
+ const endQuaternion = getEndQuaternion(endPosition, target, options);
67681
+ const endPose = getUprightHeadingPitchAtPose(endPosition, endQuaternion);
67682
+ const endRoll = getRollAtPose(endPosition, endQuaternion);
67683
+ const endZoom = camera instanceof OrthographicCamera ? Math.max(options.endZoom ?? camera.zoom, 1e-6) : null;
67684
+ return buildFlightState(camera, {
67685
+ duration,
67686
+ endPosition,
67687
+ endQuaternion,
67688
+ endZoom,
67689
+ endHeading: endPose.heading,
67690
+ endPitch: endPose.pitch,
67691
+ endRoll
67692
+ });
67693
+ }
67694
+ function getFlyToParamsFromBoundingSphere(camera, target, radius, options = {}) {
67695
+ const safeRadius = Math.max(radius, 1);
67696
+ let offsetDistance = safeRadius;
67697
+ let endZoom = options.endZoom;
67698
+ if (camera instanceof PerspectiveCamera) {
67699
+ const verticalFov = MathUtils.degToRad(camera.fov);
67700
+ const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * camera.aspect);
67701
+ const minHalfFov = Math.max(0.1, Math.min(verticalFov, horizontalFov) / 2);
67702
+ offsetDistance = safeRadius / Math.sin(minHalfFov) + safeRadius * 0.75;
67703
+ } else if (camera instanceof OrthographicCamera) {
67704
+ const visibleHeight = Math.max(safeRadius * 2.8, 1);
67705
+ endZoom = (camera.top - camera.bottom) / visibleHeight;
67706
+ offsetDistance = Math.max(safeRadius * 2, visibleHeight * 0.5);
67707
+ }
67708
+ const position = getBoundingSphereFlyToPosition(
67709
+ camera,
67710
+ target,
67711
+ offsetDistance,
67712
+ options
67713
+ );
67714
+ return {
67715
+ position,
67716
+ target: target.clone(),
67717
+ options: {
67718
+ ...options,
67719
+ endZoom
67720
+ }
67721
+ };
67722
+ }
67723
+ function flyTo(camera, flight, time) {
67724
+ if (flight.startTime === null) {
67725
+ flight.startTime = time;
67726
+ }
67727
+ const rawT = MathUtils.clamp(
67728
+ (time - flight.startTime) / flight.duration,
67729
+ 0,
67730
+ 1
67731
+ );
67732
+ const easedT = rawT < 0.5 ? 4 * rawT * rawT * rawT : 1 - Math.pow(-2 * rawT + 2, 3) / 2;
67733
+ const position = getFlyToPosition(flight, easedT);
67734
+ const quaternion = flight.maintainUpright ? getUprightInterpolatedQuaternion(flight, position, easedT) : _flyQuaternion.slerpQuaternions(
67735
+ flight.startQuaternion,
67736
+ flight.endQuaternion,
67737
+ easedT
67738
+ );
67739
+ const zoom = flight.startZoom !== null && flight.endZoom !== null ? MathUtils.lerp(flight.startZoom, flight.endZoom, easedT) : null;
67740
+ applyFlyToPose(camera, position, quaternion, zoom);
67741
+ return rawT === 1;
67742
+ }
67743
+ function getFlyToPosition(flight, t2) {
67744
+ const { startPosition, endPosition, arcHeight } = flight;
67745
+ const startLength = startPosition.length();
67746
+ const endLength = endPosition.length();
67747
+ if (startLength < 1e-6 || endLength < 1e-6) {
67748
+ return _flyDirection.lerpVectors(startPosition, endPosition, t2);
67749
+ }
67750
+ _flyDirectionStart.copy(startPosition).divideScalar(startLength);
67751
+ _flyDirectionEnd.copy(endPosition).divideScalar(endLength);
67752
+ const angle = _flyDirectionStart.angleTo(_flyDirectionEnd);
67753
+ if (angle > 1e-5) {
67754
+ if (angle < Math.PI - 1e-5) {
67755
+ _flyAxis.crossVectors(_flyDirectionStart, _flyDirectionEnd).normalize();
67756
+ } else {
67757
+ _flyAxis.crossVectors(_flyDirectionStart, _flyWorldUp);
67758
+ if (_flyAxis.lengthSq() < 1e-6) {
67759
+ _flyAxis.crossVectors(_flyDirectionStart, _flyWorldNorth);
67760
+ }
67761
+ _flyAxis.normalize();
67762
+ }
67763
+ _flyDirection.copy(_flyDirectionStart).applyAxisAngle(_flyAxis, angle * t2).normalize();
67764
+ } else {
67765
+ _flyDirection.copy(_flyDirectionStart);
67766
+ }
67767
+ const radius = MathUtils.lerp(startLength, endLength, t2) + Math.sin(Math.PI * t2) * arcHeight;
67768
+ return _flyDirection.multiplyScalar(radius);
67769
+ }
67770
+ function getUprightInterpolatedQuaternion(flight, position, t2) {
67771
+ const heading = lerpAngle(flight.startHeading, flight.endHeading, t2);
67772
+ const pitch = MathUtils.lerp(flight.startPitch, flight.endPitch, t2);
67773
+ return getHeadingPitchRollQuaternion(position, heading, pitch, 0);
67774
+ }
67775
+ function buildFlightState(camera, {
67776
+ duration,
67777
+ endPosition,
67778
+ endQuaternion,
67779
+ endZoom,
67780
+ endHeading,
67781
+ endPitch,
67782
+ endRoll
67783
+ }) {
67784
+ const startPosition = camera.position.clone();
67785
+ const startQuaternion = camera.quaternion.clone();
67786
+ const startPose = getUprightHeadingPitchAtPose(
67787
+ startPosition,
67788
+ startQuaternion
67789
+ );
67790
+ const startRoll = getRollAtPose(startPosition, startQuaternion);
67791
+ const startZoom = camera instanceof OrthographicCamera ? camera.zoom : null;
67792
+ if (isImmediateFlight(
67793
+ duration,
67794
+ startPosition,
67795
+ endPosition,
67796
+ startQuaternion,
67797
+ endQuaternion,
67798
+ startZoom,
67799
+ endZoom
67800
+ )) {
67801
+ applyFlyToPose(camera, endPosition, endQuaternion, endZoom);
67802
+ return null;
67803
+ }
67804
+ return {
67805
+ startTime: null,
67806
+ duration,
67807
+ startPosition,
67808
+ endPosition,
67809
+ startQuaternion,
67810
+ endQuaternion,
67811
+ startZoom,
67812
+ endZoom,
67813
+ arcHeight: getArcHeight(startPosition, endPosition),
67814
+ maintainUpright: shouldMaintainUpright(startRoll, endRoll),
67815
+ startHeading: startPose.heading,
67816
+ endHeading,
67817
+ startPitch: startPose.pitch,
67818
+ endPitch
67819
+ };
67820
+ }
67821
+ function isImmediateFlight(duration, startPosition, endPosition, startQuaternion, endQuaternion, startZoom, endZoom) {
67822
+ return duration <= 0 || startPosition.distanceToSquared(endPosition) < 1e-6 && startQuaternion.angleTo(endQuaternion) < 1e-6 && (startZoom === null || endZoom === null || Math.abs(startZoom - endZoom) < 1e-6);
67823
+ }
67824
+ function getArcHeight(startPosition, endPosition) {
67825
+ const chordLength = startPosition.distanceTo(endPosition);
67826
+ return Math.max(
67827
+ chordLength * 0.35,
67828
+ Math.abs(endPosition.length() - startPosition.length()) * 0.5,
67829
+ 500
67830
+ );
67831
+ }
67832
+ function shouldMaintainUpright(startRoll, endRoll) {
67833
+ return Math.abs(startRoll) <= UPRIGHT_ROLL_THRESHOLD && Math.abs(endRoll) <= UPRIGHT_ROLL_THRESHOLD;
67834
+ }
67835
+ function getLookAtQuaternion(position, target) {
67836
+ if (isCenterModePosition(position)) {
67837
+ _matrix3.lookAt(position, target, _flyCenterUp);
67838
+ return new Quaternion().setFromRotationMatrix(_matrix3);
67839
+ }
67840
+ _flyUp.copy(target);
67841
+ if (_flyUp.lengthSq() < 1e-6) {
67842
+ _flyUp.copy(_flyWorldUp);
67843
+ } else {
67844
+ _flyUp.normalize();
67845
+ }
67846
+ _flyEast.crossVectors(_flyWorldNorth, _flyUp);
67847
+ if (_flyEast.lengthSq() < 1e-6) {
67848
+ _flyEast.crossVectors(_flyWorldUp, _flyUp);
67849
+ }
67850
+ if (_flyEast.lengthSq() < 1e-6) {
67851
+ _flyEast.set(1, 0, 0);
67852
+ }
67853
+ _flyEast.normalize();
67854
+ _flyUp.crossVectors(_flyUp, _flyEast).normalize();
67855
+ _matrix3.lookAt(position, target, _flyUp);
67856
+ return new Quaternion().setFromRotationMatrix(_matrix3);
67857
+ }
67858
+ function getForwardFromQuaternion(quaternion, target) {
67859
+ return target.set(0, 0, -1).applyQuaternion(quaternion).normalize();
67860
+ }
67861
+ function getUprightHeadingPitchAtPose(position, quaternion) {
67862
+ if (isCenterModePosition(position)) {
67863
+ const forward2 = getForwardFromQuaternion(quaternion, _flyForward);
67864
+ _flyCameraRight.set(1, 0, 0).applyQuaternion(quaternion);
67865
+ _flyReferenceRight.copy(_flyCameraRight).projectOnPlane(_flyCenterUp);
67866
+ let heading2 = 0;
67867
+ if (_flyReferenceRight.lengthSq() > HEADING_RIGHT_DEGENERATE_EPSILON) {
67868
+ _flyReferenceRight.normalize();
67869
+ heading2 = Math.atan2(
67870
+ -_flyReferenceRight.dot(_flyCenterNorth),
67871
+ _flyReferenceRight.dot(_flyWorldRight)
67872
+ );
67873
+ } else {
67874
+ const horizontalForward = _flyDirection.copy(forward2).projectOnPlane(_flyCenterUp);
67875
+ if (horizontalForward.lengthSq() > HEADING_RIGHT_DEGENERATE_EPSILON) {
67876
+ horizontalForward.normalize();
67877
+ heading2 = Math.atan2(
67878
+ horizontalForward.dot(_flyWorldRight),
67879
+ horizontalForward.dot(_flyCenterNorth)
67880
+ );
67881
+ }
67882
+ }
67883
+ return {
67884
+ heading: heading2,
67885
+ pitch: Math.asin(MathUtils.clamp(forward2.dot(_flyCenterUp), -1, 1))
67886
+ };
67887
+ }
67888
+ if (position.lengthSq() < 1e-6) {
67889
+ return {
67890
+ heading: 0,
67891
+ pitch: 0
67892
+ };
67893
+ }
67894
+ const forward = getForwardFromQuaternion(quaternion, _flyForward);
67895
+ getLocalFrame(position);
67896
+ _flyCameraRight.set(1, 0, 0).applyQuaternion(quaternion);
67897
+ _flyReferenceRight.copy(_flyCameraRight).projectOnPlane(_flyUp);
67898
+ let heading = 0;
67899
+ if (_flyReferenceRight.lengthSq() > HEADING_RIGHT_DEGENERATE_EPSILON) {
67900
+ _flyReferenceRight.normalize();
67901
+ heading = Math.atan2(
67902
+ -_flyReferenceRight.dot(_flyNorth),
67903
+ _flyReferenceRight.dot(_flyEast)
67904
+ );
67905
+ } else {
67906
+ const horizontalForward = _flyDirection.copy(forward).projectOnPlane(_flyUp);
67907
+ if (horizontalForward.lengthSq() > HEADING_RIGHT_DEGENERATE_EPSILON) {
67908
+ horizontalForward.normalize();
67909
+ heading = Math.atan2(
67910
+ horizontalForward.dot(_flyEast),
67911
+ horizontalForward.dot(_flyNorth)
67912
+ );
67913
+ }
67914
+ }
67915
+ return {
67916
+ heading,
67917
+ pitch: Math.asin(MathUtils.clamp(forward.dot(_flyUp), -1, 1))
67918
+ };
67919
+ }
67920
+ function getRollAtPose(position, quaternion) {
67921
+ const forward = getForwardFromQuaternion(quaternion, _flyForward);
67922
+ if (isCenterModePosition(position)) {
67923
+ if (Math.abs(forward.dot(_flyCenterUp)) >= ROLL_UNDEFINED_DOT_THRESHOLD) {
67924
+ return 0;
67925
+ }
67926
+ getReferenceBasis(
67927
+ forward,
67928
+ _flyWorldRight,
67929
+ _flyCenterUp,
67930
+ _flyReferenceRight,
67931
+ _flyReferenceUp
67932
+ );
67933
+ } else {
67934
+ if (position.lengthSq() < 1e-6) {
67935
+ return 0;
67936
+ }
67937
+ getLocalFrame(position);
67938
+ if (Math.abs(forward.dot(_flyUp)) >= ROLL_UNDEFINED_DOT_THRESHOLD) {
67939
+ return 0;
67940
+ }
67941
+ getReferenceBasis(
67942
+ forward,
67943
+ _flyEast,
67944
+ _flyUp,
67945
+ _flyReferenceRight,
67946
+ _flyReferenceUp
67947
+ );
67948
+ }
67949
+ _flyReferenceUp.projectOnPlane(forward);
67950
+ _flyCameraUp.set(0, 1, 0).applyQuaternion(quaternion).projectOnPlane(forward);
67951
+ if (_flyReferenceUp.lengthSq() < 1e-6 || _flyCameraUp.lengthSq() < 1e-6) {
67952
+ return 0;
67953
+ }
67954
+ _flyReferenceUp.normalize();
67955
+ _flyCameraUp.normalize();
67956
+ return Math.atan2(
67957
+ _flyDirection.crossVectors(_flyReferenceUp, _flyCameraUp).dot(forward),
67958
+ _flyReferenceUp.dot(_flyCameraUp)
67959
+ );
67960
+ }
67961
+ function getEndQuaternion(position, target, options) {
67962
+ const { heading, pitch, roll } = options;
67963
+ if (heading === void 0 && pitch === void 0 && roll === void 0) {
67964
+ return getLookAtQuaternion(position, target);
67965
+ }
67966
+ if (heading === void 0 && pitch === void 0) {
67967
+ const quaternion = getLookAtQuaternion(position, target);
67968
+ if (roll) {
67969
+ _flyForward.subVectors(target, position).normalize();
67970
+ quaternion.multiply(_flyQuaternion.setFromAxisAngle(_flyForward, roll));
67971
+ }
67972
+ return quaternion;
67973
+ }
67974
+ return getHeadingPitchRollQuaternion(
67975
+ isCenterModePosition(position) ? position : target,
67976
+ heading ?? 0,
67977
+ pitch ?? -Math.PI / 2,
67978
+ roll ?? 0
67979
+ );
67980
+ }
67981
+ function getBoundingSphereFlyToPosition(camera, target, range, options) {
67982
+ const { heading, pitch } = options;
67983
+ if (heading === void 0 && pitch === void 0) {
67984
+ const direction = target.lengthSq() > 1e-6 ? _flyDirection.copy(target).normalize() : camera.position.lengthSq() > 1e-6 ? _flyDirection.copy(camera.position).normalize() : _flyDirection.set(0, -1, 0);
67985
+ return direction.multiplyScalar(range).add(target);
67986
+ }
67987
+ const resolvedHeading = heading ?? 0;
67988
+ const resolvedPitch = pitch ?? -Math.PI / 2;
67989
+ const centerForward = getCenterModeHeadingPitchRollForward(
67990
+ resolvedHeading,
67991
+ resolvedPitch
67992
+ );
67993
+ const centerPosition = _flyDirection.copy(target).addScaledVector(centerForward, -range);
67994
+ if (isCenterModePosition(centerPosition)) {
67995
+ return centerPosition;
67996
+ }
67997
+ const forward = getHeadingPitchRollForward(
67998
+ target,
67999
+ resolvedHeading,
68000
+ resolvedPitch
68001
+ );
68002
+ return _flyDirection.copy(target).addScaledVector(forward, -range);
68003
+ }
68004
+ function getHeadingPitchRollQuaternion(referencePoint, heading, pitch, roll) {
68005
+ if (isCenterModePosition(referencePoint)) {
68006
+ getCenterModeHeadingPitchRollBasis(heading, pitch, roll);
68007
+ _matrix1.makeBasis(_flyRight, _flyUp, _flyBackward);
68008
+ return new Quaternion().setFromRotationMatrix(_matrix1);
68009
+ }
68010
+ if (referencePoint.lengthSq() < 1e-6) {
68011
+ return new Quaternion();
68012
+ }
68013
+ getHeadingPitchRollBasis(referencePoint, heading, pitch, roll);
68014
+ _matrix1.makeBasis(_flyRight, _flyUp, _flyBackward);
68015
+ return new Quaternion().setFromRotationMatrix(_matrix1);
68016
+ }
68017
+ function getHeadingPitchRollForward(referencePoint, heading, pitch) {
68018
+ if (isCenterModePosition(referencePoint)) {
68019
+ return getCenterModeHeadingPitchRollForward(heading, pitch);
68020
+ }
68021
+ if (referencePoint.lengthSq() < 1e-6) {
68022
+ return _flyForward.set(0, 0, -1);
68023
+ }
68024
+ getLocalFrame(referencePoint);
68025
+ const cosPitch = Math.cos(pitch);
68026
+ const sinPitch = Math.sin(pitch);
68027
+ const cosHeading = Math.cos(heading);
68028
+ const sinHeading = Math.sin(heading);
68029
+ _flyForward.copy(_flyNorth).multiplyScalar(cosHeading * cosPitch).addScaledVector(_flyEast, sinHeading * cosPitch).addScaledVector(_flyUp, sinPitch).normalize();
68030
+ return _flyForward;
68031
+ }
68032
+ function getCenterModeHeadingPitchRollForward(heading, pitch) {
68033
+ const cosPitch = Math.cos(pitch);
68034
+ const sinPitch = Math.sin(pitch);
68035
+ const cosHeading = Math.cos(heading);
68036
+ const sinHeading = Math.sin(heading);
68037
+ _flyForward.set(
68038
+ sinHeading * cosPitch,
68039
+ cosHeading * cosPitch,
68040
+ sinPitch
68041
+ );
68042
+ return _flyForward.normalize();
68043
+ }
68044
+ function getHeadingPitchRollBasis(referencePoint, heading, pitch, roll) {
68045
+ if (isCenterModePosition(referencePoint)) {
68046
+ getCenterModeHeadingPitchRollBasis(heading, pitch, roll);
68047
+ return;
68048
+ }
68049
+ getHeadingPitchRollForward(referencePoint, heading, pitch);
68050
+ _flyRight.copy(_flyEast).multiplyScalar(Math.cos(heading)).addScaledVector(_flyNorth, -Math.sin(heading)).normalize();
68051
+ _flyUp.crossVectors(_flyRight, _flyForward).normalize();
68052
+ if (roll !== 0) {
68053
+ _flyRight.applyAxisAngle(_flyForward, roll).normalize();
68054
+ _flyUp.applyAxisAngle(_flyForward, roll).normalize();
68055
+ }
68056
+ _flyBackward.copy(_flyForward).negate();
68057
+ }
68058
+ function getCenterModeHeadingPitchRollBasis(heading, pitch, roll) {
68059
+ getCenterModeHeadingPitchRollForward(heading, pitch);
68060
+ _flyRight.copy(_flyWorldRight).multiplyScalar(Math.cos(heading)).addScaledVector(_flyCenterNorth, -Math.sin(heading)).normalize();
68061
+ _flyUp.crossVectors(_flyRight, _flyForward).normalize();
68062
+ if (roll !== 0) {
68063
+ _flyRight.applyAxisAngle(_flyForward, roll).normalize();
68064
+ _flyUp.applyAxisAngle(_flyForward, roll).normalize();
68065
+ }
68066
+ _flyBackward.copy(_flyForward).negate();
68067
+ }
68068
+ function getLocalFrame(target) {
68069
+ _matrix1.copy(eastNorthUpToFixedFrame(target));
68070
+ _flyEast.setFromMatrixColumn(_matrix1, 0).normalize();
68071
+ _flyNorth.setFromMatrixColumn(_matrix1, 1).normalize();
68072
+ _flyUp.setFromMatrixColumn(_matrix1, 2).normalize();
68073
+ }
68074
+ function isCenterModePosition(position) {
68075
+ return position.lengthSq() <= CAMERA_CENTER_MODE_DISTANCE_SQ;
68076
+ }
68077
+ function getReferenceBasis(forward, east, up, rightTarget, upTarget) {
68078
+ rightTarget.crossVectors(forward, up);
68079
+ if (rightTarget.lengthSq() < 1e-6) {
68080
+ rightTarget.copy(east).projectOnPlane(forward);
68081
+ if (rightTarget.lengthSq() < 1e-6) {
68082
+ rightTarget.set(1, 0, 0).projectOnPlane(forward);
68083
+ }
68084
+ }
68085
+ rightTarget.normalize();
68086
+ upTarget.crossVectors(rightTarget, forward).normalize();
68087
+ }
68088
+ function lerpAngle(start, end, t2) {
68089
+ let delta = end - start;
68090
+ if (delta > Math.PI) {
68091
+ delta -= Math.PI * 2;
68092
+ } else if (delta < -Math.PI) {
68093
+ delta += Math.PI * 2;
68094
+ }
68095
+ return start + delta * t2;
68096
+ }
68097
+ function applyFlyToPose(camera, position, quaternion, zoom = null) {
68098
+ camera.position.copy(position);
68099
+ camera.quaternion.copy(quaternion);
68100
+ if (camera instanceof OrthographicCamera && zoom !== null) {
68101
+ camera.zoom = Math.max(zoom, 1e-6);
68102
+ camera.updateProjectionMatrix();
68103
+ }
68104
+ camera.updateMatrixWorld();
68105
+ }
68106
+ var _matrix3, _matrix1, _flyDirectionStart, _flyDirectionEnd, _flyDirection, _flyAxis, _flyWorldNorth, _flyWorldRight, _flyCenterNorth, _flyCenterUp, _flyWorldUp, _flyUp, _flyEast, _flyNorth, _flyForward, _flyRight, _flyBackward, _flyCameraUp, _flyCameraRight, _flyReferenceUp, _flyReferenceRight, _flyQuaternion, _ellipsoidRadii, _oneOverRadiiSquared, _geoEast, _geoNorth, _geoUp, UPRIGHT_ROLL_THRESHOLD, ROLL_UNDEFINED_DOT_THRESHOLD, HEADING_RIGHT_DEGENERATE_EPSILON;
68107
+ var init_cameraFlyTo = __esm({
68108
+ "src/viewer/navigation/cameraFlyTo.js"() {
68109
+ init_three_module();
68110
+ init_config();
68111
+ _matrix3 = new Matrix4();
68112
+ _matrix1 = new Matrix4();
68113
+ _flyDirectionStart = new Vector3();
68114
+ _flyDirectionEnd = new Vector3();
68115
+ _flyDirection = new Vector3();
68116
+ _flyAxis = new Vector3();
68117
+ _flyWorldNorth = new Vector3(0, 0, 1);
68118
+ _flyWorldRight = new Vector3(1, 0, 0);
68119
+ _flyCenterNorth = new Vector3(0, 1, 0);
68120
+ _flyCenterUp = new Vector3(0, 0, 1);
68121
+ _flyWorldUp = new Vector3(0, 1, 0);
68122
+ _flyUp = new Vector3();
68123
+ _flyEast = new Vector3();
68124
+ _flyNorth = new Vector3();
68125
+ _flyForward = new Vector3();
68126
+ _flyRight = new Vector3();
68127
+ _flyBackward = new Vector3();
68128
+ _flyCameraUp = new Vector3();
68129
+ _flyCameraRight = new Vector3();
68130
+ _flyReferenceUp = new Vector3();
68131
+ _flyReferenceRight = new Vector3();
68132
+ _flyQuaternion = new Quaternion();
68133
+ _ellipsoidRadii = new Vector3(
68134
+ 6378137,
68135
+ 6378137,
68136
+ 6356752314245179e-9
68137
+ );
68138
+ _oneOverRadiiSquared = new Vector3(
68139
+ 1 / (_ellipsoidRadii.x * _ellipsoidRadii.x),
68140
+ 1 / (_ellipsoidRadii.y * _ellipsoidRadii.y),
68141
+ 1 / (_ellipsoidRadii.z * _ellipsoidRadii.z)
68142
+ );
68143
+ _geoEast = new Vector3();
68144
+ _geoNorth = new Vector3();
68145
+ _geoUp = new Vector3();
68146
+ UPRIGHT_ROLL_THRESHOLD = MathUtils.degToRad(5);
68147
+ ROLL_UNDEFINED_DOT_THRESHOLD = Math.cos(MathUtils.degToRad(5));
68148
+ HEADING_RIGHT_DEGENERATE_EPSILON = 1e-6;
68149
+ }
68150
+ });
68151
+
67653
68152
  // src/viewer/navigation/flyTo.js
67654
68153
  function createFlyToController({
67655
68154
  camera,
@@ -67669,6 +68168,8 @@ function createFlyToController({
67669
68168
  const pickRaycaster = new Raycaster();
67670
68169
  const pickTargets = [];
67671
68170
  const sphere = new Sphere();
68171
+ let activeCameraFlight = null;
68172
+ let activeCameraFlightStatus = "";
67672
68173
  function getActiveEllipsoid() {
67673
68174
  return getTiles()?.ellipsoid || globeController.getEllipsoid();
67674
68175
  }
@@ -67710,6 +68211,43 @@ function createFlyToController({
67710
68211
  }
67711
68212
  return geoCamera.getCartographicFromWorldPosition(coordinateWorldPosition);
67712
68213
  }
68214
+ function startCameraFlight(position, target, options = {}, { activeStatus = "Moving camera.", doneStatus = "Moved camera." } = {}) {
68215
+ cameraController.setCamera(camera);
68216
+ activeCameraFlight = createCameraFlight(camera, position, target, options);
68217
+ activeCameraFlightStatus = doneStatus;
68218
+ if (!activeCameraFlight) {
68219
+ cameraController.setCamera(camera);
68220
+ setStatus(doneStatus);
68221
+ return;
68222
+ }
68223
+ setStatus(activeStatus);
68224
+ }
68225
+ function startBoundingSphereFlight(target, radius, options = {}, status = {}) {
68226
+ const flyToParams = getFlyToParamsFromBoundingSphere(
68227
+ camera,
68228
+ target,
68229
+ radius,
68230
+ options
68231
+ );
68232
+ startCameraFlight(
68233
+ flyToParams.position,
68234
+ flyToParams.target,
68235
+ flyToParams.options,
68236
+ status
68237
+ );
68238
+ }
68239
+ function update(time = performance.now()) {
68240
+ if (!activeCameraFlight) {
68241
+ return false;
68242
+ }
68243
+ const done = flyTo(camera, activeCameraFlight, time);
68244
+ if (done) {
68245
+ activeCameraFlight = null;
68246
+ setStatus(activeCameraFlightStatus);
68247
+ activeCameraFlightStatus = "";
68248
+ }
68249
+ return true;
68250
+ }
67713
68251
  async function applyTilesSetPositionFromPointerEvent(event) {
67714
68252
  const coordinate = pickCoordinateFromPointerEvent(event);
67715
68253
  if (!coordinate) {
@@ -67731,27 +68269,29 @@ function createFlyToController({
67731
68269
  return null;
67732
68270
  }
67733
68271
  }
67734
- function frameTileset() {
68272
+ function frameTileset({
68273
+ activeStatus = "Moving camera to the tileset.",
68274
+ doneStatus = "Moved camera to the tileset."
68275
+ } = {}) {
67735
68276
  if (!getTilesetBoundingSphere(sphere)) {
67736
68277
  return false;
67737
68278
  }
67738
- const pose = geoCamera.getFlyToPoseFromBoundingSphere(
68279
+ startBoundingSphereFlight(
67739
68280
  sphere.center,
67740
- sphere.radius,
67741
- moveToTilesPose
68281
+ sphere.radius / 2,
68282
+ moveToTilesPose,
68283
+ {
68284
+ activeStatus,
68285
+ doneStatus
68286
+ }
67742
68287
  );
67743
- camera.position.copy(pose.position);
67744
- camera.quaternion.copy(pose.quaternion);
67745
- camera.updateMatrixWorld(true);
67746
- cameraController.setCamera(camera);
67747
68288
  return true;
67748
68289
  }
67749
68290
  function moveCameraToTiles() {
67750
68291
  if (frameTileset()) {
67751
- setStatus("Moved camera to the tileset.");
67752
- } else {
67753
- setStatus("Tileset is not ready to frame yet.", true);
68292
+ return;
67754
68293
  }
68294
+ setStatus("Tileset is not ready to frame yet.", true);
67755
68295
  }
67756
68296
  function moveCameraToCoordinate(coordinate) {
67757
68297
  geoCamera.getCoordinateWorldPosition(
@@ -67760,16 +68300,15 @@ function createFlyToController({
67760
68300
  coordinate.height,
67761
68301
  coordinateWorldPosition
67762
68302
  );
67763
- const pose = geoCamera.getFlyToPoseFromBoundingSphere(
68303
+ startBoundingSphereFlight(
67764
68304
  coordinateWorldPosition,
67765
68305
  moveToCoordinateRadius,
67766
- moveToTilesPose
68306
+ moveToTilesPose,
68307
+ {
68308
+ activeStatus: "Moving camera to the specified coordinate.",
68309
+ doneStatus: "Moved camera to the specified coordinate."
68310
+ }
67767
68311
  );
67768
- camera.position.copy(pose.position);
67769
- camera.quaternion.copy(pose.quaternion);
67770
- camera.updateMatrixWorld(true);
67771
- cameraController.setCamera(camera);
67772
- setStatus("Moved camera to the specified coordinate.");
67773
68312
  }
67774
68313
  return {
67775
68314
  applyTilesSetPositionFromPointerEvent,
@@ -67777,12 +68316,14 @@ function createFlyToController({
67777
68316
  getActiveEllipsoid,
67778
68317
  moveCameraToCoordinate,
67779
68318
  moveCameraToTiles,
67780
- pickCoordinateFromPointerEvent
68319
+ pickCoordinateFromPointerEvent,
68320
+ update
67781
68321
  };
67782
68322
  }
67783
68323
  var init_flyTo = __esm({
67784
68324
  "src/viewer/navigation/flyTo.js"() {
67785
68325
  init_three_module();
68326
+ init_cameraFlyTo();
67786
68327
  init_utils();
67787
68328
  }
67788
68329
  });
@@ -67954,6 +68495,15 @@ function clientPointToNdc(point, domRect) {
67954
68495
  y: 1 - (point.y - domRect.top) / domRect.height * 2
67955
68496
  };
67956
68497
  }
68498
+ function getClientSelectionQuad(start, end) {
68499
+ const clientRect = getClientSelectionRect(start, end);
68500
+ return [
68501
+ new Vector2(clientRect.minX, clientRect.minY),
68502
+ new Vector2(clientRect.maxX, clientRect.minY),
68503
+ new Vector2(clientRect.maxX, clientRect.maxY),
68504
+ new Vector2(clientRect.minX, clientRect.maxY)
68505
+ ];
68506
+ }
67957
68507
  function getNdcSelectionRect(clientRect, domRect) {
67958
68508
  const topLeft = clientPointToNdc(
67959
68509
  new Vector2(clientRect.minX, clientRect.minY),
@@ -67970,6 +68520,19 @@ function getNdcSelectionRect(clientRect, domRect) {
67970
68520
  maxY: Math.max(topLeft.y, bottomRight.y)
67971
68521
  };
67972
68522
  }
68523
+ function getNdcSelectionQuad(clientPoints, domRect) {
68524
+ return clientPoints.map((point) => clientPointToNdc(point, domRect));
68525
+ }
68526
+ function getNdcBounds(points) {
68527
+ const xs2 = points.map((point) => point.x);
68528
+ const ys2 = points.map((point) => point.y);
68529
+ return {
68530
+ maxX: Math.max(...xs2),
68531
+ maxY: Math.max(...ys2),
68532
+ minX: Math.min(...xs2),
68533
+ minY: Math.min(...ys2)
68534
+ };
68535
+ }
67973
68536
  function updateOverlayRect(overlayEl, rectEl, clientRect) {
67974
68537
  if (!overlayEl || !rectEl) {
67975
68538
  return;
@@ -68151,10 +68714,10 @@ function createFarPlaneData({
68151
68714
  width
68152
68715
  };
68153
68716
  }
68154
- function createFrustumData(camera, rect, depthRange) {
68717
+ function createFrustumDataFromQuad(camera, quad, depthRange) {
68155
68718
  const { farDepth, nearDepth } = normalizeDepthRange(camera, depthRange);
68156
- const centerX = (rect.minX + rect.maxX) * 0.5;
68157
- const centerY = (rect.minY + rect.maxY) * 0.5;
68719
+ const centerX = quad.reduce((total, point) => total + point.x, 0) / quad.length;
68720
+ const centerY = quad.reduce((total, point) => total + point.y, 0) / quad.length;
68158
68721
  const nearCenter = createPointAtViewDepth(
68159
68722
  camera,
68160
68723
  centerX,
@@ -68181,55 +68744,13 @@ function createFrustumData(camera, rect, depthRange) {
68181
68744
  const insidePoint = selectionForward.clone().multiplyScalar((nearDistance + farDistance) * 0.5).add(camera.position);
68182
68745
  plane.setFromNormalAndCoplanarPoint(selectionForward, farCenter);
68183
68746
  const farClipPlane = plane.clone();
68184
- const farTopLeft = createPointOnPlane(
68185
- camera,
68186
- rect.minX,
68187
- rect.maxY,
68188
- farClipPlane
68189
- );
68190
- const farTopRight = createPointOnPlane(
68191
- camera,
68192
- rect.maxX,
68193
- rect.maxY,
68194
- farClipPlane
68195
- );
68196
- const farBottomRight = createPointOnPlane(
68197
- camera,
68198
- rect.maxX,
68199
- rect.minY,
68200
- farClipPlane
68201
- );
68202
- const farBottomLeft = createPointOnPlane(
68203
- camera,
68204
- rect.minX,
68205
- rect.minY,
68206
- farClipPlane
68747
+ const [farTopLeft, farTopRight, farBottomRight, farBottomLeft] = quad.map(
68748
+ (point) => createPointOnPlane(camera, point.x, point.y, farClipPlane)
68207
68749
  );
68208
68750
  plane.setFromNormalAndCoplanarPoint(selectionForward, nearCenter);
68209
68751
  const nearPlane = plane.clone();
68210
- const nearTopLeft = createPointOnPlane(
68211
- camera,
68212
- rect.minX,
68213
- rect.maxY,
68214
- nearPlane
68215
- );
68216
- const nearTopRight = createPointOnPlane(
68217
- camera,
68218
- rect.maxX,
68219
- rect.maxY,
68220
- nearPlane
68221
- );
68222
- const nearBottomRight = createPointOnPlane(
68223
- camera,
68224
- rect.maxX,
68225
- rect.minY,
68226
- nearPlane
68227
- );
68228
- const nearBottomLeft = createPointOnPlane(
68229
- camera,
68230
- rect.minX,
68231
- rect.minY,
68232
- nearPlane
68752
+ const [nearTopLeft, nearTopRight, nearBottomRight, nearBottomLeft] = quad.map(
68753
+ (point) => createPointOnPlane(camera, point.x, point.y, nearPlane)
68233
68754
  );
68234
68755
  if (!farTopLeft || !farTopRight || !farBottomRight || !farBottomLeft || !nearTopLeft || !nearTopRight || !nearBottomRight || !nearBottomLeft) {
68235
68756
  return null;
@@ -68306,8 +68827,21 @@ function createFrustumData(camera, rect, depthRange) {
68306
68827
  selectionForward: selectionForward.toArray()
68307
68828
  };
68308
68829
  }
68830
+ function createFrustumData(camera, rect, depthRange) {
68831
+ return createFrustumDataFromQuad(
68832
+ camera,
68833
+ [
68834
+ { x: rect.minX, y: rect.maxY },
68835
+ { x: rect.maxX, y: rect.maxY },
68836
+ { x: rect.maxX, y: rect.minY },
68837
+ { x: rect.minX, y: rect.minY }
68838
+ ],
68839
+ depthRange
68840
+ );
68841
+ }
68309
68842
  function createSelectionData({
68310
68843
  camera,
68844
+ clientPoints,
68311
68845
  domElement,
68312
68846
  end,
68313
68847
  getDepthRange,
@@ -68317,19 +68851,27 @@ function createSelectionData({
68317
68851
  if (domRect.width <= 0 || domRect.height <= 0) {
68318
68852
  return null;
68319
68853
  }
68320
- const clientRect = getClientSelectionRect(start, end);
68854
+ const hasClientQuad = Array.isArray(clientPoints) && clientPoints.length === 4;
68855
+ const points = hasClientQuad ? clientPoints : getClientSelectionQuad(start, end);
68856
+ const clientRect = {
68857
+ maxX: Math.max(...points.map((point) => point.x)),
68858
+ maxY: Math.max(...points.map((point) => point.y)),
68859
+ minX: Math.min(...points.map((point) => point.x)),
68860
+ minY: Math.min(...points.map((point) => point.y))
68861
+ };
68321
68862
  const width = clientRect.maxX - clientRect.minX;
68322
68863
  const height = clientRect.maxY - clientRect.minY;
68323
68864
  if (width * width + height * height < SCREEN_SELECTION_MIN_DRAG_DISTANCE_SQ) {
68324
68865
  return null;
68325
68866
  }
68326
- const rect = getNdcSelectionRect(clientRect, domRect);
68867
+ const quad = getNdcSelectionQuad(points, domRect);
68868
+ const rect = hasClientQuad ? getNdcBounds(quad) : getNdcSelectionRect(clientRect, domRect);
68327
68869
  camera.updateProjectionMatrix();
68328
68870
  camera.updateMatrixWorld(true);
68329
68871
  cameraPosition.copy(camera.position);
68330
68872
  const depthRange = normalizeDepthRange(camera, getDepthRange());
68331
68873
  const viewProjectionMatrix = new Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).toArray();
68332
- const frustum = createFrustumData(camera, rect, depthRange);
68874
+ const frustum = hasClientQuad ? createFrustumDataFromQuad(camera, quad, depthRange) : createFrustumData(camera, rect, depthRange);
68333
68875
  if (!frustum) {
68334
68876
  return null;
68335
68877
  }
@@ -68687,6 +69229,8 @@ function createScreenSelectionPointerTracker({
68687
69229
  camera,
68688
69230
  domElement,
68689
69231
  getDepthRange,
69232
+ onOverlayClear,
69233
+ onOverlayUpdate,
68690
69234
  onSelectionCreated,
68691
69235
  overlayEl,
68692
69236
  rectEl
@@ -68695,8 +69239,18 @@ function createScreenSelectionPointerTracker({
68695
69239
  let drag = null;
68696
69240
  function clearDrag() {
68697
69241
  drag = null;
69242
+ onOverlayClear?.();
68698
69243
  clearOverlay(overlayEl, rectEl);
68699
69244
  }
69245
+ function updateDragOverlay() {
69246
+ const clientRect = {
69247
+ ...getClientSelectionRect(drag.start, drag.current)
69248
+ };
69249
+ if (onOverlayUpdate?.(clientRect) === true) {
69250
+ return;
69251
+ }
69252
+ updateOverlayRect(overlayEl, rectEl, clientRect);
69253
+ }
68700
69254
  function setActive(nextActive) {
68701
69255
  active = !!nextActive;
68702
69256
  if (!active) {
@@ -68715,11 +69269,7 @@ function createScreenSelectionPointerTracker({
68715
69269
  start: dragStart.clone(),
68716
69270
  current: dragCurrent.clone()
68717
69271
  };
68718
- updateOverlayRect(
68719
- overlayEl,
68720
- rectEl,
68721
- getClientSelectionRect(drag.start, drag.current)
68722
- );
69272
+ updateDragOverlay();
68723
69273
  domElement.setPointerCapture?.(event.pointerId);
68724
69274
  event.preventDefault();
68725
69275
  event.stopPropagation();
@@ -68731,11 +69281,7 @@ function createScreenSelectionPointerTracker({
68731
69281
  }
68732
69282
  const domRect = domElement.getBoundingClientRect();
68733
69283
  getClampedClientPoint(event, domRect, drag.current);
68734
- updateOverlayRect(
68735
- overlayEl,
68736
- rectEl,
68737
- getClientSelectionRect(drag.start, drag.current)
68738
- );
69284
+ updateDragOverlay();
68739
69285
  event.preventDefault();
68740
69286
  event.stopPropagation();
68741
69287
  return true;
@@ -68744,6 +69290,9 @@ function createScreenSelectionPointerTracker({
68744
69290
  if (!active || !drag || event.pointerId !== drag.pointerId) {
68745
69291
  return false;
68746
69292
  }
69293
+ const clientRect = {
69294
+ ...getClientSelectionRect(drag.start, drag.current)
69295
+ };
68747
69296
  const selection = createSelectionData({
68748
69297
  camera,
68749
69298
  domElement,
@@ -68753,7 +69302,7 @@ function createScreenSelectionPointerTracker({
68753
69302
  });
68754
69303
  domElement.releasePointerCapture?.(event.pointerId);
68755
69304
  clearDrag();
68756
- onSelectionCreated(selection);
69305
+ onSelectionCreated(selection, clientRect);
68757
69306
  event.preventDefault();
68758
69307
  event.stopPropagation();
68759
69308
  return true;
@@ -68832,6 +69381,56 @@ function getScreenSelectionPayload(selection) {
68832
69381
  viewProjectionMatrix: selection.viewProjectionMatrix.slice()
68833
69382
  };
68834
69383
  }
69384
+ function setScreenSelectionShape(selection, {
69385
+ cameraPosition: sourceCameraPosition,
69386
+ depthRange,
69387
+ farPlane,
69388
+ planeMatrices,
69389
+ rect,
69390
+ selectionForward: sourceSelectionForward,
69391
+ viewProjectionMatrix
69392
+ }, currentTransformMatrix) {
69393
+ if (!selection) {
69394
+ return;
69395
+ }
69396
+ const previousFarDepth = Number(selection.depthRange?.farDepth);
69397
+ const copiedPlaneMatrices = planeMatrices.map((matrix) => matrix.slice());
69398
+ const referenceTransformMatrix = copyMatrix4Array(currentTransformMatrix);
69399
+ selection.basePlaneMatrices = copiedPlaneMatrices.map(
69400
+ (matrix) => matrix.slice()
69401
+ );
69402
+ selection.cameraPosition = copyVectorArray(sourceCameraPosition);
69403
+ selection.currentTransformMatrix = referenceTransformMatrix.slice();
69404
+ selection.depthRange = copyDepthRange(depthRange);
69405
+ selection.farPlane = copyFarPlane(farPlane);
69406
+ selection.planeMatrices = copiedPlaneMatrices;
69407
+ selection.referenceTransformMatrix = referenceTransformMatrix;
69408
+ selection.rect = copyRect(rect);
69409
+ selection.selectionForward = copyVectorArray(sourceSelectionForward, [
69410
+ 0,
69411
+ 0,
69412
+ -1
69413
+ ]);
69414
+ selection.viewProjectionMatrix = viewProjectionMatrix.slice();
69415
+ updateScreenSelectionWorldState(selection, currentTransformMatrix);
69416
+ if (!Number.isFinite(previousFarDepth)) {
69417
+ return;
69418
+ }
69419
+ const nextFarDepth = Math.min(
69420
+ selection.depthRange.maxFarDepth,
69421
+ Math.max(
69422
+ selection.depthRange.nearDepth + SCREEN_SELECTION_MIN_DEPTH_RANGE,
69423
+ previousFarDepth
69424
+ )
69425
+ );
69426
+ if (Math.abs(nextFarDepth - selection.depthRange.farDepth) > 1e-9) {
69427
+ setScreenSelectionFarDepth(
69428
+ selection,
69429
+ nextFarDepth,
69430
+ currentTransformMatrix
69431
+ );
69432
+ }
69433
+ }
68835
69434
  function updateScreenSelectionWorldState(selection, currentTransformMatrix, active = selection?.farHandle?.visible) {
68836
69435
  if (!selection) {
68837
69436
  return;
@@ -68876,6 +69475,313 @@ var init_screenSelection = __esm({
68876
69475
  }
68877
69476
  });
68878
69477
 
69478
+ // src/viewer/screenSelection/editOverlay.js
69479
+ function clampValue(value, min, max) {
69480
+ return Math.min(max, Math.max(min, value));
69481
+ }
69482
+ function copyClientPoint(point) {
69483
+ return {
69484
+ x: Number(point?.x) || 0,
69485
+ y: Number(point?.y) || 0
69486
+ };
69487
+ }
69488
+ function copyClientPoints(points) {
69489
+ return points.map(copyClientPoint);
69490
+ }
69491
+ function getClientPointsBounds(points) {
69492
+ const xs2 = points.map((point) => point.x);
69493
+ const ys2 = points.map((point) => point.y);
69494
+ return {
69495
+ maxX: Math.max(...xs2),
69496
+ maxY: Math.max(...ys2),
69497
+ minX: Math.min(...xs2),
69498
+ minY: Math.min(...ys2)
69499
+ };
69500
+ }
69501
+ function getClientRectPoints(rect) {
69502
+ return [
69503
+ { x: Number(rect?.minX) || 0, y: Number(rect?.minY) || 0 },
69504
+ { x: Number(rect?.maxX) || 0, y: Number(rect?.minY) || 0 },
69505
+ { x: Number(rect?.maxX) || 0, y: Number(rect?.maxY) || 0 },
69506
+ { x: Number(rect?.minX) || 0, y: Number(rect?.maxY) || 0 }
69507
+ ];
69508
+ }
69509
+ function clampClientPoint(point, domRect) {
69510
+ return {
69511
+ x: clampValue(point.x, domRect.left, domRect.right),
69512
+ y: clampValue(point.y, domRect.top, domRect.bottom)
69513
+ };
69514
+ }
69515
+ function clampClientPoints(points, domElement) {
69516
+ const domRect = domElement.getBoundingClientRect();
69517
+ return points.map((point) => clampClientPoint(point, domRect));
69518
+ }
69519
+ function getPointTurnCross(a2, b5, c2) {
69520
+ return (b5.x - a2.x) * (c2.y - b5.y) - (b5.y - a2.y) * (c2.x - b5.x);
69521
+ }
69522
+ function isConvexClientQuad(points) {
69523
+ if (!Array.isArray(points) || points.length !== 4) {
69524
+ return false;
69525
+ }
69526
+ let turnSign = 0;
69527
+ for (let index = 0; index < points.length; index++) {
69528
+ const cross = getPointTurnCross(
69529
+ points[index],
69530
+ points[(index + 1) % points.length],
69531
+ points[(index + 2) % points.length]
69532
+ );
69533
+ if (Math.abs(cross) <= SCREEN_EDIT_MIN_CONVEX_CROSS_ABS) {
69534
+ return false;
69535
+ }
69536
+ const nextSign = Math.sign(cross);
69537
+ if (turnSign === 0) {
69538
+ turnSign = nextSign;
69539
+ } else if (nextSign !== turnSign) {
69540
+ return false;
69541
+ }
69542
+ }
69543
+ return true;
69544
+ }
69545
+ function getPartPoint(points, part) {
69546
+ const indices = SCREEN_EDIT_PART_POINT_INDICES[part] || [];
69547
+ if (indices.length === 0) {
69548
+ return { x: 0, y: 0 };
69549
+ }
69550
+ const x5 = indices.reduce((total, index) => total + points[index].x, 0) / indices.length;
69551
+ const y6 = indices.reduce((total, index) => total + points[index].y, 0) / indices.length;
69552
+ return { x: x5, y: y6 };
69553
+ }
69554
+ function getPartAngle(points, part) {
69555
+ const indices = SCREEN_EDIT_PART_POINT_INDICES[part] || [];
69556
+ if (indices.length !== 2) {
69557
+ return null;
69558
+ }
69559
+ const start = points[indices[0]];
69560
+ const end = points[indices[1]];
69561
+ return Math.atan2(end.y - start.y, end.x - start.x);
69562
+ }
69563
+ function getPartLength(points, part) {
69564
+ const indices = SCREEN_EDIT_PART_POINT_INDICES[part] || [];
69565
+ if (indices.length !== 2) {
69566
+ return null;
69567
+ }
69568
+ const start = points[indices[0]];
69569
+ const end = points[indices[1]];
69570
+ return Math.hypot(end.x - start.x, end.y - start.y);
69571
+ }
69572
+ function interpolatePoint(a2, b5, t2) {
69573
+ return {
69574
+ x: a2.x + (b5.x - a2.x) * t2,
69575
+ y: a2.y + (b5.y - a2.y) * t2
69576
+ };
69577
+ }
69578
+ function updateScreenEditGrid(grid, localPoints, visible) {
69579
+ if (!grid) {
69580
+ return;
69581
+ }
69582
+ grid.replaceChildren();
69583
+ grid.hidden = !visible;
69584
+ if (!visible) {
69585
+ return;
69586
+ }
69587
+ const [topLeft, topRight, bottomRight, bottomLeft] = localPoints;
69588
+ for (let index = 1; index < SCREEN_EDIT_GRID_DIVISIONS; index++) {
69589
+ const t2 = index / SCREEN_EDIT_GRID_DIVISIONS;
69590
+ const top = interpolatePoint(topLeft, topRight, t2);
69591
+ const bottom = interpolatePoint(bottomLeft, bottomRight, t2);
69592
+ const left = interpolatePoint(topLeft, bottomLeft, t2);
69593
+ const right = interpolatePoint(topRight, bottomRight, t2);
69594
+ const verticalLine = document.createElementNS(
69595
+ "http://www.w3.org/2000/svg",
69596
+ "line"
69597
+ );
69598
+ const horizontalLine = document.createElementNS(
69599
+ "http://www.w3.org/2000/svg",
69600
+ "line"
69601
+ );
69602
+ verticalLine.setAttribute("x1", String(top.x));
69603
+ verticalLine.setAttribute("y1", String(top.y));
69604
+ verticalLine.setAttribute("x2", String(bottom.x));
69605
+ verticalLine.setAttribute("y2", String(bottom.y));
69606
+ horizontalLine.setAttribute("x1", String(left.x));
69607
+ horizontalLine.setAttribute("y1", String(left.y));
69608
+ horizontalLine.setAttribute("x2", String(right.x));
69609
+ horizontalLine.setAttribute("y2", String(right.y));
69610
+ grid.append(verticalLine, horizontalLine);
69611
+ }
69612
+ }
69613
+ function ensureEditableRectHandles(rectEl) {
69614
+ if (!rectEl || rectEl.dataset.editHandlesReady === "true") {
69615
+ return;
69616
+ }
69617
+ const svg2 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
69618
+ const polygon = document.createElementNS(
69619
+ "http://www.w3.org/2000/svg",
69620
+ "polygon"
69621
+ );
69622
+ const grid = document.createElementNS("http://www.w3.org/2000/svg", "g");
69623
+ svg2.classList.add("screen-selection-edit-svg");
69624
+ grid.classList.add("screen-selection-edit-grid");
69625
+ polygon.classList.add("screen-selection-edit-polygon");
69626
+ svg2.append(polygon, grid);
69627
+ rectEl.appendChild(svg2);
69628
+ SCREEN_EDIT_HANDLE_PARTS.forEach((part) => {
69629
+ const handle = document.createElement("span");
69630
+ handle.classList.add(
69631
+ "screen-selection-edit-handle",
69632
+ `screen-selection-edit-${part}`
69633
+ );
69634
+ handle.dataset.editPart = part;
69635
+ rectEl.appendChild(handle);
69636
+ });
69637
+ rectEl.dataset.editHandlesReady = "true";
69638
+ }
69639
+ function pointSegmentDistanceSq(point, start, end) {
69640
+ const dx = end.x - start.x;
69641
+ const dy = end.y - start.y;
69642
+ const lengthSq = dx * dx + dy * dy;
69643
+ if (lengthSq <= 1e-12) {
69644
+ const px2 = point.x - start.x;
69645
+ const py2 = point.y - start.y;
69646
+ return px2 * px2 + py2 * py2;
69647
+ }
69648
+ const t2 = clampValue(
69649
+ ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSq,
69650
+ 0,
69651
+ 1
69652
+ );
69653
+ const x5 = start.x + dx * t2;
69654
+ const y6 = start.y + dy * t2;
69655
+ const px = point.x - x5;
69656
+ const py = point.y - y6;
69657
+ return px * px + py * py;
69658
+ }
69659
+ function createScreenEditOverlay({ overlayEl, rectEl }) {
69660
+ let activePart = null;
69661
+ function applyActivePart() {
69662
+ SCREEN_EDIT_HANDLE_PARTS.forEach((part) => {
69663
+ const handle = rectEl?.querySelector(`[data-edit-part="${part}"]`);
69664
+ handle?.classList.toggle("active", part === activePart);
69665
+ });
69666
+ }
69667
+ function render(clientPoints, { showGrid = false } = {}) {
69668
+ ensureEditableRectHandles(rectEl);
69669
+ rectEl?.classList.add("editable");
69670
+ rectEl?.classList.toggle("drawing", showGrid);
69671
+ const bounds = getClientPointsBounds(clientPoints);
69672
+ updateOverlayRect(overlayEl, rectEl, bounds);
69673
+ const width = Math.max(1, bounds.maxX - bounds.minX);
69674
+ const height = Math.max(1, bounds.maxY - bounds.minY);
69675
+ const localPoints = clientPoints.map((point) => ({
69676
+ x: point.x - bounds.minX,
69677
+ y: point.y - bounds.minY
69678
+ }));
69679
+ const svg2 = rectEl?.querySelector(".screen-selection-edit-svg");
69680
+ const polygon = rectEl?.querySelector(".screen-selection-edit-polygon");
69681
+ const grid = rectEl?.querySelector(".screen-selection-edit-grid");
69682
+ svg2?.setAttribute("viewBox", `0 0 ${width} ${height}`);
69683
+ polygon?.setAttribute(
69684
+ "points",
69685
+ localPoints.map((point) => `${point.x},${point.y}`).join(" ")
69686
+ );
69687
+ updateScreenEditGrid(grid, localPoints, showGrid);
69688
+ SCREEN_EDIT_HANDLE_PARTS.forEach((part) => {
69689
+ const point = getPartPoint(localPoints, part);
69690
+ const handle = rectEl?.querySelector(`[data-edit-part="${part}"]`);
69691
+ if (!handle) {
69692
+ return;
69693
+ }
69694
+ handle.style.left = `${point.x}px`;
69695
+ handle.style.top = `${point.y}px`;
69696
+ const angle = getPartAngle(localPoints, part);
69697
+ const length = getPartLength(localPoints, part);
69698
+ if (length != null) {
69699
+ const maxVisualLength = Math.max(0, length - 2);
69700
+ const visualLength = Math.max(
69701
+ 0,
69702
+ Math.min(SCREEN_EDIT_EDGE_HANDLE_MAX_LENGTH, maxVisualLength)
69703
+ );
69704
+ const activeScaleX = visualLength > 0 ? Math.min(
69705
+ SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_X,
69706
+ Math.max(1, maxVisualLength / visualLength)
69707
+ ) : 1;
69708
+ handle.style.width = `${visualLength}px`;
69709
+ handle.style.height = `${length <= SCREEN_EDIT_EDGE_HANDLE_MIN_LENGTH ? 2 : 4}px`;
69710
+ handle.style.setProperty(
69711
+ "--screen-selection-edit-active-scale-x",
69712
+ String(activeScaleX)
69713
+ );
69714
+ handle.style.setProperty(
69715
+ "--screen-selection-edit-active-scale-y",
69716
+ String(SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_Y)
69717
+ );
69718
+ } else {
69719
+ handle.style.width = "";
69720
+ handle.style.height = "";
69721
+ handle.style.removeProperty("--screen-selection-edit-active-scale-x");
69722
+ handle.style.removeProperty("--screen-selection-edit-active-scale-y");
69723
+ }
69724
+ handle.style.transform = angle == null ? "translate(-50%, -50%)" : `translate(-50%, -50%) rotate(${angle}rad)`;
69725
+ });
69726
+ applyActivePart();
69727
+ }
69728
+ function clear() {
69729
+ activePart = null;
69730
+ rectEl?.classList.remove("drawing", "editable");
69731
+ applyActivePart();
69732
+ }
69733
+ function setActivePart(part) {
69734
+ activePart = SCREEN_EDIT_HANDLE_PARTS.includes(part) ? part : null;
69735
+ applyActivePart();
69736
+ }
69737
+ return {
69738
+ clear,
69739
+ render,
69740
+ setActivePart
69741
+ };
69742
+ }
69743
+ var SCREEN_EDIT_EDGE_HIT_SIZE, SCREEN_EDIT_CORNER_HIT_SIZE, SCREEN_EDIT_EDGE_HANDLE_MAX_LENGTH, SCREEN_EDIT_EDGE_HANDLE_MIN_LENGTH, SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_X, SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_Y, SCREEN_EDIT_GRID_DIVISIONS, SCREEN_EDIT_MIN_CONVEX_CROSS_ABS, SCREEN_EDIT_HANDLE_PARTS, SCREEN_EDIT_CORNER_PARTS, SCREEN_EDIT_EDGE_PARTS, SCREEN_EDIT_PART_POINT_INDICES;
69744
+ var init_editOverlay = __esm({
69745
+ "src/viewer/screenSelection/editOverlay.js"() {
69746
+ init_geometry();
69747
+ SCREEN_EDIT_EDGE_HIT_SIZE = 8;
69748
+ SCREEN_EDIT_CORNER_HIT_SIZE = 16;
69749
+ SCREEN_EDIT_EDGE_HANDLE_MAX_LENGTH = 26;
69750
+ SCREEN_EDIT_EDGE_HANDLE_MIN_LENGTH = 6;
69751
+ SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_X = 1.5;
69752
+ SCREEN_EDIT_EDGE_HANDLE_ACTIVE_SCALE_Y = 1.5;
69753
+ SCREEN_EDIT_GRID_DIVISIONS = 8;
69754
+ SCREEN_EDIT_MIN_CONVEX_CROSS_ABS = 1e-3;
69755
+ SCREEN_EDIT_HANDLE_PARTS = [
69756
+ "top-left",
69757
+ "top",
69758
+ "top-right",
69759
+ "right",
69760
+ "bottom-right",
69761
+ "bottom",
69762
+ "bottom-left",
69763
+ "left"
69764
+ ];
69765
+ SCREEN_EDIT_CORNER_PARTS = [
69766
+ "top-left",
69767
+ "top-right",
69768
+ "bottom-right",
69769
+ "bottom-left"
69770
+ ];
69771
+ SCREEN_EDIT_EDGE_PARTS = ["top", "right", "bottom", "left"];
69772
+ SCREEN_EDIT_PART_POINT_INDICES = {
69773
+ "bottom-left": [3],
69774
+ "bottom-right": [2],
69775
+ bottom: [2, 3],
69776
+ left: [3, 0],
69777
+ right: [1, 2],
69778
+ top: [0, 1],
69779
+ "top-left": [0],
69780
+ "top-right": [1]
69781
+ };
69782
+ }
69783
+ });
69784
+
68879
69785
  // src/viewer/dom/cropUi.js
68880
69786
  function updateCropControls({
68881
69787
  activeScreenSelectionId,
@@ -68993,9 +69899,13 @@ function createCropController({
68993
69899
  let nextSelectionId = 1;
68994
69900
  let activeSelectionId = null;
68995
69901
  let pendingMode = false;
69902
+ let pendingScreenEdit = null;
69903
+ let pendingEditDrag = null;
69904
+ let editCursor = "";
68996
69905
  let hasGaussianSplats = false;
68997
69906
  const sphere = new Sphere();
68998
69907
  const cameraForward2 = new Vector3();
69908
+ const screenEditOverlay = createScreenEditOverlay({ overlayEl, rectEl });
68999
69909
  function getActiveSelection() {
69000
69910
  if (activeSelectionId == null) {
69001
69911
  return null;
@@ -69011,6 +69921,115 @@ function createCropController({
69011
69921
  }))
69012
69922
  ];
69013
69923
  }
69924
+ function setEditCursor(cursor) {
69925
+ const nextCursor = cursor || "";
69926
+ if (editCursor === nextCursor) {
69927
+ return;
69928
+ }
69929
+ domElement.style.cursor = nextCursor;
69930
+ editCursor = nextCursor;
69931
+ }
69932
+ function clearEditCursor() {
69933
+ setEditCursor("");
69934
+ }
69935
+ function getActivePendingEdit() {
69936
+ if (!pendingScreenEdit || activeSelectionId !== pendingScreenEdit.selectionId) {
69937
+ return null;
69938
+ }
69939
+ const match = pendingSelections.find(
69940
+ (selection) => selection.id === pendingScreenEdit.selectionId
69941
+ );
69942
+ return match ? pendingScreenEdit : null;
69943
+ }
69944
+ function clearScreenEditOverlay() {
69945
+ screenEditOverlay.clear();
69946
+ clearEditCursor();
69947
+ }
69948
+ function syncPendingEditOverlay() {
69949
+ const edit = getActivePendingEdit();
69950
+ if (!edit) {
69951
+ clearScreenEditOverlay();
69952
+ if (!pendingMode) {
69953
+ clearOverlay(overlayEl, rectEl);
69954
+ }
69955
+ return;
69956
+ }
69957
+ screenEditOverlay.render(edit.clientPoints, { showGrid: false });
69958
+ }
69959
+ function createCameraPoseSnapshot() {
69960
+ camera.updateMatrixWorld(true);
69961
+ return {
69962
+ position: camera.position.toArray(),
69963
+ projectionMatrix: camera.projectionMatrix.toArray(),
69964
+ quaternion: camera.quaternion.toArray()
69965
+ };
69966
+ }
69967
+ function projectionChanged(source, target) {
69968
+ if (!Array.isArray(source) || !Array.isArray(target)) {
69969
+ return true;
69970
+ }
69971
+ return source.some(
69972
+ (value, index) => Math.abs(value - target[index]) > CAMERA_PROJECTION_EPSILON
69973
+ );
69974
+ }
69975
+ function cameraPoseChanged(cameraPose) {
69976
+ if (!cameraPose) {
69977
+ return true;
69978
+ }
69979
+ camera.updateMatrixWorld(true);
69980
+ const dx = camera.position.x - cameraPose.position[0];
69981
+ const dy = camera.position.y - cameraPose.position[1];
69982
+ const dz = camera.position.z - cameraPose.position[2];
69983
+ if (dx * dx + dy * dy + dz * dz > CAMERA_POSITION_EPSILON_SQ) {
69984
+ return true;
69985
+ }
69986
+ const quaternion = cameraPose.quaternion;
69987
+ const quaternionDot = Math.abs(
69988
+ camera.quaternion.x * quaternion[0] + camera.quaternion.y * quaternion[1] + camera.quaternion.z * quaternion[2] + camera.quaternion.w * quaternion[3]
69989
+ );
69990
+ if (1 - Math.min(1, quaternionDot) > CAMERA_QUATERNION_EPSILON) {
69991
+ return true;
69992
+ }
69993
+ return projectionChanged(
69994
+ camera.projectionMatrix.toArray(),
69995
+ cameraPose.projectionMatrix
69996
+ );
69997
+ }
69998
+ function clearPendingScreenEdit() {
69999
+ pendingScreenEdit = null;
70000
+ pendingEditDrag = null;
70001
+ syncPendingEditOverlay();
70002
+ }
70003
+ function freezePendingScreenEdit(showStatus = false) {
70004
+ const hadEdit = !!pendingScreenEdit;
70005
+ if (!hadEdit) {
70006
+ return false;
70007
+ }
70008
+ clearPendingScreenEdit();
70009
+ if (showStatus) {
70010
+ setStatus(
70011
+ "Screen selection shape fixed after camera movement. Drag the 3D far plane, then Confirm or Cancel."
70012
+ );
70013
+ }
70014
+ return true;
70015
+ }
70016
+ function freezePendingScreenEditIfCameraChanged() {
70017
+ if (!pendingScreenEdit || pendingEditDrag) {
70018
+ return false;
70019
+ }
70020
+ if (!cameraPoseChanged(pendingScreenEdit.cameraPose)) {
70021
+ return false;
70022
+ }
70023
+ return freezePendingScreenEdit(true);
70024
+ }
70025
+ function createPendingScreenEdit(selection, clientRect) {
70026
+ pendingScreenEdit = {
70027
+ cameraPose: createCameraPoseSnapshot(),
70028
+ clientPoints: clampClientPoints(getClientRectPoints(clientRect), domElement),
70029
+ selectionId: selection.id
70030
+ };
70031
+ syncPendingEditOverlay();
70032
+ }
69014
70033
  function syncWorldState() {
69015
70034
  const transform = getCurrentRootTransformArray();
69016
70035
  getEntries().forEach(({ selection }) => {
@@ -69090,10 +70109,171 @@ function createCropController({
69090
70109
  camera,
69091
70110
  domElement,
69092
70111
  getDepthRange,
70112
+ onOverlayClear: clearScreenEditOverlay,
70113
+ onOverlayUpdate: (clientRect) => {
70114
+ screenEditOverlay.render(getClientRectPoints(clientRect), {
70115
+ showGrid: true
70116
+ });
70117
+ return true;
70118
+ },
69093
70119
  onSelectionCreated: handleSelectionCreated,
69094
70120
  overlayEl,
69095
70121
  rectEl
69096
70122
  });
70123
+ function createEditHit(part) {
70124
+ return { cursor: "grab", part };
70125
+ }
70126
+ function getPendingEditHit(event) {
70127
+ if (freezePendingScreenEditIfCameraChanged()) {
70128
+ return null;
70129
+ }
70130
+ const edit = getActivePendingEdit();
70131
+ if (!edit) {
70132
+ return null;
70133
+ }
70134
+ const pointer = { x: event.clientX, y: event.clientY };
70135
+ for (const part of SCREEN_EDIT_CORNER_PARTS) {
70136
+ const point = getPartPoint(edit.clientPoints, part);
70137
+ const dx = pointer.x - point.x;
70138
+ const dy = pointer.y - point.y;
70139
+ if (dx * dx + dy * dy <= SCREEN_EDIT_CORNER_HIT_SIZE ** 2) {
70140
+ return createEditHit(part);
70141
+ }
70142
+ }
70143
+ for (const part of SCREEN_EDIT_EDGE_PARTS) {
70144
+ const [startIndex, endIndex] = SCREEN_EDIT_PART_POINT_INDICES[part];
70145
+ if (pointSegmentDistanceSq(
70146
+ pointer,
70147
+ edit.clientPoints[startIndex],
70148
+ edit.clientPoints[endIndex]
70149
+ ) <= SCREEN_EDIT_EDGE_HIT_SIZE ** 2) {
70150
+ return createEditHit(part);
70151
+ }
70152
+ }
70153
+ return null;
70154
+ }
70155
+ function getPendingEditDragPoints(event) {
70156
+ const { part, startClientX, startClientY, startPoints } = pendingEditDrag;
70157
+ const domRect = domElement.getBoundingClientRect();
70158
+ const indices = SCREEN_EDIT_PART_POINT_INDICES[part] || [];
70159
+ let dx = event.clientX - startClientX;
70160
+ let dy = event.clientY - startClientY;
70161
+ indices.forEach((index) => {
70162
+ const point = startPoints[index];
70163
+ dx = Math.max(dx, domRect.left - point.x);
70164
+ dx = Math.min(dx, domRect.right - point.x);
70165
+ dy = Math.max(dy, domRect.top - point.y);
70166
+ dy = Math.min(dy, domRect.bottom - point.y);
70167
+ });
70168
+ return startPoints.map(
70169
+ (point, index) => indices.includes(index) ? {
70170
+ x: point.x + dx,
70171
+ y: point.y + dy
70172
+ } : copyClientPoint(point)
70173
+ );
70174
+ }
70175
+ function updatePendingEditSelection(clientPoints) {
70176
+ const edit = getActivePendingEdit();
70177
+ if (!edit) {
70178
+ return false;
70179
+ }
70180
+ const match = findSelection(edit.selectionId);
70181
+ if (!match) {
70182
+ return false;
70183
+ }
70184
+ if (!isConvexClientQuad(clientPoints)) {
70185
+ return false;
70186
+ }
70187
+ const selectionData = createSelectionData({
70188
+ camera,
70189
+ clientPoints,
70190
+ domElement,
70191
+ getDepthRange
70192
+ });
70193
+ if (!selectionData) {
70194
+ return false;
70195
+ }
70196
+ edit.clientPoints = copyClientPoints(clientPoints);
70197
+ setScreenSelectionShape(
70198
+ match.selection,
70199
+ selectionData,
70200
+ getCurrentRootTransformArray()
70201
+ );
70202
+ syncPendingEditOverlay();
70203
+ syncTransformControlsState();
70204
+ return true;
70205
+ }
70206
+ function handlePendingEditPointerDown(event) {
70207
+ if (event.button !== 0) {
70208
+ return false;
70209
+ }
70210
+ const hit = getPendingEditHit(event);
70211
+ if (!hit) {
70212
+ return false;
70213
+ }
70214
+ pendingEditDrag = {
70215
+ part: hit.part,
70216
+ pointerId: event.pointerId,
70217
+ startClientX: event.clientX,
70218
+ startClientY: event.clientY,
70219
+ startPoints: copyClientPoints(pendingScreenEdit.clientPoints),
70220
+ updated: false
70221
+ };
70222
+ domElement.setPointerCapture?.(event.pointerId);
70223
+ screenEditOverlay.setActivePart(hit.part);
70224
+ setEditCursor("grabbing");
70225
+ event.preventDefault();
70226
+ event.stopPropagation();
70227
+ return true;
70228
+ }
70229
+ function handlePendingEditPointerMove(event) {
70230
+ if (pendingEditDrag) {
70231
+ if (event.pointerId !== pendingEditDrag.pointerId) {
70232
+ return false;
70233
+ }
70234
+ pendingEditDrag.updated = updatePendingEditSelection(getPendingEditDragPoints(event)) || pendingEditDrag.updated;
70235
+ event.preventDefault();
70236
+ event.stopPropagation();
70237
+ return true;
70238
+ }
70239
+ if (event.buttons) {
70240
+ return false;
70241
+ }
70242
+ const hit = getPendingEditHit(event);
70243
+ screenEditOverlay.setActivePart(hit?.part);
70244
+ setEditCursor(hit?.cursor || "");
70245
+ return false;
70246
+ }
70247
+ function handlePendingEditPointerUp(event) {
70248
+ if (!pendingEditDrag || event.pointerId !== pendingEditDrag.pointerId) {
70249
+ return false;
70250
+ }
70251
+ domElement.releasePointerCapture?.(event.pointerId);
70252
+ const updated = pendingEditDrag.updated;
70253
+ pendingEditDrag = null;
70254
+ const hit = getPendingEditHit(event);
70255
+ screenEditOverlay.setActivePart(hit?.part);
70256
+ setEditCursor(hit?.cursor || "");
70257
+ setStatus(
70258
+ updated ? "Updated screen selection convex quadrilateral." : "Screen selection must stay convex.",
70259
+ !updated
70260
+ );
70261
+ event.preventDefault();
70262
+ event.stopPropagation();
70263
+ return true;
70264
+ }
70265
+ function handlePendingEditPointerCancel(event) {
70266
+ if (!pendingEditDrag || event.pointerId !== pendingEditDrag.pointerId) {
70267
+ return false;
70268
+ }
70269
+ domElement.releasePointerCapture?.(event.pointerId);
70270
+ pendingEditDrag = null;
70271
+ screenEditOverlay.setActivePart(null);
70272
+ clearEditCursor();
70273
+ event.preventDefault();
70274
+ event.stopPropagation();
70275
+ return true;
70276
+ }
69097
70277
  function setMode(active) {
69098
70278
  pendingMode = active && hasGaussianSplats && pendingSelections.length === 0;
69099
70279
  pointerTracker.setActive(pendingMode);
@@ -69106,6 +70286,7 @@ function createCropController({
69106
70286
  }
69107
70287
  cameraController.enabled = !transformControls.dragging;
69108
70288
  syncTransformControlsState();
70289
+ syncPendingEditOverlay();
69109
70290
  refreshUi();
69110
70291
  }
69111
70292
  function cancelMode() {
@@ -69114,7 +70295,7 @@ function createCropController({
69114
70295
  }
69115
70296
  setMode(false);
69116
70297
  }
69117
- function handleSelectionCreated(selectionData) {
70298
+ function handleSelectionCreated(selectionData, clientRect) {
69118
70299
  if (pendingSelections.length > 0) {
69119
70300
  setMode(false);
69120
70301
  setStatus(
@@ -69136,12 +70317,13 @@ function createCropController({
69136
70317
  pendingSelections.push(selection);
69137
70318
  activeSelectionId = selection.id;
69138
70319
  setMode(false);
70320
+ createPendingScreenEdit(selection, clientRect);
69139
70321
  syncEditSdfs();
69140
70322
  syncFarHandles();
69141
70323
  syncTransformControlsState();
69142
70324
  refreshUi();
69143
70325
  setStatus(
69144
- "Added screen exclude selection. Drag the 3D far plane, then Confirm or Cancel before drawing another."
70326
+ "Added screen exclude selection. Drag corner points or edges into a convex quadrilateral before moving the camera, then adjust the 3D far plane and Confirm or Cancel."
69145
70327
  );
69146
70328
  }
69147
70329
  function toggle() {
@@ -69173,6 +70355,7 @@ function createCropController({
69173
70355
  }
69174
70356
  const count = pendingSelections.length;
69175
70357
  selections.push(...pendingSelections);
70358
+ clearPendingScreenEdit();
69176
70359
  pendingSelections = [];
69177
70360
  activeSelectionId = null;
69178
70361
  setMode(false);
@@ -69193,6 +70376,7 @@ function createCropController({
69193
70376
  )) {
69194
70377
  activeSelectionId = null;
69195
70378
  }
70379
+ clearPendingScreenEdit();
69196
70380
  pendingSelections.forEach(disposeScreenSelection);
69197
70381
  pendingSelections = [];
69198
70382
  syncEditSdfs();
@@ -69259,10 +70443,12 @@ function createCropController({
69259
70443
  setTransformMode(null);
69260
70444
  syncEditSdfs();
69261
70445
  syncFarHandles();
70446
+ syncPendingEditOverlay();
69262
70447
  refreshUi();
69263
70448
  syncTransformControlsState();
70449
+ const canEditPendingRect = !wasActive && !!getActivePendingEdit();
69264
70450
  setStatus(
69265
- wasActive ? "Screen selection deactivated." : "Drag the 3D far plane handle to adjust screen selection depth."
70451
+ wasActive ? "Screen selection deactivated." : canEditPendingRect ? "Drag corner points or edges into a convex quadrilateral before moving the camera, or drag the 3D far plane to adjust depth." : "Drag the 3D far plane handle to adjust screen selection depth."
69266
70452
  );
69267
70453
  }
69268
70454
  function removeFromList(list, selectionId) {
@@ -69283,6 +70469,9 @@ function createCropController({
69283
70469
  if (Number(selectionId) === activeSelectionId) {
69284
70470
  activeSelectionId = null;
69285
70471
  }
70472
+ if (Number(selectionId) === pendingScreenEdit?.selectionId) {
70473
+ clearPendingScreenEdit();
70474
+ }
69286
70475
  syncEditSdfs();
69287
70476
  syncFarHandles();
69288
70477
  refreshUi();
@@ -69300,6 +70489,7 @@ function createCropController({
69300
70489
  selections = [];
69301
70490
  pendingSelections = [];
69302
70491
  activeSelectionId = null;
70492
+ clearPendingScreenEdit();
69303
70493
  syncEditSdfs();
69304
70494
  syncFarHandles();
69305
70495
  refreshUi();
@@ -69313,6 +70503,7 @@ function createCropController({
69313
70503
  activeSelectionId = null;
69314
70504
  syncEditSdfs();
69315
70505
  syncFarHandles();
70506
+ syncPendingEditOverlay();
69316
70507
  cancelMode();
69317
70508
  refreshUi();
69318
70509
  }
@@ -69323,8 +70514,44 @@ function createCropController({
69323
70514
  activeSelectionId = null;
69324
70515
  syncEditSdfs();
69325
70516
  syncFarHandles();
70517
+ syncPendingEditOverlay();
69326
70518
  refreshUi();
69327
70519
  }
70520
+ function shouldCapturePointerDown(event) {
70521
+ return event.button === 0 && (pendingMode || getPendingEditHit(event) !== null);
70522
+ }
70523
+ function handlePointerDown(event) {
70524
+ if (pointerTracker.handlePointerDown(event)) {
70525
+ return true;
70526
+ }
70527
+ return handlePendingEditPointerDown(event);
70528
+ }
70529
+ function handlePointerMove(event) {
70530
+ if (pointerTracker.handlePointerMove(event)) {
70531
+ return true;
70532
+ }
70533
+ return handlePendingEditPointerMove(event);
70534
+ }
70535
+ function handlePointerUp(event) {
70536
+ if (pointerTracker.handlePointerUp(event)) {
70537
+ return true;
70538
+ }
70539
+ return handlePendingEditPointerUp(event);
70540
+ }
70541
+ function handlePointerCancel(event) {
70542
+ if (pointerTracker.handlePointerCancel(event)) {
70543
+ return true;
70544
+ }
70545
+ return handlePendingEditPointerCancel(event);
70546
+ }
70547
+ cameraController.addEventListener(
70548
+ "update",
70549
+ freezePendingScreenEditIfCameraChanged
70550
+ );
70551
+ cameraController.addEventListener(
70552
+ "finish",
70553
+ freezePendingScreenEditIfCameraChanged
70554
+ );
69328
70555
  return {
69329
70556
  cancel,
69330
70557
  cancelMode,
@@ -69334,25 +70561,32 @@ function createCropController({
69334
70561
  getActiveSelection,
69335
70562
  getPayload,
69336
70563
  getPendingMode: () => pendingMode,
69337
- handlePointerCancel: pointerTracker.handlePointerCancel,
69338
- handlePointerDown: pointerTracker.handlePointerDown,
69339
- handlePointerMove: pointerTracker.handlePointerMove,
69340
- handlePointerUp: pointerTracker.handlePointerUp,
70564
+ handlePointerCancel,
70565
+ handlePointerDown,
70566
+ handlePointerMove,
70567
+ handlePointerUp,
69341
70568
  handleSelectionRemove,
69342
70569
  handleSelectionSelect,
69343
70570
  handleTransformControlObjectChange,
69344
70571
  hasPendingSelections: () => pendingSelections.length > 0,
69345
70572
  notifyTransformModeChanged,
69346
70573
  setHasGaussianSplats,
70574
+ shouldCapturePointerDown,
69347
70575
  syncWorldState,
69348
70576
  toggle
69349
70577
  };
69350
70578
  }
70579
+ var CAMERA_POSITION_EPSILON_SQ, CAMERA_QUATERNION_EPSILON, CAMERA_PROJECTION_EPSILON;
69351
70580
  var init_cropController = __esm({
69352
70581
  "src/viewer/screenSelection/cropController.js"() {
69353
70582
  init_three_module();
69354
70583
  init_screenSelection();
70584
+ init_geometry();
70585
+ init_editOverlay();
69355
70586
  init_cropUi();
70587
+ CAMERA_POSITION_EPSILON_SQ = 1e-12;
70588
+ CAMERA_QUATERNION_EPSILON = 1e-10;
70589
+ CAMERA_PROJECTION_EPSILON = 1e-10;
69356
70590
  }
69357
70591
  });
69358
70592
 
@@ -69961,7 +71195,7 @@ var require_app = __commonJS({
69961
71195
  savedControlDisabledStates = null;
69962
71196
  }
69963
71197
  cameraController.setPointerDownFilter((event) => {
69964
- return !(cropController?.getPendingMode() && event.button === 0);
71198
+ return !(cropController?.shouldCapturePointerDown(event) ?? false);
69965
71199
  });
69966
71200
  var { transformControls, transformControlsHelper } = createViewerTransformControls({
69967
71201
  camera,
@@ -69993,7 +71227,7 @@ var require_app = __commonJS({
69993
71227
  var geoCamera = createGeoCameraController({
69994
71228
  camera,
69995
71229
  centerModeDistanceSq: CAMERA_CENTER_MODE_DISTANCE_SQ,
69996
- getActiveEllipsoid: () => flyTo.getActiveEllipsoid()
71230
+ getActiveEllipsoid: () => flyTo2.getActiveEllipsoid()
69997
71231
  });
69998
71232
  var geometricError = createGeometricErrorController({
69999
71233
  defaultErrorTarget: DEFAULT_ERROR_TARGET,
@@ -70012,7 +71246,7 @@ var require_app = __commonJS({
70012
71246
  target.radius *= editableGroup.matrixWorld.getMaxScaleOnAxis();
70013
71247
  return true;
70014
71248
  }
70015
- var flyTo = createFlyToController({
71249
+ var flyTo2 = createFlyToController({
70016
71250
  camera,
70017
71251
  cameraController,
70018
71252
  domElement: renderer.domElement,
@@ -70034,7 +71268,7 @@ var require_app = __commonJS({
70034
71268
  syncTransformControlsState: () => transformModeController.syncControls(),
70035
71269
  transformControls,
70036
71270
  applyTilesPlacementFromPointerEvent: async (event) => {
70037
- const coordinate = await flyTo.applyTilesSetPositionFromPointerEvent(event);
71271
+ const coordinate = await flyTo2.applyTilesSetPositionFromPointerEvent(event);
70038
71272
  if (coordinate) {
70039
71273
  updateCoordinateInputs(
70040
71274
  coordinate.latitude,
@@ -70122,7 +71356,7 @@ var require_app = __commonJS({
70122
71356
  transformModeController.setMode(null);
70123
71357
  function moveCameraToTiles() {
70124
71358
  cancelPositionPickModes();
70125
- flyTo.moveCameraToTiles();
71359
+ flyTo2.moveCameraToTiles();
70126
71360
  }
70127
71361
  function moveCameraToCoordinate() {
70128
71362
  cancelPositionPickModes();
@@ -70130,7 +71364,7 @@ var require_app = __commonJS({
70130
71364
  if (!coordinate) {
70131
71365
  return;
70132
71366
  }
70133
- flyTo.moveCameraToCoordinate(coordinate);
71367
+ flyTo2.moveCameraToCoordinate(coordinate);
70134
71368
  }
70135
71369
  async function moveTilesToCoordinate() {
70136
71370
  cancelPositionPickModes();
@@ -70205,9 +71439,11 @@ var require_app = __commonJS({
70205
71439
  if (framed) {
70206
71440
  return;
70207
71441
  }
70208
- if (flyTo.frameTileset()) {
71442
+ if (flyTo2.frameTileset({
71443
+ activeStatus: "Framing tileset...",
71444
+ doneStatus: "Tileset ready."
71445
+ })) {
70209
71446
  framed = true;
70210
- setStatus("Tileset ready.");
70211
71447
  }
70212
71448
  };
70213
71449
  next.addEventListener(
@@ -70328,6 +71564,7 @@ var require_app = __commonJS({
70328
71564
  loadTileset(TILESET_URL);
70329
71565
  function frame() {
70330
71566
  cameraController.update();
71567
+ flyTo2.update();
70331
71568
  rootTransform.flush();
70332
71569
  globeController.update();
70333
71570
  tiles?.update();