"use strict"; (function(){ const root = document.querySelector(".aero-calc"); if (!root) { console.error('calculateur legacy: .aero-calc container not found'); return; } const $ = id => { const el = root.querySelector("#"+id); // silencieusement retourner null si l'élément n'existe pas return el; }; const C = {}; function readConsts(){ try { const ct = $("aero-ct"); if(ct) C.CT=parseFloat(ct.value); else console.warn('aero-ct not found'); const sair = $("aero-sair"); if(sair) C.sAir=parseFloat(sair.value); else console.warn('aero-sair not found'); const g = $("aero-g"); if(g) C.g=parseFloat(g.value); else console.warn('aero-g not found'); const sea = $("aero-sea"); if(sea) C.sea=parseFloat(sea.value); else console.warn('aero-sea not found'); const k = $("aero-k"); if(k) C.k=parseFloat(k.value); else console.warn('aero-k not found'); const pmax = $("aero-pmax"); if(pmax) C.pMax=parseFloat(pmax.value); else console.warn('aero-pmax not found'); const ytop = $("aero-ytop"); if(ytop) C.yTop=parseFloat(ytop.value); else console.warn('aero-ytop not found'); const ybot = $("aero-ybot"); if(ybot) C.yBot=parseFloat(ybot.value); else console.warn('aero-ybot not found'); } catch(e) { console.error('readConsts error:', e); } } function pressure(y){ let p=Math.exp(C.k*(y-C.sea)); if(p>C.pMax)p=C.pMax; return p<0?0:p; } // Le mod echantillonne la pression au CENTRE du bloc : getBlockPos().getCenter() -> y+0.5 const Y_OFFSET = 0.5; const propThrust = (P,N)=>C.CT*Math.pow(Math.max(P,0),1.5)*N; const balloonForce = (V,y)=>Math.max(V,0)*C.sAir*C.g*pressure(y); const balloonLift = (V,y)=>Math.max(V,0)*C.sAir*pressure(y); function maxAltitude(V,m){ if(V<=0||m<=0) return {y:C.yTop,status:"ceiling"}; const t=m/(V*C.sAir); if(t>C.pMax) return {y:C.yBot,status:"grounded"}; let y=C.sea+Math.log(t)/C.k; if(y>=C.yTop) return {y:C.yTop,status:"ceiling"}; if(y<=C.yBot) return {y:C.yBot,status:"bottom"}; return {y,status:"ok"}; } const requiredVolume=(m,y)=>m/(C.sAir*pressure(y)); const nf=new Intl.NumberFormat("fr-FR",{maximumFractionDigits:0}); const nf2=new Intl.NumberFormat("fr-FR",{maximumFractionDigits:2}); const nf3=new Intl.NumberFormat("fr-FR",{maximumFractionDigits:3}); function big(x){ if(!isFinite(x)) return "n/a"; return Math.abs(x)>=1000?nf.format(x):nf2.format(x); } function updateProp(){ try { const sails = $("aero-sails"); const P = sails ? parseFloat(sails.value)||0 : 0; const rpm = $("aero-rpm"); const N = rpm ? parseFloat(rpm.value)||0 : 0; const palt = $("aero-palt"); const y = palt ? parseFloat(palt.value)||0 : 0; const T=propThrust(P,N); const thrust = $("aero-othrust"); if(thrust) thrust.innerHTML=big(Math.abs(T))+' pN'; const thrustapp = $("aero-othrustapp"); if(thrustapp) thrustapp.innerHTML=big(Math.abs(T)*pressure(y+Y_OFFSET))+' pN'; } catch(e) { console.error('updateProp error:', e); } } function updateBalloon(){ try { const vol = $("aero-vol"); const V = vol ? parseFloat(vol.value)||0 : 0; const balt = $("aero-balt"); const y = balt ? parseFloat(balt.value)||0 : 0; const force = $("aero-oforce"); if(force) force.innerHTML=big(balloonForce(V,y))+' pN'; const lift = $("aero-olift"); if(lift) lift.innerHTML=big(balloonLift(V,y))+' kpg'; const press = $("aero-opress"); if(press) press.textContent=nf3.format(pressure(y)); } catch(e) { console.error('updateBalloon error:', e); } } function updateVolume(){ try { const vmass = $("aero-vmass"); const m = vmass ? parseFloat(vmass.value)||0 : 0; const valt = $("aero-valt"); const y = valt ? parseFloat(valt.value)||0 : 0; const V=requiredVolume(m,y); const volreq = $("aero-ovolreq"); if(volreq) volreq.innerHTML=big(V)+' '; const cube = $("aero-ocube"); if(cube) cube.textContent=(isFinite(V)&&V>0) ? "≈ cube de "+nf2.format(Math.cbrt(V))+" blocs de côté · "+nf.format(Math.ceil(V))+" blocs d'air" : ""; const warn = $("aero-vwarn"); if(warn) warn.innerHTML=(pressure(y)<=0) ? "Altitude hors atmosphère : pression nulle, aucun volume fini ne suffit." : ""; } catch(e) { console.error('updateVolume error:', e); } } // ---- graphe ---- let mode="mass"; const canvas=$("aero-chart"), tip=$("aero-tip"); if (!canvas) { console.error('calculateur legacy: canvas not found, graph disabled'); } const ctx = canvas ? canvas.getContext("2d") : null; let geom=null; const tv=n=>getComputedStyle(root).getPropertyValue(n).trim()||"#888"; function niceStep(raw){ const p=Math.pow(10,Math.floor(Math.log10(raw))); const n=raw/p; return (n<1.5?1:n<3?2:n<7?5:10)*p; } function abbr(v){ const a=Math.abs(v); if(a>=1e6)return nf2.format(v/1e6)+"M"; if(a>=1e3)return nf2.format(v/1e3)+"k"; return nf.format(v); } function drawChart(){ if(!canvas||!ctx) { console.warn('calculateur legacy: graph not available'); return; } const dpr=window.devicePixelRatio||1; const cssW=canvas.parentElement.clientWidth; const cssH=Math.max(260,Math.round(cssW*0.44)); canvas.style.height=cssH+"px"; canvas.width=Math.round(cssW*dpr); canvas.height=Math.round(cssH*dpr); ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,cssW,cssH); const padL=54,padR=16,padT=14,padB=38, pw=cssW-padL-padR, ph=cssH-padT-padB; const fg=tv("--aero-fg"), muted=tv("--aero-muted"), border=tv("--aero-border"), accent=tv("--aero-accent"), warn=tv("--aero-warn"); let xMin,xMax,xLabel,fixed; if(mode==="mass"){ const fixvol = $("aero-fixvol"); fixed=Math.max(1, fixvol ? parseFloat(fixvol.value)||1 : 1); xMin=1; xMax=Math.max(2,fixed*C.sAir*C.pMax*1.15); xLabel="poids m (kpg)"; } else { const fixmass = $("aero-fixmass"); fixed=Math.max(1, fixmass ? parseFloat(fixmass.value)||1 : 1); xMin=Math.max(1,fixed/(C.sAir*C.pMax)*0.5); xMax=Math.max(xMin*1.5,4*fixed/C.sAir); xLabel="volume V (m³)"; } const yMin=C.yBot,yMax=C.yTop; const X=x=>padL+(x-xMin)/(xMax-xMin)*pw; const Y=y=>padT+(1-(y-yMin)/(yMax-yMin))*ph; ctx.font='11px '+(getComputedStyle(root).fontFamily||"sans-serif"); // grille ctx.strokeStyle=border; ctx.fillStyle=muted; ctx.lineWidth=1; ctx.textBaseline="middle"; ctx.textAlign="right"; const yStep=niceStep((yMax-yMin)/6); for(let yy=Math.ceil(yMin/yStep)*yStep; yy<=yMax; yy+=yStep){ const py=Y(yy); ctx.beginPath();ctx.moveTo(padL,py);ctx.lineTo(padL+pw,py);ctx.stroke(); ctx.fillText(String(yy),padL-7,py); } ctx.textAlign="center"; ctx.textBaseline="top"; const xStep=niceStep((xMax-xMin)/6); for(let xx=Math.ceil(xMin/xStep)*xStep; xx<=xMax; xx+=xStep){ const px=X(xx); ctx.beginPath();ctx.moveTo(px,padT);ctx.lineTo(px,padT+ph);ctx.stroke(); ctx.fillText(abbr(xx),px,padT+ph+7); } ctx.fillStyle=muted; ctx.fillText(xLabel,padL+pw/2,padT+ph+22); ctx.save();ctx.translate(13,padT+ph/2);ctx.rotate(-Math.PI/2);ctx.fillText("altitude max y",0,0);ctx.restore(); function refLine(yv,label){ if(yvyMax) return; const py=Y(yv); ctx.strokeStyle=muted; ctx.setLineDash([4,4]); ctx.lineWidth=1; ctx.beginPath();ctx.moveTo(padL,py);ctx.lineTo(padL+pw,py);ctx.stroke(); ctx.setLineDash([]); ctx.fillStyle=muted; ctx.textAlign="left"; ctx.textBaseline="bottom"; ctx.fillText(label,padL+5,py-2); } refLine(C.sea,"niveau mer "+C.sea); refLine(C.yTop,"limite "+C.yTop); // courbe const steps=240,pts=[]; for(let i=0;i<=steps;i++){ const x=xMin+(xMax-xMin)*i/steps; const r=(mode==="mass")?maxAltitude(fixed,x):maxAltitude(x,fixed); pts.push({x,y:r.y,status:r.status}); } // zone grounded ctx.fillStyle=warn; ctx.globalAlpha=.10; let gs=null; for(let i=0;i<=steps;i++){ const g=pts[i].status==="grounded"; if(g&&gs===null)gs=pts[i].x; if((!g||i===steps)&&gs!==null){ ctx.fillRect(X(gs),padT,X(pts[i].x)-X(gs),ph); gs=null; } } ctx.globalAlpha=1; ctx.lineWidth=2; ctx.strokeStyle=accent; ctx.beginPath(); let started=false; for(const p of pts){ if(p.status==="grounded"){started=false;continue;} const px=X(p.x),py=Y(p.y); if(!started){ctx.moveTo(px,py);started=true;}else ctx.lineTo(px,py); } ctx.stroke(); geom={padL,pw,padT,ph,xMin,xMax,X,Y,fixed}; } if(canvas && tip) { canvas.addEventListener("mousemove",e=>{ if(!geom)return; const rect=canvas.getBoundingClientRect(); const mx=e.clientX-rect.left; if(mxgeom.padL+geom.pw){tip.style.opacity=0;return;} const x=geom.xMin+(mx-geom.padL)/geom.pw*(geom.xMax-geom.xMin); const r=(mode==="mass")?maxAltitude(geom.fixed,x):maxAltitude(x,geom.fixed); let txt = r.status==="grounded" ? abbr(x)+" : ne décolle pas" : r.status==="ceiling" ? abbr(x)+" : plafond (y "+Math.round(r.y)+")" : abbr(x)+" : y "+Math.round(r.y); tip.textContent=txt; tip.style.left=geom.X(x)+"px"; tip.style.top=geom.Y(r.y)+"px"; tip.style.opacity=1; }); canvas.addEventListener("mouseleave",()=>{tip.style.opacity=0;}); } const modeSelector = $("aero-mode"); // Affiche uniquement l'input correspondant au mode courant function applyModeVisibility(){ const fixVolWrap = $("aero-fixvolwrap"); const fixMassWrap = $("aero-fixmasswrap"); if(fixVolWrap) fixVolWrap.style.display = mode==="mass"?"":"none"; if(fixMassWrap) fixMassWrap.style.display= mode==="vol"?"":"none"; } // ---- Partage par URL (paramètres GET, tous optionnels) ---- const paramInputs = Array.from(root.querySelectorAll("input, select")).filter(el=>el.id&&el.id.startsWith("aero-")); const keyOf = el => el.id.slice(5); const defaults = new Map(paramInputs.map(el=>[el, el.value])); function applyUrlParams(){ const params = new URLSearchParams(window.location.search); paramInputs.forEach(el=>{ const k=keyOf(el); if(params.has(k)) el.value=params.get(k); }); } function syncUrl(){ try { const params = new URLSearchParams(); paramInputs.forEach(el=>{ if(el.value!==defaults.get(el)) params.set(keyOf(el), el.value); }); const qs = params.toString(); window.history.replaceState(null, "", window.location.pathname + (qs?"?"+qs:"") + window.location.hash); } catch(e){ console.error('calculateur legacy: syncUrl error:', e); } } if(modeSelector) { modeSelector.addEventListener("change",e=>{ mode=e.target.value; applyModeVisibility(); drawChart(); syncUrl(); }); } root.querySelectorAll("input,select").forEach(el=>el.addEventListener("input",()=>{ try { readConsts(); updateProp(); updateBalloon(); updateVolume(); drawChart(); syncUrl(); } catch(e) { console.error('calculateur legacy: error during input event:', e); } })); window.addEventListener("resize",()=>{ if(geom) drawChart(); }); try { applyUrlParams(); if(modeSelector) mode = modeSelector.value; applyModeVisibility(); readConsts(); updateProp(); updateBalloon(); updateVolume(); drawChart(); } catch(e) { console.error('calculateur legacy: error during initialization:', e); } })();