feat: catálogo de estrelas embutido (Yale BSC, V<=5.5, 2887 estrelas)
tools/build_catalog.py baixa o bsc5-short.json, converte RA/Dec/V/K e emite starcatalog.js compacto (65KB) com paleta de cores por temperatura (corpo negro dessaturado). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015HtPHsTnDcLVSCiF5FGHZj
This commit is contained in:
parent
0caf1fbbf8
commit
998de4a945
2 changed files with 3010 additions and 0 deletions
2893
plugin/contents/ui/starcatalog.js
Normal file
2893
plugin/contents/ui/starcatalog.js
Normal file
File diff suppressed because it is too large
Load diff
117
tools/build_catalog.py
Normal file
117
tools/build_catalog.py
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Gera plugin/contents/ui/starcatalog.js a partir do Yale Bright Star Catalog.
|
||||||
|
|
||||||
|
Baixa bsc5-short.json (RA/Dec/V/K), filtra V <= MAG_LIMIT, converte a
|
||||||
|
temperatura de cor em RGB (aprox. corpo negro, quantizada numa paleta pequena)
|
||||||
|
e emite um JS compacto:
|
||||||
|
|
||||||
|
const STAR_PALETTE = [[r,g,b], ...];
|
||||||
|
const STAR_DATA = [[raDeg, decDeg, vmag, colorIdx], ...]; // ordenado por mag
|
||||||
|
|
||||||
|
Uso: python3 tools/build_catalog.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
URL = "https://raw.githubusercontent.com/brettonw/YaleBrightStarCatalog/master/bsc5-short.json"
|
||||||
|
OUT = Path(__file__).resolve().parent.parent / "plugin/contents/ui/starcatalog.js"
|
||||||
|
MAG_LIMIT = 5.5
|
||||||
|
|
||||||
|
# Limites (K) e centro representativo de cada faixa de temperatura
|
||||||
|
TEMP_BINS = [
|
||||||
|
(0, 3500, 3000), # M — alaranjado-avermelhado
|
||||||
|
(3500, 4500, 4000), # K — laranja
|
||||||
|
(4500, 5500, 5000), # G — amarelo-branco
|
||||||
|
(5500, 6500, 6000), # F — branco-amarelado
|
||||||
|
(6500, 8000, 7200), # A — branco
|
||||||
|
(8000, 11000, 9500), # B tardio — branco-azulado
|
||||||
|
(11000, 16000, 13000), # B — azul-branco
|
||||||
|
(16000, 10**9, 20000), # O/B quente — azul
|
||||||
|
]
|
||||||
|
SATURATION = 0.6 # estrelas reais parecem quase brancas; 1.0 = cor plena
|
||||||
|
|
||||||
|
|
||||||
|
def blackbody_rgb(kelvin: float) -> tuple[int, int, int]:
|
||||||
|
"""Aproximação de Tanner Helland (válida ~1000K-40000K)."""
|
||||||
|
t = kelvin / 100.0
|
||||||
|
if t <= 66:
|
||||||
|
r = 255.0
|
||||||
|
g = 99.4708025861 * math.log(t) - 161.1195681661
|
||||||
|
b = 0.0 if t <= 19 else 138.5177312231 * math.log(t - 10) - 305.0447927307
|
||||||
|
else:
|
||||||
|
r = 329.698727446 * (t - 60) ** -0.1332047592
|
||||||
|
g = 288.1221695283 * (t - 60) ** -0.0755148492
|
||||||
|
b = 255.0
|
||||||
|
def clamp(x):
|
||||||
|
return max(0, min(255, round(x)))
|
||||||
|
return clamp(r), clamp(g), clamp(b)
|
||||||
|
|
||||||
|
|
||||||
|
def desaturate(rgb, amount):
|
||||||
|
r, g, b = rgb
|
||||||
|
lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||||
|
mix = lambda c: round(lum + (c - lum) * amount)
|
||||||
|
return mix(r), mix(g), mix(b)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_ra(s: str) -> float:
|
||||||
|
m = re.match(r"(\d+)h\s*(\d+)m\s*([\d.]+)s", s)
|
||||||
|
h, mi, se = float(m.group(1)), float(m.group(2)), float(m.group(3))
|
||||||
|
return (h + mi / 60 + se / 3600) * 15.0
|
||||||
|
|
||||||
|
|
||||||
|
def parse_dec(s: str) -> float:
|
||||||
|
m = re.match(r"([+-])(\d+)°\s*(\d+)′\s*([\d.]+)″", s)
|
||||||
|
sign = -1.0 if m.group(1) == "-" else 1.0
|
||||||
|
d, mi, se = float(m.group(2)), float(m.group(3)), float(m.group(4))
|
||||||
|
return sign * (d + mi / 60 + se / 3600)
|
||||||
|
|
||||||
|
|
||||||
|
def temp_index(kelvin: float) -> int:
|
||||||
|
for i, (lo, hi, _) in enumerate(TEMP_BINS):
|
||||||
|
if lo <= kelvin < hi:
|
||||||
|
return i
|
||||||
|
return len(TEMP_BINS) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
raw = json.loads(urllib.request.urlopen(URL, timeout=30).read())
|
||||||
|
stars = []
|
||||||
|
for entry in raw:
|
||||||
|
if "V" not in entry or "RA" not in entry or "Dec" not in entry:
|
||||||
|
continue
|
||||||
|
vmag = float(entry["V"])
|
||||||
|
if vmag > MAG_LIMIT:
|
||||||
|
continue
|
||||||
|
kelvin = float(entry.get("K", 6000))
|
||||||
|
stars.append((
|
||||||
|
round(parse_ra(entry["RA"]), 2),
|
||||||
|
round(parse_dec(entry["Dec"]), 2),
|
||||||
|
round(vmag, 2),
|
||||||
|
temp_index(kelvin),
|
||||||
|
))
|
||||||
|
|
||||||
|
stars.sort(key=lambda s: s[2]) # mais brilhantes primeiro
|
||||||
|
|
||||||
|
palette = [desaturate(blackbody_rgb(center), SATURATION)
|
||||||
|
for _, _, center in TEMP_BINS]
|
||||||
|
|
||||||
|
rows = ",\n".join(
|
||||||
|
f"[{ra},{dec},{mag},{ci}]" for ra, dec, mag, ci in stars
|
||||||
|
)
|
||||||
|
OUT.write_text(
|
||||||
|
"/* Gerado por tools/build_catalog.py — NÃO editar à mão.\n"
|
||||||
|
f" Yale Bright Star Catalog, V <= {MAG_LIMIT}, {len(stars)} estrelas.\n"
|
||||||
|
" Formato: [raGraus, decGraus, magV, idxPaleta] ordenado por magnitude. */\n"
|
||||||
|
f"const STAR_PALETTE = {json.dumps([list(p) for p in palette])};\n"
|
||||||
|
f"const STAR_DATA = [\n{rows}\n];\n"
|
||||||
|
)
|
||||||
|
print(f"{len(stars)} estrelas -> {OUT} ({OUT.stat().st_size / 1024:.0f} KB)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue