aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/js/skypanel.js
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
committergodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
commit3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch)
tree4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/js/skypanel.js
downloadworldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz
worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/js/skypanel.js')
-rw-r--r--viewer/js/skypanel.js80
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);
+ }
+}