diff options
Diffstat (limited to 'viewer/js/skypanel.js')
| -rw-r--r-- | viewer/js/skypanel.js | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/viewer/js/skypanel.js b/viewer/js/skypanel.js new file mode 100644 index 0000000..fff0440 --- /dev/null +++ b/viewer/js/skypanel.js @@ -0,0 +1,80 @@ +// 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); + } +} |
