v0.4.9: naadwanden (2 delen, deur), kopbalken op naden, isolatie niet meer getekend
- HouseModel: naadwand per naad (ensureSeamWalls bij nieuwe woning en unit toevoegen), twee delen met beplating alleen aan de kamerzijde, openingen gespiegeld - WallModel: buitenzijde 'geen', thicknessOf eenzijdig - FloorModel: kopbalken (rims) voor vloer en dak op naden, ook in PDF en totalen - isolatie: 3D-lagen en filter verwijderd, berekening blijft Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f163f4deb1
commit
9bd7faeaa6
+17
-55
@@ -98,7 +98,9 @@ window.WallModel = (() => {
|
||||
|
||||
// Buitenzijde-materialen (sleutel -> weergavenaam). 'spaanplaat' is voor
|
||||
// binnenwanden (aan beide zijden spaanplaat).
|
||||
const EXTERIOR_MATERIALS = { vochtdoek: 'Vochtdoek', cementplaat: 'Cement-gebonden platen', spaanplaat: 'Spaanplaat' };
|
||||
// 'geen': geen beplating aan de buitenzijde — naadwand-deel dat tegen het
|
||||
// deel van de andere unit aan staat (HouseModel, naadwanden).
|
||||
const EXTERIOR_MATERIALS = { vochtdoek: 'Vochtdoek', cementplaat: 'Cement-gebonden platen', spaanplaat: 'Spaanplaat', geen: 'Geen (naadzijde)' };
|
||||
|
||||
// Heuristiek (geen volwaardige constraint-solver): elke opening wordt eerst
|
||||
// individueel gepositioneerd op basis van zijn anker, dan van links naar
|
||||
@@ -234,10 +236,12 @@ window.WallModel = (() => {
|
||||
return buildConfig(state.wall, state.openings || []);
|
||||
}
|
||||
|
||||
// Totale wanddikte (mm): regelwerk + beplating aan beide zijden. Gebruikt
|
||||
// door de unit om wanden met hun buitenkant op de vloerrand te zetten.
|
||||
function thicknessOf(config) {
|
||||
return Math.max(config.studDepth, config.plateDepth) + 2 * (config.sheathingThick + SKIN_GAP_MM);
|
||||
// Totale wanddikte (mm): regelwerk + beplating aan beide zijden (bij
|
||||
// buitenMateriaal 'geen' alleen aan de binnenzijde). Gebruikt door de unit
|
||||
// om wanden met hun buitenkant op de vloerrand te zetten.
|
||||
function thicknessOf(config, buitenMateriaal) {
|
||||
const sides = buitenMateriaal === 'geen' ? 1 : 2;
|
||||
return Math.max(config.studDepth, config.plateDepth) + sides * (config.sheathingThick + SKIN_GAP_MM);
|
||||
}
|
||||
|
||||
// Regelwerk: volledige stijlen (incl. koningsstijlen) + kreupele stijlen.
|
||||
@@ -475,24 +479,19 @@ window.WallModel = (() => {
|
||||
? new THREE.MeshStandardMaterial({ color: 0xffffff, map: doekMap, roughness: 0.7, side: THREE.DoubleSide })
|
||||
: new THREE.MeshStandardMaterial({ color: DOEK_COLOR, roughness: 0.7, side: THREE.DoubleSide }),
|
||||
cementplaatMaterial: new THREE.MeshStandardMaterial({ color: 0x808285, roughness: 0.8, side: THREE.DoubleSide }),
|
||||
// Zelfde uitstraling als de vloer-isolatie (FloorModel).
|
||||
battenMaterial: new THREE.MeshStandardMaterial({ color: 0xd9b77e, roughness: 0.7 }),
|
||||
isolatieMaterial: new THREE.MeshStandardMaterial({
|
||||
color: 0xffe08c, roughness: 0.9, transparent: true, opacity: 0.5, side: THREE.DoubleSide, depthWrite: false
|
||||
})
|
||||
battenMaterial: new THREE.MeshStandardMaterial({ color: 0xd9b77e, roughness: 0.7 })
|
||||
};
|
||||
}
|
||||
|
||||
// Bouwt de volledige 3D-wand (in meters, lokaal assenstelsel: X = breedte
|
||||
// gecentreerd, Y = hoogte vanaf onderkant onderregel, +Z = buitenzijde/zuid)
|
||||
// en geeft daarnaast de volume-boekhouding terug voor de specificaties.
|
||||
// opts: { materials, buitenMateriaal, cladding, trasraam, spaanplaatVisible, buitenVisible, isolatieVisible }
|
||||
// opts: { materials, buitenMateriaal, cladding, trasraam, spaanplaatVisible, buitenVisible }
|
||||
// trasraam: { bottom, top } in mm (wand-coördinaten, zie trasraamFor) of null.
|
||||
// skinExt: { left, right } in mm (zie cornerExt) of null.
|
||||
// facade: { u0, extStart, isEnd } (zie computeBattens) of null.
|
||||
function build3D(CONFIG, calcData, opts) {
|
||||
const { woodMaterial, spaanplaatMaterial, doekMaterial, cementplaatMaterial, isolatieMaterial, battenMaterial } = opts.materials;
|
||||
const isolatieVisible = opts.isolatieVisible !== false;
|
||||
const { woodMaterial, spaanplaatMaterial, doekMaterial, cementplaatMaterial, battenMaterial } = opts.materials;
|
||||
const buitenMateriaal = opts.buitenMateriaal || 'vochtdoek';
|
||||
const spaanplaatVisible = opts.spaanplaatVisible !== false;
|
||||
const buitenVisible = opts.buitenVisible !== false;
|
||||
@@ -692,48 +691,7 @@ window.WallModel = (() => {
|
||||
}
|
||||
});
|
||||
|
||||
// D2. ISOLATIE — vult elk vak tussen twee opeenvolgende stijlen (vol of
|
||||
// kreupel) over de stijldiepte, van bovenkant onderregel tot onderkant
|
||||
// bovenregel (volgt de dakhelling). Binnen de breedte van een opening
|
||||
// alleen onder de onderdorpel en boven de bovendorpel. Puur visueel,
|
||||
// zelfde transparante laag als bij de vloer.
|
||||
const members = calcData.fullStuds.map(x => x * toM)
|
||||
.concat(calcData.jackStuds.map(j => j.x * toM))
|
||||
.map(x => [x - sWidthM / 2, x + sWidthM / 2])
|
||||
.sort((a, b) => a[0] - b[0]);
|
||||
const merged = [];
|
||||
members.forEach(m => {
|
||||
const last = merged[merged.length - 1];
|
||||
if (last && m[0] <= last[1] + 1e-9) last[1] = Math.max(last[1], m[1]);
|
||||
else merged.push([m[0], m[1]]);
|
||||
});
|
||||
function addIsolatieBlock(x0, x1, yBottom, yTopLeft, yTopRight) {
|
||||
if (x1 - x0 <= 1e-6 || Math.min(yTopLeft, yTopRight) - yBottom <= 1e-6) return;
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(x0, yBottom);
|
||||
shape.lineTo(x1, yBottom);
|
||||
shape.lineTo(x1, yTopRight);
|
||||
shape.lineTo(x0, yTopLeft);
|
||||
shape.closePath();
|
||||
const mesh = new THREE.Mesh(new THREE.ExtrudeGeometry(shape, { steps: 1, depth: sDepthM, bevelEnabled: false }), isolatieMaterial);
|
||||
mesh.position.set(0, 0, -sDepthM / 2);
|
||||
mesh.visible = isolatieVisible;
|
||||
wallGroup3D.add(mesh);
|
||||
}
|
||||
for (let i = 0; i < merged.length - 1; i++) {
|
||||
const x0 = merged[i][1], x1 = merged[i + 1][0];
|
||||
const yTopL = topYAt(x0) - vertOffset, yTopR = topYAt(x1) - vertOffset;
|
||||
const mid = (x0 + x1) / 2;
|
||||
const op = opsM.find(o => mid > o.xMin && mid < o.xMax);
|
||||
if (!op) {
|
||||
addIsolatieBlock(x0, x1, pThickM, yTopL, yTopR);
|
||||
} else {
|
||||
const sillBottom = op.yMin - sWidthM;
|
||||
if (sillBottom > pThickM) addIsolatieBlock(x0, x1, pThickM, sillBottom, sillBottom);
|
||||
const headerTop = op.yMax + sWidthM;
|
||||
addIsolatieBlock(x0, x1, headerTop, Math.max(headerTop, yTopL), Math.max(headerTop, yTopR));
|
||||
}
|
||||
}
|
||||
// (Isolatie wordt niet getekend; de spouwinhoud zit in computeSpecs.)
|
||||
|
||||
// E. VERTICALE PLATEN — gedeelde opbouw voor spaanplaat (binnenzijde,
|
||||
// altijd) EN cement-gebonden platen (buitenzijde, als die gekozen is):
|
||||
@@ -912,6 +870,8 @@ window.WallModel = (() => {
|
||||
const spaanBuiten = buildVerticalBoardSkin(spaanplaatMaterial, 'front', buitenVisible);
|
||||
spaanplaatBuitenAreaM2 = spaanBuiten.areaM2;
|
||||
spaanplaatBuitenVolumeM3 = spaanBuiten.volumeM3;
|
||||
} else if (buitenMateriaal === 'geen') {
|
||||
// Naadzijde: geen beplating (het deel van de andere unit staat ertegen).
|
||||
} else if (buitenMateriaal === 'cementplaat') {
|
||||
const cementResult = buildVerticalBoardSkin(cementplaatMaterial, 'front', buitenVisible, null,
|
||||
Object.assign(extM(0), { zOut: sThickM + 2 * skinGapM }));
|
||||
@@ -1104,6 +1064,8 @@ window.WallModel = (() => {
|
||||
buitenAreaM2 = stats.spaanplaatBuitenAreaM2;
|
||||
buitenKg = stats.spaanplaatBuitenVolumeM3 * DENSITY_SPAANPLAAT_KGM3;
|
||||
buitenVolumeM3 = stats.spaanplaatBuitenVolumeM3;
|
||||
} else if (buitenMateriaal === 'geen') {
|
||||
buitenAreaM2 = 0; buitenKg = 0; buitenVolumeM3 = 0;
|
||||
} else if (buitenMateriaal === 'cementplaat') {
|
||||
buitenAreaM2 = stats.cementplaatAreaM2;
|
||||
buitenKg = stats.cementplaatVolumeM3 * DENSITY_CEMENTPLAAT_KGM3;
|
||||
|
||||
Reference in New Issue
Block a user