Extraction du cœur autonomie de calculs.trans-former.fr (12 modules, sans singleton, couplages sans mutation, console derrière debug), découpage en trois moteurs d'axe + intégrateur (mode global = calculs, mode par_axe = plateforme), moteurs commun.contexte (Corse 2A/2B, DOM 97x corrigés) et commun.geometrie, table des coefficients harmonisés (docs/CONSTANTES.md pour Jules, R-7), validation structurelle des données (schemas/), tests hors ligne : 28 assertions d'origine + épingle générée depuis le code d'astro-pro + écarts d'harmonisation figés + contrat des moteurs. dist/ committé. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UBHYPdeQYy2m1GVJp6b12
80 lines
4.3 KiB
JavaScript
80 lines
4.3 KiB
JavaScript
// 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);
|
||
},
|
||
};
|