Add fixed constellation inspection mode

This commit is contained in:
vini 2026-07-19 14:32:48 -03:00
parent 344fa08dbb
commit 4e01c768c9
2 changed files with 96 additions and 12 deletions

View file

@ -1219,6 +1219,7 @@ 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) {
@ -1485,12 +1486,16 @@ function constellationArtTransform(source, target) {
return [a, b, -b, a, e, f];
}
function drawConstellationArt(c, constellation, lst, W, horizonPx) {
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 - VIEW_AZ) % 360 + 540) % 360 - 180;
return {
@ -1541,31 +1546,66 @@ function drawConstellationArt(c, constellation, lst, W, horizonPx) {
c.restore();
}
function makeConstellationPreviewProjector(constellation, W, horizonPx) {
const centerRa = constellation.label ? constellation.label[0] : 0;
const centerDec = constellation.label ? constellation.label[1] : 0;
const flatten = point => {
const dra = ((point[0] - centerRa) % 360 + 540) % 360 - 180;
return [dra * Math.cos(centerDec * DEG), point[1] - centerDec];
};
const points = constellation.lines.flat().map(flatten);
const xs = points.map(p => p[0]), ys = points.map(p => p[1]);
const minX = Math.min(...xs), maxX = Math.max(...xs);
const minY = Math.min(...ys), maxY = Math.max(...ys);
const scale = Math.min(
W * 0.34 / Math.max(1, maxX - minX),
horizonPx * 0.48 / Math.max(1, maxY - minY)
);
const midX = (minX + maxX) / 2, midY = (minY + maxY) / 2;
const screenX = W / 2, screenY = horizonPx * 0.42;
return point => {
const p = flatten(point);
return { x: screenX + (p[0] - midX) * scale,
y: screenY - (p[1] - midY) * scale };
};
}
/* Figuras convencionais dos 12 signos zodiacais, ancoradas em RA/Dec J2000.
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];
const rankWidth = [0, 1.35, 1.05, 0.82];
const preview = constellationPreviewId
? CONSTELLATIONS.find(item => item.id === constellationPreviewId)
: null;
const visible = preview ? [preview] : CONSTELLATIONS;
const previewProjector = preview
? makeConstellationPreviewProjector(preview, W, horizonPx)
: null;
const projectPoint = point => {
if (previewProjector) return previewProjector(point);
const aa = altAz(point[0], point[1], lst);
return skyToScreen(aa.alt, aa.az, W, horizonPx);
};
c.save();
c.lineCap = 'round';
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 visible) {
drawConstellationArt(c, constellation, lst, W, horizonPx, previewProjector);
}
for (const constellation of CONSTELLATIONS) {
for (const constellation of visible) {
const rank = Math.max(1, Math.min(3, constellation.rank || 3));
c.strokeStyle = `rgba(105,205,255,${rankAlpha[rank]})`;
c.lineWidth = rankWidth[rank];
c.strokeStyle = `rgba(105,205,255,${preview ? 0.82 : rankAlpha[rank]})`;
c.lineWidth = preview ? 2.1 : rankWidth[rank];
for (const line of constellation.lines) {
c.beginPath();
let previous = null;
for (const point of line) {
const aa = altAz(point[0], point[1], lst);
const sc = skyToScreen(aa.alt, aa.az, W, horizonPx);
const sc = projectPoint(point);
if (!sc) {
previous = null;
continue;
@ -1583,6 +1623,22 @@ function drawConstellations(c, lst, W, H, horizonPx) {
}
}
if (preview) {
const seen = new Set();
c.shadowColor = 'rgba(160,225,255,0.95)';
c.shadowBlur = 9;
c.fillStyle = 'rgba(225,245,255,0.96)';
for (const point of preview.lines.flat()) {
const key = point.join(',');
if (seen.has(key)) continue;
seen.add(key);
const sc = projectPoint(point);
c.beginPath();
c.arc(sc.x, sc.y, Math.max(2.4, H * 0.0018), 0, Math.PI * 2);
c.fill();
}
}
/* Todos os signos recebem nome; colisões na tela ainda são evitadas. */
c.shadowBlur = 5;
c.font = `${Math.max(10, Math.round(H * 0.0105))}px ui-monospace, monospace`;
@ -1590,10 +1646,9 @@ function drawConstellations(c, lst, W, H, horizonPx) {
c.textBaseline = 'middle';
c.fillStyle = 'rgba(170,225,255,0.66)';
const occupied = [];
for (const constellation of CONSTELLATIONS) {
for (const constellation of visible) {
if (!constellation.label) continue;
const aa = altAz(constellation.label[0], constellation.label[1], lst);
const sc = skyToScreen(aa.alt, aa.az, W, horizonPx);
const sc = projectPoint(constellation.label);
if (!sc || sc.y < 24 || sc.y > horizonPx - 18) continue;
const width = c.measureText(constellation.name).width + 14;
const box = { x1: sc.x - width / 2, y1: sc.y - 9,
@ -1601,7 +1656,7 @@ function drawConstellations(c, lst, W, H, horizonPx) {
if (occupied.some(o => box.x1 < o.x2 && box.x2 > o.x1 &&
box.y1 < o.y2 && box.y2 > o.y1)) continue;
occupied.push(box);
c.fillText(constellation.name, sc.x, sc.y);
c.fillText(preview ? `Prévia: ${constellation.name}` : constellation.name, sc.x, sc.y);
}
c.restore();
}
@ -1627,6 +1682,23 @@ let constellationsFileVal = null;
.finally(() => setTimeout(pollConstellationsFile, 800));
})();
/* Modo de inspeção temporário: centraliza um único signo sem alterar suas
coordenadas internas. Controlado por preview-constellation.sh. */
let constellationPreviewFileVal = null;
(function pollConstellationPreviewFile() {
fetch('file:///tmp/skeledance-constellation-preview?t=' + Date.now())
.then(r => r.text())
.then(t => {
const v = t.trim();
if (v === constellationPreviewFileVal) return;
constellationPreviewFileVal = v;
constellationPreviewId = CONSTELLATIONS.some(item => item.id === v) ? v : null;
projectSky();
})
.catch(() => {})
.finally(() => setTimeout(pollConstellationPreviewFile, 800));
})();
/* ── Passo de projeção: recalcula posições de tela (a cada 30s o céu gira
~0.125°, imperceptível). Camada estática (Via Láctea + estrelas fracas)
é pré-renderizada fora da tela; só as brilhantes cintilam por frame.

12
preview-constellation.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
STATE_FILE=/tmp/skeledance-constellation-preview
SIGN=${1:-Vir}
if [ "$SIGN" = "off" ]; then
printf 'off\n' > "$STATE_FILE"
printf 'Prévia de constelação desativada\n'
else
printf '%s\n' "$SIGN" > "$STATE_FILE"
printf 'Prévia fixa: %s\n' "$SIGN"
fi