// axes-actifs.ts — Toggles d'axes (V3 vague 1, M2) // Produit une config dérivée où les axes désactivés ont un poids nul, // sans toucher aux modules cœur de l'algo. La désactivation passe // uniquement par les inputs/config (poids), conformément à la spec. // Origine calculs/src/lib/autonomie/axes-actifs.js, inchangé (typé). import { AXES } from './types.js'; /** * Transforme algo-config pour neutraliser les axes désactivés. * - poids_axes_base[axe] = 0 pour tout axe inactif * - tous les deltas de modulateurs référençant un axe inactif sont retirés * - marqueur `_axes_desactives` (lu par reajuste.ts pour ne pas réinjecter * des postes optionnels dans un axe éteint lors du CAS B underspend) * * @param config — algo-config.json parsé (non muté) * @returns nouvelle config */ export function configPourAxes(config, actifs) { const desactives = AXES.filter((a) => actifs && actifs[a] === false); if (desactives.length === 0) return config; // Clone profond (config petite, JSON-safe) const cfg = JSON.parse(JSON.stringify(config)); cfg.poids_axes_base = cfg.poids_axes_base || { eau: 40, energie: 40, alim: 20 }; for (const axe of desactives) cfg.poids_axes_base[axe] = 0; // Neutraliser les deltas des modulateurs qui re-donneraient du poids // à un axe désactivé (ex : zone MED -> eau+10, parcelle grande -> alim+10). const mod = cfg.modulateurs || {}; for (const key of Object.keys(mod)) { const val = mod[key]; if (val && typeof val === 'object' && !Array.isArray(val)) { for (const axe of desactives) { if (axe in val) delete val[axe]; } } } cfg._axes_desactives = desactives; return cfg; } /** * Vrai si au moins un axe est actif (0 axe actif = état vide géré par l'UI, * l'algo ne doit pas tourner). */ export function auMoinsUnAxe(actifs) { return AXES.some((a) => !actifs || actifs[a] !== false); } export { AXES };