48319c3fe2a8c266890b51274b233c79acfe2e01
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
48319c3fe2 |
Move the solar system on JPL's mean elements, so it stays right as the clock runs
Every body carried one set of osculating elements from Horizons at 2025-01-01, run forward by
Kepler with a GM from a table of mass ratios. That set is exact at its instant and drifts from
then on, and the clock now runs a month a second: the Moon, with Earth's mass ratio lacking its
own and the osculating axis, went round in 27.70 days instead of 27.32, 66 degrees out after a
year, and its locked face was spun at the same wrong rate.
Planets and Pluto now take Standish's Table 2a/2b ("Keplerian Elements for Approximate
Positions of the Major Planets"): elements against the J2000 ecliptic, their rates per century,
and the b, c, s, f terms of Jupiter to Pluto, fit for 3000 BC to AD 3000. Table 1 is closer near
the present (Saturn 0.23 degrees at worst 1950-2100, against 0.32 here) but is only fit for
1800-2050, and by AD 3000 has Saturn 4.3 degrees out where Table 2 holds every planet within 0.3.
The moons take JPL SSD's satellite mean elements: sidereal mean motion n to ten figures, the
periods of their node and periapsis, and each one's local Laplace plane by its pole. They
propagate with n itself, never a GM: gmForParent and its mass table are gone. Horizons still
gives size, spin and obliquity.
Both tables are read from the Internet Archive's copy of JPL's pages, pinned to one capture: the
live approx_pos page has dropped Pluto, and the live sats/elem page has dropped n and rounds the
period to four or five figures (Phobos 0.3187 d, a revolution out within a decade).
What the tables leave implicit, measured against Horizons before it was accepted:
- The precession periods are magnitudes. A node regresses on a prograde orbit and advances on a
retrograde one; a periapsis advances except where a resonance forces the eccentricity. Io's
and Europa's follow their conjunction line backwards at 2 n(Europa) - n(Io) = 0.74 degrees a
day, which is exactly the 1.625- and 1.394-year periods in the table. Read as advancing, Io
was 0.9 degrees out and Europa 2.1.
- On a retrograde orbit the node's turning is added back to the mean anomaly. Taken off, Triton
drifted a degree a year, 105 degrees by 2100.
- The Laplace frame's x axis is where the plane rises through the ICRF equator, RA of the pole
plus 90. Read against the ecliptic, Io was 2.8 degrees out, Phobos 54 and Titan 127.
Orbit lines are now drawn in their own plane and turned by a quaternion each tick, so a turning
node carries the line with the body: fixed at one date, the Moon's line would be up to 69 000 km
off it nine years on. The Earth row is the Earth-Moon barycentre, 4 700 km from Earth, 0.002
degrees from the Sun. A tidally locked moon's day is now 360 / n, its sidereal period (the Moon
27.321662 d), so it stays locked to the orbit it is drawn on.
Angular error against Horizons VECTORS (ICRF, TDB; heliocentric for planets, planet-centred for
moons), degrees, read from the live renderer's markers in the running app:
body 1950-01-01 1975-01-01 1987-07-23 2000-01-01 2025-01-01 2037-03-06 2050-01-01 2075-01-01 2100-01-01 max
mercury 0.004 0.002 0.003 0.002 0.002 0.001 0.000 0.002 0.000 0.004
venus 0.003 0.007 0.003 0.004 0.004 0.004 0.003 0.004 0.004 0.007
earth 0.003 0.008 0.002 0.005 0.004 0.009 0.003 0.002 0.003 0.009
mars 0.009 0.010 0.008 0.024 0.009 0.012 0.009 0.011 0.028 0.028
jupiter 0.063 0.030 0.171 0.135 0.013 0.020 0.056 0.041 0.075 0.171
saturn 0.080 0.064 0.018 0.320 0.066 0.114 0.044 0.164 0.177 0.320
uranus 0.018 0.169 0.068 0.050 0.101 0.015 0.141 0.017 0.114 0.169
neptune 0.070 0.028 0.004 0.021 0.036 0.037 0.013 0.029 0.072 0.072
pluto 0.045 0.054 0.041 0.033 0.019 0.020 0.023 0.027 0.026 0.054
moon 0.486 1.928 0.127 0.631 1.407 1.086 0.720 0.339 1.180 1.928
phobos 2.068 0.294 0.881 1.113 0.313 0.636 2.089 5.862 11.099 11.099
deimos 0.077 0.043 0.310 0.066 0.164 0.068 0.034 0.468 0.044 0.468
io 0.021 0.015 0.010 0.019 0.009 0.035 0.006 0.011 0.022 0.035
europa 0.036 0.039 0.053 0.064 0.078 0.032 0.006 0.034 0.044 0.078
ganymede 0.132 0.103 0.018 0.007 0.023 0.054 0.091 0.118 0.044 0.132
callisto 0.040 0.019 0.023 0.019 0.038 0.008 0.060 0.119 0.056 0.119
titan 0.003 0.019 0.023 0.023 0.027 0.028 0.048 0.008 0.014 0.048
triton 0.051 0.029 0.009 0.021 0.052 0.048 0.063 0.089 0.137 0.137
Three miss what was hoped for, and why:
- Jupiter 0.17, Saturn 0.32, Uranus 0.17 against the 0.1 hoped for: short-period perturbations
of the giants by one another, which no Keplerian fit carries. Standish states his own Table 2
errors as 600, 1 000 and 2 000 arcseconds (0.17, 0.28, 0.56 degrees). Out to AD 3000, measured
at 1800, 2200, 2400, 2600 and 3000, every planet stays within 0.3.
- The Moon, 1.9: evection (1.27) and variation (0.66), which a mean ellipse leaves out.
- Phobos, 2.1 until 2050, then 5.9 in 2075 and 11.1 in 2100, growing as the square of the time:
its tidal acceleration, which the table has no column for. Its elements are MAR080's, epoch
1950. The map's dates are also UTC where the elements are TDB, 69 s today,
which is 0.9 degrees of Phobos and nothing for anything else.
Held in place by:
- build.ts: each body's mean elements against Horizons' own osculating elements on the ETL's
2025-01-01, at most 0.25 degrees for a planet and 2.5 for a moon (measured: Uranus 0.101, the
Moon 1.407; a regressing Triton node reads 10.24 and fails), and every moon's day equal to its
sidereal period (a 1% error fails).
- Unit tests freezing nine Horizons vectors (Earth 2100, Jupiter 1950, Saturn 2075, Pluto 1975,
the Moon 2050, Io and Europa 1950, Titan and Triton 2100) through SystemOrbitsRenderer, the
Moon kept on its own turning line, the retrograde rule, the Standish terms, the Laplace frame,
and both table parsers. Nine mutants each fail the test named for them, and the two
validators each refuse a mutated build of the real catalogue.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
b7f277ea04 |
Answer the review: a short answer makes more survivors, and must not be skipped
Three things this got wrong. The direction: truncating Gaia leaves the HYG rows whose counterpart it dropped without one, so survivors rise — 10 886 today, 12 711 at half the rows, 16 258 at a third — which the comment claimed was the other way, and which decides whether the 15 000 ceiling can be leaned on at all (it catches a truncation past about two thirds, and nothing shallower). The throw: `fetchStars` catches everything a source throws and skips it, so a truncated CSV was reported as "the archive was unreachable" one step after `writeStarAssets` had already overwritten the published catalogue. Marked with `GaiaAnswerError` and rethrown there, so an answer that cannot be worked with fails the run where it happened. Measured end to end in a throwaway working directory, 300 000 rows in the cache: fails, names the cache file to delete, assets untouched. With the rethrow taken back out again: assets written, then "the archive was unreachable". The row limit: `rows.length >= ROW_LIMIT` is true for every reduced ETL_GAIA_ROW_LIMIT, so the tripwire fired on exactly the deliberate slice the override exists for — and told the operator to raise it. Gated on the same flag as its neighbour. `ETL_GAIA_ROW_LIMIT=20000` now runs through; without the gate it dies on the limit it was given. Also: the row floor names the one cache file it is about rather than a glob that takes the Hipparcos cross-match with it, and says an edited query is a third reason it can fire — DEFAULT_QUERY_ROWS now sits under the query it counts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
44f6a8d086 |
Refuse a Gaia answer that came back short, and read the body inside the retry
The merge gate asks whether Gaia contributed any stars, never how many. The TAP service truncates on its own timeout and still serves a well-formed CSV with a 200, ordered by magnitude — so a half answer is the bright half, which is the half HYG overlaps. Every gate passes: Gaia stars are present, HYG survivors go down rather than up, unmerged twins can only fall. The weekly job would publish a catalogue missing two hundred thousand stars and the runner would cache it for the weeks after. `fetchGaiaStars` now refuses fewer than 95% of the 412 765 rows its query holds, as its sibling query already did, and refuses an answer that fills the row limit. `fetchText` retried the request but not the body: a connection reset part-way through the 57 MB CSV rejected out of the loop, with no wait and no second attempt. The read now happens inside it. Also corrected: the merge gate's account of the HYG survivors (two thirds of them are stars Gaia measures but the main query never downloads, since Gaia puts them past the 250 pc cutoff), and the refresh workflow's comment on what happens when the archive is unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
29d3ddb6ef |
Say so when Gaia is missing, rather than as 68 000 unmatched stars
With the merge gate in place, a Gaia DR3 outage no longer ships a HYG-only catalogue: the ETL skips the unreachable source, and validateMerge then fails on the survivor count. That is the right outcome and the wrong message: "68 000 HYG stars found no Gaia counterpart" sends the reader looking at the merge. Gaia contributing nothing is now checked first, by name. Two comments said Gaia was best-effort, in data-refresh.yml and on the merge in fetchStars. They now say what happens instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
f935bae3b3 |
Fail the ETL on a merge that keeps the same star twice
The catalogues are regenerated by a scheduled job that pushes straight to main once the unit suite and a production build pass in the same run. Both passed, every Monday, on a catalogue that carried 23 000 stars twice: the suite tests code against fixtures, and no fixture is 400 000 real stars. Nothing between the ETL and the map ever looked at what came out. Two numbers now have to hold, and each is the signature of a way the merge has actually failed here. Different catalogues placing a star within an arcsecond of each other is never two stars at this depth, and one catalogue does not list a star twice, so every cross-source pair that close is a miss. Nineteen survive today — each a second HYG row wanting a Gaia entry that already absorbed one, which is how Gliese lists some doubles — against 1 112 in the catalogue on main, where a Hipparcos parallax off by half outvoted a direction that agreed to a hundredth of an arcsecond. The ceiling is 100. The epoch failure leaves no close pair at all, because sixteen years of proper motion had already carried the two entries tens of arcseconds apart. What it leaves instead is HYG rows that found no counterpart: 36 056 on main against the 10 876 Gaia genuinely lacks — the stars it saturates on and the red dwarfs past its magnitude cut. The ceiling is 15 000. The pair sweep sorts by declination and walks a one-arcsecond window, so it costs about 300 ms on 423 641 stars — cheap enough to run on every ETL, which is the point: the gate has to sit where the bot already is, before the push, because a GITHUB_TOKEN push fires no CI of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
080bbe16dc |
Match exoplanet hosts on the sky, at both epochs the archive might mean
The host cross-reference matched in 3D, nearest star within half a parsec. That is the wrong space for the same reason the star merge learned it: a direction is measured, a distance is inferred. At 170 pc half a parsec is a ten-arcminute cone, wide enough to hand the planets of stars our catalogue does not carry to whatever bright star floats nearest — HATS-6 b sat on HD 39500, seventy arcseconds away. At 60 pc it is tighter than the routine disagreement between the archive's Hipparcos distances and our Gaia ones, which is how four bright giants (7 CMa, HD 81688, omi UMa, xi Aql) lost their planets and GJ 15 A's landed on a neighbouring entry. Hosts are now resolved like stars are merged: by name first, then the nearest star on the sky within a transverse budget — angle times the archive's distance, 0.01 pc — whose distance does not flatly contradict the archive's (the merge's own 50 % ratio). The budget is transverse because the dominant error is proper motion over an epoch difference, a physical displacement that is the same in parsecs at every distance: as an angle it is 60" for Proxima and 2" for a host at 100 pc. Measured on the 504 hosts whose archive name matches a catalogue name outright, true pairs reach 3.4e-3 pc; shifting every host a quarter of a degree finds nothing else within 0.01 but Proxima's own entry, whose budget at 1.3 pc is wider than the shift. The archive never says which epoch a position is for, and they are mixed: alf Tau and GJ 273 publish J2000, HD 133131 and TOI-2459 publish Gaia's J2016. So the query asks for sy_pmra/sy_pmdec too, tries each position at both ends of those sixteen years, and judges a star on whichever is closer. Guess one epoch and a fast star's planets land on a companion: J2016 puts Aldebaran's on Gl 171.1B, J2000 puts GJ 15 A's on a Gaia entry 15.9" out. 1 972 of 6 354 planets now sit on a host, 1 548 before: 432 gained, 26 on a better star (GJ 15 A to Groombridge 34, GJ 676 A off its companion, HD 19994 to 94 Cet), 8 lost — six false 3D matches to stars the catalogue never contained, and GJ 273 b/c, whose archive row says 5.92 pc for Luyten's Star at 3.79: a distance in flat contradiction is exactly what the ratio guard exists to refuse, and the number to fix is upstream. The 2 pc "rematch" apparatus is gone. build.ts recomputed every match after fetchExoplanets had already written the file — at a different tolerance, so the log reported a match count the data did not contain — and the offline entry point that persisted it had no caller. One matcher, one set of constants, used once. The archive cache is now keyed by a hash of the TAP query, so a response cached before the proper-motion columns cannot serve rows without them, where a missing cell would quietly read as "does not move"; the row's astrometry is stored with each planet, which is what made these tolerances measurable offline in the first place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi |
||
|
|
efa9e4084a |
Draw the whole catalogue, and build the aggregation the rest would need
Two things, one verified and one that cannot be. The render budget is now the whole catalogue: 68388 stars, one instanced draw call, which is what a GPU should be asked to do. The budget itself stays, because the catalogue is meant to grow past what any machine should draw at once — Gaia alone could contribute a million — and at that point the selection is what keeps the field legible rather than a grey wash. A `?stars=` override handles the machines that cannot, including the software rasterizer the end-to-end suite runs against, whose frame rate is two orders of magnitude below a real GPU's and which was measuring the rasterizer rather than the app. The aggregation is the second thing, and none of it has run. Every ESA, NOIRLab, SDSS and Euclid endpoint is unreachable from here — only GitHub raw is, which is why HYG and OpenNGC are the current sources. So this is infrastructure and a Gaia query written against the published DR3 schema, not data. What the framework encodes is that these surveys are not interchangeable. The distinction is not size but whether a catalogue knows how far away its objects are, because a 3D map cannot place a star it only has a direction for. Gaia is the only one of the five that can add stars here, because it is the only one that measures parallaxes. DECaPS2 has fifty times Gaia's object count and photometry alone — not one of its 3.32 billion objects can be placed in depth. Euclid's bulge is 8 kpc away, where a parallax is microarcseconds; its contribution would be imagery. SDSS-V and SAGA are keyed to stars something else already places, so they enrich rather than extend. Those roles are recorded as data the ETL prints, not as prose that can drift. Overlapping catalogues are reconciled on direction rather than on 3D proximity, which is the one non-obvious part. Two surveys agree on a star's direction to within an arcsecond and disagree on its distance by tens of per cent, so a star at 200 pc is 50 pc from itself between catalogues while being unmistakably the same object. Matching in 3D would need a tolerance so loose it swallowed real neighbours. The better parallax wins where both reach; where only one does, the star stays. Names become dense-with-holes with a source dictionary, because a survey catalogue has no proper names — writing "Gaia DR3 4472832130942575872" once per star would cost 25 MB per million to repeat what two adjacent fields already say. An empty entry costs three bytes and is regenerated on load. The Sun needed its own case in the merge: it sits at the origin, has no direction to compare, and appears in every catalogue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G |
||
|
|
29fd92d118 |
Widen the star catalogue, and separate what is drawn from what is known
The map held 8750 stars within 50 pc and rendered 371 systems. Both were lower than they needed to be, for different reasons. The star catalogue was capped by its own encoding as much as by the cutoff: one JSON object per star, eight key names repeated each time, 157 bytes a star. At the range HYG actually reaches that is 17 MB to download and parse before the first frame. So the numbers move into two binary column stores — positions in stars.bin, which the GPU is handed verbatim, and id/magnitude/colour/spectral index in stars-meta.bin — and the JSON keeps only the strings, with 2600 distinct spectral classifications collapsed to a dictionary. The layout is defined once, in star-catalog.ts, and the ETL and the app both use it, so the writer and the reader cannot drift. The cutoff then goes to 250 pc: 68388 stars, 7.8x as many for 1.7x the bytes. That is where HYG's measurements stop rather than a round number — 98.6% of its rows are Hipparcos, whose parallaxes are good to about a milliarcsecond, so beyond 250 pc it would be plotting noise. Drawing all of them is a separate question from knowing them, and it is answered separately. The field draws a budget: every star inside 25 pc, because the nearest are faint red dwarfs and Proxima Centauri is magnitude 11, then the brightest of everything beyond. Search, navigation and the planet cross-reference still see the whole catalogue. A real GPU would draw all 68388 without noticing; the budget is for the machines that would not, and it is one constant. Systems were limited by something else entirely. The archive data already shipped named 4735 host stars and only 388 resolved, because the rest lay outside a 50 pc catalogue — and the cross-reference kept only its own result, so redoing it meant re-downloading an archive that is not reachable from here. Host coordinates are now stored with each planet, and the match is re-resolved at build time against whatever catalogue the run produced. Even name matching alone, which needs no coordinates and so works on the records already shipped, rescues 335 planets across 238 systems: 371 renderable systems become 609. Two selection rules were tuned for a 50 pc bubble and no longer fit. Tethers followed the Sun's nearest neighbours, which are a speck at this range, and now follow the brightest; labels were ranked by proximity, which named whatever sat nearest the middle of the screen, and are now ranked by brightness — so the view names Canopus, Achernar and Spica rather than a clump of catalogue designations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G |
||
|
|
8d8c65bdb2 |
Propagate exoplanets with their real orbital period
Every exoplanet was propagated with gmForParent(undefined) — the Sun's gravitational parameter — so the whole catalogue orbited as though each host were exactly one solar mass. Most hosts are red dwarfs far lighter than that, and a heavier central mass pulls harder and shortens the period, so their planets were whirling round much too fast: TRAPPIST-1 is 0.09 solar masses, and its planets were completing an orbit in roughly a third of the true time. pl_orbper was already in the TAP query and was being discarded on the way into the record. It is now kept, along with st_mass. A period and a semi-major axis together pin the host's gravitational parameter exactly, via GM = n^2 a^3 — no stellar model, no assumption, just the inverse of the orbitalPeriodDays helper that was already there. resolveGravitationalParameter picks the best available source: the measured period, else the published host mass, else one solar mass as before. A derived value implying something outside 0.01-150 solar masses is rejected and falls through, since a period and axis taken from disagreeing solutions would otherwise send a planet spinning at a visibly absurd rate. Note the direction of the error, which is the opposite of what it looks like: assuming a *heavier* host than reality makes a planet orbit *faster*. A test pins it, and caught me stating it backwards first. The NASA Exoplanet Archive is unreachable from this environment (egress policy returns 403 on CONNECT), so exoplanets.json cannot be regenerated here and still carries no periods. Behaviour is therefore unchanged until `npm run etl` is run somewhere with archive access, at which point every planet with a published period starts moving correctly with no further code changes. build.ts reports how many records gained a period, and rejects non-positive ones. Tests: 171 passing, up from 151, including a new end-to-end check that TRAPPIST-1 b with its real period completes exactly one orbit in 1.51088 days and sits a full diameter away at half that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G |
||
|
|
06cf7d2a15 |
Fix four defects a user hits in the first minute
Found by surveying the codebase against the plan; each was verified against the
committed assets or the running app before being touched.
TRAPPIST-1 was orbiting the Sun. The Exoplanet Archive leaves sy_dist blank for
some systems, and fetchExoplanets.ts read it with bare Number() — Number('') is
0, which is finite, so it slipped past the Number.isFinite guard in
resolveHostStarId, placed the host at the origin, and matched Sol at distance
exactly 0. 127 records shipped with hostStarId 0, all seven TRAPPIST-1 planets
among them, and the system view filters on that id, so drilling into Sol drew
127 alien worlds inside the real solar system.
Fixed in three places: resolveHostStarId now rejects a non-positive distance
(the robust guard, covering every caller), fetchExoplanets.ts uses the
parseOptionalNumber that already sat unused in that same file for ra/dec/dist,
and validateExoplanets asserts nothing ever resolves to the Sun again — the Sun
has no exoplanets, so that tripwire costs nothing and is permanent.
The archive's endpoint is blocked by this environment's egress policy, so the
ETL cannot be re-run here. The committed asset was corrected in place instead,
which is safe because the outcome is deterministic: the name path runs first and
none of the 127 resolve by name, so all of them reached id 0 positionally and
the fixed pipeline yields null for exactly that set. Cross-referenced hosts drop
from 761 to 634; record count is unchanged.
Dragging to rotate selected stars. Selection was bound to the raw click event,
which browsers fire on release however far the pointer travelled and which
OrbitControls does not suppress — so any drag ending over a star launched a
camera flight, and in system view routed away to /body/:id. Now tracks
pointerdown and ignores a release more than 5 px from it.
Ghost systems accumulated on every star-to-star hop. SystemOrbitsRenderer.dispose
released geometries and materials but never detached its group, so old orbit
lines stayed parented forever — still traversed and re-uploaded each frame with
disposed geometries, drawn over the new system and unpickable. dispose() now
detaches and clears.
Galaxy star labels stayed pinned inside the system view. They are CSS2D objects
parented to the scene rather than to galaxyGroup, so hiding the group left up to
15 parsec-space names clumped over the system's star. Cleared on entry. Also
gated the per-frame Kepler propagation on actually being in a system; it ran in
galaxy view too, because the renderer is never nulled on exit.
Tests: 116 passing, up from 112. Build, both typechecks and the Playwright suite
are green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
|
||
|
|
3a859360ba |
Add the deep-sky backdrop, the last unbuilt piece of the plan
The design doc scopes deep-sky objects as a galaxy-view backdrop and lists fetchDeepSky.ts, deepsky.json and deepsky.model.ts, but none of it existed — it was the only part of the plan with no implementation behind it. ETL: fetchDeepSky.ts pulls the OpenNGC catalog, classifies each object as a galaxy/nebula/cluster, and keeps the ~460 worth drawing (everything Messier, everything with a common name, and anything brighter than magnitude 9) out of ~12,000 mostly-anonymous rows. build.ts runs it and validates the output. Distances are the hard part: OpenNGC has no distance column, and both fallbacks fail for the best-known objects. M31, M33 and M42 are Local Group members whose redshift is negative or absent, and a galaxy's catalog parallax comes from a cross-matched foreground star — 6 mas for M31 would put a 780 kpc galaxy at 167 pc. So records store a unit direction on the celestial sphere rather than a position (the line of sight is always known precisely, and the objects are drawn on a fixed backdrop shell where true distance is unusable anyway), and distance is optional metadata carrying its own provenance. Parallax is trusted only for galactic objects, redshift only above z=0.003 where expansion outweighs peculiar velocity. 330 of 463 get a distance; the rest honestly report none. Rendering: DeepSkyRenderer paints the objects as soft additive billboards on a 2500 pc shell — clear of the 50 pc star field, beyond the camera's 2000 pc orbit limit, and inside its 5000 pc far plane. Size comes from real angular extent, so Andromeda is six times wider than the full Moon, clamped at both ends. Sprites rather than points because the WebGPU backend caps point primitives at one pixel; materials are shared per kind and brightness band, so 460 objects cost nine of them. The brightest dozen get permanent labels, which needed the label overlay to accept string ids alongside numeric star ids. The backdrop is decorative, so a failure to load its dataset is logged and the star field comes up regardless. Also documents the app in the README, which until now covered only the plugin marketplace. Tests: 112 passing, up from 54. Build, both typechecks and the Playwright suite are green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G |
||
|
|
d7e8ea1d4d |
@
Add star-map Angular app, ETL pipeline, and caveman plugin Angular 3D star map (galaxy/system/body views, Three.js rendering, navigation store) plus the NASA ETL tooling that builds the star, exoplanet and solar-system datasets, Playwright e2e suite, and the cs:caveman Claude Code plugin (command, agent, skill). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> @ |