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
70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
#!/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()
|