/** astroengine chart -- public API: natal charts, aspects, retrogrades. */ import { EngineData, AYANAMSA_J2000, XyzSource } from "./core.js"; import type { SyntheticRender } from "./synthetic.js"; import type { AspectPhase } from "./electional.js"; export declare const BODIES: readonly ["sun", "moon", "mercury", "venus", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto", "chiron", "mean_node", "true_node"]; export type Body = (typeof BODIES)[number]; /** Computable on request (not in the default chart set). */ export declare const EXTRA_BODIES: readonly ["mean_lilith", "true_lilith"]; /** Core names keep autocomplete; any string id is accepted (data packs). */ export type BodyId = Body | (typeof EXTRA_BODIES)[number] | (string & {}); /** Points: excluded from aspect search by default. */ export declare const NOT_ASPECTABLE: Set; export declare const SIGNS: string[]; export declare const ASPECTS: Record; export declare const DEFAULT_ORBS: Record; export type HouseSystem = "placidus" | "porphyry" | "equal" | "whole_sign" | "koch" | "regiomontanus" | "campanus" | "alcabitius" | "morinus" | "meridian" | "polich_page" | "vehlow"; /** The canonical house-system ids, in a stable order (also used for error text). */ export declare const HOUSE_SYSTEMS: readonly HouseSystem[]; /** Resolve a forgiving house-system string (any case, spaces or hyphens, or a * known alias) to a canonical {@link HouseSystem}, or throw listing the valid * ids. Lets MCP, share links, and hand-written calls pass "whole sign", * "Whole_Sign", "whole", etc. without tripping the strict union. */ export declare function normalizeHouseSystem(raw: string): HouseSystem; export type Element = "fire" | "earth" | "air" | "water"; export type Modality = "cardinal" | "fixed" | "mutable"; /** Triplicity (element) of a sign: `"fire"`, `"earth"`, `"air"`, or `"water"`. */ export declare function element(sign: number | string): Element; /** Quadruplicity (modality) of a sign: `"cardinal"`, `"fixed"`, or `"mutable"`. */ export declare function modality(sign: number | string): Modality; /** 1-based quadrant (I–IV) of a 1-based house number: houses 1–3 -> 1, etc. */ export declare function quadrant(house: number): number; export declare const DOMICILE: Record; export declare const EXALTATION: Record; /** * Essential dignities a body holds in a sign: any of `"domicile"`, * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite * domicile and exaltation). Empty when the body is peregrine there or has no * classical rulership (the outer planets, Chiron, the nodes). * * @param body Body id, e.g. `"mars"`. * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`. * @returns The dignities held, in the order above; empty if none. * @example * ```ts * dignities("mars", "Aries"); // ["domicile"] * dignities("sun", "Libra"); // ["fall"] * ``` */ export declare function dignities(body: string, sign: number | string): string[]; export type Ayanamsa = keyof typeof AYANAMSA_J2000 & string; export type Zodiac = "tropical" | `sidereal:${string}`; export interface Observer { lat: number; lonEast: number; altM?: number; } /** Options shared by the single-body calls ({@link Engine.position}, * {@link Engine.longitude}) and by charts. */ export interface CalcOptions { /** Tropical (the default) or a sidereal ayanamsa, e.g. `"sidereal:lahiri"`. */ zodiac?: Zodiac; /** Apply topocentric parallax for `observer`. Defaults to `false`. */ topocentric?: boolean; /** Observer location; required when `topocentric` is set. */ observer?: Observer; } /** Options for {@link Engine.chart} and {@link Engine.chartAt}, extending * {@link CalcOptions}. */ export interface ChartOptions extends CalcOptions { /** House system to compute. Defaults to `"placidus"`. */ houseSystem?: HouseSystem; /** Extra bodies to compute beyond the core chart set. */ bodies?: BodyId[]; /** Per-aspect orb overrides in degrees, keyed by aspect name. A partial * record merges over {@link DEFAULT_ORBS} (see {@link findAspects}). */ orbs?: Record; /** Aspect separation mode: zodiacal longitude (default) or true spatial * great-circle separation. See {@link FindAspectsOptions.separation}. */ separation?: "longitude" | "spatial"; /** Custom aspect angle table for the chart's aspect list. * See {@link FindAspectsOptions.aspects}. */ aspects?: Record; } /** A body's full apparent position, as returned by {@link Engine.position}. */ export interface Position { /** Ecliptic longitude in degrees, `[0, 360)`. */ lon: number; /** Daily motion in longitude, degrees/day; negative when retrograde. */ speed: number; /** Whether the body is in apparent retrograde motion (`speed < 0`). */ retrograde: boolean; /** Zodiac sign containing `lon`, e.g. `"Leo"`. */ sign: string; /** Longitude within the sign, degrees `[0, 30)`. */ signDeg: number; /** Ecliptic latitude, deg (0 for nodes). */ lat: number; /** Daily motion in ecliptic latitude, degrees/day. Engine-produced * positions always carry it (same central difference as `speed`); it is * optional only so injected or legacy Position values stay assignable. * Used by spatial-mode aspect phase; treated as 0 when absent. */ latSpeed?: number; /** Geocentric distance in AU (Moon included); null for nodes and Lilith. */ dist: number | null; /** Equatorial right ascension, true equinox of date, degrees. */ ra: number; /** Equatorial declination, true equinox of date, degrees. */ dec: number; } /** A {@link Position} enriched with chart-relative placement, as returned per * body by {@link Engine.chart} and {@link Engine.chartAt}. */ export interface ChartBody extends Position { /** 1-based house the body falls in, by the chart's cusps (1–12). */ house: number; /** Essential dignities held in the body's sign (see {@link dignities}); * empty when peregrine or for bodies without classical rulerships. */ dignities: string[]; } /** Default chart bodies that can be Chebyshev-packed, so they can fall * outside a pack's fitted range (and be omitted from a chart). Chiron is * always packed; Pluto is packed when the wide-range pack is loaded (the * Meeus fallback otherwise always resolves, but the type must cover the * packed engine, where an out-of-range Pluto lands in * {@link Chart.unavailable}). Opt-in asteroids are packed too, but arrive * as arbitrary ids through the string index. */ export type PackedBody = "chiron" | "pluto"; /** Bodies guaranteed to be in every chart: the analytic Sun–Neptune and the * lunar nodes, which resolve across all supported epochs. (Chiron and pack * Pluto are Chebyshev-packed and can fall outside their fitted range, so * they are *not* guaranteed.) */ export type AlwaysBody = Exclude; /** * A chart's bodies, keyed by id. The analytic core ({@link AlwaysBody}) is * always present and needs no presence check. {@link PackedBody} bodies (Chiron) * and any opt-in extras requested via {@link ChartOptions.bodies} may be absent * when the instant is outside their fitted range (see {@link Chart.unavailable}), * so those accesses are typed `ChartBody | undefined` and must be guarded. */ export type ChartBodies = Record & Partial> & { [id: string]: ChartBody | undefined; }; /** One aspect between two bodies in a {@link Chart}. */ export interface Aspect { /** First body id. */ a: string; /** Second body id. */ b: string; /** Aspect name, e.g. `"trine"`. */ aspect: string; /** Orb from exact, in degrees. */ orb: number; /** Applying, separating, or exact -- from the two bodies' longitude speeds. */ phase: AspectPhase; /** Closeness in `[0, 1]`: `1` exact, `0` at the orb limit. */ strength: number; } /** A full natal chart, as returned by {@link Engine.chart} and * {@link Engine.chartAt}. Longitudes are degrees in the chart's `zodiac`. */ export interface Chart { /** The instant, as a Julian Day (UT). */ jdUt: number; /** The zodiac the longitudes are expressed in. */ zodiac: Zodiac; /** House system actually used. May differ from the request: Placidus and * Koch are undefined above the polar circles and fall back to whole_sign. */ houseSystem: HouseSystem; /** The house system originally requested, before any polar fallback. */ houseSystemRequested: HouseSystem; /** Apparent position per body, enriched with house and dignities, keyed by * body id. See {@link ChartBody}. */ bodies: ChartBodies; /** Body ids that were requested but omitted because the instant falls outside * their fitted range (e.g. Chiron and other Chebyshev-packed bodies before * ~1850 or after ~2150). Empty for the usual modern dates. The analytic * bodies (Sun through Neptune and the nodes) are always present. */ unavailable: string[]; /** Validity statements about this chart: bodies computed outside their * measured validated span, and the delta-T uncertainty at historical * epochs. Empty for the usual modern dates. The chart still computes -- * these are data, not errors -- but a consumer must not present a warned * position as validated. */ warnings: ChartWarning[]; /** Chart angles in degrees: Ascendant, Midheaven, Vertex, East Point. */ angles: { asc: number; mc: number; vertex: number; eastPoint: number; }; /** The twelve house cusp longitudes in degrees, house 1 first. */ cusps: number[]; /** Aspects found among the bodies, within the active orbs. */ aspects: Aspect[]; } /** One validity statement on a {@link Chart} (see {@link Chart.warnings}). */ export type ChartWarning = { kind: "outside_validated_range"; /** The body whose position is computed but unvalidated at this instant. */ body: string; /** The measured span it sits outside (calendar years). */ validated: { from: number; to: number; }; text: string; } | { kind: "delta_t_uncertain"; /** One-sigma delta-T uncertainty at this epoch, seconds (Morrison & * Stephenson 2004 for historical years; stated extrapolation beyond * the measured tables). */ sigmaSeconds: number; /** The positional consequence for the fastest movers: the angles rotate * 0.25 deg per minute of clock error, degrees. */ angleSmearDeg: number; /** The Moon's smear over the same clock error, arcminutes. */ moonSmearArcmin: number; text: string; }; export declare class Engine { readonly data: EngineData; private moonCheb; private chironCheb; private intpApog; private packs; private runtimeSources; private renderAttrs; constructor(data: EngineData); /** * Register a runtime {@link XyzSource} under a body id, so it resolves through * {@link Engine.position}, {@link Engine.longitude}, {@link Engine.chartAt}, * and everything built on them (transits, returns, retrograde, SkyView) with * no special-casing — exactly like a baked-in Chebyshev or Kepler pack. The * source yields heliocentric ecliptic-J2000 xyz in AU at a **TT** Julian Day, * the same contract Chiron and the Uranian bodies satisfy; the engine applies * light-time, aberration, precession and nutation to it like any real body. * * This is the seam the `synthetic` module plugs imaginary bodies into (see * {@link registerSyntheticSystem}). A registered id shadows a baked-in pack of * the same name and persists for the engine's lifetime. * * @param id The body id to register (any string). * @param source A heliocentric xyz source; see {@link XyzSource}. * @returns This engine, for chaining. */ registerSource(id: string, source: XyzSource): this; /** Author how a runtime body should look in SkyView (size, magnitude, colour). * Position still comes from {@link registerSource}; this owns appearance only. */ registerRender(id: string, render: SyntheticRender): this; /** SkyView appearance for a registered body, if any. */ renderFor(id: string): SyntheticRender | undefined; /** Whether `body` resolves through the generic packed-source path: a baked-in * Chebyshev/Kepler pack or a runtime source from {@link registerSource}. */ private hasPack; private pack; private moonInRange; /** * The body ids this engine can compute, given the data pack it was * constructed with. The core set is always present; extra asteroids and * hypotheticals appear only when their Chebyshev or Kepler packs are loaded. * * @returns Body ids accepted by {@link Engine.position}, * {@link Engine.longitude}, and {@link Engine.chart}. * @example * ```ts * engine.bodies().includes("ceres"); // true only if the Ceres pack is loaded * ``` */ bodies(): BodyId[]; /** * Low-level apparent geocentric ecliptic coordinates at a **TT** Julian Day, * in **radians**. This is the engine's internal building block for the events * module; it takes TT (not UT) and does no zodiac shift. Most callers want * {@link Engine.position} (full Position in degrees) or * {@link Engine.longitude} (longitude in degrees) instead. * * @param body A body id from {@link Engine.bodies}. * @param jde Julian Day in **TT** (Terrestrial Time), e.g. `jdTT(jdUt)`. * @returns `[lon, lat, dist]` — longitude and latitude in **radians** (true * equinox of date), distance in AU, or `null` distance for nodes and * Lilith points. * @throws Error if no data is loaded for `body`. */ ecliptic(body: BodyId, jde: number): [number, number, number | null]; /** Degrees to subtract from a true-equinox tropical longitude. */ private ayanShift; /** * Apparent place of a catalog fixed star at a Julian Day (UT). Requires the * fixed-star catalog to be present in the data pack; see * {@link Engine.starNames} for the available names. * * @param name Catalog star name, e.g. `"Regulus"` (see * {@link Engine.starNames}). * @param jdUt Julian Day in UT. * @param opts Calculation options; only `zodiac` is meaningful here (tropical * by default, or a sidereal ayanamsa). * @returns Ecliptic `lon`/`lat`, equatorial `ra`/`dec` (all degrees), the * zodiac `sign` and `signDeg`, and the star's visual magnitude `mag`. * @throws Error if `name` is not in the loaded catalog. * @example * ```ts * const regulus = engine.fixedStar("Regulus", julianDay(2025, 1, 1)); * regulus.sign; // e.g. "Leo" * regulus.mag; // apparent magnitude * ``` */ fixedStar(name: string, jdUt: number, opts?: CalcOptions): { lon: number; lat: number; ra: number; dec: number; mag: number; sign: string; signDeg: number; }; /** * The names in the loaded fixed-star catalog, sorted. Empty if no catalog is * present in the data pack. Pass any of these to {@link Engine.fixedStar}. * * @returns Sorted catalog star names. */ starNames(): string[]; /** * Fixed-star conjunctions in a chart: each body within `orb` of a catalog * star, in the chart's own zodiac. Feed the result to * {@link interpretationContext} as `stars` to project `star` fact atoms (the * Chart itself carries no star catalog). * * @param chart A chart from {@link Engine.chart} / {@link Engine.chartAt}. * @param opts `orb` (default 1°); `stars` to restrict to named stars (then no * magnitude filter); else `maxMag` keeps only stars brighter than it * (default 2.5) so obscure catalog entries do not flood the result. * @returns Conjunctions sorted by increasing orb. */ starConjunctions(chart: Chart, opts?: { orb?: number; maxMag?: number; stars?: string[]; }): { body: string; star: string; orb: number; }[]; /** * The seven Hermetic lots of a chart, each placed by sign and house. Sect is * read from the Sun (above the horizon -> a day chart). Feed the result to * {@link interpretationContext} as `lots` to project `lot` fact atoms. * * @param chart A chart from {@link Engine.chart} / {@link Engine.chartAt}; it * must carry the seven classical planets. * @returns One entry per lot with its longitude, sign, `signDeg`, and house, * or an empty array if a required planet is absent. */ lots(chart: Chart): { lot: string; lon: number; sign: string; signDeg: number; house: number; }[]; private lonLatOnly; private lonOnly; /** * Apparent geocentric ecliptic longitude of a body, in degrees `[0, 360)`, * at a Julian Day (UT). The fast path when you need only a longitude — a * transit position, an aspect angle, a sign — without the full * {@link Position}. In the tropical zodiac this is referred to the true * equinox of date; sidereal subtracts the ayanamsa. * * @param body A body id from {@link Engine.bodies}. * @param jdUt Julian Day in UT. * @param opts Calculation options: `zodiac` (tropical or a sidereal * ayanamsa), and `topocentric` with an `observer` for a parallax-corrected * place. * @returns Ecliptic longitude in degrees, `[0, 360)`. * @example * ```ts * engine.longitude("mars", julianDay(2025, 6, 1)); // tropical * engine.longitude("mars", julianDay(2025, 6, 1), { zodiac: "sidereal:lahiri" }); * ``` * @see {@link Engine.position} for speed, retrograde, latitude, and distance. */ longitude(body: BodyId, jdUt: number, opts?: CalcOptions): number; /** * Geometric heliocentric ecliptic position (Sun-centred) at a Julian Day * (UT), referred to the ecliptic of date. Unlike {@link Engine.position}, * this is a geometric place — no light-time, aberration, or nutation — and is * undefined for the Sun, the Moon, and the lunar nodes. * * @param body A Sun-orbiting body (planet or asteroid) from * {@link Engine.bodies}. * @param jdUt Julian Day in UT. * @returns Heliocentric `lon`/`lat` in degrees and `dist` in AU. * @throws Error if `body` has no heliocentric solution (e.g. the Moon). */ heliocentric(body: BodyId, jdUt: number): { lon: number; lat: number; dist: number; }; /** * Full apparent position of a body at a Julian Day (UT): ecliptic longitude * and daily speed (with a retrograde flag), the zodiac sign, ecliptic * latitude, geocentric distance, and equatorial right ascension and * declination. The general-purpose single-body call; use * {@link Engine.longitude} when you need only the longitude. * * @param body A body id from {@link Engine.bodies}. * @param jdUt Julian Day in UT. * @param opts Calculation options: `zodiac` (tropical or a sidereal * ayanamsa), and `topocentric` with an `observer` for a parallax-corrected * place. * @returns A {@link Position}: `lon`, `speed`, `retrograde`, `sign`, * `signDeg`, `lat`, `dist` (AU; `null` for nodes and Lilith), `ra`, `dec`. * @example * ```ts * const mars = engine.position("mars", julianDay(2025, 6, 1)); * mars.retrograde; // boolean * mars.speed; // degrees/day (negative when retrograde) * ``` */ position(body: BodyId, jdUt: number, opts?: CalcOptions): Position; /** * Full natal chart: body positions, house cusps, angles, and aspects for one * instant and place. * * The first six arguments are calendar fields in **UT** — not local civil * time, and not a Julian Day. Passing a JD in `y` builds an absurd instant and * throws `RangeError`; use {@link Engine.chartAt} for a chart from a JD. For a * birth time given in a local time zone, resolve it to UT first (see the * `caelus-birth` package). * * @param y Year in UT, e.g. `1990` — a calendar year, not a Julian Day. * @param mo Month, `1`–`12`. * @param d Day of month, `1`–`31`. * @param h Hour in UT, `0`–`23`. * @param mi Minute, `0`–`59`. * @param s Second, `0`–`59`. * @param lat Geographic latitude in degrees, north positive. * @param lonEast Geographic longitude in degrees, **east positive** (so * 82.46° W is `-82.46`). * @param opts A house-system name (e.g. `"placidus"`) or a * {@link ChartOptions} bag for zodiac, topocentric mode, extra bodies, and * custom orbs. Defaults to Placidus houses in the tropical zodiac. * @returns A {@link Chart}: `bodies`, `cusps`, `angles`, and `aspects`, plus * `jdUt` and the house system actually used (Placidus and Koch fall back to * whole-sign above the polar circles). A body outside its fitted range * (e.g. Chiron before ~1850) is omitted from `bodies` and listed in * `unavailable` rather than failing the whole chart. * @throws RangeError only if the instant itself is absurd — far outside any * supported epoch — which almost always means a Julian Day was passed where * calendar fields belong. * @example * ```ts * // 1990-06-10 14:30 UT at Tampa, FL (27.95° N, 82.46° W), Placidus houses * const chart = engine.chart(1990, 6, 10, 14, 30, 0, 27.95, -82.46, "placidus"); * chart.bodies.sun.lon; // Sun's ecliptic longitude, degrees * chart.angles.asc; // Ascendant, degrees * ``` * @see {@link Engine.chartAt} to build the same chart from a Julian Day. */ chart(y: number, mo: number, d: number, h: number, mi: number, s: number, lat: number, lonEast: number, opts?: HouseSystem | ChartOptions): Chart; /** * Full natal chart from a Julian Day (UT) — identical output to * {@link Engine.chart}, without the calendar round-trip. Reach for this when * you already hold a JD: transit and event scans, `rankMoments` winners, or * `position`/`longitude` workflows. * * @param jdUt Julian Day in UT, e.g. from {@link julianDay} or a scan. * @param lat Geographic latitude in degrees, north positive. * @param lonEast Geographic longitude in degrees, east positive. * @param opts A house-system name or a {@link ChartOptions} bag. Defaults to * Placidus houses in the tropical zodiac. * @returns The same {@link Chart} shape returned by {@link Engine.chart}. * @example * ```ts * const jd = julianDay(1990, 6, 10, 14, 30, 0); * const chart = engine.chartAt(jd, 27.95, -82.46, "placidus"); * ``` * @see {@link Engine.chart} for the calendar-field entry point. */ chartAt(jdUt: number, lat: number, lonEast: number, opts?: HouseSystem | ChartOptions): Chart; } /** Options for {@link findAspects}. */ export interface FindAspectsOptions { /** * How to measure the separation between two bodies. * * `"longitude"` (the default) is the zodiacal aspect: difference in * ecliptic longitude, the tradition's primary object. `"spatial"` is the * true great-circle separation on the celestial sphere * ({@link angularSeparation3d}), accounting for ecliptic latitude — the * correction Robson describes for bodies with latitude, where "the aspects * to the body of a star with latitude do not fall in the zodiacal degrees * one would expect". The two answers coincide on the ecliptic and diverge * for the Moon, Pluto, the asteroids, and every fixed star; the default * stays `"longitude"` because the zodiacal aspect is the primary object * and the spatial separation is an opt-in refinement. */ separation?: "longitude" | "spatial"; /** Aspect angle table (name → degrees), defaulting to the five Ptolemaic * {@link ASPECTS}. A caller may inject minors (`{ quincunx: 150, ... }`); * each named aspect needs an orb in the orbs table or it is skipped. */ aspects?: Record; } export declare function findAspects(bodies: Record, orbs?: Record, opts?: FindAspectsOptions): Aspect[]; export declare function fmtLon(deg: number): string;