@motioncomplex/cosmos-lib 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +125 -0
  2. package/dist/api.d.ts +246 -0
  3. package/dist/cache.d.ts +11 -0
  4. package/dist/clock.d.ts +181 -0
  5. package/dist/constants.d.ts +43 -0
  6. package/dist/data/constellations.d.ts +58 -0
  7. package/dist/data/cutouts.d.ts +70 -0
  8. package/dist/data/deep-sky.d.ts +27 -0
  9. package/dist/data/images.d.ts +147 -0
  10. package/dist/data/index.d.ts +422 -0
  11. package/dist/data/messier.d.ts +61 -0
  12. package/dist/data/ps1-files.d.ts +11 -0
  13. package/dist/data/showers.d.ts +62 -0
  14. package/dist/data/solar-system.d.ts +34 -0
  15. package/dist/data/stars.d.ts +57 -0
  16. package/dist/data/textures.d.ts +67 -0
  17. package/dist/eclipse.d.ts +176 -0
  18. package/dist/index.cjs +1 -0
  19. package/dist/index.d.ts +237 -0
  20. package/dist/index.js +713 -0
  21. package/dist/math.d.ts +532 -0
  22. package/dist/media-DVOcIMa1.js +252 -0
  23. package/dist/media-DlE7RKBL.cjs +1 -0
  24. package/dist/media.d.ts +217 -0
  25. package/dist/moon.d.ts +170 -0
  26. package/dist/planner.d.ts +224 -0
  27. package/dist/react/index.cjs +1 -0
  28. package/dist/react/index.d.ts +167 -0
  29. package/dist/react/index.js +163 -0
  30. package/dist/skymap-hittest.d.ts +69 -0
  31. package/dist/skymap-interactive-CLg6FA0X.js +6377 -0
  32. package/dist/skymap-interactive-D2OZFwJ7.cjs +1 -0
  33. package/dist/skymap-interactive.d.ts +153 -0
  34. package/dist/skymap.d.ts +172 -0
  35. package/dist/sun.d.ts +119 -0
  36. package/dist/three/factories.d.ts +160 -0
  37. package/dist/three/flight.d.ts +116 -0
  38. package/dist/three/index.cjs +20 -0
  39. package/dist/three/index.d.ts +21 -0
  40. package/dist/three/index.js +404 -0
  41. package/dist/three/lod.d.ts +100 -0
  42. package/dist/three/shaders.d.ts +22 -0
  43. package/dist/three/types.d.ts +169 -0
  44. package/dist/transitions.d.ts +246 -0
  45. package/dist/types.d.ts +730 -0
  46. package/dist/units.d.ts +132 -0
  47. package/package.json +93 -0
