"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)+' m³';
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);
}
})();