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 obsLat = -24.72, obsLon = -53.74; // Toledo-PR; sobrescrito pela config
|
||||||
let showConstellations = false;
|
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
|
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
|
// usuário está voltada p/ leste, então o wallpaper age
|
||||||
// como janela: estrelas nascem subindo dentro da cena
|
// como janela: estrelas nascem subindo dentro da cena
|
||||||
|
|
@ -1400,8 +1411,62 @@ function renderMilkyWay(lstDeg, glareOn) {
|
||||||
return true;
|
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.
|
/* 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) {
|
function drawConstellations(c, lst, W, H, horizonPx) {
|
||||||
if (!showConstellations || typeof CONSTELLATIONS === 'undefined') return;
|
if (!showConstellations || typeof CONSTELLATIONS === 'undefined') return;
|
||||||
const rankAlpha = [0, 0.52, 0.38, 0.25];
|
const rankAlpha = [0, 0.52, 0.38, 0.25];
|
||||||
|
|
@ -1412,6 +1477,9 @@ function drawConstellations(c, lst, W, H, horizonPx) {
|
||||||
c.lineJoin = 'round';
|
c.lineJoin = 'round';
|
||||||
c.shadowColor = 'rgba(60,185,255,0.65)';
|
c.shadowColor = 'rgba(60,185,255,0.65)';
|
||||||
c.shadowBlur = 7;
|
c.shadowBlur = 7;
|
||||||
|
for (const constellation of CONSTELLATIONS) {
|
||||||
|
drawConstellationArt(c, constellation, lst, W, horizonPx);
|
||||||
|
}
|
||||||
for (const constellation of CONSTELLATIONS) {
|
for (const constellation of CONSTELLATIONS) {
|
||||||
const rank = Math.max(1, Math.min(3, constellation.rank || 3));
|
const rank = Math.max(1, Math.min(3, constellation.rank || 3));
|
||||||
c.strokeStyle = `rgba(105,205,255,${rankAlpha[rank]})`;
|
c.strokeStyle = `rgba(105,205,255,${rankAlpha[rank]})`;
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ def rounded_point(point: list[float]) -> list[float]:
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
sky_culture = fetch(STELLARIUM_URL)
|
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"]
|
name_data = fetch(D3_BASE + "constellations.json")["features"]
|
||||||
stars = {
|
stars = {
|
||||||
int(feature["id"]): rounded_point(feature["geometry"]["coordinates"])
|
int(feature["id"]): rounded_point(feature["geometry"]["coordinates"])
|
||||||
|
|
@ -93,12 +93,27 @@ def main() -> None:
|
||||||
if missing:
|
if missing:
|
||||||
raise RuntimeError(f"{ident}: HIP ausente no catálogo: {missing}")
|
raise RuntimeError(f"{ident}: HIP ausente no catálogo: {missing}")
|
||||||
lines.append([stars[hip] for hip in hip_line])
|
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] = {
|
merged[ident] = {
|
||||||
"id": ident,
|
"id": ident,
|
||||||
"name": ZODIAC[ident],
|
"name": ZODIAC[ident],
|
||||||
"rank": 1 if ident in {"Ari", "Tau", "Gem", "Leo", "Vir", "Sco", "Sgr"} else 2,
|
"rank": 1 if ident in {"Ari", "Tau", "Gem", "Leo", "Vir", "Sco", "Sgr"} else 2,
|
||||||
"label": metadata.get(ident, {}).get("label"),
|
"label": metadata.get(ident, {}).get("label"),
|
||||||
"lines": lines,
|
"lines": lines,
|
||||||
|
"art": art,
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = json.dumps(
|
payload = json.dumps(
|
||||||
|
|
|
||||||