@@ -0,0 +1,224 @@
1
+ import type { CelestialObject, ObserverParams, ObjectType, PlanetName } from './types.js';
2
+ /** A visible object with its current horizontal position and metadata. */
3
+ export interface VisibleObject {
4
+ /** The catalog object. */
5
+ object: CelestialObject;
6
+ /** Current altitude in degrees (above horizon). */
7
+ alt: number;
8
+ /** Current azimuth in degrees (0=N, 90=E, 180=S, 270=W). */
9
+ az: number;
10
+ /** Angular separation from the Moon in degrees, or `null` if Moon position unavailable. */
11
+ moonSeparation: number | null;
12
+ /** Moon interference score 0–1 (0 = no interference, 1 = full moon on top of object). */
13
+ moonInterference: number;
14
+ }
15
+ /** Options for {@link Planner.whatsUp}. */
16
+ export interface WhatsUpOptions {
17
+ /** Minimum altitude in degrees above horizon. @defaultValue `10` */
18
+ minAltitude?: number;
19
+ /** Maximum (faintest) apparent magnitude to include. @defaultValue `6` */
20
+ magnitudeLimit?: number;
21
+ /** Filter by object type(s). */
22
+ types?: ObjectType[];
23
+ /** Filter by constellation abbreviation (3-letter, case-insensitive). */
24
+ constellation?: string;
25
+ /** Filter by catalog tag (e.g. `'messier'`). */
26
+ tag?: string;
27
+ /** Maximum number of results. @defaultValue `50` */
28
+ limit?: number;
29
+ }
30
+ /** A point on the altitude-vs-time visibility curve. */
31
+ export interface VisibilityCurvePoint {
32
+ /** The date/time of this sample. */
33
+ date: Date;
34
+ /** Altitude in degrees at this time. */
35
+ alt: number;
36
+ /** Azimuth in degrees at this time. */
37
+ az: number;
38
+ }
39
+ /** Result from {@link Planner.bestWindow}. */
40
+ export interface BestWindowResult {
41
+ /** Time of maximum altitude. */
42
+ peak: Date;
43
+ /** Maximum altitude in degrees. */
44
+ peakAltitude: number;
45
+ /** Rise time (altitude crosses `minAlt`), or `null` if already up at start of night. */
46
+ rise: Date | null;
47
+ /** Set time (altitude drops below `minAlt`), or `null` if still up at end of night. */
48
+ set: Date | null;
49
+ }
50
+ /** Result from planet opposition/conjunction detection. */
51
+ export interface PlanetEvent {
52
+ /** The planet involved. */
53
+ planet: PlanetName;
54
+ /** Type of event. */
55
+ type: 'opposition' | 'conjunction';
56
+ /** Approximate date of the event. */
57
+ date: Date;
58
+ /** Elongation from the Sun in degrees at the event. */
59
+ elongation: number;
60
+ }
61
+ /** Moon interference details for a target. */
62
+ export interface MoonInterference {
63
+ /** Angular separation from the Moon in degrees. */
64
+ separation: number;
65
+ /** Moon illumination fraction 0–1. */
66
+ illumination: number;
67
+ /** Combined interference score 0–1 (0 = no interference, 1 = worst case). */
68
+ score: number;
69
+ }
70
+ /** Airmass at a given time. */
71
+ export interface AirmassPoint {
72
+ /** The date/time of this sample. */
73
+ date: Date;
74
+ /** Altitude in degrees. */
75
+ alt: number;
76
+ /** Airmass value (1.0 at zenith, higher near horizon). */
77
+ airmass: number;
78
+ }
79
+ /**
80
+ * Observation planning utilities — "What can I see tonight?"
81
+ *
82
+ * Provides functions to determine which objects are visible for a given
83
+ * observer, compute optimal viewing windows, altitude curves, moon
84
+ * interference scoring, airmass calculations, and planet event detection.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * import { Planner } from '@motioncomplex/cosmos-lib'
89
+ *
90
+ * const observer = { lat: 47.05, lng: 8.31, date: new Date('2024-08-15') }
91
+ *
92
+ * // What's visible tonight?
93
+ * const visible = Planner.whatsUp(observer)
94
+ * visible.forEach(v => console.log(v.object.name, `alt: ${v.alt.toFixed(1)}°`))
95
+ *
96
+ * // Best time to observe M42
97
+ * const window = Planner.bestWindow('m42', observer)
98
+ * console.log('Peak altitude:', window?.peakAltitude.toFixed(1))
99
+ *
100
+ * // Altitude curve for Sirius
101
+ * const curve = Planner.visibilityCurve('sirius', observer)
102
+ * ```
103
+ */
104
+ export declare const Planner: {
105
+ /**
106
+ * Objects currently above the horizon, sorted by altitude (highest first).
107
+ *
108
+ * Returns all catalog objects that are above the specified minimum altitude
109
+ * at the observer's date/time, filtered by optional magnitude limit,
110
+ * object type, constellation, or catalog tag. Each result includes the
111
+ * current alt/az position and moon interference scoring.
112
+ *
113
+ * @param observer - Observer location and time.
114
+ * @param options - Filtering and sorting options.
115
+ * @returns Visible objects sorted by altitude (highest first).
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const visible = Planner.whatsUp(
120
+ * { lat: 47.05, lng: 8.31, date: new Date('2024-08-15T22:00:00Z') },
121
+ * { minAltitude: 15, magnitudeLimit: 5, types: ['nebula', 'cluster', 'galaxy'] }
122
+ * )
123
+ * ```
124
+ */
125
+ readonly whatsUp: (observer: ObserverParams, options?: WhatsUpOptions) => VisibleObject[];
126
+ /**
127
+ * Find the best observation window for an object on a given night.
128
+ *
129
+ * Samples the object's altitude between astronomical dusk and dawn,
130
+ * returning the peak altitude time and the rise/set times relative
131
+ * to the minimum observable altitude.
132
+ *
133
+ * @param objectId - Catalog object ID (e.g. `'m42'`, `'sirius'`, `'jupiter'`).
134
+ * @param observer - Observer location and date (date determines which night).
135
+ * @param minAlt - Minimum useful altitude in degrees. @defaultValue `10`
136
+ * @returns The best window, or `null` if the object is never above `minAlt` during darkness.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const window = Planner.bestWindow('m42', { lat: 47.05, lng: 8.31, date: new Date('2024-01-15') })
141
+ * if (window) {
142
+ * console.log('Peak at', window.peak, `(${window.peakAltitude.toFixed(1)}°)`)
143
+ * }
144
+ * ```
145
+ */
146
+ readonly bestWindow: (objectId: string, observer: ObserverParams, minAlt?: number) => BestWindowResult | null;
147
+ /**
148
+ * Compute altitude vs. time for an object over a night.
149
+ *
150
+ * Returns an array of altitude/azimuth samples from astronomical dusk
151
+ * to astronomical dawn, suitable for plotting a visibility curve.
152
+ *
153
+ * @param objectId - Catalog object ID.
154
+ * @param observer - Observer location and date.
155
+ * @param steps - Number of samples. @defaultValue `100`
156
+ * @returns Array of time/alt/az points, or `null` if the object is unknown or no darkness occurs.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const curve = Planner.visibilityCurve('sirius', { lat: 47.05, lng: 8.31, date: new Date('2024-01-15') })
161
+ * // Plot curve.map(p => ({ x: p.date, y: p.alt }))
162
+ * ```
163
+ */
164
+ readonly visibilityCurve: (objectId: string, observer: ObserverParams, steps?: number) => VisibilityCurvePoint[] | null;
165
+ /**
166
+ * Detect oppositions and conjunctions for outer planets near a given date.
167
+ *
168
+ * Scans a date range (default: 1 year forward) for dates where a planet's
169
+ * elongation from the Sun is near 180° (opposition) or 0° (conjunction).
170
+ *
171
+ * @param observer - Observer with date as the start of the search range.
172
+ * @param options - Search options.
173
+ * @returns Array of detected planet events, sorted by date.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const events = Planner.planetEvents({ lat: 47, lng: 8, date: new Date('2024-01-01') })
178
+ * events.forEach(e => console.log(e.planet, e.type, e.date.toISOString()))
179
+ * ```
180
+ */
181
+ readonly planetEvents: (observer: ObserverParams, options?: {
182
+ planets?: PlanetName[];
183
+ days?: number;
184
+ }) => PlanetEvent[];
185
+ /**
186
+ * Moon interference score for a specific target.
187
+ *
188
+ * Combines angular separation from the Moon with the current lunar
189
+ * illumination to produce a 0–1 interference score. Score of 0 means
190
+ * no interference (new moon or far away), 1 means maximum interference
191
+ * (full moon very close to the target).
192
+ *
193
+ * @param objectId - Catalog object ID.
194
+ * @param observer - Observer location and time.
195
+ * @returns Moon interference details, or `null` if the object is unknown.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * const mi = Planner.moonInterference('m42', { lat: 47, lng: 8, date: new Date() })
200
+ * if (mi && mi.score > 0.5) console.log('Significant moon interference!')
201
+ * ```
202
+ */
203
+ readonly moonInterference: (objectId: string, observer: ObserverParams) => MoonInterference | null;
204
+ /**
205
+ * Airmass curve for an object over a night.
206
+ *
207
+ * Returns airmass values (using Kasten & Young 1989 formula) sampled
208
+ * from astronomical dusk to dawn. Airmass is 1.0 at zenith and increases
209
+ * toward the horizon. Only points where the object is above the horizon
210
+ * are included (airmass at or below 0° altitude is infinite).
211
+ *
212
+ * @param objectId - Catalog object ID.
213
+ * @param observer - Observer location and date.
214
+ * @param steps - Number of samples. @defaultValue `100`
215
+ * @returns Array of time/alt/airmass points (only above-horizon), or `null`.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * const am = Planner.airmassCurve('sirius', { lat: 47, lng: 8, date: new Date('2024-01-15') })
220
+ * // Plot am.map(p => ({ x: p.date, y: p.airmass }))
221
+ * ```
222
+ */
223
+ readonly airmassCurve: (objectId: string, observer: ObserverParams, steps?: number) => AirmassPoint[] | null;
224
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react/jsx-runtime"),c=require("react"),i=require("../skymap-interactive-D2OZFwJ7.cjs"),D=typeof window<"u";function C(e,t,s=1e4){const[o,f]=c.useState(null);return c.useEffect(()=>{const a=()=>{const u=i.Data.get(e);if(!u){f(null);return}const p=new Date,r=q(u,p);if(!r){f(null);return}f(i.AstroMath.equatorialToHorizontal(r,{...t,date:p}))};if(a(),!D)return;const h=setInterval(a,s);return()=>clearInterval(h)},[e,t.lat,t.lng,s]),o}function E(e,t=6e4){const[s,o]=c.useState(()=>i.Moon.phase(e??new Date));return c.useEffect(()=>{if(e){o(i.Moon.phase(e));return}const f=()=>o(i.Moon.phase(new Date));if(f(),!D)return;const a=setInterval(f,t);return()=>clearInterval(a)},[e==null?void 0:e.valueOf(),t]),s}function M(e={}){const[t,s]=c.useState(()=>e.startDate??new Date),[o,f]=c.useState(e.speed??1),[a,h]=c.useState(e.autoPlay??!1),u=c.useRef(null);u.current===null&&(u.current=new i.AstroClock(e)),c.useEffect(()=>{const n=u.current,d=k=>s(k.date),g=()=>h(!0),S=()=>h(!1),l=k=>s(k.date),P=k=>f(k.speed);return n.on("tick",d),n.on("play",g),n.on("pause",S),n.on("datechange",l),n.on("speedchange",P),()=>{n.off("tick",d),n.off("play",g),n.off("pause",S),n.off("datechange",l),n.off("speedchange",P),n.dispose(),u.current=null}},[]);const p=c.useCallback(()=>{var n;return(n=u.current)==null?void 0:n.play()},[]),r=c.useCallback(()=>{var n;return(n=u.current)==null?void 0:n.pause()},[]),y=c.useCallback(n=>{var d;return(d=u.current)==null?void 0:d.setDate(n)},[]),w=c.useCallback(n=>{var d;return(d=u.current)==null?void 0:d.setSpeed(n)},[]);return{date:t,speed:o,playing:a,play:p,pause:r,setDate:y,setSpeed:w,clock:u.current}}function v(e,t={},s=3e4){var a,h;const[o,f]=c.useState([]);return c.useEffect(()=>{const u=()=>{const r={...e,date:e.date??new Date};f(i.Planner.whatsUp(r,t))};if(u(),!D)return;const p=setInterval(u,s);return()=>clearInterval(p)},[e.lat,e.lng,(a=e.date)==null?void 0:a.valueOf(),s,t.minAltitude,t.magnitudeLimit,t.limit,t.tag,t.constellation,(h=t.types)==null?void 0:h.join(",")]),o}function x(e,t){var s;return c.useMemo(()=>{const o={...e,date:t??e.date??new Date};return i.Sun.twilight(o)},[e.lat,e.lng,t==null?void 0:t.valueOf(),(s=e.date)==null?void 0:s.valueOf()])}function I({objects:e,width:t="100%",height:s="400px",onSelect:o,onHover:f,skymapRef:a,...h}){const u=c.useRef(null),p=c.useRef(null);return c.useEffect(()=>{const r=u.current;if(!r||!D)return;const y=r.parentElement;if(!y)return;const w=window.devicePixelRatio||1,n=y.getBoundingClientRect();r.width=n.width*w,r.height=n.height*w,r.style.width=`${n.width}px`,r.style.height=`${n.height}px`;const d=e??i.Data.all().filter(l=>l.ra!=null&&l.dec!=null),g=new i.InteractiveSkyMap(r,d,h);p.current=g,a&&"current"in a&&(a.current=g),o&&g.on("select",({object:l})=>o(l)),f&&g.on("hover",({object:l})=>f(l));const S=()=>{const l=y.getBoundingClientRect();r.width=l.width*w,r.height=l.height*w,r.style.width=`${l.width}px`,r.style.height=`${l.height}px`,g.render()};return window.addEventListener("resize",S),()=>{window.removeEventListener("resize",S),g.dispose(),p.current=null,a&&"current"in a&&(a.current=null)}},[]),m.jsx("div",{style:{width:t,height:s,position:"relative"},children:m.jsx("canvas",{ref:u,style:{width:"100%",height:"100%",display:"block"},"aria-label":"Interactive sky map"})})}function q(e,t){if(e.ra!==null&&e.dec!==null)return{ra:e.ra,dec:e.dec};if(e.type==="planet")try{const s=i.AstroMath.planetEcliptic(e.id,t);return i.AstroMath.eclipticToEquatorial(s)}catch{return null}if(e.id==="sun"){const s=i.Sun.position(t);return{ra:s.ra,dec:s.dec}}if(e.id==="moon"){const s=i.Moon.position(t);return{ra:s.ra,dec:s.dec}}return null}exports.SkyMap=I;exports.useAstroClock=M;exports.useMoonPhase=E;exports.useSkyPosition=C;exports.useTwilight=x;exports.useWhatsUp=v;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * React hooks for `@motioncomplex/cosmos-lib`.
3
+ *
4
+ * Import from the `/react` sub-path:
5
+ *
6
+ * ```ts
7
+ * import { useSkyPosition, useMoonPhase, useWhatsUp } from '@motioncomplex/cosmos-lib/react'
8
+ * ```
9
+ *
10
+ * All hooks are SSR-safe — they return sensible defaults during server
11
+ * rendering and only start intervals/effects in the browser. React is an
12
+ * optional peer dependency; this module is not included in the core bundle.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ import { type RefObject } from 'react';
17
+ import { AstroClock } from '../clock.js';
18
+ import { InteractiveSkyMap } from '../skymap-interactive.js';
19
+ import type { ObserverParams, HorizontalCoord, MoonPhase, TwilightTimes, CelestialObject, InteractiveSkyMapOptions } from '../types.js';
20
+ import type { VisibleObject, WhatsUpOptions } from '../planner.js';
21
+ import type { AstroClockOptions } from '../clock.js';
22
+ /**
23
+ * Reactive altitude/azimuth for any catalog object.
24
+ *
25
+ * Recomputes the horizontal position at a configurable interval (default 10s).
26
+ * For solar-system bodies, dynamically computes the current RA/Dec.
27
+ *
28
+ * @param objectId - Catalog object ID (e.g. `'sirius'`, `'mars'`, `'m42'`).
29
+ * @param observer - Observer location.
30
+ * @param intervalMs - Update interval in milliseconds. @defaultValue `10000`
31
+ * @returns Current `{ alt, az }` or `null` if the object is unknown.
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * const pos = useSkyPosition('sirius', { lat: 47.05, lng: 8.31 })
36
+ * return <p>Sirius: {pos?.alt.toFixed(1)}° alt</p>
37
+ * ```
38
+ */
39
+ export declare function useSkyPosition(objectId: string, observer: ObserverParams, intervalMs?: number): HorizontalCoord | null;
40
+ /**
41
+ * Reactive Moon phase data.
42
+ *
43
+ * Returns current phase, illumination, age, and name, updating at
44
+ * the specified interval (default 60s).
45
+ *
46
+ * @param date - Fixed date, or omit for live updates.
47
+ * @param intervalMs - Update interval in ms (only used when `date` is omitted). @defaultValue `60000`
48
+ * @returns Current {@link MoonPhase} data.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * const phase = useMoonPhase()
53
+ * return <p>{phase.name} — {(phase.illumination * 100).toFixed(0)}%</p>
54
+ * ```
55
+ */
56
+ export declare function useMoonPhase(date?: Date, intervalMs?: number): MoonPhase;
57
+ /** Return value of {@link useAstroClock}. */
58
+ export interface UseAstroClockReturn {
59
+ /** Current simulation date (updates on each tick). */
60
+ date: Date;
61
+ /** Current speed multiplier. */
62
+ speed: number;
63
+ /** Whether the clock is playing. */
64
+ playing: boolean;
65
+ /** Start or resume playback. */
66
+ play: () => void;
67
+ /** Pause playback. */
68
+ pause: () => void;
69
+ /** Set the simulation date. */
70
+ setDate: (date: Date) => void;
71
+ /** Set the speed multiplier. */
72
+ setSpeed: (speed: number) => void;
73
+ /** The underlying AstroClock instance. */
74
+ clock: AstroClock;
75
+ }
76
+ /**
77
+ * AstroClock instance managed by React lifecycle.
78
+ *
79
+ * Creates, controls, and disposes an {@link AstroClock} automatically.
80
+ * State updates (date, speed, playing) trigger re-renders.
81
+ *
82
+ * @param options - AstroClock options.
83
+ * @returns Controls and reactive state for the clock.
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * const { date, playing, play, pause, setSpeed } = useAstroClock({ speed: 60 })
88
+ * return (
89
+ * <div>
90
+ * <p>{date.toLocaleTimeString()}</p>
91
+ * <button onClick={playing ? pause : play}>{playing ? 'Pause' : 'Play'}</button>
92
+ * </div>
93
+ * )
94
+ * ```
95
+ */
96
+ export declare function useAstroClock(options?: AstroClockOptions): UseAstroClockReturn;
97
+ /**
98
+ * Reactive list of visible objects above the horizon.
99
+ *
100
+ * Recomputes at the specified interval using {@link Planner.whatsUp}.
101
+ *
102
+ * @param observer - Observer location.
103
+ * @param options - WhatsUp filtering options.
104
+ * @param intervalMs - Update interval in ms. @defaultValue `30000`
105
+ * @returns Array of visible objects with alt/az and moon interference.
106
+ *
107
+ * @example
108
+ * ```tsx
109
+ * const visible = useWhatsUp({ lat: 47, lng: 8 }, { magnitudeLimit: 4, limit: 10 })
110
+ * return <ul>{visible.map(v => <li key={v.object.id}>{v.object.name} ({v.alt.toFixed(0)}°)</li>)}</ul>
111
+ * ```
112
+ */
113
+ export declare function useWhatsUp(observer: ObserverParams, options?: WhatsUpOptions, intervalMs?: number): VisibleObject[];
114
+ /**
115
+ * Reactive twilight times (sunrise, sunset, dawn, dusk).
116
+ *
117
+ * Recomputes daily or when the observer changes.
118
+ *
119
+ * @param observer - Observer location.
120
+ * @param date - Fixed date, or omit for today.
121
+ * @returns {@link TwilightTimes} for the given location and date.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * const tw = useTwilight({ lat: 51.5, lng: -0.1 })
126
+ * return <p>Sunset: {tw.sunset?.toLocaleTimeString()}</p>
127
+ * ```
128
+ */
129
+ export declare function useTwilight(observer: ObserverParams, date?: Date): TwilightTimes;
130
+ /** Props for {@link SkyMap}. */
131
+ export interface SkyMapProps extends InteractiveSkyMapOptions {
132
+ /** Objects to render. Defaults to `Data.all()`. */
133
+ objects?: CelestialObject[];
134
+ /** Width CSS value. @defaultValue `'100%'` */
135
+ width?: string | number;
136
+ /** Height CSS value. @defaultValue `'400px'` */
137
+ height?: string | number;
138
+ /** Callback when an object is selected. */
139
+ onSelect?: (object: CelestialObject) => void;
140
+ /** Callback when a hover changes. */
141
+ onHover?: (object: CelestialObject | null) => void;
142
+ /** Ref to access the underlying InteractiveSkyMap instance. */
143
+ skymapRef?: RefObject<InteractiveSkyMap | null>;
144
+ }
145
+ /**
146
+ * React component wrapping {@link InteractiveSkyMap}.
147
+ *
148
+ * Manages canvas lifecycle, DPI scaling, resize handling, and event
149
+ * wiring automatically. SSR-safe — renders an empty `<canvas>` on the
150
+ * server and initialises the sky map on mount.
151
+ *
152
+ * @example
153
+ * ```tsx
154
+ * import { SkyMap } from '@motioncomplex/cosmos-lib/react'
155
+ *
156
+ * <SkyMap
157
+ * projection="stereographic"
158
+ * center={{ ra: 83.8, dec: -5.4 }}
159
+ * scale={400}
160
+ * observer={{ lat: 47.05, lng: 8.31 }}
161
+ * onSelect={obj => console.log(obj.name)}
162
+ * width="100%"
163
+ * height="500px"
164
+ * />
165
+ * ```
166
+ */
167
+ export declare function SkyMap({ objects, width, height, onSelect, onHover, skymapRef, ...opts }: SkyMapProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,163 @@
1
+ import { jsx as C } from "react/jsx-runtime";
2
+ import { useRef as v, useEffect as D, useState as g, useCallback as P, useMemo as q } from "react";
3
+ import { D as E, I as z, b as F, M as k, S as A, A as x, P as M } from "../skymap-interactive-CLg6FA0X.js";
4
+ const S = typeof window < "u";
5
+ function B(t, e, r = 1e4) {
6
+ const [l, u] = g(null);
7
+ return D(() => {
8
+ const a = () => {
9
+ const c = E.get(t);
10
+ if (!c) {
11
+ u(null);
12
+ return;
13
+ }
14
+ const f = /* @__PURE__ */ new Date(), s = O(c, f);
15
+ if (!s) {
16
+ u(null);
17
+ return;
18
+ }
19
+ u(x.equatorialToHorizontal(s, { ...e, date: f }));
20
+ };
21
+ if (a(), !S) return;
22
+ const d = setInterval(a, r);
23
+ return () => clearInterval(d);
24
+ }, [t, e.lat, e.lng, r]), l;
25
+ }
26
+ function L(t, e = 6e4) {
27
+ const [r, l] = g(() => k.phase(t ?? /* @__PURE__ */ new Date()));
28
+ return D(() => {
29
+ if (t) {
30
+ l(k.phase(t));
31
+ return;
32
+ }
33
+ const u = () => l(k.phase(/* @__PURE__ */ new Date()));
34
+ if (u(), !S) return;
35
+ const a = setInterval(u, e);
36
+ return () => clearInterval(a);
37
+ }, [t == null ? void 0 : t.valueOf(), e]), r;
38
+ }
39
+ function U(t = {}) {
40
+ const [e, r] = g(() => t.startDate ?? /* @__PURE__ */ new Date()), [l, u] = g(t.speed ?? 1), [a, d] = g(t.autoPlay ?? !1), c = v(null);
41
+ c.current === null && (c.current = new F(t)), D(() => {
42
+ const n = c.current, o = (m) => r(m.date), h = () => d(!0), y = () => d(!1), i = (m) => r(m.date), I = (m) => u(m.speed);
43
+ return n.on("tick", o), n.on("play", h), n.on("pause", y), n.on("datechange", i), n.on("speedchange", I), () => {
44
+ n.off("tick", o), n.off("play", h), n.off("pause", y), n.off("datechange", i), n.off("speedchange", I), n.dispose(), c.current = null;
45
+ };
46
+ }, []);
47
+ const f = P(() => {
48
+ var n;
49
+ return (n = c.current) == null ? void 0 : n.play();
50
+ }, []), s = P(() => {
51
+ var n;
52
+ return (n = c.current) == null ? void 0 : n.pause();
53
+ }, []), w = P((n) => {
54
+ var o;
55
+ return (o = c.current) == null ? void 0 : o.setDate(n);
56
+ }, []), p = P((n) => {
57
+ var o;
58
+ return (o = c.current) == null ? void 0 : o.setSpeed(n);
59
+ }, []);
60
+ return {
61
+ date: e,
62
+ speed: l,
63
+ playing: a,
64
+ play: f,
65
+ pause: s,
66
+ setDate: w,
67
+ setSpeed: p,
68
+ clock: c.current
69
+ };
70
+ }
71
+ function W(t, e = {}, r = 3e4) {
72
+ var a, d;
73
+ const [l, u] = g([]);
74
+ return D(() => {
75
+ const c = () => {
76
+ const s = { ...t, date: t.date ?? /* @__PURE__ */ new Date() };
77
+ u(M.whatsUp(s, e));
78
+ };
79
+ if (c(), !S) return;
80
+ const f = setInterval(c, r);
81
+ return () => clearInterval(f);
82
+ }, [
83
+ t.lat,
84
+ t.lng,
85
+ (a = t.date) == null ? void 0 : a.valueOf(),
86
+ r,
87
+ e.minAltitude,
88
+ e.magnitudeLimit,
89
+ e.limit,
90
+ e.tag,
91
+ e.constellation,
92
+ (d = e.types) == null ? void 0 : d.join(",")
93
+ ]), l;
94
+ }
95
+ function G(t, e) {
96
+ var r;
97
+ return q(() => {
98
+ const l = { ...t, date: e ?? t.date ?? /* @__PURE__ */ new Date() };
99
+ return A.twilight(l);
100
+ }, [t.lat, t.lng, e == null ? void 0 : e.valueOf(), (r = t.date) == null ? void 0 : r.valueOf()]);
101
+ }
102
+ function H({
103
+ objects: t,
104
+ width: e = "100%",
105
+ height: r = "400px",
106
+ onSelect: l,
107
+ onHover: u,
108
+ skymapRef: a,
109
+ ...d
110
+ }) {
111
+ const c = v(null), f = v(null);
112
+ return D(() => {
113
+ const s = c.current;
114
+ if (!s || !S) return;
115
+ const w = s.parentElement;
116
+ if (!w) return;
117
+ const p = window.devicePixelRatio || 1, n = w.getBoundingClientRect();
118
+ s.width = n.width * p, s.height = n.height * p, s.style.width = `${n.width}px`, s.style.height = `${n.height}px`;
119
+ const o = t ?? E.all().filter((i) => i.ra != null && i.dec != null), h = new z(s, o, d);
120
+ f.current = h, a && "current" in a && (a.current = h), l && h.on("select", ({ object: i }) => l(i)), u && h.on("hover", ({ object: i }) => u(i));
121
+ const y = () => {
122
+ const i = w.getBoundingClientRect();
123
+ s.width = i.width * p, s.height = i.height * p, s.style.width = `${i.width}px`, s.style.height = `${i.height}px`, h.render();
124
+ };
125
+ return window.addEventListener("resize", y), () => {
126
+ window.removeEventListener("resize", y), h.dispose(), f.current = null, a && "current" in a && (a.current = null);
127
+ };
128
+ }, []), /* @__PURE__ */ C("div", { style: { width: e, height: r, position: "relative" }, children: /* @__PURE__ */ C(
129
+ "canvas",
130
+ {
131
+ ref: c,
132
+ style: { width: "100%", height: "100%", display: "block" },
133
+ "aria-label": "Interactive sky map"
134
+ }
135
+ ) });
136
+ }
137
+ function O(t, e) {
138
+ if (t.ra !== null && t.dec !== null) return { ra: t.ra, dec: t.dec };
139
+ if (t.type === "planet")
140
+ try {
141
+ const r = x.planetEcliptic(t.id, e);
142
+ return x.eclipticToEquatorial(r);
143
+ } catch {
144
+ return null;
145
+ }
146
+ if (t.id === "sun") {
147
+ const r = A.position(e);
148
+ return { ra: r.ra, dec: r.dec };
149
+ }
150
+ if (t.id === "moon") {
151
+ const r = k.position(e);
152
+ return { ra: r.ra, dec: r.dec };
153
+ }
154
+ return null;
155
+ }
156
+ export {
157
+ H as SkyMap,
158
+ U as useAstroClock,
159
+ L as useMoonPhase,
160
+ B as useSkyPosition,
161
+ G as useTwilight,
162
+ W as useWhatsUp
163
+ };
@@ -0,0 +1,69 @@
1
+ import type { EquatorialCoord, ProjectedObject, ProjectionName } from './types.js';
2
+ /**
3
+ * Inverse stereographic projection.
4
+ *
5
+ * Converts pixel offsets (relative to canvas centre, with y increasing
6
+ * **upward**) back to equatorial coordinates given the projection centre
7
+ * and scale.
8
+ *
9
+ * @param px - Horizontal pixel offset from canvas centre.
10
+ * @param py - Vertical pixel offset from canvas centre (positive = up).
11
+ * @param center - Projection centre in equatorial coordinates.
12
+ * @param scale - Pixel scale factor (same value passed to the forward projection).
13
+ * @returns The equatorial coordinate, or `null` if the point is at the
14
+ * antipodal singularity.
15
+ */
16
+ export declare function stereographicInverse(px: number, py: number, center: EquatorialCoord, scale: number): EquatorialCoord | null;
17
+ /**
18
+ * Inverse gnomonic (tangent-plane) projection.
19
+ *
20
+ * @param px - Horizontal pixel offset from canvas centre.
21
+ * @param py - Vertical pixel offset from canvas centre (positive = up).
22
+ * @param center - Projection tangent point in equatorial coordinates.
23
+ * @param scale - Pixel scale factor.
24
+ * @returns The equatorial coordinate, or `null` if the result is invalid.
25
+ */
26
+ export declare function gnomonicInverse(px: number, py: number, center: EquatorialCoord, scale: number): EquatorialCoord | null;
27
+ /**
28
+ * Inverse Mollweide (equal-area) projection.
29
+ *
30
+ * Takes **absolute** canvas pixel coordinates (matching the forward
31
+ * projection output) and returns equatorial coordinates.
32
+ *
33
+ * @param canvasX - Absolute x pixel position on the canvas.
34
+ * @param canvasY - Absolute y pixel position on the canvas.
35
+ * @param width - Canvas width in pixels.
36
+ * @param height - Canvas height in pixels.
37
+ * @returns The equatorial coordinate, or `null` if the point falls outside
38
+ * the Mollweide ellipse boundary.
39
+ */
40
+ export declare function mollweideInverse(canvasX: number, canvasY: number, width: number, height: number): EquatorialCoord | null;
41
+ /**
42
+ * Convert a canvas pixel position to equatorial coordinates.
43
+ *
44
+ * Dispatches to the appropriate inverse projection based on the current
45
+ * projection mode. The `canvasX` / `canvasY` values should be in the
46
+ * **canvas coordinate space** (i.e. after accounting for DPI scaling).
47
+ *
48
+ * @param canvasX - Pixel x on the canvas.
49
+ * @param canvasY - Pixel y on the canvas.
50
+ * @param width - Canvas width in pixels.
51
+ * @param height - Canvas height in pixels.
52
+ * @param projection - Active projection mode.
53
+ * @param center - Current view centre (ignored for Mollweide).
54
+ * @param scale - Current zoom scale factor (ignored for Mollweide).
55
+ * @returns The equatorial coordinate at that pixel, or `null` if outside
56
+ * the valid projection area.
57
+ */
58
+ export declare function canvasToEquatorial(canvasX: number, canvasY: number, width: number, height: number, projection: ProjectionName, center: EquatorialCoord, scale: number): EquatorialCoord | null;
59
+ /**
60
+ * Find the nearest projected object within a pixel radius of a given point.
61
+ *
62
+ * @param cache - Array of projected objects (rebuilt each render frame).
63
+ * @param canvasX - Pixel x on the canvas.
64
+ * @param canvasY - Pixel y on the canvas.
65
+ * @param hitRadius - Maximum distance in pixels to consider a match.
66
+ * @returns The closest {@link ProjectedObject}, or `null` if nothing is
67
+ * within range.
68
+ */
69
+ export declare function hitTest(cache: readonly ProjectedObject[], canvasX: number, canvasY: number, hitRadius: number): ProjectedObject | null;