using Windows.Foundation;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Controls;
public sealed partial class GalaxyMapView
{
private readonly Microsoft.UI.Dispatching.DispatcherQueueTimer cameraMotionTimer;
private GalaxyCameraPose? cameraTarget;
private long cameraMotionGeneration;
private bool applyingCameraPose;
// Explicit application stop tolerances: the original two BSS epsilon
// values cannot be recovered from the offline image. Factors are original.
private static readonly GalaxyCameraStopThresholds MotionStop = new(.0001, .000001, .0001);
internal bool CameraMotionPendingForProbe => cameraTarget is not null;
internal GalaxyCameraPose? TargetCameraPoseForProbe => cameraTarget;
internal GalaxyCameraPose ActualCameraPoseForProbe => ActualCameraPose;
private double CameraFocalLength => GalaxyOriginalProjection.FocalLength(Math.Max(1,ActualWidth),Math.Max(1,ActualHeight));
private GalaxyCameraPose ActualCameraPose
{
get { var camera=Camera;return new(camera.Focus,yaw,pitch,camera.Distance); }
}
private GalaxyCameraPose EnsureCameraTarget()
{
cameraTimer.Stop();cameraAnimation=null;
if(cameraTarget is {} pending)return pending;
var actual=ActualCameraPose;
// Normalize the old screen-pan representation before beginning a
// world-focus animation, preserving every star's current projection.
if(panX!=0||panY!=0)
{
applyingCameraPose=true;
try{panX=panY=0;Centre=actual.Focus;}
finally{applyingCameraPose=false;}
}
cameraTarget=actual;
return actual;
}
private GalaxyCamera TargetCamera(GalaxyCameraPose target) => GalaxyCamera.Create(target.Focus,
Math.Max(1,ActualWidth),Math.Max(1,ActualHeight),target.Yaw,target.Pitch,CameraFocalLength/target.Distance);
private void SetCameraTarget(GalaxyCameraPose target,bool cameraCraning=false,double craneInput=0)
{
cameraTarget=GalaxyOriginalCameraControls.PrepareTarget(target,cameraCraning,craneInput);
cameraMotionGeneration++;
if(loaded&&pageActive&&!cameraMotionTimer.IsRunning)cameraMotionTimer.Start();
}
private void FinishCameraInput()
{
if(cameraTarget is {} target)
SetCameraTarget(target,CameraCraneActive,CameraCraneActive?1:0);
}
private bool CameraCraneActive => cameraKeys.Contains(Windows.System.VirtualKey.R)
||cameraKeys.Contains(Windows.System.VirtualKey.F)
||dragging&&mouseGesture&&GalaxyMouseControls.Gesture(mouseButtons)==GalaxyMouseGesture.Crane;
private void CancelCameraMotion()
{
cameraMotionTimer.Stop();cameraTarget=null;cameraMotionGeneration++;
}
private void CancelCameraInput()
{
CancelCameraMotion();cameraTimer.Stop();cameraAnimation=null;
cameraKeys.Clear();keyboardTimer.Stop();
dragging=false;mouseGesture=false;mouseButtons=GalaxyMouseButtons.None;
CancelHold();ReleasePointerCaptures();
}
private void AdvanceCameraMotion()
{
if(!loaded||!pageActive||!IsInteractive){CancelCameraMotion();return;}
if(cameraTarget is not {} target){cameraMotionTimer.Stop();return;}
var generation=cameraMotionGeneration;
var actual=ActualCameraPose;
var next=GalaxyOriginalCameraControls.Smooth(actual,target,MotionStop);
yaw=next.Yaw;pitch=next.Pitch;
scale=CameraFocalLength/next.Distance;
MoveCameraFocus(next.Focus);
// Floor snapping publishes selection synchronously. A subscriber can
// recenter or start another motion from that callback; this old tick
// must never cancel the replacement target or publish its old state.
if(generation!=cameraMotionGeneration)return;
Invalidate();ScheduleViewport();
if(next==target)
{
CancelCameraMotion();FlushViewport();
}
}
/// Deterministic ordinary camera ticks, without changing input time.
internal void AdvanceCameraMotionForProbe(int ticks=1)
{
if(ticks is <0 or >4096)throw new ArgumentOutOfRangeException(nameof(ticks));
for(var i=0;iFinishCameraInput();
internal void SetCameraPoseForProbe(GalaxyCameraPose pose)
{
var args=Environment.GetCommandLineArgs();
if(!args.Contains("--offline-preview")||!args.Contains("--probe-galaxy-rework"))throw new InvalidOperationException("仅允许独立离线星图验收设置相机夹具。");
if(!pose.Focus.IsValid||!double.IsFinite(pose.Yaw)||!double.IsFinite(pose.Pitch)||!double.IsFinite(pose.Distance)||pose.Distance