🌘 closer: event band — the ±2 yr window gets its own full-width strip (opus)
SYZYGY's 104 diamonds clustered into ~1.6% of the 250-yr timeline (invisible in deep time). Add a second strip under #timeline-outer that maps ONLY the computed ±2 yr window to full width — diamonds live there (same ev-* classes + click-jump), and the main strip gets a brass bracket marking the window's true extent (1.6% near, 0.5% in deep time). windowFraction exported + gated. Verified: 22/22 (+band gate); diamonds spread 0.4–98.7%; click-jump works in near AND deep mode; zero console errors both epochs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dec2b89de6
commit
bdba119484
@ -202,3 +202,20 @@ html, body {
|
||||
.ev-elong { top: -8px; background: #7fc7d6; box-shadow: 0 0 5px rgba(127, 199, 214, 0.6); }
|
||||
.ev-conj { top: 2px; width: 5px; height: 5px; background: var(--brass-dim); opacity: 0.6; }
|
||||
.ev-conj:hover { opacity: 1; }
|
||||
|
||||
/* PERIHELION-O — event band: the computed ±2 yr window mapped to full width in a
|
||||
strip below the timeline (diamonds spread out); a bracket on the main strip
|
||||
marks the window's real extent. Reuses the ev-* diamond classes above. */
|
||||
#event-band {
|
||||
position: relative; height: 14px; margin-top: 6px;
|
||||
border-radius: 5px; background: rgba(255, 255, 255, 0.04); border: 1px solid var(--border);
|
||||
}
|
||||
#event-band #event-marks { inset: 1px 0; }
|
||||
#event-band .ev-mark { top: 50%; transform: translate(-50%, -50%) rotate(45deg); }
|
||||
#event-band .ev-mark:hover { transform: translate(-50%, -50%) rotate(45deg) scale(1.7); }
|
||||
#event-band .ev-conj { top: 50%; }
|
||||
#event-window-bracket {
|
||||
position: absolute; top: -3px; bottom: -3px; z-index: 3; pointer-events: none;
|
||||
border: 1px solid var(--brass); border-radius: 4px;
|
||||
background: rgba(217, 164, 65, 0.14); box-shadow: 0 0 7px rgba(217, 164, 65, 0.45);
|
||||
}
|
||||
|
||||
@ -140,6 +140,13 @@ export function nextEvent(bodyId, jd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fraction of an instant within the event band's [winStart, winEnd] window (0..1).
|
||||
// The band maps the ±2 yr window to full width regardless of near/deep epoch.
|
||||
// Exported so verify.html gates the mapping without a DOM.
|
||||
export function windowFraction(ms, winStartMs, winEndMs) {
|
||||
return (ms - winStartMs) / ((winEndMs - winStartMs) || 1);
|
||||
}
|
||||
|
||||
// ---- default factory: timeline diamonds + HUD row ----
|
||||
const WINDOW_DAYS = 730; // ±2 years, computed lazily around the current sim date
|
||||
function cssKind(type) { return type === 'opposition' ? 'opp' : type.startsWith('elongation') ? 'elong' : 'conj'; }
|
||||
@ -149,9 +156,19 @@ export default function create(ctx) {
|
||||
const strip = document.getElementById('timeline-outer');
|
||||
if (!strip) return null; // no time bar (e.g. headless) — plain exports still work
|
||||
|
||||
// PERIHELION-O event band — the ±2 yr computed window mapped to FULL width in a
|
||||
// second strip below the timeline, so diamonds spread out instead of clustering
|
||||
// into ~1.6% of the 250-yr strip (worse in deep time). A bracket on the main
|
||||
// strip marks the window's true extent, tying the two together.
|
||||
const band = document.createElement('div');
|
||||
band.id = 'event-band';
|
||||
const marks = document.createElement('div');
|
||||
marks.id = 'event-marks';
|
||||
strip.appendChild(marks);
|
||||
band.appendChild(marks);
|
||||
strip.parentNode.insertBefore(band, strip.nextSibling); // directly under timeline-outer
|
||||
const bracket = document.createElement('div');
|
||||
bracket.id = 'event-window-bracket';
|
||||
strip.appendChild(bracket);
|
||||
|
||||
const minMs = CONFIG.time.minMs, maxMs = CONFIG.time.maxMs, spanMs = maxMs - minMs;
|
||||
let on = true, events = [];
|
||||
@ -183,9 +200,10 @@ export default function create(ctx) {
|
||||
|
||||
function render() {
|
||||
marks.textContent = '';
|
||||
const winStartMs = unixMsFromJd(winStart), winEndMs = unixMsFromJd(winEnd);
|
||||
for (const e of events) {
|
||||
const ms = unixMsFromJd(e.jd);
|
||||
const f = (ms - minMs) / spanMs;
|
||||
const f = windowFraction(ms, winStartMs, winEndMs); // position WITHIN the band
|
||||
if (f < 0 || f > 1) continue;
|
||||
const d = document.createElement('div');
|
||||
d.className = `ev-mark ev-${cssKind(e.type)}`;
|
||||
@ -197,6 +215,12 @@ export default function create(ctx) {
|
||||
});
|
||||
marks.appendChild(d);
|
||||
}
|
||||
// bracket: where the ±2 yr window sits within the full CONFIG.time range (tiny
|
||||
// in near mode, tinier in deep time — that's the point of the band below it).
|
||||
const bl = ((winStartMs - minMs) / spanMs) * 100;
|
||||
const bw = ((winEndMs - winStartMs) / spanMs) * 100;
|
||||
bracket.style.left = `${Math.min(Math.max(0, bl), 100).toFixed(4)}%`;
|
||||
bracket.style.width = `${Math.max(0.5, Math.min(bw, 100)).toFixed(4)}%`;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
17
verify.html
17
verify.html
@ -286,6 +286,23 @@
|
||||
finish();
|
||||
}
|
||||
|
||||
/* PERIHELION-O — event band: the computed ±2 yr window maps to full band width.
|
||||
windowFraction is pure; the click-jump-in-band is a browser real-input test. */
|
||||
{
|
||||
const { windowFraction, findEvents } = await import('./js/layers/events.js');
|
||||
const okEnds = windowFraction(1000, 1000, 2000) === 0
|
||||
&& windowFraction(2000, 1000, 2000) === 1
|
||||
&& windowFraction(1500, 1000, 2000) === 0.5;
|
||||
// a real event (Mars 2027 opposition) lands strictly inside a ±2 yr band.
|
||||
const c = lib.jdFromUnixMs(Date.UTC(2026, 6, 16));
|
||||
const wS = lib.unixMsFromJd(c - 730), wE = lib.unixMsFromJd(c + 730);
|
||||
const opp = findEvents(c - 730, c + 730).find((e) => e.bodies[0] === 'mars' && e.type === 'opposition');
|
||||
const fr = opp ? windowFraction(lib.unixMsFromJd(opp.jd), wS, wE) : NaN;
|
||||
row('event band maps ±2 yr window to full width', okEnds && !!opp && fr > 0 && fr < 1,
|
||||
`ends 0/0.5/1 ok=${okEnds} · Mars-opp band-fraction=${opp ? fr.toFixed(3) : 'n/a'}`);
|
||||
finish();
|
||||
}
|
||||
|
||||
/* PERIHELION-V — deep-time (extended Table 2a/2b) accuracy gates.
|
||||
Capture the "mine" positions with the extended range ON, then restore it OFF
|
||||
synchronously and atomically: ES modules are singletons, so setExtendedRange
|
||||
|
||||
Loading…
Reference in New Issue
Block a user