diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index cb0b43c54..37a9e6c0a 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -399,6 +399,7 @@ camera moves with easing, duration and holds. Playback and seek share one coordinate sampler; navigation/manual input revokes authored motion and pending holds. Anchor heights and explicit endpoints require the WGS84 ellipsoid reference. Legacy projects retain their ordinary flights, content and edits. +Seeking between ordinary camera poses uses the shortest longitude arc across the dateline while preserving both authored endpoint longitudes. See [camera directions](DIRECTOR-CAMERA.md). Realtime voice composes separate connection, response/tool, Radio handoff, input @@ -5310,8 +5311,3 @@ datacenters, dams and the Nepal locator retain that shared credit while displaye Military fragments stitch by source identity. Different mapped parcels merge only when their footprints touch or contain one another, never across a seam tolerance gap. - - -### fix(director): seek camera longitude across the shortest dateline arc - -Interpolate longitude with the existing shortest-angle easing, normalize intermediate longitudes, and preserve exact authored endpoints at progress zero and one. diff --git a/src/director/camera.js b/src/director/camera.js index 059c7a873..e009c2cba 100644 --- a/src/director/camera.js +++ b/src/director/camera.js @@ -28,6 +28,11 @@ export function resolveCameraMove(scene, shot) { }; } +/** Wrap an interpolated camera longitude to WGS84 without changing authored endpoints. */ +export function normalizeCameraLongitude(longitude) { + return ((((longitude + 180) % 360) + 360) % 360) - 180; +} + /** Sample the same authored curve for playback and seeking, taking the short longitude/angle arc. */ export function sampleCameraMove(move, progress) { const t = Math.max(0, Math.min(1, Number(progress) || 0)); @@ -45,7 +50,7 @@ export function sampleCameraMove(move, progress) { const { from, to } = move; return { lat: lerp(from.lat, to.lat), - lon: ((((angle(from.lon, to.lon) + 180) % 360) + 360) % 360) - 180, + lon: normalizeCameraLongitude(angle(from.lon, to.lon)), alt: lerp(from.alt, to.alt), heading: angle(from.heading, to.heading), pitch: lerp(from.pitch, to.pitch), diff --git a/src/director/timeline.js b/src/director/timeline.js index 1da08d498..fbaca4301 100644 --- a/src/director/timeline.js +++ b/src/director/timeline.js @@ -2,6 +2,7 @@ import { resolveCameraPose, resolveCameraMove, sampleCameraMove, + normalizeCameraLongitude, } from './camera.js'; const DEFAULT_SHOT_DURATION_SEC = 4; @@ -50,8 +51,7 @@ export function cameraAtProgress(fromCamera, toCamera, progress) { ? Number(source.lon) : t === 1 ? Number(target.lon) - : ((((lerpAngle(source.lon, target.lon) + 180) % 360) + 360) % 360) - - 180, + : normalizeCameraLongitude(lerpAngle(source.lon, target.lon)), alt: lerp(source.alt, target.alt), heading: lerpAngle(source.heading, target.heading), pitch: lerp(source.pitch, target.pitch),