Prevent distorted constellation artwork
This commit is contained in:
parent
b659ae09ab
commit
344fa08dbb
1 changed files with 45 additions and 19 deletions
|
|
@ -1461,26 +1461,28 @@ 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. */
|
||||
/* 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 [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];
|
||||
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, 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;
|
||||
energy += px * px + py * py;
|
||||
}
|
||||
if (energy < 1e-6) return null;
|
||||
const a = dot / energy;
|
||||
const b = cross / energy;
|
||||
const e = qc[0] - a * pc[0] + b * pc[1];
|
||||
const f = qc[1] - b * pc[0] - a * pc[1];
|
||||
return [a, b, -b, a, e, f];
|
||||
}
|
||||
|
||||
function drawConstellationArt(c, constellation, lst, W, horizonPx) {
|
||||
|
|
@ -1498,12 +1500,36 @@ function drawConstellationArt(c, constellation, lst, W, horizonPx) {
|
|||
};
|
||||
});
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue