Skip to content

Commit 302e5cf

Browse files
cjw6kclaude
andcommitted
fix: Camera presets now track actual celestial body positions
Instead of hardcoded camera positions, each preset now: - Targets the actual current position of the celestial body (Moon, Sun) - Uses an offset from that body for camera placement - Moon/Sun views now correctly follow the bodies in their orbits This ensures clicking "Moon" always shows the Moon regardless of where it is in its orbit. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6cc7ca9 commit 302e5cf

File tree

3 files changed

+47
-28
lines changed

3 files changed

+47
-28
lines changed

tidal-harmonics/src/components/canvas/CameraController.tsx

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,57 @@
1-
import { useRef, useEffect } from 'react';
1+
import { useRef, useEffect, useMemo } from 'react';
22
import { useThree, useFrame } from '@react-three/fiber';
33
import { OrbitControls } from '@react-three/drei';
44
import { Vector3, PerspectiveCamera } from 'three';
55
import type { OrbitControls as OrbitControlsImpl } from 'three-stdlib';
6-
import { useCameraStore } from '@/stores/cameraStore';
6+
import { useCameraStore, CAMERA_PRESET_CONFIGS } from '@/stores/cameraStore';
7+
import { useCelestialPositions } from '@/hooks/useCelestialPositions';
78

89
export function CameraController() {
910
const controlsRef = useRef<OrbitControlsImpl>(null);
1011
const { camera } = useThree();
1112
const preset = useCameraStore((s) => s.preset);
12-
const presets = useCameraStore((s) => s.presets);
1313
const isTransitioning = useCameraStore((s) => s.isTransitioning);
1414
const setTransitioning = useCameraStore((s) => s.setTransitioning);
1515

16-
const targetPreset = presets[preset];
16+
const { earth, moon, sun } = useCelestialPositions();
17+
const presetConfig = CAMERA_PRESET_CONFIGS[preset];
18+
19+
// Compute actual target position based on which body we're viewing
20+
const targetBodyPosition = useMemo(() => {
21+
switch (preset) {
22+
case 'moon':
23+
return new Vector3(...moon);
24+
case 'sun':
25+
return new Vector3(...sun);
26+
case 'earth':
27+
case 'overview':
28+
default:
29+
return new Vector3(...earth);
30+
}
31+
}, [preset, earth, moon, sun]);
32+
33+
// Camera position = body position + offset
34+
const targetCameraPosition = useMemo(() => {
35+
const offset = new Vector3(...presetConfig.offset);
36+
return targetBodyPosition.clone().add(offset);
37+
}, [targetBodyPosition, presetConfig.offset]);
1738

1839
useEffect(() => {
19-
if (targetPreset?.fov !== undefined && camera instanceof PerspectiveCamera) {
20-
camera.fov = targetPreset.fov;
40+
if (presetConfig?.fov !== undefined && camera instanceof PerspectiveCamera) {
41+
camera.fov = presetConfig.fov;
2142
camera.updateProjectionMatrix();
2243
}
23-
}, [camera, targetPreset]);
44+
}, [camera, presetConfig]);
2445

2546
useFrame(() => {
2647
if (!controlsRef.current) return;
2748

2849
if (isTransitioning) {
29-
const targetPosition = new Vector3(...targetPreset.position);
30-
const targetTarget = new Vector3(...targetPreset.target);
31-
32-
camera.position.lerp(targetPosition, 0.05);
33-
controlsRef.current.target.lerp(targetTarget, 0.05);
50+
camera.position.lerp(targetCameraPosition, 0.05);
51+
controlsRef.current.target.lerp(targetBodyPosition, 0.05);
3452

35-
const positionDistance = camera.position.distanceTo(targetPosition);
36-
const targetDistance = controlsRef.current.target.distanceTo(targetTarget);
53+
const positionDistance = camera.position.distanceTo(targetCameraPosition);
54+
const targetDistance = controlsRef.current.target.distanceTo(targetBodyPosition);
3755

3856
if (positionDistance < 0.1 && targetDistance < 0.1) {
3957
setTransitioning(false);

tidal-harmonics/src/hooks/useCamera.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { useCameraStore } from '@/stores/cameraStore';
1+
import { useCameraStore, CAMERA_PRESET_CONFIGS } from '@/stores/cameraStore';
22

33
export function useCamera() {
44
const store = useCameraStore();
55

6-
const currentPresetConfig = store.presets[store.preset];
6+
const currentPresetConfig = CAMERA_PRESET_CONFIGS[store.preset];
77

88
return {
99
...store,

tidal-harmonics/src/stores/cameraStore.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
11
import { create } from 'zustand';
2-
import type { CameraPreset, CameraTarget } from '@/types';
2+
import type { CameraPreset } from '@/types';
3+
4+
// Camera offset from target body (not absolute position)
5+
interface CameraPresetConfig {
6+
offset: [number, number, number]; // Offset from the target body
7+
fov: number;
8+
}
39

410
interface CameraState {
511
preset: CameraPreset;
612
isTransitioning: boolean;
7-
presets: Record<CameraPreset, CameraTarget>;
813
setPreset: (preset: CameraPreset) => void;
914
setTransitioning: (transitioning: boolean) => void;
1015
}
1116

12-
const CAMERA_PRESETS: Record<CameraPreset, CameraTarget> = {
17+
// Offsets are relative to the target body's position
18+
export const CAMERA_PRESET_CONFIGS: Record<CameraPreset, CameraPresetConfig> = {
1319
overview: {
14-
position: [50, 30, 50],
15-
target: [0, 0, 0],
20+
offset: [50, 30, 50], // Offset from Earth (origin)
1621
fov: 60,
1722
},
1823
earth: {
19-
position: [0, 0, 8],
20-
target: [0, 0, 0],
24+
offset: [0, 3, 8], // Close orbit around Earth
2125
fov: 45,
2226
},
2327
moon: {
24-
position: [35, 5, 5],
25-
target: [30, 0, 0],
28+
offset: [3, 2, 5], // Offset from Moon's current position
2629
fov: 45,
2730
},
2831
sun: {
29-
position: [-188, 10, 10],
30-
target: [-200, 0, 0],
32+
offset: [12, 8, 12], // Offset from Sun's current position
3133
fov: 45,
3234
},
3335
};
3436

3537
export const useCameraStore = create<CameraState>((set) => ({
3638
preset: 'overview',
3739
isTransitioning: false,
38-
presets: CAMERA_PRESETS,
3940

4041
setPreset: (preset) =>
4142
set({

0 commit comments

Comments
 (0)