// 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);
}
}