diff --git a/plugin/contents/ui/dancer.html b/plugin/contents/ui/dancer.html
index c273192..19032c3 100644
--- a/plugin/contents/ui/dancer.html
+++ b/plugin/contents/ui/dancer.html
@@ -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.
diff --git a/preview-constellation.sh b/preview-constellation.sh
new file mode 100755
index 0000000..bc4734e
--- /dev/null
+++ b/preview-constellation.sh
@@ -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