// geometrie.ts — moteur `commun.geometrie` : emprise, niveaux, hauteur, pente, // mitoyenneté → surfaces de parois, SHAB estimée, volume. Reprend les formules // de `calcSurfaces` du simulateur autoconstruction V1 (calculs), sans le DOM, // avec les coefficients sortis dans coefficients.ts. import { Trace } from './contrat.js'; import { COEFFICIENTS } from './coefficients.js'; import { arrondi2 } from './argent.js'; import { VERSION } from '../version.js'; export function calculeGeometrie(entrees, coefficients = COEFFICIENTS) { const trace = new Trace(); const c = coefficients; const emprise = Math.max(0, entrees.emprise_m2 || 0); const niveaux = Math.max(1, Math.round(entrees.niveaux ?? 1)); if (entrees.niveaux == null) trace.avertit('nombre de niveaux inconnu : 1 niveau supposé'); let hauteur = entrees.hauteur_m ?? 0; if (!(hauteur > 0)) { hauteur = niveaux * c.geometrie_hauteur_etage_m.valeur; trace.avertit(`hauteur inconnue : ${niveaux} × ${c.geometrie_hauteur_etage_m.valeur} m supposés`); trace.source('geometrie_hauteur_etage_m', c.geometrie_hauteur_etage_m.valeur, c.geometrie_hauteur_etage_m.source, c.geometrie_hauteur_etage_m.date); } const hauteur_etage = hauteur / niveaux; let pente = entrees.pente_toit_deg; if (pente == null || !(pente >= 0) || pente >= 90) { pente = c.geometrie_pente_defaut_deg.valeur; trace.avertit(`pente de toit inconnue : ${pente}° supposés`); trace.source('geometrie_pente_defaut_deg', pente, c.geometrie_pente_defaut_deg.source, c.geometrie_pente_defaut_deg.date); } const mitoyen = Math.min(2, Math.max(0, Math.round(entrees.mitoyennete ?? 0))); // SHAB let shab; let shab_estimee; if (entrees.surface_habitable_m2 && entrees.surface_habitable_m2 > 0) { shab = entrees.surface_habitable_m2; shab_estimee = false; } else { shab = emprise * niveaux * c.geometrie_ratio_shab_emprise.valeur; shab_estimee = true; trace.avertit('surface habitable estimée depuis l\'emprise (aucune surface connue)'); trace.source('geometrie_ratio_shab_emprise', c.geometrie_ratio_shab_emprise.valeur, c.geometrie_ratio_shab_emprise.source, c.geometrie_ratio_shab_emprise.date); } // Périmètre (V1 : √(S × 1,3) × 4) et part exposée const perimetre = 4 * Math.sqrt(emprise * c.geometrie_facteur_perimetre.valeur); trace.source('geometrie_facteur_perimetre', c.geometrie_facteur_perimetre.valeur, c.geometrie_facteur_perimetre.source, c.geometrie_facteur_perimetre.date); const perimetre_expose = perimetre * (1 - c.geometrie_part_mur_mitoyen.valeur * mitoyen); if (mitoyen > 0) trace.source('geometrie_part_mur_mitoyen', c.geometrie_part_mur_mitoyen.valeur, c.geometrie_part_mur_mitoyen.source, c.geometrie_part_mur_mitoyen.date); // Parois const murs_exterieurs = perimetre_expose * hauteur; const vitrage = shab * c.geometrie_ratio_vitrage_shab.valeur; trace.source('geometrie_ratio_vitrage_shab', arrondi2(c.geometrie_ratio_vitrage_shab.valeur), c.geometrie_ratio_vitrage_shab.source, c.geometrie_ratio_vitrage_shab.date); const murs_opaques = Math.max(0, murs_exterieurs - vitrage); const rad = (pente * Math.PI) / 180; const toiture = (emprise / Math.cos(rad)) * c.geometrie_majoration_toiture.valeur; trace.source('geometrie_majoration_toiture', c.geometrie_majoration_toiture.valeur, c.geometrie_majoration_toiture.source, c.geometrie_majoration_toiture.date); const volume = shab * hauteur_etage; const sorties = { shab_m2: arrondi2(shab), shab_estimee, perimetre_m: arrondi2(perimetre), perimetre_expose_m: arrondi2(perimetre_expose), murs_exterieurs_m2: arrondi2(murs_exterieurs), murs_opaques_m2: arrondi2(murs_opaques), vitrage_m2: arrondi2(vitrage), toiture_m2: arrondi2(toiture), plancher_bas_m2: arrondi2(emprise), volume_m3: arrondi2(volume), hauteur_m: arrondi2(hauteur), hauteur_etage_m: arrondi2(hauteur_etage), }; return trace.resultat(sorties, VERSION); } export const geometrie = { id: 'commun.geometrie', calculer(entrees, donnees) { return calculeGeometrie(entrees, donnees?.coefficients ?? COEFFICIENTS); }, };