diff --git a/index.html b/index.html index c4e7ccd..65953ee 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,13 @@ + + @@ -110,17 +117,18 @@ Calibration empirique sur la vidéo earth-rotation.mp4 : - Centre de la Terre dans le frame 1280×720 : (640, 360) - Rayon visible : 340 px - - Longitude centrale à t=0 : 140°E - - Vitesse de rotation : -5.75°/s (apparent) - La SVG utilise viewBox 1280×720 avec preserveAspectRatio="xMidYMid slice", - même comportement que le video object-fit:cover → alignement parfait. + - Longitude centrale à t=0 : 150°E + - Vitesse de rotation : -6°/s (apparent) + Override en URL : ?lon0=150&rate=-6&debug=1 -------------------------------------------------------------------- */ (function () { - const EARTH_CX = 640; - const EARTH_CY = 360; - const EARTH_R = 340; - const LON0 = 140; // central longitude visible à t=0 - const ROT_RATE = -5.75; // °/s (négatif = view's central decreases) + const params = new URLSearchParams(location.search); + const EARTH_CX = +params.get('cx') || 640; + const EARTH_CY = +params.get('cy') || 360; + const EARTH_R = +params.get('r') || 340; + const LON0 = parseFloat(params.get('lon0') ?? '150'); + const ROT_RATE = parseFloat(params.get('rate') ?? '-6'); + const DEBUG = params.has('debug'); const PARIS = { lat: 48.85, lon: 2.35 }; const TANA = { lat: -18.9, lon: 47.5 }; @@ -172,6 +180,49 @@ const arcVecs = []; for (let i = 0; i <= SAMPLES; i++) arcVecs.push(slerp(parisVec, tanaVec, i / SAMPLES)); + // Debug overlay : projected sphere outline + equator + longitude grid + const dbgLayer = document.getElementById('debugLayer'); + const dbgEarth = document.getElementById('dbgEarth'); + const dbgEquator = document.getElementById('dbgEquator'); + const dbgGrid = document.getElementById('dbgGrid'); + const dbgLabel = document.getElementById('dbgLabel'); + if (DEBUG) { + dbgLayer.style.display = ''; + dbgEarth.setAttribute('cx', EARTH_CX); + dbgEarth.setAttribute('cy', EARTH_CY); + dbgEarth.setAttribute('r', EARTH_R); + } + function buildArcPath(vecs, rotAngle) { + let d = '', open = false; + for (const v of vecs) { + const p = project(rotateY(v, rotAngle)); + if (p.z > 0) { + d += (open ? ' L ' : 'M ') + p.x.toFixed(1) + ' ' + p.y.toFixed(1); + open = true; + } else { open = false; } + } + return d; + } + // Pre-compute equator + meridians (every 30°) + const eqVecs = []; + for (let i = 0; i <= 180; i++) eqVecs.push(toCart(0, i * 2 - 180)); + const meridianSets = []; + for (let lon = -180; lon < 180; lon += 30) { + const set = []; + for (let lat = -90; lat <= 90; lat += 5) set.push(toCart(lat, lon)); + meridianSets.push(set); + if (DEBUG) { + const p = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + p.setAttribute('stroke', '#ffea00'); + p.setAttribute('stroke-width', '0.6'); + p.setAttribute('fill', 'none'); + p.setAttribute('opacity', '0.5'); + p.setAttribute('stroke-dasharray', '3 3'); + p.dataset.lon = lon; + dbgGrid.appendChild(p); + } + } + function update() { // Time source : video.currentTime (loops automatically). Fallback : Date.now() const t = (video && !isNaN(video.currentTime)) ? video.currentTime : (performance.now() / 1000); @@ -237,6 +288,17 @@ pulseDot.style.opacity = 0; } + // Debug overlay update + if (DEBUG) { + dbgEquator.setAttribute('d', buildArcPath(eqVecs, rotAngle)); + const meridianPaths = dbgGrid.children; + for (let i = 0; i < meridianPaths.length; i++) { + meridianPaths[i].setAttribute('d', buildArcPath(meridianSets[i], rotAngle)); + } + const central = ((LON0 + ROT_RATE * t) % 360 + 540) % 360 - 180; + dbgLabel.textContent = 't=' + t.toFixed(1) + 's central=' + central.toFixed(1) + '° rate=' + ROT_RATE + '°/s lon0=' + LON0; + } + // Group opacity = fade out when both pins are on the back side const visFactor = Math.max(0, Math.min(1, (Math.max(pPar.z, pTan.z) + 0.05) * 4)); routeGroup.setAttribute('opacity', visFactor.toFixed(2));