Refactor simulation and viewer architecture
This commit is contained in:
193
apps/viewer/src/viewerNavigationController.ts
Normal file
193
apps/viewer/src/viewerNavigationController.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import * as THREE from "three";
|
||||
import {
|
||||
determineActiveSystemId,
|
||||
focusOnSelection,
|
||||
getCameraFocusWorldPosition,
|
||||
resolveSelectionPosition,
|
||||
seedSystemFocusLocal,
|
||||
toDisplayLocalPosition,
|
||||
} from "./viewerCamera";
|
||||
import {
|
||||
syncFollowStateFromSelection,
|
||||
updateFollowCamera,
|
||||
updateSystemDetailVisibility,
|
||||
} from "./viewerControls";
|
||||
import { computeNodeLocalPosition, resolveBubblePosition, resolvePointPosition } from "./viewerWorldPresentation";
|
||||
import { getAnimatedShipLocalPosition, resolveShipHeading } from "./viewerPresentation";
|
||||
import type {
|
||||
CameraMode,
|
||||
NodeVisual,
|
||||
PlanetVisual,
|
||||
Selectable,
|
||||
ShipVisual,
|
||||
SystemVisual,
|
||||
WorldState,
|
||||
} from "./viewerTypes";
|
||||
|
||||
export interface ViewerNavigationContext {
|
||||
getWorld: () => WorldState | undefined;
|
||||
getWorldTimeSyncMs: () => number;
|
||||
getActiveSystemId: () => string | undefined;
|
||||
setActiveSystemId: (value: string | undefined) => void;
|
||||
getCameraMode: () => CameraMode;
|
||||
setCameraMode: (value: CameraMode) => void;
|
||||
getCameraTargetShipId: () => string | undefined;
|
||||
setCameraTargetShipId: (value: string | undefined) => void;
|
||||
getCurrentDistance: () => number;
|
||||
getSelectedItems: () => Selectable[];
|
||||
getOrbitYaw: () => number;
|
||||
galaxyFocus: THREE.Vector3;
|
||||
systemFocusLocal: THREE.Vector3;
|
||||
camera: THREE.PerspectiveCamera;
|
||||
shipVisuals: Map<string, ShipVisual>;
|
||||
nodeVisuals: Map<string, NodeVisual>;
|
||||
planetVisuals: PlanetVisual[];
|
||||
systemVisuals: Map<string, SystemVisual>;
|
||||
followCameraPosition: THREE.Vector3;
|
||||
followCameraFocus: THREE.Vector3;
|
||||
followCameraDirection: THREE.Vector3;
|
||||
followCameraDesiredDirection: THREE.Vector3;
|
||||
followCameraOffset: THREE.Vector3;
|
||||
createWorldPresentationContext: () => any;
|
||||
updatePanels: () => void;
|
||||
updateGamePanel: (mode: string) => void;
|
||||
}
|
||||
|
||||
export class ViewerNavigationController {
|
||||
constructor(private readonly context: ViewerNavigationContext) {}
|
||||
|
||||
focusOnSelection(selection: Selectable) {
|
||||
focusOnSelection({
|
||||
world: this.context.getWorld(),
|
||||
selection,
|
||||
worldTimeSyncMs: this.context.getWorldTimeSyncMs(),
|
||||
nodeVisuals: this.context.nodeVisuals,
|
||||
planetVisuals: this.context.planetVisuals,
|
||||
computeNodeLocalPosition: (node, timeSeconds) => computeNodeLocalPosition(this.context.createWorldPresentationContext(), node, timeSeconds),
|
||||
resolveBubblePosition: (bubbleId) => {
|
||||
const bubble = this.context.getWorld()?.localBubbles.get(bubbleId);
|
||||
return bubble ? resolveBubblePosition(this.context.createWorldPresentationContext(), bubble) : undefined;
|
||||
},
|
||||
resolvePointPosition: (systemId, nodeId) => resolvePointPosition(this.context.createWorldPresentationContext(), systemId, nodeId),
|
||||
activeSystemId: this.context.getActiveSystemId(),
|
||||
galaxyFocus: this.context.galaxyFocus,
|
||||
systemFocusLocal: this.context.systemFocusLocal,
|
||||
});
|
||||
}
|
||||
|
||||
resolveSelectionPosition(selection: Selectable) {
|
||||
return resolveSelectionPosition({
|
||||
world: this.context.getWorld(),
|
||||
selection,
|
||||
worldTimeSyncMs: this.context.getWorldTimeSyncMs(),
|
||||
nodeVisuals: this.context.nodeVisuals,
|
||||
planetVisuals: this.context.planetVisuals,
|
||||
computeNodeLocalPosition: (node, timeSeconds) => computeNodeLocalPosition(this.context.createWorldPresentationContext(), node, timeSeconds),
|
||||
resolveBubblePosition: (bubbleId) => {
|
||||
const bubble = this.context.getWorld()?.localBubbles.get(bubbleId);
|
||||
return bubble ? resolveBubblePosition(this.context.createWorldPresentationContext(), bubble) : undefined;
|
||||
},
|
||||
resolvePointPosition: (systemId, nodeId) => resolvePointPosition(this.context.createWorldPresentationContext(), systemId, nodeId),
|
||||
});
|
||||
}
|
||||
|
||||
updateActiveSystem() {
|
||||
const nextActiveSystemId = determineActiveSystemId({
|
||||
world: this.context.getWorld(),
|
||||
cameraMode: this.context.getCameraMode(),
|
||||
cameraTargetShipId: this.context.getCameraTargetShipId(),
|
||||
currentDistance: this.context.getCurrentDistance(),
|
||||
selectedItems: this.context.getSelectedItems(),
|
||||
galaxyFocus: this.context.galaxyFocus,
|
||||
});
|
||||
if (nextActiveSystemId === this.context.getActiveSystemId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextActiveSystemId) {
|
||||
this.seedSystemFocusLocal(nextActiveSystemId);
|
||||
}
|
||||
|
||||
this.context.setActiveSystemId(nextActiveSystemId);
|
||||
this.updateSystemDetailVisibility();
|
||||
this.context.updatePanels();
|
||||
this.context.updateGamePanel("Live");
|
||||
}
|
||||
|
||||
updateFollowCamera(delta: number) {
|
||||
const nextState = updateFollowCamera({
|
||||
world: this.context.getWorld(),
|
||||
cameraMode: this.context.getCameraMode(),
|
||||
cameraTargetShipId: this.context.getCameraTargetShipId(),
|
||||
shipVisuals: this.context.shipVisuals,
|
||||
currentDistance: this.context.getCurrentDistance(),
|
||||
camera: this.context.camera,
|
||||
followCameraPosition: this.context.followCameraPosition,
|
||||
followCameraFocus: this.context.followCameraFocus,
|
||||
followCameraDirection: this.context.followCameraDirection,
|
||||
followCameraDesiredDirection: this.context.followCameraDesiredDirection,
|
||||
followCameraOffset: this.context.followCameraOffset,
|
||||
systemFocusLocal: this.context.systemFocusLocal,
|
||||
delta,
|
||||
getAnimatedShipLocalPosition,
|
||||
toDisplayLocalPosition: (localPosition, systemId) => this.toDisplayLocalPosition(localPosition, systemId),
|
||||
resolveShipHeading: (visual, worldPosition) => resolveShipHeading(visual, worldPosition, this.context.getOrbitYaw()),
|
||||
});
|
||||
this.context.setCameraMode(nextState.cameraMode);
|
||||
this.context.setCameraTargetShipId(nextState.cameraTargetShipId);
|
||||
return nextState.handled;
|
||||
}
|
||||
|
||||
syncFollowStateFromSelection() {
|
||||
const nextState = syncFollowStateFromSelection(
|
||||
this.context.getSelectedItems(),
|
||||
this.context.getCameraMode(),
|
||||
this.context.getCameraTargetShipId(),
|
||||
);
|
||||
this.context.setCameraMode(nextState.cameraMode);
|
||||
this.context.setCameraTargetShipId(nextState.cameraTargetShipId);
|
||||
}
|
||||
|
||||
updateSystemDetailVisibility() {
|
||||
updateSystemDetailVisibility(this.context.systemVisuals, this.context.getActiveSystemId());
|
||||
}
|
||||
|
||||
getCameraFocusWorldPosition() {
|
||||
return getCameraFocusWorldPosition({
|
||||
world: this.context.getWorld(),
|
||||
activeSystemId: this.context.getActiveSystemId(),
|
||||
galaxyFocus: this.context.galaxyFocus,
|
||||
systemFocusLocal: this.context.systemFocusLocal,
|
||||
});
|
||||
}
|
||||
|
||||
seedSystemFocusLocal(systemId: string) {
|
||||
seedSystemFocusLocal({
|
||||
world: this.context.getWorld(),
|
||||
systemId,
|
||||
cameraMode: this.context.getCameraMode(),
|
||||
cameraTargetShipId: this.context.getCameraTargetShipId(),
|
||||
selectedItems: this.context.getSelectedItems(),
|
||||
systemFocusLocal: this.context.systemFocusLocal,
|
||||
worldTimeSyncMs: this.context.getWorldTimeSyncMs(),
|
||||
nodeVisuals: this.context.nodeVisuals,
|
||||
planetVisuals: this.context.planetVisuals,
|
||||
computeNodeLocalPosition: (node, timeSeconds) => computeNodeLocalPosition(this.context.createWorldPresentationContext(), node, timeSeconds),
|
||||
resolveBubblePosition: (bubbleId) => {
|
||||
const bubble = this.context.getWorld()?.localBubbles.get(bubbleId);
|
||||
return bubble ? resolveBubblePosition(this.context.createWorldPresentationContext(), bubble) : undefined;
|
||||
},
|
||||
resolvePointPosition: (systemIdValue, nodeId) => resolvePointPosition(this.context.createWorldPresentationContext(), systemIdValue, nodeId),
|
||||
});
|
||||
}
|
||||
|
||||
toDisplayLocalPosition(localPosition: THREE.Vector3, systemId?: string) {
|
||||
return toDisplayLocalPosition({
|
||||
world: this.context.getWorld(),
|
||||
systemId,
|
||||
activeSystemId: this.context.getActiveSystemId(),
|
||||
localPosition,
|
||||
systemFocusLocal: this.context.systemFocusLocal,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user