// skypanel.js — Sky & seasons bar: day/moment sliders, play, sun marker, tropics and polar circles import { $ } from './ui.js'; import { sunVec, subsolar, dayFraction, seasonName, TILT_DEG, YEAR_DAYS, DAY_MOMENTS } from './sky.js'; import { PLANET } from './planet.js'; const parallel = lat => Array.from({ length: 181 }, (_, i) => ({ lat, lon: -180 + 2 * i })); export const SKY_LINES = [TILT_DEG, -TILT_DEG, 90 - TILT_DEG, TILT_DEG - 90].map(parallel); const int = v => Math.round(v).toLocaleString('en-US'); export function dayLabel(day) { const s = seasonName(day); return `day ${day} of ${YEAR_DAYS}${s ? ` · ${s}` : ''}`; } const pad = v => String(v).padStart(2, '0'); export const momentLabel = mo => (PLANET.moment_s ? `moment ${int(mo)} of ${int(DAY_MOMENTS)}` : `${pad(Math.floor(mo / 60))}:${pad(Math.round(mo) % 60)}`); export function daylightLabel(lat, day) { const f = dayFraction(lat, day); return `daylight ${Math.round(f * 100)}% (${(f * PLANET.day_hours).toFixed(1)} h)`; } export class SkyPanel { constructor(app) { this.app = app; this.timer = null; this.day = $('#sky-day'); this.mo = $('#sky-mo'); this.day.max = YEAR_DAYS - 1; this.mo.max = DAY_MOMENTS - 1; $('#sky-toggle').addEventListener('click', () => this.set(!app.state.sky)); this.day.addEventListener('input', () => { app.state.day = Number(this.day.value); this.apply(); }); this.mo.addEventListener('input', () => { app.state.mo = Number(this.mo.value); this.apply(); }); $('#sky-play').addEventListener('click', () => this.play(!this.timer)); this.set(app.state.sky); } set(on) { this.app.state.sky = on; $('#skybar').hidden = !on; $('#sky-toggle').classList.toggle('on', on); if (!on) this.play(false); this.apply(); } play(on) { // one day per 10 s clearInterval(this.timer); this.timer = null; $('#sky-play').textContent = on ? '⏸' : '▶'; if (!on) return; this.timer = setInterval(() => { const s = this.app.state; s.mo += Math.round(DAY_MOMENTS / 200); if (s.mo >= DAY_MOMENTS) { s.mo -= DAY_MOMENTS; s.day = (s.day + 1) % YEAR_DAYS; } this.apply(); }, 50); } apply() { const s = this.app.state, sun = subsolar(s.day, s.mo); this.day.value = String(s.day); this.mo.value = String(s.mo); $('#sky-day-label').textContent = dayLabel(s.day); $('#sky-mo-label').textContent = momentLabel(s.mo); this.app.setSun(s.sky ? sunVec(s.day, s.mo) : null); this.app.overlays.setMarkers('sun', s.sky ? [{ lat: sun.lat, lon: sun.lon, label: '☉' }] : []); if (this.linesOn !== s.sky) { // the parallels only change when the sky is switched this.app.overlays.setExtra('sky', s.sky ? SKY_LINES : [], 0xffd166); this.linesOn = s.sky; } this.refresh(); } refresh() { // day length at the view centre if (!this.app.state.sky || !this.app.view) return; $('#sky-daylen').textContent = daylightLabel(this.app.view.getState().lat, this.app.state.day); } }