Add anchored zodiac artwork
11
plugin/contents/ui/constellation-art/LICENSE.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Stellarium Modern constellation illustrations
|
||||
|
||||
The twelve PNG illustrations in this directory are taken from the
|
||||
[Stellarium Modern sky culture](https://github.com/Stellarium/stellarium/tree/master/skycultures/modern/illustrations).
|
||||
|
||||
The Stellarium sky-culture description licenses the illustrations under the
|
||||
[Free Art License 1.3](https://artlibre.org/licence/lal/en/).
|
||||
|
||||
The images are redistributed here with their original names and are rendered
|
||||
with an affine transformation so their three Hipparcos anchor stars remain
|
||||
aligned with the simulated sky.
|
||||
BIN
plugin/contents/ui/constellation-art/aquarius.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
plugin/contents/ui/constellation-art/aries.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
plugin/contents/ui/constellation-art/cancer.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
plugin/contents/ui/constellation-art/capricornus.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
plugin/contents/ui/constellation-art/gemini.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
plugin/contents/ui/constellation-art/leo.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
plugin/contents/ui/constellation-art/libra.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
plugin/contents/ui/constellation-art/pisces.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
plugin/contents/ui/constellation-art/sagittarius.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
plugin/contents/ui/constellation-art/scorpius.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
plugin/contents/ui/constellation-art/taurus.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
plugin/contents/ui/constellation-art/virgo.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
|
|
@ -1219,6 +1219,17 @@ const ctx = canvas.getContext('2d');
|
|||
|
||||
let obsLat = -24.72, obsLon = -53.74; // Toledo-PR; sobrescrito pela config
|
||||
let showConstellations = false;
|
||||
const constellationArtImages = new Map();
|
||||
if (typeof CONSTELLATIONS !== 'undefined') {
|
||||
for (const constellation of CONSTELLATIONS) {
|
||||
if (!constellation.art) continue;
|
||||
const img = new Image();
|
||||
img.onload = () => projectSky();
|
||||
img.onerror = () => console.error('[skeledance] falha na arte de ' + constellation.name);
|
||||
img.src = constellation.art.file;
|
||||
constellationArtImages.set(constellation.id, img);
|
||||
}
|
||||
}
|
||||
const VIEW_AZ = 90; // centro da tela olha para o LESTE — a tela física do
|
||||
// usuário está voltada p/ leste, então o wallpaper age
|
||||
// como janela: estrelas nascem subindo dentro da cena
|
||||
|
|
@ -1400,8 +1411,62 @@ function renderMilkyWay(lstDeg, glareOn) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Resolve a transformação afim que prende três pixels da ilustração às suas
|
||||
três estrelas Hipparcos projetadas. */
|
||||
function constellationArtTransform(source, target) {
|
||||
const [p1, p2, p3] = source;
|
||||
const [q1, q2, q3] = target;
|
||||
const den = p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) +
|
||||
p3[0] * (p1[1] - p2[1]);
|
||||
if (Math.abs(den) < 1e-6) return null;
|
||||
const solve = (v1, v2, v3) => ({
|
||||
x: (v1 * (p2[1] - p3[1]) + v2 * (p3[1] - p1[1]) +
|
||||
v3 * (p1[1] - p2[1])) / den,
|
||||
y: (v1 * (p3[0] - p2[0]) + v2 * (p1[0] - p3[0]) +
|
||||
v3 * (p2[0] - p1[0])) / den,
|
||||
t: (v1 * (p2[0] * p3[1] - p3[0] * p2[1]) +
|
||||
v2 * (p3[0] * p1[1] - p1[0] * p3[1]) +
|
||||
v3 * (p1[0] * p2[1] - p2[0] * p1[1])) / den,
|
||||
});
|
||||
const x = solve(q1.x, q2.x, q3.x);
|
||||
const y = solve(q1.y, q2.y, q3.y);
|
||||
return [x.x, y.x, x.y, y.y, x.t, y.t];
|
||||
}
|
||||
|
||||
function drawConstellationArt(c, constellation, lst, W, horizonPx) {
|
||||
const art = constellation.art;
|
||||
const img = constellationArtImages.get(constellation.id);
|
||||
if (!art || !img || !img.complete || !img.naturalWidth) return;
|
||||
|
||||
const target = art.anchors.map(anchor => {
|
||||
const aa = altAz(anchor.sky[0], anchor.sky[1], lst);
|
||||
const daz = ((aa.az - VIEW_AZ) % 360 + 540) % 360 - 180;
|
||||
return {
|
||||
x: (0.5 + daz / FOV_AZ) * W,
|
||||
y: horizonPx * (1 - aa.alt / ALT_TOP),
|
||||
alt: aa.alt,
|
||||
};
|
||||
});
|
||||
if (target.every(p => p.alt < -8) || target.every(p => p.alt > ALT_TOP + 8)) return;
|
||||
|
||||
const matrix = constellationArtTransform(
|
||||
art.anchors.map(anchor => anchor.pos), target
|
||||
);
|
||||
if (!matrix || matrix.some(v => !Number.isFinite(v))) return;
|
||||
|
||||
c.save();
|
||||
c.beginPath();
|
||||
c.rect(0, 0, W, horizonPx);
|
||||
c.clip();
|
||||
c.globalCompositeOperation = 'screen';
|
||||
c.globalAlpha = 0.19;
|
||||
c.setTransform(...matrix);
|
||||
c.drawImage(img, 0, 0, art.size[0], art.size[1]);
|
||||
c.restore();
|
||||
}
|
||||
|
||||
/* Figuras convencionais dos 12 signos zodiacais, ancoradas em RA/Dec J2000.
|
||||
São desenhadas antes das estrelas para os pontos reais ficarem por cima. */
|
||||
Arte, linhas e estrelas reais ficam sobrepostas nesta ordem. */
|
||||
function drawConstellations(c, lst, W, H, horizonPx) {
|
||||
if (!showConstellations || typeof CONSTELLATIONS === 'undefined') return;
|
||||
const rankAlpha = [0, 0.52, 0.38, 0.25];
|
||||
|
|
@ -1412,6 +1477,9 @@ function drawConstellations(c, lst, W, H, horizonPx) {
|
|||
c.lineJoin = 'round';
|
||||
c.shadowColor = 'rgba(60,185,255,0.65)';
|
||||
c.shadowBlur = 7;
|
||||
for (const constellation of CONSTELLATIONS) {
|
||||
drawConstellationArt(c, constellation, lst, W, horizonPx);
|
||||
}
|
||||
for (const constellation of CONSTELLATIONS) {
|
||||
const rank = Math.max(1, Math.min(3, constellation.rank || 3));
|
||||
c.strokeStyle = `rgba(105,205,255,${rankAlpha[rank]})`;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ def rounded_point(point: list[float]) -> list[float]:
|
|||
|
||||
def main() -> None:
|
||||
sky_culture = fetch(STELLARIUM_URL)
|
||||
star_data = fetch(D3_BASE + "stars.6.json")["features"]
|
||||
star_data = fetch(D3_BASE + "stars.8.json")["features"]
|
||||
name_data = fetch(D3_BASE + "constellations.json")["features"]
|
||||
stars = {
|
||||
int(feature["id"]): rounded_point(feature["geometry"]["coordinates"])
|
||||
|
|
@ -93,12 +93,27 @@ def main() -> None:
|
|||
if missing:
|
||||
raise RuntimeError(f"{ident}: HIP ausente no catálogo: {missing}")
|
||||
lines.append([stars[hip] for hip in hip_line])
|
||||
image = constellation.get("image")
|
||||
art = None
|
||||
if image:
|
||||
anchors = []
|
||||
for anchor in image["anchors"]:
|
||||
hip = int(anchor["hip"])
|
||||
if hip not in stars:
|
||||
raise RuntimeError(f"{ident}: âncora HIP ausente: {hip}")
|
||||
anchors.append({"pos": anchor["pos"], "sky": stars[hip]})
|
||||
art = {
|
||||
"file": "constellation-art/" + Path(image["file"]).name,
|
||||
"size": image["size"],
|
||||
"anchors": anchors,
|
||||
}
|
||||
merged[ident] = {
|
||||
"id": ident,
|
||||
"name": ZODIAC[ident],
|
||||
"rank": 1 if ident in {"Ari", "Tau", "Gem", "Leo", "Vir", "Sco", "Sgr"} else 2,
|
||||
"label": metadata.get(ident, {}).get("label"),
|
||||
"lines": lines,
|
||||
"art": art,
|
||||
}
|
||||
|
||||
payload = json.dumps(
|
||||
|
|
|
|||