Reusable 3D Earth globe widget (pure OCaml + WebGL)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(ocaml-globe): add Satellite abstraction with cached Kepler elements

New module Globe.Satellite:
- v: create from J2000 state vector, precomputes orbital elements
and ghost orbit points (once, at creation)
- position_at: current GL position from precomputed elements (~20 FLOPs)
- trail_positions: per-frame trail array for Orbit.add_trail
- ghost_points: cached full-orbit for Orbit.add_ghost (never recomputed)
- dot: position + color pair for Orbit.draw dots

Eliminates manual caching of elements/ghosts in app code.
Typical usage cuts ~80 lines from main.ml:

let sat = Globe.Satellite.v ~pos ~vel ~color () in
(* once: *)
Orbit.add_ghost gl orbit (Satellite.ghost_points sat) ~color:Color.ghost;
(* each frame: *)
Orbit.clear_trails orbit;
Orbit.add_trail gl orbit (Satellite.trail_positions sat ~dt) ...

+92 -1
+1 -1
lib/dune
··· 1 1 (library 2 2 (name globe) 3 3 (public_name globe) 4 - (libraries coordinate fmt)) 4 + (libraries kepler coordinate fmt))
+51
lib/satellite.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Thomas Gazagnaire. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Satellite state management for globe rendering. 7 + 8 + Caches precomputed Kepler elements, generates ghost orbits and 9 + per-frame trail positions. Pure OCaml, no WebGL dependency. *) 10 + 11 + type t = { 12 + elements : Kepler.Analytic.elements; 13 + pos : Kepler.Vec3.t; 14 + vel : Kepler.Vec3.t; 15 + color : Color.t; 16 + period : float; 17 + ghost_points : Math.Vec3.t option array; 18 + trail_length : int; 19 + } 20 + 21 + let v ~pos ~vel ~color ?(trail_length = 50) () = 22 + let elements = Kepler.Analytic.precompute ~pos ~vel in 23 + let period = Kepler.Analytic.period elements in 24 + let ghost_points = 25 + let n = 120 in 26 + Array.init n (fun i -> 27 + let t = Float.of_int i *. period /. Float.of_int n in 28 + let p = Kepler.Analytic.at_precomputed elements ~dt:t in 29 + Some (Gl_coord.of_astro p.x p.y p.z)) 30 + in 31 + { elements; pos; vel; color; period; ghost_points; trail_length } 32 + 33 + let color t = t.color 34 + let period t = t.period 35 + let ghost_points t = t.ghost_points 36 + 37 + let position_at t ~dt = 38 + let p = Kepler.Analytic.at_precomputed t.elements ~dt in 39 + Gl_coord.of_astro p.x p.y p.z 40 + 41 + let trail_positions t ~dt = 42 + let n = t.trail_length in 43 + let step = t.period /. Float.of_int (n * 3) in 44 + Array.init n (fun i -> 45 + let t_offset = dt -. (Float.of_int (n - 1 - i) *. step) in 46 + let p = Kepler.Analytic.at_precomputed t.elements ~dt:t_offset in 47 + Some (Gl_coord.of_astro p.x p.y p.z)) 48 + 49 + let dot t ~dt = 50 + let pos = position_at t ~dt in 51 + (pos, t.color)
+40
lib/satellite.mli
··· 1 + (** Satellite state management for globe rendering. 2 + 3 + Caches precomputed Kepler elements, generates ghost orbits and 4 + per-frame trail positions. Pure OCaml, no WebGL dependency. *) 5 + 6 + type t 7 + (** Satellite state with cached orbital elements and ghost orbit. *) 8 + 9 + val v : 10 + pos:Kepler.Vec3.t -> 11 + vel:Kepler.Vec3.t -> 12 + color:Color.t -> 13 + ?trail_length:int -> 14 + unit -> 15 + t 16 + (** [v ~pos ~vel ~color ?trail_length ()] creates a satellite from a 17 + J2000 state vector. Precomputes orbital elements and ghost orbit. 18 + [trail_length] defaults to 50 points. *) 19 + 20 + val color : t -> Color.t 21 + (** [color t] is the satellite's display color. *) 22 + 23 + val period : t -> float 24 + (** [period t] is the orbital period in seconds. *) 25 + 26 + val ghost_points : t -> Math.Vec3.t option array 27 + (** [ghost_points t] is the precomputed full-orbit ghost (GL coords). 28 + Computed once at creation, reusable across frames. *) 29 + 30 + val position_at : t -> dt:float -> Math.Vec3.t 31 + (** [position_at t ~dt] is the satellite's GL position at [dt] seconds 32 + from epoch. Very fast (~20 FLOPs). *) 33 + 34 + val trail_positions : t -> dt:float -> Math.Vec3.t option array 35 + (** [trail_positions t ~dt] generates trail points up to [dt], fading 36 + into the past. For use with {!Globe_webgl.Orbit.add_trail}. *) 37 + 38 + val dot : t -> dt:float -> Math.Vec3.t * Color.t 39 + (** [dot t ~dt] is the satellite's current position and color, 40 + for building {!Globe_webgl.Orbit.dot} values. *)