07cc5321d1
CI / Lint, typecheck, test, build (pull_request) Failing after 9s
Calculators (apps/live-map/src/calculators/): - eclipse.ts: findEclipseWindows(bodies, opts) — coarse scan with threshold-crossing detection, bisection to refine start/end, ternary search to find peak. Handles eclipse already in progress at scan-start. Uses sun = parentId===null body. - overpass.ts: findOverpasses(opts) — coarse scan for local distance minima, ternary refinement. Targets: vessel, body, ground station (lat/lon/alt → heliocentric). UI: - panels/CalculatorsPanel.tsx: collapsible bottom-center panel with two tabs. Eclipse form: observer, eclipser, from UT → 3 windows. Overpass form: observer vessel, target kind+id, max dist → 5 passes. - timeFormat.ts: shared KSP-time formatters. Live-map camera polish (apps/live-map/src/scene/): - camera.ts: CameraController — log-scale distance (z→exp(z)*1e8 m, range -3..12), spherical orbit around target, smooth lerp to selected body/vessel. Mouse wheel zooms, drag rotates, click raycasts for track toggle. Pointer-move-distance gate to distinguish click from drag. - glow.ts: additive shader-based atmospheric halo (rim-falloff fragment shader, BackSide) attached as child of body mesh. - layout.ts: bodyPositionAt now returns true heliocentric (walks parent chain); previous version returned parent-relative for non-root children which broke the eclipse calculator. Bug fix: - packages/orbital-math/src/occultation.ts: sign of projection check was inverted. `proj <= 0` correctly returns 0 (occluder behind observer), `proj > 0` triggers eclipse computation. Tests: 28 live-map tests (10 scene + 12 calculator + 6 camera), 45 total across the workspace, all passing.
53 lines
2.3 KiB
TypeScript
53 lines
2.3 KiB
TypeScript
/**
|
|
* Geometric occultation: given a position (relative to a body's center)
|
|
* and the radii of the occluder (R1) and the body the observer is on (R2),
|
|
* is the observer currently in shadow?
|
|
*
|
|
* Used for both:
|
|
* - "is this vessel in the planet's shadow?" (R1 = planet radius, R2 ≈ 0)
|
|
* - "is this ground station blocked by the local terrain?" (R1 = planet, R2 = earth station)
|
|
*
|
|
* Returns the fraction (0..1) of the line of sight to the sun that is
|
|
* occluded. 0 = full sun, 1 = total eclipse.
|
|
*
|
|
* Note: the canonical way to do this is to compute the half-angle between
|
|
* the sun and the occluding body as seen by the observer. We treat the
|
|
* sun as effectively at infinity (parallel rays) which is fine for KSP
|
|
* since Kerbol is the system root and we're never going to need parallax
|
|
* precision at this scale.
|
|
*/
|
|
export function shadowFraction(
|
|
observerToSun: { x: number; y: number; z: number },
|
|
occluderToObserver: { x: number; y: number; z: number },
|
|
occluderRadius: number,
|
|
): number {
|
|
// Vector from observer to sun, normalized
|
|
const sunDist = Math.hypot(observerToSun.x, observerToSun.y, observerToSun.z);
|
|
if (sunDist === 0) return 0;
|
|
const sx = observerToSun.x / sunDist;
|
|
const sy = observerToSun.y / sunDist;
|
|
const sz = observerToSun.z / sunDist;
|
|
|
|
// Project occluder center onto the sun-direction line.
|
|
// Both `observerToSun` and `occluderToObserver` point AWAY from
|
|
// the observer (toward the sun / toward the occluder). When the
|
|
// occluder sits between observer and sun, both vectors point in
|
|
// roughly the same direction and `proj` is positive.
|
|
const proj = occluderToObserver.x * sx + occluderToObserver.y * sy + occluderToObserver.z * sz;
|
|
if (proj <= 0) {
|
|
// Occluder is behind the observer (opposite direction from sun) → no eclipse
|
|
return 0;
|
|
}
|
|
// Perpendicular distance from occluder center to sun ray
|
|
const px = occluderToObserver.x - proj * sx;
|
|
const py = occluderToObserver.y - proj * sy;
|
|
const pz = occluderToObserver.z - proj * sz;
|
|
const perpDist = Math.hypot(px, py, pz);
|
|
|
|
if (perpDist >= occluderRadius) return 0;
|
|
// Approximate the angular size of the sun as seen from the occluder
|
|
// vs the angular size of the occluder; we use 1.0 for the sun
|
|
// (i.e. effectively point source) — good enough for visualization.
|
|
return Math.min(1, 1 - perpDist / occluderRadius);
|
|
}
|