Phase 0: monorepo skeleton (hub, live-map, api, packages, infra, CI)
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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
|
||||
const proj = occluderToObserver.x * sx + occluderToObserver.y * sy + occluderToObserver.z * sz;
|
||||
if (proj >= 0) {
|
||||
// Occluder is behind the observer relative to the 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 chord length through the occluder disc
|
||||
const halfChord = Math.sqrt(occluderRadius * occluderRadius - perpDist * perpDist);
|
||||
// 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.
|
||||
// For a "fraction in shadow" treat the occluder disc as fully shadowing
|
||||
// when perpDist + halfChord reaches the observer; that simplifies to
|
||||
// perpDist < occluderRadius which we already check.
|
||||
return Math.min(1, 1 - perpDist / occluderRadius);
|
||||
}
|
||||
Reference in New Issue
Block a user