feat: Via Láctea real — mapa all-sky do Gaia amostrado por pixel
Substitui a simulação procedural pelo mapa "Gaia's sky in colour" (ESA/Gaia/DPAC, CC BY-SA 3.0 IGO): tools/build_skymap.py baixa o original 8000x4000 em projeção Hammer, reprojeta para plate carrée e gera milkyway.jpg (3072x1536). O dancer.html amostra a textura por pixel em (l,b) galácticas reais — grade pré-computada por resolução/ latitude, com só a rotação sideral variando por passo (matriz E2G·Rz combinada). Nível de preto remove o véu de fundo do mapa; extinção e clarão aplicados por pixel. Remove mwGlow/mwGrain/poeira procedural. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015HtPHsTnDcLVSCiF5FGHZj
This commit is contained in:
parent
2014dcf9b7
commit
c4c9574946
4 changed files with 168 additions and 171 deletions
70
tools/build_skymap.py
Normal file
70
tools/build_skymap.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Gera plugin/contents/ui/milkyway.jpg a partir do mapa all-sky do Gaia.
|
||||
|
||||
Fonte: "Gaia's sky in colour" (ESA/Gaia/DPAC, CC BY-SA 3.0 IGO), 8000x4000,
|
||||
projeção HAMMER em coordenadas galácticas (elipse, centro l=0/b=0, l crescendo
|
||||
para a esquerda, b=+90 no topo). Este script reprojeta para plate carrée
|
||||
(mesma convenção de l/b) e reduz para textura leve.
|
||||
|
||||
O dancer.html amostra a textura por pixel via (l, b) reais — a banda é a
|
||||
Via Láctea fotografada de verdade, projetada em tempo real.
|
||||
|
||||
Requer Pillow e numpy. Uso: python3 tools/build_skymap.py
|
||||
"""
|
||||
|
||||
import io
|
||||
import math
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
URL = ("https://upload.wikimedia.org/wikipedia/commons/e/ea/"
|
||||
"Gaia%E2%80%99s_sky_in_colour_ESA393127.png")
|
||||
CACHE = Path.home() / ".cache/skeledance/gaia_allsky.png"
|
||||
OUT = Path(__file__).resolve().parent.parent / "plugin/contents/ui/milkyway.jpg"
|
||||
SIZE = (3072, 1536) # textura final (plate carrée 2:1)
|
||||
OVERSAMPLE = 2 # reprojeta a 2x e reduz com LANCZOS (anti-aliasing)
|
||||
QUALITY = 85
|
||||
|
||||
|
||||
def fetch() -> bytes:
|
||||
if CACHE.exists():
|
||||
print(f"Usando cache {CACHE}")
|
||||
return CACHE.read_bytes()
|
||||
print(f"Baixando {URL} ...")
|
||||
req = urllib.request.Request(URL, headers={"User-Agent": "skeledance-build/1.0"})
|
||||
raw = urllib.request.urlopen(req, timeout=180).read()
|
||||
CACHE.parent.mkdir(parents=True, exist_ok=True)
|
||||
CACHE.write_bytes(raw)
|
||||
print(f" {len(raw) / 1e6:.1f} MB (cacheado)")
|
||||
return raw
|
||||
|
||||
|
||||
def main():
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
src = np.asarray(Image.open(io.BytesIO(fetch())).convert("RGB"))
|
||||
sh, sw = src.shape[:2]
|
||||
|
||||
tw, th = SIZE[0] * OVERSAMPLE, SIZE[1] * OVERSAMPLE
|
||||
# Grade de saída plate carrée: l = 180 -> -180 (esq -> dir), b = 90 -> -90
|
||||
l = np.deg2rad(180 - (np.arange(tw) + 0.5) * 360 / tw)
|
||||
b = np.deg2rad(90 - (np.arange(th) + 0.5) * 180 / th)
|
||||
L, B = np.meshgrid(l, b)
|
||||
|
||||
# Projeção Hammer direta -> posição na imagem-fonte
|
||||
denom = np.sqrt(1 + np.cos(B) * np.cos(L / 2))
|
||||
x = 2 * math.sqrt(2) * np.cos(B) * np.sin(L / 2) / denom # + = esquerda
|
||||
y = math.sqrt(2) * np.sin(B) / denom # + = cima
|
||||
sx = np.clip(((1 - x / (2 * math.sqrt(2))) * sw / 2).astype(int), 0, sw - 1)
|
||||
sy = np.clip(((1 - y / math.sqrt(2)) * sh / 2).astype(int), 0, sh - 1)
|
||||
|
||||
out = Image.fromarray(src[sy, sx])
|
||||
out = out.resize(SIZE, Image.LANCZOS)
|
||||
out.save(OUT, "JPEG", quality=QUALITY, optimize=True)
|
||||
print(f"{OUT} ({OUT.stat().st_size / 1024:.0f} KB, {SIZE[0]}x{SIZE[1]})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue