Recalibrate intro line: shift LON0 to 150, bump rate to -6 deg/s
The pins were drifting east of the actual cities — Paris ended up over Italy and Antananarivo in the Indian Ocean. Linear regression over five calibration frames (visible central longitude at t=0,10,15,18,22) gives LON0~151, rate~-6.0, which I round to 150 and -6. Also expose all knobs as URL params (?lon0=&rate=&cx=&cy=&r=) and add a ?debug=1 overlay that draws the projected sphere outline + equator + 30 deg meridian grid, so the calibration can be eyeballed live. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8033c0fd02
commit
eb01e75a3f
80
index.html
80
index.html
@ -54,6 +54,13 @@
|
|||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
|
<g id="debugLayer" style="display:none">
|
||||||
|
<circle id="dbgEarth" r="0" cx="0" cy="0" fill="none" stroke="#00e5ff" stroke-width="1.5" opacity="0.7"/>
|
||||||
|
<path id="dbgEquator" d="" stroke="#ffea00" stroke-width="1" fill="none" opacity="0.7" stroke-dasharray="4 4"/>
|
||||||
|
<g id="dbgGrid"></g>
|
||||||
|
<text id="dbgLabel" x="20" y="40" fill="#00e5ff" font-family="monospace" font-size="18" font-weight="700"></text>
|
||||||
|
</g>
|
||||||
|
|
||||||
<g id="routeGroup" opacity="0">
|
<g id="routeGroup" opacity="0">
|
||||||
<path id="routeHalo" d="" stroke="#ff2a3d" stroke-width="9" fill="none" opacity="0.3" filter="url(#redGlow)"/>
|
<path id="routeHalo" d="" stroke="#ff2a3d" stroke-width="9" fill="none" opacity="0.3" filter="url(#redGlow)"/>
|
||||||
<path id="routeLine" d="" stroke="url(#redLine)" stroke-width="2" fill="none" stroke-linecap="round" filter="url(#redGlow)"/>
|
<path id="routeLine" d="" stroke="url(#redLine)" stroke-width="2" fill="none" stroke-linecap="round" filter="url(#redGlow)"/>
|
||||||
@ -110,17 +117,18 @@
|
|||||||
Calibration empirique sur la vidéo earth-rotation.mp4 :
|
Calibration empirique sur la vidéo earth-rotation.mp4 :
|
||||||
- Centre de la Terre dans le frame 1280×720 : (640, 360)
|
- Centre de la Terre dans le frame 1280×720 : (640, 360)
|
||||||
- Rayon visible : 340 px
|
- Rayon visible : 340 px
|
||||||
- Longitude centrale à t=0 : 140°E
|
- Longitude centrale à t=0 : 150°E
|
||||||
- Vitesse de rotation : -5.75°/s (apparent)
|
- Vitesse de rotation : -6°/s (apparent)
|
||||||
La SVG utilise viewBox 1280×720 avec preserveAspectRatio="xMidYMid slice",
|
Override en URL : ?lon0=150&rate=-6&debug=1
|
||||||
même comportement que le video object-fit:cover → alignement parfait.
|
|
||||||
-------------------------------------------------------------------- */
|
-------------------------------------------------------------------- */
|
||||||
(function () {
|
(function () {
|
||||||
const EARTH_CX = 640;
|
const params = new URLSearchParams(location.search);
|
||||||
const EARTH_CY = 360;
|
const EARTH_CX = +params.get('cx') || 640;
|
||||||
const EARTH_R = 340;
|
const EARTH_CY = +params.get('cy') || 360;
|
||||||
const LON0 = 140; // central longitude visible à t=0
|
const EARTH_R = +params.get('r') || 340;
|
||||||
const ROT_RATE = -5.75; // °/s (négatif = view's central decreases)
|
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 PARIS = { lat: 48.85, lon: 2.35 };
|
||||||
const TANA = { lat: -18.9, lon: 47.5 };
|
const TANA = { lat: -18.9, lon: 47.5 };
|
||||||
@ -172,6 +180,49 @@
|
|||||||
const arcVecs = [];
|
const arcVecs = [];
|
||||||
for (let i = 0; i <= SAMPLES; i++) arcVecs.push(slerp(parisVec, tanaVec, i / SAMPLES));
|
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() {
|
function update() {
|
||||||
// Time source : video.currentTime (loops automatically). Fallback : Date.now()
|
// Time source : video.currentTime (loops automatically). Fallback : Date.now()
|
||||||
const t = (video && !isNaN(video.currentTime)) ? video.currentTime : (performance.now() / 1000);
|
const t = (video && !isNaN(video.currentTime)) ? video.currentTime : (performance.now() / 1000);
|
||||||
@ -237,6 +288,17 @@
|
|||||||
pulseDot.style.opacity = 0;
|
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
|
// 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));
|
const visFactor = Math.max(0, Math.min(1, (Math.max(pPar.z, pTan.z) + 0.05) * 4));
|
||||||
routeGroup.setAttribute('opacity', visFactor.toFixed(2));
|
routeGroup.setAttribute('opacity', visFactor.toFixed(2));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user