Remove constellation artwork, keep line traces only
Drops the 12 zodiac PNGs, their loader/similarity-transform renderer and the art anchors in constellations.js; build_constellations.py no longer emits art data. Constellation mode now shows just the Stellarium traces, labels and stars. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015HtPHsTnDcLVSCiF5FGHZj
|
|
@ -34,8 +34,7 @@ plugin/
|
|||
main.qml # Plasma WallpaperItem — hosts everything
|
||||
dancer.html # Self-contained animation (HTML + CSS + JS)
|
||||
starcatalog.js # Generated star data (Yale BSC) — see tools/build_catalog.py
|
||||
constellations.js # Generated zodiac lines/anchors — see tools/build_constellations.py
|
||||
constellation-art/ # 12 zodiac PNGs (black background → alpha at load)
|
||||
constellations.js # Generated zodiac lines — see tools/build_constellations.py
|
||||
milkyway.jpg # Gaia all-sky photo, plate carrée — see tools/build_skymap.py
|
||||
astronomy.browser.min.js # Astronomy Engine (MIT) — Moon/planet ephemerides
|
||||
tools/
|
||||
|
|
@ -59,7 +58,7 @@ Pure HTML/CSS/JS, no external dependencies. All visuals are self-contained:
|
|||
| Realistic sky (stars) | `<canvas>`; real-time alt-az projection of `starcatalog.js` (8,404 Yale BSC stars, V ≤ 6.5) for the configured lat/long (default Toledo-PR). Static layer pre-rendered offscreen with triple-buffer + 1.4 s crossfade between reprojections (every 30 s; 4 s during timelapse/camera rotation); bright stars twinkle per frame. Debug: `window.__skyTimeOffset` (ms) fast-forwards the sky |
|
||||
| Milky Way | Per-pixel sampling of `milkyway.jpg` (Gaia photo, full res): `buildMwGrid` caches viewport unit vectors + extinction/glare, `renderMilkyWay` applies only the sidereal rotation (camera azimuth folded into the equatorial→galactic matrix) and stylized "development" (deep blacks, saturation, cool shadows). Decoded texture stays resident (~75 MB) |
|
||||
| Moon & planets | Astronomy Engine topocentric ephemerides: Moon with real phase texture + bright-limb angle, 7 planets with magnitude-scaled dots (labels only in constellation mode) |
|
||||
| Zodiac constellations | Stellarium Modern lines + anchored PNG art (similarity transform, no shear); toggled via `/tmp` file or `C` key |
|
||||
| Zodiac constellations | Stellarium Modern lines (traces only, no artwork); toggled via `/tmp` file or `C` key |
|
||||
| Nebula blobs, grid floor, horizon | CSS animations |
|
||||
| DANCEBOT-9000 robot | Pure CSS (divs + `@keyframes`) |
|
||||
| Chest EQ bars & VU meter | JS-driven `requestAnimationFrame` |
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# 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.
|
||||
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
|
@ -1233,40 +1233,6 @@ const ctx = canvas.getContext('2d');
|
|||
let obsLat = -24.72, obsLon = -53.74; // Toledo-PR; sobrescrito pela config
|
||||
let showConstellations = false;
|
||||
let constellationPreviewId = null;
|
||||
const constellationArtImages = new Map();
|
||||
if (typeof CONSTELLATIONS !== 'undefined') {
|
||||
for (const constellation of CONSTELLATIONS) {
|
||||
if (!constellation.art) continue;
|
||||
const img = new Image();
|
||||
const entry = { img, render: null };
|
||||
img.onload = () => {
|
||||
/* Os originais usam preto como transparência. Convertê-lo em alpha
|
||||
evita que a transformação afim revele os limites retangulares. */
|
||||
const out = document.createElement('canvas');
|
||||
out.width = constellation.art.size[0];
|
||||
out.height = constellation.art.size[1];
|
||||
const oc = out.getContext('2d', { willReadFrequently: true });
|
||||
oc.drawImage(img, 0, 0, out.width, out.height);
|
||||
const pixels = oc.getImageData(0, 0, out.width, out.height);
|
||||
for (let i = 0; i < pixels.data.length; i += 4) {
|
||||
const r = pixels.data[i], g = pixels.data[i + 1], b = pixels.data[i + 2];
|
||||
const light = Math.max(r, g, b);
|
||||
const mask = Math.max(0, Math.min(1, (light - 9) / 118));
|
||||
pixels.data[i] = 105 + light * 0.42;
|
||||
pixels.data[i + 1] = 175 + light * 0.27;
|
||||
pixels.data[i + 2] = 235 + light * 0.08;
|
||||
pixels.data[i + 3] = pixels.data[i + 3] * mask;
|
||||
}
|
||||
oc.clearRect(0, 0, out.width, out.height);
|
||||
oc.putImageData(pixels, 0, 0);
|
||||
entry.render = out;
|
||||
projectSky();
|
||||
};
|
||||
img.onerror = () => console.error('[skeledance] falha na arte de ' + constellation.name);
|
||||
img.src = constellation.art.file;
|
||||
constellationArtImages.set(constellation.id, entry);
|
||||
}
|
||||
}
|
||||
const CAMERA_HOME_AZ = 90; // posição inicial: leste
|
||||
const CAMERA_TURN_MS = 5 * 60 * 1000;
|
||||
let viewAz = CAMERA_HOME_AZ;
|
||||
|
|
@ -1546,105 +1512,6 @@ function renderMilkyWay(lstDeg, glareOn) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Ajuste de similaridade: aproxima as três âncoras Hipparcos usando apenas
|
||||
translação, rotação e escala uniforme. Diferente de uma transformação
|
||||
afim completa, nunca achata nem alonga a anatomia das ilustrações. */
|
||||
function constellationArtTransform(source, target) {
|
||||
const pc = source.reduce((p, v) => [p[0] + v[0], p[1] + v[1]], [0, 0])
|
||||
.map(v => v / source.length);
|
||||
const qc = target.reduce((p, v) => [p[0] + v.x, p[1] + v.y], [0, 0])
|
||||
.map(v => v / target.length);
|
||||
let dot = 0, cross = 0, mirrorA = 0, mirrorB = 0, energy = 0;
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const px = source[i][0] - pc[0], py = source[i][1] - pc[1];
|
||||
const qx = target[i].x - qc[0], qy = target[i].y - qc[1];
|
||||
dot += px * qx + py * qy;
|
||||
cross += px * qy - py * qx;
|
||||
mirrorA += px * qx - py * qy;
|
||||
mirrorB += py * qx + px * qy;
|
||||
energy += px * px + py * py;
|
||||
}
|
||||
if (energy < 1e-6) return null;
|
||||
const candidate = (a, b, c, d) => {
|
||||
const e = qc[0] - a * pc[0] - c * pc[1];
|
||||
const f = qc[1] - b * pc[0] - d * pc[1];
|
||||
const matrix = [a, b, c, d, e, f];
|
||||
const error = source.reduce((sum, p, i) => {
|
||||
const dx = a * p[0] + c * p[1] + e - target[i].x;
|
||||
const dy = b * p[0] + d * p[1] + f - target[i].y;
|
||||
return sum + dx * dx + dy * dy;
|
||||
}, 0);
|
||||
return { matrix, error };
|
||||
};
|
||||
const rotate = candidate(dot / energy, cross / energy,
|
||||
-cross / energy, dot / energy);
|
||||
/* Ao observar a esfera por dentro, várias cartas precisam inverter a
|
||||
orientação leste/oeste. Esta alternativa inclui a reflexão sem shear. */
|
||||
const reflect = candidate(mirrorA / energy, mirrorB / energy,
|
||||
mirrorB / energy, -mirrorA / energy);
|
||||
return (reflect.error < rotate.error ? reflect : rotate).matrix;
|
||||
}
|
||||
|
||||
function drawConstellationArt(c, constellation, lst, W, horizonPx, previewProjector) {
|
||||
const art = constellation.art;
|
||||
const imageEntry = constellationArtImages.get(constellation.id);
|
||||
if (!art || !imageEntry || !imageEntry.render) return;
|
||||
|
||||
const target = art.anchors.map(anchor => {
|
||||
if (previewProjector) {
|
||||
const sc = previewProjector(anchor.sky);
|
||||
return { x: sc.x, y: sc.y, alt: ALT_TOP / 2 };
|
||||
}
|
||||
const aa = altAz(anchor.sky[0], anchor.sky[1], lst);
|
||||
const daz = ((aa.az - viewAz) % 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;
|
||||
if (!target.some(p => p.alt >= -8 && p.alt <= ALT_TOP + 8 &&
|
||||
p.x >= -W * 0.15 && p.x <= W * 1.15)) return;
|
||||
const targetXs = target.map(p => p.x);
|
||||
if (Math.max(...targetXs) - Math.min(...targetXs) > W * 0.62) return;
|
||||
|
||||
const matrix = constellationArtTransform(
|
||||
art.anchors.map(anchor => anchor.pos), target
|
||||
);
|
||||
if (!matrix || matrix.some(v => !Number.isFinite(v))) return;
|
||||
|
||||
/* Mesmo com escala uniforme, uma âncora próxima da descontinuidade do
|
||||
azimute pode sugerir uma figura gigantesca. Limite visual conservador. */
|
||||
const scale = Math.hypot(matrix[0], matrix[1]);
|
||||
const maxScale = Math.min(
|
||||
W * 0.44 / art.size[0], horizonPx * 0.82 / art.size[1]
|
||||
);
|
||||
if (scale > maxScale) {
|
||||
const sourceCenter = [art.size[0] / 2, art.size[1] / 2];
|
||||
const screenCenter = [
|
||||
matrix[0] * sourceCenter[0] + matrix[2] * sourceCenter[1] + matrix[4],
|
||||
matrix[1] * sourceCenter[0] + matrix[3] * sourceCenter[1] + matrix[5],
|
||||
];
|
||||
const shrink = maxScale / scale;
|
||||
for (let i = 0; i < 4; i++) matrix[i] *= shrink;
|
||||
matrix[4] = screenCenter[0] - matrix[0] * sourceCenter[0] -
|
||||
matrix[2] * sourceCenter[1];
|
||||
matrix[5] = screenCenter[1] - matrix[1] * sourceCenter[0] -
|
||||
matrix[3] * sourceCenter[1];
|
||||
}
|
||||
|
||||
c.save();
|
||||
c.beginPath();
|
||||
c.rect(0, 0, W, horizonPx);
|
||||
c.clip();
|
||||
c.globalCompositeOperation = 'screen';
|
||||
c.globalAlpha = 0.24;
|
||||
c.setTransform(...matrix);
|
||||
c.drawImage(imageEntry.render, 0, 0, art.size[0], art.size[1]);
|
||||
c.restore();
|
||||
}
|
||||
|
||||
function makeConstellationPreviewProjector(constellation, W, horizonPx) {
|
||||
const centerRa = constellation.label ? constellation.label[0] : 0;
|
||||
const centerDec = constellation.label ? constellation.label[1] : 0;
|
||||
|
|
@ -1669,8 +1536,7 @@ function makeConstellationPreviewProjector(constellation, W, horizonPx) {
|
|||
};
|
||||
}
|
||||
|
||||
/* Figuras convencionais dos 12 signos zodiacais, ancoradas em RA/Dec J2000.
|
||||
Arte, linhas e estrelas reais ficam sobrepostas nesta ordem. */
|
||||
/* Traçados convencionais dos 12 signos zodiacais, ancorados em RA/Dec J2000. */
|
||||
function drawConstellations(c, lst, W, H, horizonPx) {
|
||||
if (!showConstellations || typeof CONSTELLATIONS === 'undefined') return;
|
||||
const rankAlpha = [0, 0.52, 0.38, 0.25];
|
||||
|
|
@ -1693,9 +1559,6 @@ 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 visible) {
|
||||
drawConstellationArt(c, constellation, lst, W, horizonPx, previewProjector);
|
||||
}
|
||||
for (const constellation of visible) {
|
||||
const rank = Math.max(1, Math.min(3, constellation.rank || 3));
|
||||
c.strokeStyle = `rgba(105,205,255,${preview ? 0.82 : rankAlpha[rank]})`;
|
||||
|
|
|
|||
|
|
@ -93,27 +93,12 @@ 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(
|
||||
|
|
|
|||