Map Moon phases and labeled planets
This commit is contained in:
parent
2bcb973020
commit
44fb0b09a6
3 changed files with 440 additions and 4 deletions
|
|
@ -1199,6 +1199,7 @@ body.chuva #sunChuvaFace { opacity: 1; transition: opacity 5s ease; }
|
|||
|
||||
</div><!-- .scene -->
|
||||
|
||||
<script src="astronomy.browser.min.js"></script>
|
||||
<script src="starcatalog.js"></script>
|
||||
<script src="constellations.js"></script>
|
||||
<script>
|
||||
|
|
@ -1267,6 +1268,16 @@ const HORIZON_F = 0.585; // fração da altura da tela até o horizonte
|
|||
const TWINKLE_MAG = 4.0; // até esta magnitude a estrela cintila; acima é estática
|
||||
const DEG = Math.PI / 180;
|
||||
window.__skyTimeOffset = 0; // debug: ms somados ao relógio p/ testar a rotação
|
||||
const PLANET_SPECS = [
|
||||
[Astronomy.Body.Mercury, 'Mercúrio', [205, 190, 165]],
|
||||
[Astronomy.Body.Venus, 'Vênus', [255, 232, 180]],
|
||||
[Astronomy.Body.Mars, 'Marte', [255, 120, 82]],
|
||||
[Astronomy.Body.Jupiter, 'Júpiter', [255, 218, 170]],
|
||||
[Astronomy.Body.Saturn, 'Saturno', [238, 205, 135]],
|
||||
[Astronomy.Body.Uranus, 'Urano', [145, 225, 235]],
|
||||
[Astronomy.Body.Neptune, 'Netuno', [105, 150, 255]],
|
||||
];
|
||||
let solarSystemFrame = { moon: null, planets: [] };
|
||||
let skyTimeScale = 1;
|
||||
let skyClockRealMs = Date.now();
|
||||
let skyClockSimMs = skyClockRealMs;
|
||||
|
|
@ -1375,7 +1386,7 @@ const E2G = [
|
|||
para plate carrée por tools/build_skymap.py. A geometria é real; apenas
|
||||
exposição, contraste e saturação são estilizados para o wallpaper. */
|
||||
const MW_BLACK = 46;
|
||||
const MW_EXPOSURE = 0.96;
|
||||
const MW_EXPOSURE = 0.78;
|
||||
const MW_SAMPLE_SCALE = 1;
|
||||
const mwTex = { data: null, w: 0, h: 0 };
|
||||
const mwCanvas = document.createElement('canvas');
|
||||
|
|
@ -1772,6 +1783,149 @@ let constellationPreviewFileVal = null;
|
|||
.finally(() => setTimeout(pollConstellationPreviewFile, 800));
|
||||
})();
|
||||
|
||||
/* ── Lua e planetas: efemérides topocêntricas do Astronomy Engine ── */
|
||||
function buildMoonPhaseTexture(phase) {
|
||||
const size = 96, radius = size * 0.46;
|
||||
const out = document.createElement('canvas');
|
||||
out.width = out.height = size;
|
||||
const oc = out.getContext('2d');
|
||||
const image = oc.createImageData(size, size);
|
||||
const px = image.data;
|
||||
const elongation = Math.min(phase, 360 - phase) * DEG;
|
||||
/* A textura nasce com o lado claro à direita; drawSolarSystem a gira para
|
||||
apontar ao Sol astronômico na esfera local. */
|
||||
const sunX = Math.sin(elongation), sunZ = -Math.cos(elongation);
|
||||
for (let y = 0, i = 0; y < size; y++) {
|
||||
for (let x = 0; x < size; x++, i += 4) {
|
||||
const nx = (x + 0.5 - size / 2) / radius;
|
||||
const ny = (y + 0.5 - size / 2) / radius;
|
||||
const rr = nx * nx + ny * ny;
|
||||
if (rr >= 1) continue;
|
||||
const nz = Math.sqrt(1 - rr);
|
||||
const sunlight = nx * sunX + nz * sunZ;
|
||||
const limb = 0.68 + 0.32 * nz;
|
||||
const maria = 0.92 + 0.045 * Math.sin(nx * 19 + ny * 7) +
|
||||
0.035 * Math.sin(nx * 8 - ny * 23);
|
||||
const light = sunlight > 0
|
||||
? (0.42 + 0.58 * sunlight) * limb * maria
|
||||
: 0.055 * limb;
|
||||
px[i] = 225 * light;
|
||||
px[i + 1] = 230 * light;
|
||||
px[i + 2] = 218 * light + (sunlight <= 0 ? 9 : 0);
|
||||
px[i + 3] = 255 * Math.min(1, (1 - rr) * 42);
|
||||
}
|
||||
}
|
||||
oc.putImageData(image, 0, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
function updateSolarSystem(date, lst, W, H, horizonPx) {
|
||||
if (typeof Astronomy === 'undefined') return;
|
||||
try {
|
||||
const observer = new Astronomy.Observer(obsLat, obsLon, 0);
|
||||
const bodyCoordinates = body => {
|
||||
const eq = Astronomy.Equator(body, date, observer, true, true);
|
||||
const aa = altAz(eq.ra * 15, eq.dec, lst);
|
||||
const sc = skyToScreen(aa.alt, aa.az, W, horizonPx);
|
||||
return { eq, aa, sc };
|
||||
};
|
||||
const projectBody = body => {
|
||||
const coords = bodyCoordinates(body);
|
||||
return coords.sc ? { ...coords.sc, alt: coords.aa.alt, az: coords.aa.az } : null;
|
||||
};
|
||||
|
||||
const moonCoords = bodyCoordinates(Astronomy.Body.Moon);
|
||||
const sunCoords = bodyCoordinates(Astronomy.Body.Sun);
|
||||
const moonScreen = moonCoords.sc
|
||||
? { ...moonCoords.sc, alt: moonCoords.aa.alt, az: moonCoords.aa.az }
|
||||
: null;
|
||||
const phase = Astronomy.MoonPhase(date);
|
||||
const moonIllumination = Astronomy.Illumination(Astronomy.Body.Moon, date);
|
||||
const moonRadius = Math.max(12, Math.min(22, H * 0.0085));
|
||||
const deltaAz = (((sunCoords.aa.az - moonCoords.aa.az) % 360 + 540) % 360 - 180) * DEG;
|
||||
const moonAlt = moonCoords.aa.alt * DEG, sunAlt = sunCoords.aa.alt * DEG;
|
||||
const bearing = Math.atan2(
|
||||
Math.sin(deltaAz) * Math.cos(sunAlt),
|
||||
Math.cos(moonAlt) * Math.sin(sunAlt) -
|
||||
Math.sin(moonAlt) * Math.cos(sunAlt) * Math.cos(deltaAz)
|
||||
);
|
||||
const brightDx = Math.sin(bearing) /
|
||||
Math.max(0.16, Math.abs(Math.cos(moonAlt))) * (W / FOV_AZ);
|
||||
const brightDy = -Math.cos(bearing) * (horizonPx / ALT_TOP);
|
||||
const brightAngle = Math.atan2(brightDy, brightDx);
|
||||
const moon = moonScreen ? {
|
||||
...moonScreen,
|
||||
radius: moonRadius,
|
||||
phase,
|
||||
illuminated: moonIllumination.phase_fraction,
|
||||
brightAngle,
|
||||
texture: buildMoonPhaseTexture(phase),
|
||||
} : null;
|
||||
|
||||
const planets = [];
|
||||
for (const [body, name, color] of PLANET_SPECS) {
|
||||
const screen = projectBody(body);
|
||||
if (!screen) continue;
|
||||
const illumination = Astronomy.Illumination(body, date);
|
||||
planets.push({
|
||||
...screen, name, color, body,
|
||||
magnitude: illumination.mag,
|
||||
radius: Math.max(1.8, Math.min(5.2, 3.45 - illumination.mag * 0.46)),
|
||||
});
|
||||
}
|
||||
solarSystemFrame = { moon, planets };
|
||||
} catch (error) {
|
||||
console.error('[skeledance] falha nas efemérides:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function drawSolarSystem(c) {
|
||||
const { moon, planets } = solarSystemFrame;
|
||||
if (moon) {
|
||||
c.save();
|
||||
const fade = extinction(moon.alt);
|
||||
c.globalAlpha = fade;
|
||||
c.shadowColor = 'rgba(205,220,255,0.7)';
|
||||
c.shadowBlur = moon.radius * 1.25;
|
||||
c.translate(moon.x, moon.y);
|
||||
c.rotate(moon.brightAngle);
|
||||
c.drawImage(moon.texture, -moon.radius, -moon.radius,
|
||||
moon.radius * 2, moon.radius * 2);
|
||||
c.restore();
|
||||
}
|
||||
|
||||
c.save();
|
||||
c.font = `${Math.max(11, Math.round(canvas.height * 0.0095))}px ui-monospace, monospace`;
|
||||
c.textAlign = 'left';
|
||||
c.textBaseline = 'middle';
|
||||
for (const planet of planets) {
|
||||
const fade = extinction(planet.alt);
|
||||
const [r, g, b] = planet.color;
|
||||
c.save();
|
||||
c.globalAlpha = fade;
|
||||
c.shadowColor = `rgba(${r},${g},${b},0.95)`;
|
||||
c.shadowBlur = planet.radius * 3.2;
|
||||
c.fillStyle = `rgb(${r},${g},${b})`;
|
||||
c.beginPath();
|
||||
c.arc(planet.x, planet.y, planet.radius, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
if (planet.body === Astronomy.Body.Saturn) {
|
||||
c.strokeStyle = `rgba(${r},${g},${b},0.82)`;
|
||||
c.lineWidth = Math.max(1, planet.radius * 0.34);
|
||||
c.beginPath();
|
||||
c.ellipse(planet.x, planet.y, planet.radius * 1.8,
|
||||
planet.radius * 0.62, -0.18, 0, Math.PI * 2);
|
||||
c.stroke();
|
||||
}
|
||||
c.shadowColor = 'rgba(4,8,20,0.95)';
|
||||
c.shadowBlur = 4;
|
||||
c.fillStyle = `rgba(${r},${g},${b},0.92)`;
|
||||
c.fillText(planet.name, planet.x + planet.radius + 7, planet.y - 1);
|
||||
c.restore();
|
||||
}
|
||||
c.restore();
|
||||
}
|
||||
|
||||
/* ── 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.
|
||||
|
|
@ -1797,7 +1951,8 @@ function projectSky() {
|
|||
const bctx = renderCanvas.getContext('2d');
|
||||
bctx.clearRect(0, 0, W, bufferH);
|
||||
viewAz = currentCameraAz();
|
||||
const lst = localSiderealDeg(skyNowMs());
|
||||
const skyDate = new Date(skyNowMs());
|
||||
const lst = localSiderealDeg(skyDate.getTime());
|
||||
const glareOn = !document.body.classList.contains('balada') &&
|
||||
!document.body.classList.contains('chuva');
|
||||
const nextTwinkList = [];
|
||||
|
|
@ -1807,10 +1962,10 @@ function projectSky() {
|
|||
bctx.imageSmoothingQuality = 'high';
|
||||
bctx.save();
|
||||
bctx.globalCompositeOperation = 'lighter';
|
||||
bctx.globalAlpha = 0.17;
|
||||
bctx.globalAlpha = 0.13;
|
||||
bctx.filter = `blur(${Math.max(7, H * 0.009)}px) saturate(150%)`;
|
||||
bctx.drawImage(mwCanvas, 0, 0, W, horizonPx);
|
||||
bctx.globalAlpha = 0.88;
|
||||
bctx.globalAlpha = 0.72;
|
||||
bctx.filter = 'contrast(116%) saturate(158%)';
|
||||
bctx.drawImage(mwCanvas, 0, 0, W, horizonPx);
|
||||
bctx.restore();
|
||||
|
|
@ -1847,6 +2002,7 @@ function projectSky() {
|
|||
bctx.fillRect(sc.x - r, sc.y - r, r * 2, r * 2);
|
||||
}
|
||||
}
|
||||
updateSolarSystem(skyDate, lst, W, H, horizonPx);
|
||||
|
||||
/* O quadro só se torna visível depois de estar completo. O antigo fica
|
||||
intacto para um crossfade que disfarça o pequeno avanço sideral. */
|
||||
|
|
@ -1909,6 +2065,7 @@ function drawStars(ts) {
|
|||
ctx.fillStyle = `rgba(${s.cr},${s.cg},${s.cb},${(s.a * tw).toFixed(3)})`;
|
||||
ctx.fill();
|
||||
}
|
||||
drawSolarSystem(ctx);
|
||||
requestAnimationFrame(drawStars);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue