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
@@ -173,6 +173,16 @@ assets/ logo (preloader) + bestemmingsmap voor toekomstige
|
|||||||
unitrand bij een naad); spots te dicht bij een binnenwand vervallen.
|
unitrand bij een naad); spots te dicht bij een binnenwand vervallen.
|
||||||
Woning-instellingen › Verlichting › Binnenspots. In 3D (filter Lampen),
|
Woning-instellingen › Verlichting › Binnenspots. In 3D (filter Lampen),
|
||||||
plattegrond (open cirkeltje), render en totalen.
|
plattegrond (open cirkeltje), render en totalen.
|
||||||
|
- **Naden (v0.4.9).** Op elke naad staat standaard een tussenwand met in het
|
||||||
|
midden een deur (900 x 2150), ook bij "unit toevoegen"; verwijderen en
|
||||||
|
aanpassen kan gewoon. Hij bestaat uit twee delen, één per unit
|
||||||
|
(HouseModel.seamLeaves, elementen NW-1A / NW-1B), met balken en isolatie in
|
||||||
|
beide delen maar beplating alleen aan de kamerzijde (WallModel buitenzijde
|
||||||
|
'geen'); de openingen gelden voor beide delen en worden gespiegeld. Vloer en
|
||||||
|
dak krijgen op een naad langs voor-/achterrand een kopbalk
|
||||||
|
(FloorModel.computeFrame rims), langs de zijranden ligt al een balk.
|
||||||
|
- **Isolatie niet meer getekend (v0.4.9).** Filter "Isolatie" en de 3D-lagen
|
||||||
|
zijn weg; de spouw-/leegte-inhoud blijft in specificaties en totalen.
|
||||||
- **Startpunt gevellampen (v0.4.8).** Een gevel telt vanaf de zichtbare,
|
- **Startpunt gevellampen (v0.4.8).** Een gevel telt vanaf de zichtbare,
|
||||||
afgewerkte hoek: elk uiteinde + afwerking van de haakse wand (52 mm bij
|
afgewerkte hoek: elk uiteinde + afwerking van de haakse wand (52 mm bij
|
||||||
vochtdoek), plus de bedekte kopse kant (skinExt) bij tussenpassende wanden.
|
vochtdoek), plus de bedekte kopse kant (skinExt) bij tussenpassende wanden.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
||||||
* Zie LICENTIE.md.
|
* Zie LICENTIE.md.
|
||||||
*/
|
*/
|
||||||
const APP_VERSION = '0.4.8';
|
const APP_VERSION = '0.4.9';
|
||||||
// 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang.
|
// 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang.
|
||||||
|
|
||||||
let tabs = [];
|
let tabs = [];
|
||||||
|
|||||||
+30
-27
@@ -114,8 +114,12 @@ window.FloorModel = (() => {
|
|||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Balkverdeling: zelfde algoritme (calculateSegmentPositions) als de wand.
|
// Balkverdeling: zelfde algoritme (calculateSegmentPositions) als de wand.
|
||||||
// params = state.floor (of de ruwe invoerwaarden uit de editor).
|
// params = state.floor (of de ruwe invoerwaarden uit de editor).
|
||||||
|
// rims (optioneel): { front, back } — kopbalk langs de voor-/achterrand
|
||||||
|
// (haaks op de balken), bijv. op een naad tussen twee units waar de
|
||||||
|
// modules weer aan elkaar gemonteerd worden. De langsbalken worden daar
|
||||||
|
// met één balkbreedte ingekort. Langs de zijranden ligt altijd al een balk.
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
function computeFrame(params) {
|
function computeFrame(params, rims) {
|
||||||
const width = params.width || 2000;
|
const width = params.width || 2000;
|
||||||
const depth = params.depth || 2000;
|
const depth = params.depth || 2000;
|
||||||
const joistW = params.joistWidth || 70;
|
const joistW = params.joistWidth || 70;
|
||||||
@@ -131,7 +135,9 @@ window.FloorModel = (() => {
|
|||||||
calculateSegmentPositions(width - joistW, minSp, maxSp).forEach(p => joists.push(leftJoistX + p));
|
calculateSegmentPositions(width - joistW, minSp, maxSp).forEach(p => joists.push(leftJoistX + p));
|
||||||
joists = [...new Set(joists.map(x => Math.round(x)))].sort((a, b) => a - b);
|
joists = [...new Set(joists.map(x => Math.round(x)))].sort((a, b) => a - b);
|
||||||
|
|
||||||
return { joists, width, depth, joistW, joistH, underlayThick };
|
const r = { front: !!(rims && rims.front), back: !!(rims && rims.back) };
|
||||||
|
const joistLen = depth - (r.front ? joistW : 0) - (r.back ? joistW : 0);
|
||||||
|
return { joists, width, depth, joistW, joistH, underlayThick, rims: r, joistLen };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bovenkant vloer (mm) = waar de wanden van een unit op staan.
|
// Bovenkant vloer (mm) = waar de wanden van een unit op staan.
|
||||||
@@ -151,8 +157,10 @@ window.FloorModel = (() => {
|
|||||||
const { width, depth, joists, joistW, joistH, underlayThick } = calcData;
|
const { width, depth, joists, joistW, joistH, underlayThick } = calcData;
|
||||||
const widthM = width * toM, depthM = depth * toM;
|
const widthM = width * toM, depthM = depth * toM;
|
||||||
const joistWM = joistW * toM, joistHM = joistH * toM, underlayM = underlayThick * toM;
|
const joistWM = joistW * toM, joistHM = joistH * toM, underlayM = underlayThick * toM;
|
||||||
|
const rimCount = calcData.rims ? (calcData.rims.front ? 1 : 0) + (calcData.rims.back ? 1 : 0) : 0;
|
||||||
|
const joistLenM = (calcData.joistLen || depth) * toM;
|
||||||
|
|
||||||
const joistWoodM3 = joists.length * joistWM * joistHM * depthM;
|
const joistWoodM3 = joists.length * joistWM * joistHM * joistLenM + rimCount * joistWM * joistHM * widthM;
|
||||||
const joistWoodKg = joistWoodM3 * DENSITY_WOOD_KGM3;
|
const joistWoodKg = joistWoodM3 * DENSITY_WOOD_KGM3;
|
||||||
|
|
||||||
const grossAreaM2 = widthM * depthM;
|
const grossAreaM2 = widthM * depthM;
|
||||||
@@ -197,8 +205,9 @@ window.FloorModel = (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alles in één keer vanuit een vloer-state (handig voor de unit).
|
// Alles in één keer vanuit een vloer-state (handig voor de unit).
|
||||||
function computeFromState(state) {
|
// rims: zie computeFrame.
|
||||||
const calcData = computeFrame(state.floor);
|
function computeFromState(state, rims) {
|
||||||
|
const calcData = computeFrame(state.floor, rims);
|
||||||
const resolvedOpenings = resolveFloorOpenings(state.openings || [], calcData.width, calcData.depth, calcData.joists, calcData.joistW);
|
const resolvedOpenings = resolveFloorOpenings(state.openings || [], calcData.width, calcData.depth, calcData.joists, calcData.joistW);
|
||||||
const chassisData = computeChassisWheels(state.chassis, calcData.width, calcData.depth);
|
const chassisData = computeChassisWheels(state.chassis, calcData.width, calcData.depth);
|
||||||
return { calcData, resolvedOpenings, chassisData };
|
return { calcData, resolvedOpenings, chassisData };
|
||||||
@@ -208,9 +217,6 @@ window.FloorModel = (() => {
|
|||||||
return {
|
return {
|
||||||
woodMaterial: new THREE.MeshStandardMaterial({ color: 0xc29b62, roughness: 0.6 }),
|
woodMaterial: new THREE.MeshStandardMaterial({ color: 0xc29b62, roughness: 0.6 }),
|
||||||
osbMaterial: new THREE.MeshStandardMaterial({ color: 0xcda86a, roughness: 0.75, side: THREE.DoubleSide }),
|
osbMaterial: new THREE.MeshStandardMaterial({ color: 0xcda86a, roughness: 0.75, side: THREE.DoubleSide }),
|
||||||
isolatieMaterial: new THREE.MeshStandardMaterial({
|
|
||||||
color: 0xffe08c, roughness: 0.9, transparent: true, opacity: 0.5, side: THREE.DoubleSide
|
|
||||||
}),
|
|
||||||
wheelMaterial: new THREE.MeshStandardMaterial({ color: 0x151515, roughness: 0.5 })
|
wheelMaterial: new THREE.MeshStandardMaterial({ color: 0x151515, roughness: 0.5 })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -220,12 +226,11 @@ window.FloorModel = (() => {
|
|||||||
// Z = -diepte/2), Y = dikte (balkhoogte + OSB). Balken en isolatie zijn
|
// Z = -diepte/2), Y = dikte (balkhoogte + OSB). Balken en isolatie zijn
|
||||||
// simpele dozen; de OSB-onderlaag is een geëxtrudeerd Shape met ronde
|
// simpele dozen; de OSB-onderlaag is een geëxtrudeerd Shape met ronde
|
||||||
// uitsparingen voor de doorvoeren.
|
// uitsparingen voor de doorvoeren.
|
||||||
// opts: { materials, osbVisible, isolatieVisible }
|
// opts: { materials, osbVisible }
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
function build3D(calcData, resolvedOpenings, chassisData, opts) {
|
function build3D(calcData, resolvedOpenings, chassisData, opts) {
|
||||||
const { woodMaterial, osbMaterial, isolatieMaterial, wheelMaterial } = opts.materials;
|
const { woodMaterial, osbMaterial, wheelMaterial } = opts.materials;
|
||||||
const osbVisible = opts.osbVisible !== false;
|
const osbVisible = opts.osbVisible !== false;
|
||||||
const isolatieVisible = opts.isolatieVisible !== false;
|
|
||||||
const floorGroup3D = new THREE.Group();
|
const floorGroup3D = new THREE.Group();
|
||||||
|
|
||||||
const toM = 0.001;
|
const toM = 0.001;
|
||||||
@@ -233,28 +238,26 @@ window.FloorModel = (() => {
|
|||||||
const widthM = width * toM, depthM = depth * toM;
|
const widthM = width * toM, depthM = depth * toM;
|
||||||
const joistWM = joistW * toM, joistHM = joistH * toM, underlayM = underlayThick * toM;
|
const joistWM = joistW * toM, joistHM = joistH * toM, underlayM = underlayThick * toM;
|
||||||
|
|
||||||
// A. VLOERBALKEN — dozen, lopen over de volle diepte door.
|
// A. VLOERBALKEN — dozen over de diepte (ingekort bij een kopbalk), plus
|
||||||
|
// de kopbalken (rims) over de volle breedte.
|
||||||
|
const rims = calcData.rims || {};
|
||||||
|
const joistLenM = (calcData.joistLen || depth) * toM;
|
||||||
|
const joistZ = ((rims.front ? joistW : 0) - (rims.back ? joistW : 0)) / 2 * toM;
|
||||||
joists.forEach(xMM => {
|
joists.forEach(xMM => {
|
||||||
const geo = new THREE.BoxGeometry(joistWM, joistHM, depthM);
|
const geo = new THREE.BoxGeometry(joistWM, joistHM, joistLenM);
|
||||||
const mesh = new THREE.Mesh(geo, woodMaterial);
|
const mesh = new THREE.Mesh(geo, woodMaterial);
|
||||||
mesh.position.set(xMM * toM, joistHM / 2, 0);
|
mesh.position.set(xMM * toM, joistHM / 2, joistZ);
|
||||||
|
mesh.castShadow = true;
|
||||||
|
floorGroup3D.add(mesh);
|
||||||
|
});
|
||||||
|
[['front', -1], ['back', 1]].filter(([k]) => rims[k]).forEach(([, sign]) => {
|
||||||
|
const mesh = new THREE.Mesh(new THREE.BoxGeometry(widthM, joistHM, joistWM), woodMaterial);
|
||||||
|
mesh.position.set(0, joistHM / 2, sign * (depthM - joistWM) / 2);
|
||||||
mesh.castShadow = true;
|
mesh.castShadow = true;
|
||||||
floorGroup3D.add(mesh);
|
floorGroup3D.add(mesh);
|
||||||
});
|
});
|
||||||
|
|
||||||
// B. ISOLATIE — vult elk balkvak (tussen twee opeenvolgende balken) over
|
// (Isolatie wordt niet getekend; de leegte-inhoud zit in computeSpecs.)
|
||||||
// de volle balkhoogte/diepte. Zelfde "leegte tussen het regelwerk"-
|
|
||||||
// gedachte als bij wall.js, hier ook echt gevisualiseerd als laag.
|
|
||||||
const bays = computeBays(joists, joistW);
|
|
||||||
bays.forEach(bay => {
|
|
||||||
const bayWidthM = (bay.right - bay.left) * toM;
|
|
||||||
if (bayWidthM <= 0) return;
|
|
||||||
const geo = new THREE.BoxGeometry(bayWidthM, joistHM, depthM);
|
|
||||||
const mesh = new THREE.Mesh(geo, isolatieMaterial);
|
|
||||||
mesh.position.set(((bay.left + bay.right) / 2) * toM, joistHM / 2, 0);
|
|
||||||
mesh.visible = isolatieVisible;
|
|
||||||
floorGroup3D.add(mesh);
|
|
||||||
});
|
|
||||||
|
|
||||||
// C. OSB-ONDERLAAG — één geëxtrudeerd vlak over de volle vloer, met
|
// C. OSB-ONDERLAAG — één geëxtrudeerd vlak over de volle vloer, met
|
||||||
// ronde uitsparingen voor de doorvoeren. Shape wordt opgebouwd in
|
// ronde uitsparingen voor de doorvoeren. Shape wordt opgebouwd in
|
||||||
|
|||||||
+14
-2
@@ -46,8 +46,15 @@ window.FloorPdf = (() => {
|
|||||||
doc.setLineWidth(0.15);
|
doc.setLineWidth(0.15);
|
||||||
doc.line(tblX[0], ty, tblX[4] + 12, ty);
|
doc.line(tblX[0], ty, tblX[4] + 12, ty);
|
||||||
ty += 4;
|
ty += 4;
|
||||||
[String(1), String(joistW), String(joistH), String(Math.round(depth)), String(joists.length)]
|
const rims = calcData.rims || {}, joistLen = calcData.joistLen || depth;
|
||||||
|
const rimCount = (rims.front ? 1 : 0) + (rims.back ? 1 : 0);
|
||||||
|
[String(1), String(joistW), String(joistH), String(Math.round(joistLen)), String(joists.length)]
|
||||||
.forEach((v, i) => doc.text(v, tblX[i], ty));
|
.forEach((v, i) => doc.text(v, tblX[i], ty));
|
||||||
|
if (rimCount) {
|
||||||
|
ty += 4.3; // kopbalk(en) op een naad (FloorModel.computeFrame)
|
||||||
|
[String(2), String(joistW), String(joistH), String(Math.round(width)), String(rimCount)]
|
||||||
|
.forEach((v, i) => doc.text(v, tblX[i], ty));
|
||||||
|
}
|
||||||
|
|
||||||
// --- Plaatmateriaal + isolatie ---
|
// --- Plaatmateriaal + isolatie ---
|
||||||
let my = 240;
|
let my = 240;
|
||||||
@@ -89,11 +96,16 @@ window.FloorPdf = (() => {
|
|||||||
doc.setLineWidth(0.3);
|
doc.setLineWidth(0.3);
|
||||||
doc.setTextColor(...BLACK);
|
doc.setTextColor(...BLACK);
|
||||||
doc.setFontSize(5);
|
doc.setFontSize(5);
|
||||||
|
const jz0 = rims.front ? joistW : 0;
|
||||||
joists.forEach(x => {
|
joists.forEach(x => {
|
||||||
const xLeft = x + width / 2 - joistW / 2;
|
const xLeft = x + width / 2 - joistW / 2;
|
||||||
doc.rect(px(xLeft), py(0), joistW * scale, depth * scale, 'S');
|
doc.rect(px(xLeft), py(jz0), joistW * scale, joistLen * scale, 'S');
|
||||||
doc.text('1', px(xLeft + joistW / 2), py(depth / 2), { align: 'center' });
|
doc.text('1', px(xLeft + joistW / 2), py(depth / 2), { align: 'center' });
|
||||||
});
|
});
|
||||||
|
[['front', 0], ['back', depth - joistW]].filter(([k]) => rims[k]).forEach(([, z]) => {
|
||||||
|
doc.rect(px(0), py(z), width * scale, joistW * scale, 'S');
|
||||||
|
doc.text('2', px(width / 2), py(z + joistW / 2) + 1.2, { align: 'center' });
|
||||||
|
});
|
||||||
|
|
||||||
doc.setDrawColor(...RED);
|
doc.setDrawColor(...RED);
|
||||||
doc.setLineWidth(0.2);
|
doc.setLineWidth(0.2);
|
||||||
|
|||||||
+3
-6
@@ -713,7 +713,6 @@ function mountFloorEditor(container, callbacks) {
|
|||||||
const floorMaterials = roofMode ? RoofModel.createMaterials() : FloorModel.createMaterials();
|
const floorMaterials = roofMode ? RoofModel.createMaterials() : FloorModel.createMaterials();
|
||||||
|
|
||||||
let osbVisible = true;
|
let osbVisible = true;
|
||||||
let isolatieVisible = true;
|
|
||||||
let floorGroup3D = null;
|
let floorGroup3D = null;
|
||||||
let lastResolvedOpenings = [];
|
let lastResolvedOpenings = [];
|
||||||
let lastChassisData = { wheels: [], outOfBounds: false };
|
let lastChassisData = { wheels: [], outOfBounds: false };
|
||||||
@@ -721,11 +720,9 @@ function mountFloorEditor(container, callbacks) {
|
|||||||
// Weergave-filter (core/view-filter.js). Beplating = OSB-plaat (bij een
|
// Weergave-filter (core/view-filter.js). Beplating = OSB-plaat (bij een
|
||||||
// dak: plafondplaat + dakleer).
|
// dak: plafondplaat + dakleer).
|
||||||
createViewFilter(document.getElementById('floor-toolbar-3d'), [
|
createViewFilter(document.getElementById('floor-toolbar-3d'), [
|
||||||
{ key: 'beplating', label: roofMode ? 'Beplating (plafond + dakleer)' : 'Beplating (OSB-plaat)' },
|
{ key: 'beplating', label: roofMode ? 'Beplating (plafond + dakleer)' : 'Beplating (OSB-plaat)' }
|
||||||
{ key: 'isolatie', label: 'Isolatie' }
|
|
||||||
], (v) => {
|
], (v) => {
|
||||||
osbVisible = v.beplating;
|
osbVisible = v.beplating;
|
||||||
isolatieVisible = v.isolatie;
|
|
||||||
update3DScene();
|
update3DScene();
|
||||||
});
|
});
|
||||||
// Omgeving (core/skybox.js: geen/dag/nacht); de vloer staat op de tegelvloer.
|
// Omgeving (core/skybox.js: geen/dag/nacht); de vloer staat op de tegelvloer.
|
||||||
@@ -740,8 +737,8 @@ function mountFloorEditor(container, callbacks) {
|
|||||||
function update3DScene() {
|
function update3DScene() {
|
||||||
if (floorGroup3D) scene.remove(floorGroup3D);
|
if (floorGroup3D) scene.remove(floorGroup3D);
|
||||||
floorGroup3D = roofMode
|
floorGroup3D = roofMode
|
||||||
? RoofModel.build3D(calcData, lastResolvedOpenings, { materials: floorMaterials, osbVisible, isolatieVisible, dakleerVisible: osbVisible })
|
? RoofModel.build3D(calcData, lastResolvedOpenings, { materials: floorMaterials, osbVisible, dakleerVisible: osbVisible })
|
||||||
: FloorModel.build3D(calcData, lastResolvedOpenings, lastChassisData, { materials: floorMaterials, osbVisible, isolatieVisible });
|
: FloorModel.build3D(calcData, lastResolvedOpenings, lastChassisData, { materials: floorMaterials, osbVisible });
|
||||||
scene.add(floorGroup3D);
|
scene.add(floorGroup3D);
|
||||||
applySky(); // schaduw van de nieuwe vloer
|
applySky(); // schaduw van de nieuwe vloer
|
||||||
}
|
}
|
||||||
|
|||||||
+152
-19
@@ -92,6 +92,7 @@ window.HouseModel = (() => {
|
|||||||
{ id: 3, type: 'raam', w: 500, h: 1900, y: 250, anchor: 'right', offset: 450 }
|
{ id: 3, type: 'raam', w: 500, h: 1900, y: 250, anchor: 'right', offset: 450 }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
ensureSeamWalls(state);
|
||||||
computeParts(state, { skip3D: true });
|
computeParts(state, { skip3D: true });
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -208,15 +209,55 @@ window.HouseModel = (() => {
|
|||||||
return wall;
|
return wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dikte = regelwerk + 2x beplating (WallModel.thicknessOf); de diepte van
|
// Dikte = regelwerk + beplating (2x, of 1x bij een naadwand-deel met
|
||||||
// stijlen en regels wordt daarop afgestemd.
|
// buitenzijde 'geen'); de diepte van stijlen en regels wordt daarop afgestemd.
|
||||||
|
const skinSides = (wallState) => ((wallState.materials || {}).exterior === 'geen' ? 1 : 2);
|
||||||
function setThickness(wallState, thickness) {
|
function setThickness(wallState, thickness) {
|
||||||
const depth = Math.max(20, Math.round(thickness - 2 * wallState.wall.sheathingThick));
|
const depth = Math.max(20, Math.round(thickness - skinSides(wallState) * wallState.wall.sheathingThick));
|
||||||
wallState.wall.studDepth = depth;
|
wallState.wall.studDepth = depth;
|
||||||
wallState.wall.plateDepth = depth;
|
wallState.wall.plateDepth = depth;
|
||||||
}
|
}
|
||||||
function thicknessOf(wallState) {
|
function thicknessOf(wallState) {
|
||||||
return Math.max(wallState.wall.studDepth, wallState.wall.plateDepth) + 2 * wallState.wall.sheathingThick;
|
return Math.max(wallState.wall.studDepth, wallState.wall.plateDepth) + skinSides(wallState) * wallState.wall.sheathingThick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Naadwanden: op elke naad tussen twee units staat standaard een tussenwand
|
||||||
|
// met in het midden een deur. Hij bestaat uit twee delen, één per unit
|
||||||
|
// (de modules worden los vervoerd), die met hun regelwerk tegen elkaar aan
|
||||||
|
// staan: balken en isolatie in beide delen, beplating alleen aan de
|
||||||
|
// kamerzijde (buitenMateriaal 'geen' aan de naadzijde). Eén wand-state
|
||||||
|
// (hoogte, openingen) voor beide delen; de openingen gelden voor het deel
|
||||||
|
// in unit `seam.a` en worden in het andere deel gespiegeld, zodat de deur
|
||||||
|
// recht door beide delen gaat. De gebruiker kan hem aanpassen of
|
||||||
|
// verwijderen; alleen bij een nieuwe woning en bij "unit toevoegen" worden
|
||||||
|
// ontbrekende naadwanden aangemaakt (ensureSeamWalls).
|
||||||
|
// state.interiorWalls: { id, seam: { a, b }, wall }
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
const DEFAULT_SEAM_LEAF_MM = 100; // dikte per deel (regelwerk + 1x beplating)
|
||||||
|
function seamWallDefaults(height) {
|
||||||
|
const wall = WallEditor.getDefaultState();
|
||||||
|
wall.wall.heightAll = wall.wall.heightLeft = wall.wall.heightRight = height || wall.wall.heightAll;
|
||||||
|
wall.materials = { interior: 'spaanplaat', exterior: 'geen' };
|
||||||
|
const d = WallModel.OPENING_TYPES.deur;
|
||||||
|
wall.openings = [{ id: 1, type: 'deur', w: d.w, h: d.h, y: d.y, anchor: 'middle', offset: 0 }];
|
||||||
|
setThickness(wall, DEFAULT_SEAM_LEAF_MM);
|
||||||
|
return wall;
|
||||||
|
}
|
||||||
|
const sameSeam = (s, a, b) => (s.a === a && s.b === b) || (s.a === b && s.b === a);
|
||||||
|
// Maakt ontbrekende naadwanden aan (alleen naden met onlyUnitId, indien gegeven).
|
||||||
|
function ensureSeamWalls(state, onlyUnitId) {
|
||||||
|
const { seams } = computeSeams(unitRects(state));
|
||||||
|
seams.forEach(sm => {
|
||||||
|
if (onlyUnitId && sm.a !== onlyUnitId && sm.b !== onlyUnitId) return;
|
||||||
|
if (state.interiorWalls.some(iw => iw.seam && sameSeam(iw.seam, sm.a, sm.b))) return;
|
||||||
|
const id = state.interiorWalls.reduce((m, w) => Math.max(m, w.id), 0) + 1;
|
||||||
|
const height = Math.min(...[sm.a, sm.b].map(uid => {
|
||||||
|
const w = state.units.find(u => u.id === uid).unit.walls.voor.wall;
|
||||||
|
return Math.min(w.heightLeft, w.heightRight);
|
||||||
|
}));
|
||||||
|
state.interiorWalls.push({ id, seam: { a: sm.a, b: sm.b }, wall: seamWallDefaults(height) });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function interiorLayout(iw) {
|
function interiorLayout(iw) {
|
||||||
@@ -233,7 +274,7 @@ window.HouseModel = (() => {
|
|||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
// Alle onderdelen doorgerekend (+ 3D, tenzij opts.skip3D).
|
// Alle onderdelen doorgerekend (+ 3D, tenzij opts.skip3D).
|
||||||
// opts: { wallMaterials, floorMaterials, beplatingVisible, isolatieVisible, skip3D }
|
// opts: { wallMaterials, floorMaterials, beplatingVisible, skip3D }
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
// Past de woning-brede instellingen toe op de onderdeel-states (muteert
|
// Past de woning-brede instellingen toe op de onderdeel-states (muteert
|
||||||
// state): h.o.h. van alle wanden, en de dakmaat als die de vloer volgt.
|
// state): h.o.h. van alle wanden, en de dakmaat als die de vloer volgt.
|
||||||
@@ -296,8 +337,7 @@ window.HouseModel = (() => {
|
|||||||
if (!build) return { group: null, stats: null, specs: null };
|
if (!build) return { group: null, stats: null, specs: null };
|
||||||
const built = WallModel.build3D(config, frame, {
|
const built = WallModel.build3D(config, frame, {
|
||||||
materials: o.wallMaterials, buitenMateriaal, cladding, trasraam, skinExt, facade,
|
materials: o.wallMaterials, buitenMateriaal, cladding, trasraam, skinExt, facade,
|
||||||
spaanplaatVisible: o.beplatingVisible !== false, buitenVisible: o.beplatingVisible !== false,
|
spaanplaatVisible: o.beplatingVisible !== false, buitenVisible: o.beplatingVisible !== false
|
||||||
isolatieVisible: o.isolatieVisible !== false
|
|
||||||
});
|
});
|
||||||
return { group: built.group, stats: built.stats, specs: WallModel.computeSpecs(config, built.stats, buitenMateriaal) };
|
return { group: built.group, stats: built.stats, specs: WallModel.computeSpecs(config, built.stats, buitenMateriaal) };
|
||||||
}
|
}
|
||||||
@@ -305,10 +345,13 @@ window.HouseModel = (() => {
|
|||||||
const units = state.units.map(u => {
|
const units = state.units.map(u => {
|
||||||
const rect = rectById[u.id];
|
const rect = rectById[u.id];
|
||||||
const layout = UnitModel.computeLayout(u.unit, cuts[u.id]);
|
const layout = UnitModel.computeLayout(u.unit, cuts[u.id]);
|
||||||
const floorData = FloorModel.computeFromState(u.unit.floor);
|
// Kopbalk op een naad langs voor-/achterrand (haaks op de balken): daar
|
||||||
|
// worden de modules weer aan elkaar gemonteerd. Zelfde voor het dak.
|
||||||
|
const rims = { front: cuts[u.id].voor.length > 0, back: cuts[u.id].achter.length > 0 };
|
||||||
|
const floorData = FloorModel.computeFromState(u.unit.floor, rims);
|
||||||
const floorSpecs = FloorModel.computeSpecs(floorData.calcData, floorData.resolvedOpenings);
|
const floorSpecs = FloorModel.computeSpecs(floorData.calcData, floorData.resolvedOpenings);
|
||||||
const floorGroup = build ? FloorModel.build3D(floorData.calcData, floorData.resolvedOpenings, floorData.chassisData, {
|
const floorGroup = build ? FloorModel.build3D(floorData.calcData, floorData.resolvedOpenings, floorData.chassisData, {
|
||||||
materials: o.floorMaterials, osbVisible: o.beplatingVisible !== false, isolatieVisible: o.isolatieVisible !== false
|
materials: o.floorMaterials, osbVisible: o.beplatingVisible !== false
|
||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
const walls = layout.walls.map(wl => {
|
const walls = layout.walls.map(wl => {
|
||||||
@@ -331,7 +374,7 @@ window.HouseModel = (() => {
|
|||||||
|
|
||||||
// Dak: dek = vloer-state (180° gespiegeld, RoofModel), op de hoogste
|
// Dak: dek = vloer-state (180° gespiegeld, RoofModel), op de hoogste
|
||||||
// wandbovenkant van deze unit.
|
// wandbovenkant van deze unit.
|
||||||
const roofData = FloorModel.computeFromState(u.roof.deck);
|
const roofData = FloorModel.computeFromState(u.roof.deck, rims);
|
||||||
const roofSpecs = RoofModel.computeSpecs(roofData.calcData, roofData.resolvedOpenings);
|
const roofSpecs = RoofModel.computeSpecs(roofData.calcData, roofData.resolvedOpenings);
|
||||||
const wallTop = Math.max(...Object.values(u.unit.walls).map(ws => Math.max(ws.wall.heightLeft, ws.wall.heightRight)));
|
const wallTop = Math.max(...Object.values(u.unit.walls).map(ws => Math.max(ws.wall.heightLeft, ws.wall.heightRight)));
|
||||||
const roofW = roofData.calcData.width, roofD = roofData.calcData.depth;
|
const roofW = roofData.calcData.width, roofD = roofData.calcData.depth;
|
||||||
@@ -342,9 +385,9 @@ window.HouseModel = (() => {
|
|||||||
base: layout.floorTop + wallTop,
|
base: layout.floorTop + wallTop,
|
||||||
top: layout.floorTop + wallTop + RoofModel.thickness(roofData.calcData),
|
top: layout.floorTop + wallTop + RoofModel.thickness(roofData.calcData),
|
||||||
// Zichtbaarheid zoals wanden/vloeren: plafondplaat + dakleer (EPDM)
|
// Zichtbaarheid zoals wanden/vloeren: plafondplaat + dakleer (EPDM)
|
||||||
// horen bij "Beplating", de dakisolatie bij "Isolatie".
|
// horen bij "Beplating".
|
||||||
group: build ? RoofModel.build3D(roofData.calcData, roofData.resolvedOpenings, {
|
group: build ? RoofModel.build3D(roofData.calcData, roofData.resolvedOpenings, {
|
||||||
materials: o.roofMaterials, isolatieVisible: o.isolatieVisible !== false,
|
materials: o.roofMaterials,
|
||||||
osbVisible: o.beplatingVisible !== false,
|
osbVisible: o.beplatingVisible !== false,
|
||||||
dakleerVisible: o.beplatingVisible !== false
|
dakleerVisible: o.beplatingVisible !== false
|
||||||
}) : null
|
}) : null
|
||||||
@@ -363,19 +406,107 @@ window.HouseModel = (() => {
|
|||||||
})));
|
})));
|
||||||
|
|
||||||
// Binnenwanden staan op de vloer van de unit waarin hun midden valt.
|
// Binnenwanden staan op de vloer van de unit waarin hun midden valt.
|
||||||
const interior = state.interiorWalls.map((iw, i) => {
|
// Naadwanden (iw.seam) leveren twee delen op, één per unit (seamLeaves).
|
||||||
|
const interior = [];
|
||||||
|
let bwNr = 0, nwNr = 0;
|
||||||
|
state.interiorWalls.forEach(iw => {
|
||||||
|
if (iw.seam) { nwNr++; seamLeaves(iw, nwNr).forEach(l => interior.push(l)); return; }
|
||||||
|
bwNr++;
|
||||||
const lay = interiorLayout(iw);
|
const lay = interiorLayout(iw);
|
||||||
const host = units.find(u => lay.cx >= u.rect.x0 && lay.cx <= u.rect.x1 && lay.cz >= u.rect.z0 && lay.cz <= u.rect.z1);
|
const host = units.find(u => lay.cx >= u.rect.x0 && lay.cx <= u.rect.x1 && lay.cz >= u.rect.z0 && lay.cz <= u.rect.z1);
|
||||||
const floorTop = host ? host.layout.floorTop : Math.max(...units.map(u => u.layout.floorTop));
|
const floorTop = host ? host.layout.floorTop : Math.max(...units.map(u => u.layout.floorTop));
|
||||||
const config = WallModel.configFromState(iw.wall);
|
const config = WallModel.configFromState(iw.wall);
|
||||||
const frame = WallModel.computeFrame(config);
|
const frame = WallModel.computeFrame(config);
|
||||||
const buitenMateriaal = (iw.wall.materials || {}).exterior || 'spaanplaat';
|
const buitenMateriaal = (iw.wall.materials || {}).exterior || 'spaanplaat';
|
||||||
return Object.assign({
|
interior.push(Object.assign({
|
||||||
id: iw.id, element: `BW-${i + 1}`, label: `Binnenwand ${i + 1}`, config, frame, buitenMateriaal,
|
id: iw.id, element: `BW-${bwNr}`, label: `Binnenwand ${bwNr}`, config, frame, buitenMateriaal,
|
||||||
place: { cx: lay.cx, cz: lay.cz, rotY: lay.rotY, width: lay.width, thickness: WallModel.thicknessOf(config), floorTop }
|
place: { cx: lay.cx, cz: lay.cz, rotY: lay.rotY, width: lay.width, thickness: WallModel.thicknessOf(config), floorTop }
|
||||||
}, buildWall(config, frame, buitenMateriaal));
|
}, buildWall(config, frame, buitenMateriaal)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Twee delen van een naadwand. Elk deel staat met zijn regelwerk op de
|
||||||
|
// naad (buitenzijde 'geen' = naadzijde) in zijn eigen unit, en loopt van
|
||||||
|
// wand tot wand: waar een buitenwandstuk de hartlijn van het deel kruist,
|
||||||
|
// stopt het (voetafdrukken, zoals bij de gevellampen).
|
||||||
|
function seamLeaves(iw, nr) {
|
||||||
|
const sm = seams.find(x => sameSeam(x, iw.seam.a, iw.seam.b));
|
||||||
|
if (!sm) return []; // units niet (meer) tegen elkaar: naadwand vervalt
|
||||||
|
const horiz = Math.abs(sm.z0 - sm.z1) < EPS;
|
||||||
|
const baseConfig = WallModel.configFromState(iw.wall);
|
||||||
|
const maxD = Math.max(baseConfig.studDepth, baseConfig.plateDepth);
|
||||||
|
const T1 = WallModel.thicknessOf(baseConfig, 'geen');
|
||||||
|
const extPieces = [];
|
||||||
|
units.forEach(u => u.walls.forEach(w => w.pieces.forEach(p => extPieces.push(p))));
|
||||||
|
const along = (q) => horiz ? q.x : q.z, perpOf = (q) => horiz ? q.z : q.x;
|
||||||
|
const seamPerp = horiz ? sm.z0 : sm.x0;
|
||||||
|
const s0 = horiz ? sm.x0 : sm.z0, s1 = horiz ? sm.x1 : sm.z1;
|
||||||
|
|
||||||
|
const leaves = [];
|
||||||
|
[[sm.a, 1], [sm.b, -1]].forEach(([uid, side]) => {
|
||||||
|
// side +1: unit a ligt aan de lage kant, de naad aan zijn +kant.
|
||||||
|
const line = seamPerp - side * T1 / 2;
|
||||||
|
const blocked = [];
|
||||||
|
extPieces.forEach(p => {
|
||||||
|
const cs = [[-1, -1], [1, -1], [-1, 1], [1, 1]].map(([i, j]) => UnitModel.localToWorld(p.place, i * p.place.width / 2, j * p.place.thickness / 2));
|
||||||
|
const pMin = Math.min(...cs.map(perpOf)), pMax = Math.max(...cs.map(perpOf));
|
||||||
|
if (line > pMin + 0.5 && line < pMax - 0.5) blocked.push([Math.min(...cs.map(along)), Math.max(...cs.map(along))]);
|
||||||
|
});
|
||||||
|
let cursor = s0, best = null;
|
||||||
|
blocked.sort((p, q) => p[0] - q[0]).concat([[s1, s1]]).forEach(([a, b]) => {
|
||||||
|
const hi = Math.min(a, s1);
|
||||||
|
if (hi - cursor > (best ? best[1] - best[0] : 0)) best = [cursor, hi];
|
||||||
|
cursor = Math.max(cursor, b);
|
||||||
|
});
|
||||||
|
if (!best || best[1] - best[0] < 300) return;
|
||||||
|
const width = Math.round(best[1] - best[0]), mid = (best[0] + best[1]) / 2;
|
||||||
|
const rotY = horiz ? (side > 0 ? 0 : Math.PI) : (side > 0 ? Math.PI / 2 : -Math.PI / 2);
|
||||||
|
const place = {
|
||||||
|
cx: horiz ? mid : line, cz: horiz ? line : mid, rotY, width, thickness: T1,
|
||||||
|
floorTop: units.find(u => u.id === uid).layout.floorTop
|
||||||
|
};
|
||||||
|
leaves.push({ uid, side, place });
|
||||||
|
});
|
||||||
|
if (!leaves.length) return [];
|
||||||
|
|
||||||
|
// Openingen: opgeslagen voor het eerste deel; het andere deel krijgt
|
||||||
|
// dezelfde openingen op dezelfde plek in de wereld (gespiegeld).
|
||||||
|
const first = leaves[0];
|
||||||
|
iw.wall.wall.width = first.place.width; // voor de wand-editor
|
||||||
|
const firstConfig = WallModel.buildConfig(Object.assign({}, iw.wall.wall, { width: first.place.width }), iw.wall.openings || []);
|
||||||
|
const toLocalU = (pl, q) => {
|
||||||
|
const c = Math.cos(pl.rotY), sn = Math.sin(pl.rotY);
|
||||||
|
return (q.x - pl.cx) * c - (q.z - pl.cz) * sn + pl.width / 2;
|
||||||
|
};
|
||||||
|
return leaves.map((lf, k) => {
|
||||||
|
let config = firstConfig;
|
||||||
|
if (k > 0) {
|
||||||
|
const ops = firstConfig.openings.map(op => {
|
||||||
|
const wa = UnitModel.localToWorld(first.place, -first.place.width / 2 + op.x, 0);
|
||||||
|
const wb = UnitModel.localToWorld(first.place, -first.place.width / 2 + op.x + op.w, 0);
|
||||||
|
const u = Math.min(toLocalU(lf.place, wa), toLocalU(lf.place, wb));
|
||||||
|
return { id: op.id, type: op.type, w: op.w, h: op.h, y: op.y, anchor: 'left', offset: Math.round(u) };
|
||||||
|
});
|
||||||
|
config = WallModel.buildConfig(Object.assign({}, iw.wall.wall, { width: lf.place.width }), ops);
|
||||||
|
}
|
||||||
|
const frame = WallModel.computeFrame(config);
|
||||||
|
const built = buildWall(config, frame, 'geen');
|
||||||
|
if (built.group) {
|
||||||
|
// Het regelwerk staat gecentreerd op lokaal z = 0, de beplating
|
||||||
|
// alleen aan de achterzijde: schuif zodat het midden van de totale
|
||||||
|
// dikte op place.cx/cz ligt (regelwerk op de naad).
|
||||||
|
const outer = new THREE.Group();
|
||||||
|
built.group.position.z = (T1 - maxD) / 2 * 0.001;
|
||||||
|
outer.add(built.group);
|
||||||
|
built.group = outer;
|
||||||
|
}
|
||||||
|
return Object.assign({
|
||||||
|
id: iw.id, seam: iw.seam, seamUnit: lf.uid,
|
||||||
|
element: `NW-${nr}${lf.uid}`, label: `Naadwand ${nr} (unit ${lf.uid})`,
|
||||||
|
config, frame, buitenMateriaal: 'geen', place: lf.place
|
||||||
|
}, built);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Dakrand (boeidelen/daktrim) als één doorlopende rand om alle daken;
|
// Dakrand (boeidelen/daktrim) als één doorlopende rand om alle daken;
|
||||||
// boeidelen aan/uit per unit.
|
// boeidelen aan/uit per unit.
|
||||||
const roofEdge = units.length ? RoofModel.computeEdge(state.settings.roof,
|
const roofEdge = units.length ? RoofModel.computeEdge(state.settings.roof,
|
||||||
@@ -428,9 +559,11 @@ window.HouseModel = (() => {
|
|||||||
}
|
}
|
||||||
parts.units.forEach(u => {
|
parts.units.forEach(u => {
|
||||||
const fc = u.floorData.calcData;
|
const fc = u.floorData.calcData;
|
||||||
fc.joists.forEach(() => addPiece('vloerbalk', fc.joistW, fc.joistH, fc.depth));
|
fc.joists.forEach(() => addPiece('vloerbalk', fc.joistW, fc.joistH, fc.joistLen));
|
||||||
|
['front', 'back'].filter(k => fc.rims[k]).forEach(() => addPiece('vloerbalk', fc.joistW, fc.joistH, fc.width));
|
||||||
const rc = u.roof.data.calcData;
|
const rc = u.roof.data.calcData;
|
||||||
rc.joists.forEach(() => addPiece('dakbalk', rc.joistW, rc.joistH, rc.depth));
|
rc.joists.forEach(() => addPiece('dakbalk', rc.joistW, rc.joistH, rc.joistLen));
|
||||||
|
['front', 'back'].filter(k => rc.rims[k]).forEach(() => addPiece('dakbalk', rc.joistW, rc.joistH, rc.width));
|
||||||
});
|
});
|
||||||
const wallBom = [];
|
const wallBom = [];
|
||||||
pieces.forEach(p => WallModel.computeBOM(p.config, p.frame).forEach(b => { wallBom.push(b); addPiece(b.kind, b.width, b.depth, b.length); }));
|
pieces.forEach(p => WallModel.computeBOM(p.config, p.frame).forEach(b => { wallBom.push(b); addPiece(b.kind, b.width, b.depth, b.length); }));
|
||||||
@@ -568,7 +701,7 @@ window.HouseModel = (() => {
|
|||||||
return {
|
return {
|
||||||
ALIGN_LABELS, SIDE_LABELS, DEFAULT_INTERIOR_THICKNESS,
|
ALIGN_LABELS, SIDE_LABELS, DEFAULT_INTERIOR_THICKNESS,
|
||||||
clone, createEmptyProject, createDefaultSettings, createUnitEntry, applyGlobalSettings, nextUnitId, createDefaultState, unitRects, computeSeams, overlappingPairs,
|
clone, createEmptyProject, createDefaultSettings, createUnitEntry, applyGlobalSettings, nextUnitId, createDefaultState, unitRects, computeSeams, overlappingPairs,
|
||||||
interiorWallDefaults, setThickness, thicknessOf, interiorLayout,
|
interiorWallDefaults, setThickness, thicknessOf, interiorLayout, ensureSeamWalls,
|
||||||
computeParts, allWallPieces, computeTotals, validateState, fromUnitDesign, normalize
|
computeParts, allWallPieces, computeTotals, validateState, fromUnitDesign, normalize
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ window.HousePdf = (() => {
|
|||||||
})));
|
})));
|
||||||
doc.setTextColor(...BLACK);
|
doc.setTextColor(...BLACK);
|
||||||
parts.interior.forEach(p => {
|
parts.interior.forEach(p => {
|
||||||
doc.text(`${p.element}: ${p.place.width}`, px(p.place.cx), py(p.place.cz) - 1.5, { align: 'center', angle: p.place.rotY === 0 ? 0 : 90 });
|
doc.text(`${p.element}: ${p.place.width}`, px(p.place.cx), py(p.place.cz) - 1.5, { align: 'center', angle: Math.abs(Math.sin(p.place.rotY)) < 0.5 ? 0 : 90 });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Buitenmaten woning
|
// Buitenmaten woning
|
||||||
|
|||||||
+40
-7
@@ -135,7 +135,11 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
if (kind === 'floor' && unitById(a)) unitById(a).unit.floor = activeEditor.getStateJson();
|
if (kind === 'floor' && unitById(a)) unitById(a).unit.floor = activeEditor.getStateJson();
|
||||||
else if (kind === 'wall' && unitById(a)) unitById(a).unit.walls[b] = activeEditor.getStateJson();
|
else if (kind === 'wall' && unitById(a)) unitById(a).unit.walls[b] = activeEditor.getStateJson();
|
||||||
else if (kind === 'roof' && unitById(a)) unitById(a).roof.deck = activeEditor.getStateJson();
|
else if (kind === 'roof' && unitById(a)) unitById(a).roof.deck = activeEditor.getStateJson();
|
||||||
else if (kind === 'iw' && iwById(Number(a))) iwById(Number(a)).wall = activeEditor.getStateJson();
|
else if (kind === 'iw' && iwById(Number(a))) {
|
||||||
|
const iw = iwById(Number(a));
|
||||||
|
iw.wall = activeEditor.getStateJson();
|
||||||
|
if (iw.seam) iw.wall.materials = Object.assign({}, iw.wall.materials, { exterior: 'geen' }); // naadzijde blijft zonder plaat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPart(part) {
|
function showPart(part) {
|
||||||
@@ -172,7 +176,8 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
activeEditor.loadStateJson(HouseModel.clone(u.roof.deck));
|
activeEditor.loadStateJson(HouseModel.clone(u.roof.deck));
|
||||||
} else if (kind === 'iw' && iwById(Number(a))) {
|
} else if (kind === 'iw' && iwById(Number(a))) {
|
||||||
HouseModel.applyGlobalSettings(state);
|
HouseModel.applyGlobalSettings(state);
|
||||||
HouseModel.interiorLayout(iwById(Number(a))); // breedte bijwerken
|
if (iwById(Number(a)).seam) HouseModel.computeParts(state, { skip3D: true }); // breedte naadwand-deel
|
||||||
|
else HouseModel.interiorLayout(iwById(Number(a))); // breedte bijwerken
|
||||||
activeEditor = WallEditor.mount(host, { cameraKey: camKey(part), lockWidth: 'De lengte van een binnenwand volgt uit de tekening (overzicht).', lockSpacing: SPACING_LOCK_MSG });
|
activeEditor = WallEditor.mount(host, { cameraKey: camKey(part), lockWidth: 'De lengte van een binnenwand volgt uit de tekening (overzicht).', lockSpacing: SPACING_LOCK_MSG });
|
||||||
activeEditor.loadStateJson(HouseModel.clone(iwById(Number(a)).wall));
|
activeEditor.loadStateJson(HouseModel.clone(iwById(Number(a)).wall));
|
||||||
} else {
|
} else {
|
||||||
@@ -205,7 +210,15 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
});
|
});
|
||||||
if (parts.interior.length) {
|
if (parts.interior.length) {
|
||||||
html += `<div class="hn-group"><div class="hn-item hn-unit hn-static"><span class="hn-label">${UI.icon('distribute-horizontal')}Binnenwanden</span></div>`;
|
html += `<div class="hn-group"><div class="hn-item hn-unit hn-static"><span class="hn-label">${UI.icon('distribute-horizontal')}Binnenwanden</span></div>`;
|
||||||
parts.interior.forEach(p => { html += item(`iw:${p.id}`, p.element, `${p.place.width} mm`, 'hn-sub', 'distribute-horizontal'); });
|
const seen = new Set();
|
||||||
|
parts.interior.forEach(p => {
|
||||||
|
if (seen.has(p.id)) return; // naadwand: twee delen, één item
|
||||||
|
seen.add(p.id);
|
||||||
|
if (p.seam) {
|
||||||
|
const els = parts.interior.filter(x => x.id === p.id).map(x => x.element).join(', ');
|
||||||
|
html += item(`iw:${p.id}`, `Naadwand ${p.seam.a}|${p.seam.b}`, els, 'hn-sub', 'layout-split');
|
||||||
|
} else html += item(`iw:${p.id}`, p.element, `${p.place.width} mm`, 'hn-sub', 'distribute-horizontal');
|
||||||
|
});
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
}
|
}
|
||||||
document.getElementById('house-nav-tree').innerHTML = html;
|
document.getElementById('house-nav-tree').innerHTML = html;
|
||||||
@@ -234,6 +247,7 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
const side = ['rechts', 'achter', 'links', 'voor'].find(s => !taken.has(s)) || 'rechts';
|
const side = ['rechts', 'achter', 'links', 'voor'].find(s => !taken.has(s)) || 'rechts';
|
||||||
const id = HouseModel.nextUnitId(state);
|
const id = HouseModel.nextUnitId(state);
|
||||||
state.units.push(HouseModel.createUnitEntry(id, { to: parent.id, side, align: 'start', offset: 0 }, { width: 3000, depth: 3000, windowOn: [] }));
|
state.units.push(HouseModel.createUnitEntry(id, { to: parent.id, side, align: 'start', offset: 0 }, { width: 3000, depth: 3000, windowOn: [] }));
|
||||||
|
HouseModel.ensureSeamWalls(state, id); // tussenwand met deur op de nieuwe naad
|
||||||
selection = { type: 'unit', id };
|
selection = { type: 'unit', id };
|
||||||
showPart('overzicht');
|
showPart('overzicht');
|
||||||
}
|
}
|
||||||
@@ -299,7 +313,7 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
let dakVisible = true;
|
let dakVisible = true;
|
||||||
// Opgeslagen camerastand gaat voor; anders eenmalig passend maken.
|
// Opgeslagen camerastand gaat voor; anders eenmalig passend maken.
|
||||||
let cameraFitted = CameraMemory.restore(overviewCamKey, camera, controls);
|
let cameraFitted = CameraMemory.restore(overviewCamKey, camera, controls);
|
||||||
let beplatingVisible = true, isolatieVisible = true, lampenVisible = true;
|
let beplatingVisible = true, lampenVisible = true;
|
||||||
let fullRender = false;
|
let fullRender = false;
|
||||||
let skyMode = Skybox.getMode();
|
let skyMode = Skybox.getMode();
|
||||||
const skyEnv = Skybox.createEnvironment(scene);
|
const skyEnv = Skybox.createEnvironment(scene);
|
||||||
@@ -308,12 +322,10 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
// en plafondplaten, dakleer en boeideel/daktrim.
|
// en plafondplaten, dakleer en boeideel/daktrim.
|
||||||
createViewFilter(document.getElementById('ho-toolbar-3d'), [
|
createViewFilter(document.getElementById('ho-toolbar-3d'), [
|
||||||
{ key: 'beplating', label: 'Beplating' },
|
{ key: 'beplating', label: 'Beplating' },
|
||||||
{ key: 'isolatie', label: 'Isolatie' },
|
|
||||||
{ key: 'dak', label: 'Dak' },
|
{ key: 'dak', label: 'Dak' },
|
||||||
{ key: 'lampen', label: 'Lampen' }
|
{ key: 'lampen', label: 'Lampen' }
|
||||||
], (v) => {
|
], (v) => {
|
||||||
beplatingVisible = v.beplating;
|
beplatingVisible = v.beplating;
|
||||||
isolatieVisible = v.isolatie;
|
|
||||||
dakVisible = v.dak;
|
dakVisible = v.dak;
|
||||||
lampenVisible = v.lampen;
|
lampenVisible = v.lampen;
|
||||||
refresh({ keepProps: true });
|
refresh({ keepProps: true });
|
||||||
@@ -725,6 +737,8 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
|
|
||||||
function renderIwProps(iw, warn) {
|
function renderIwProps(iw, warn) {
|
||||||
const p = parts.interior.find(x => x.id === iw.id);
|
const p = parts.interior.find(x => x.id === iw.id);
|
||||||
|
if (iw.seam) return renderSeamProps(iw, warn);
|
||||||
|
if (!p) { selection = null; return renderProps(); }
|
||||||
propsEl.innerHTML = warn + head('distribute-horizontal', p.element, `Binnenwand · ${p.place.width} mm`) +
|
propsEl.innerHTML = warn + head('distribute-horizontal', p.element, `Binnenwand · ${p.place.width} mm`) +
|
||||||
cols(
|
cols(
|
||||||
section('Positie', 'crosshair', num('hi-x1', 'Begin X', iw.x1) + num('hi-z1', 'Begin Y', iw.z1) + num('hi-x2', 'Eind X', iw.x2) + num('hi-z2', 'Eind Y', iw.z2)),
|
section('Positie', 'crosshair', num('hi-x1', 'Begin X', iw.x1) + num('hi-z1', 'Begin Y', iw.z1) + num('hi-x2', 'Eind X', iw.x2) + num('hi-z2', 'Eind Y', iw.z2)),
|
||||||
@@ -737,6 +751,25 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
UI.onNum('hi-height', v => { if (v >= 500) iw.wall.wall.heightAll = iw.wall.wall.heightLeft = iw.wall.wall.heightRight = v; }, after);
|
UI.onNum('hi-height', v => { if (v >= 500) iw.wall.wall.heightAll = iw.wall.wall.heightLeft = iw.wall.wall.heightRight = v; }, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Naadwand: twee delen (één per unit), één set openingen en maten.
|
||||||
|
function renderSeamProps(iw, warn) {
|
||||||
|
const leaves = parts.interior.filter(x => x.id === iw.id);
|
||||||
|
const title = `Naadwand ${iw.seam.a} | ${iw.seam.b}`;
|
||||||
|
propsEl.innerHTML = warn + head('layout-split', title, leaves.length ? `${leaves.length} delen` : 'vervalt') +
|
||||||
|
(leaves.length ? '' : UI.warn(`Unit ${iw.seam.a} en ${iw.seam.b} staan niet (meer) tegen elkaar; deze naadwand wordt niet gebouwd.`)) +
|
||||||
|
cols(
|
||||||
|
section('Opbouw', 'layers',
|
||||||
|
num('hi-thick', 'Dikte per deel', HouseModel.thicknessOf(iw.wall), { step: 5, hint: 'Regelwerk + beplating aan de kamerzijde; naadzijde zonder plaat' }) +
|
||||||
|
num('hi-height', 'Hoogte', iw.wall.wall.heightAll)),
|
||||||
|
UI.specCard('Delen', 'layout-split', leaves.map(l => [`${l.element} (unit ${l.seamUnit})`, l.place.width, 'mm']))) +
|
||||||
|
info('Tussenwand op de naad, per unit een eigen deel dat tegen het andere aan staat. Openingen gelden voor beide delen (de deur loopt door).') +
|
||||||
|
actions(
|
||||||
|
button('Openingen bewerken', 'pencil-square', `data-go="iw:${iw.id}"`),
|
||||||
|
button('Verwijderen', 'trash', `data-remove-iw="${iw.id}"`, 'ui-btn-danger'));
|
||||||
|
UI.onNum('hi-thick', v => { if (v >= 40) HouseModel.setThickness(iw.wall, v); }, after);
|
||||||
|
UI.onNum('hi-height', v => { if (v >= 500) iw.wall.wall.heightAll = iw.wall.wall.heightLeft = iw.wall.wall.heightRight = v; }, after);
|
||||||
|
}
|
||||||
|
|
||||||
// Woning-instellingen (niets geselecteerd): alles wat voor de hele woning
|
// Woning-instellingen (niets geselecteerd): alles wat voor de hele woning
|
||||||
// geldt, in tabbladen.
|
// geldt, in tabbladen.
|
||||||
function renderHouseProps(warn) {
|
function renderHouseProps(warn) {
|
||||||
@@ -857,7 +890,7 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
|
|
||||||
// --- Alles opnieuw doorrekenen ---
|
// --- Alles opnieuw doorrekenen ---
|
||||||
function refresh(opts) {
|
function refresh(opts) {
|
||||||
parts = HouseModel.computeParts(state, { wallMaterials, floorMaterials, roofMaterials, lightingMaterials, sandwichMaterials, beplatingVisible, isolatieVisible, lampenVisible });
|
parts = HouseModel.computeParts(state, { wallMaterials, floorMaterials, roofMaterials, lightingMaterials, sandwichMaterials, beplatingVisible, lampenVisible });
|
||||||
const totals = HouseModel.computeTotals(parts);
|
const totals = HouseModel.computeTotals(parts);
|
||||||
|
|
||||||
if (houseGroup) scene.remove(houseGroup);
|
if (houseGroup) scene.remove(houseGroup);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ window.RoofModel = (() => {
|
|||||||
const toM = 0.001;
|
const toM = 0.001;
|
||||||
const group = new THREE.Group();
|
const group = new THREE.Group();
|
||||||
const deck = FloorModel.build3D(calcData, resolvedOpenings, { wheels: [] }, {
|
const deck = FloorModel.build3D(calcData, resolvedOpenings, { wheels: [] }, {
|
||||||
materials: m, osbVisible: opts.osbVisible !== false, isolatieVisible: opts.isolatieVisible !== false
|
materials: m, osbVisible: opts.osbVisible !== false
|
||||||
});
|
});
|
||||||
const deckH = (calcData.joistH + calcData.underlayThick) * toM;
|
const deckH = (calcData.joistH + calcData.underlayThick) * toM;
|
||||||
deck.scale.y = -1;
|
deck.scale.y = -1;
|
||||||
|
|||||||
+17
-55
@@ -98,7 +98,9 @@ window.WallModel = (() => {
|
|||||||
|
|
||||||
// Buitenzijde-materialen (sleutel -> weergavenaam). 'spaanplaat' is voor
|
// Buitenzijde-materialen (sleutel -> weergavenaam). 'spaanplaat' is voor
|
||||||
// binnenwanden (aan beide zijden spaanplaat).
|
// 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
|
// Heuristiek (geen volwaardige constraint-solver): elke opening wordt eerst
|
||||||
// individueel gepositioneerd op basis van zijn anker, dan van links naar
|
// individueel gepositioneerd op basis van zijn anker, dan van links naar
|
||||||
@@ -234,10 +236,12 @@ window.WallModel = (() => {
|
|||||||
return buildConfig(state.wall, state.openings || []);
|
return buildConfig(state.wall, state.openings || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Totale wanddikte (mm): regelwerk + beplating aan beide zijden. Gebruikt
|
// Totale wanddikte (mm): regelwerk + beplating aan beide zijden (bij
|
||||||
// door de unit om wanden met hun buitenkant op de vloerrand te zetten.
|
// buitenMateriaal 'geen' alleen aan de binnenzijde). Gebruikt door de unit
|
||||||
function thicknessOf(config) {
|
// om wanden met hun buitenkant op de vloerrand te zetten.
|
||||||
return Math.max(config.studDepth, config.plateDepth) + 2 * (config.sheathingThick + SKIN_GAP_MM);
|
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.
|
// 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: 0xffffff, map: doekMap, roughness: 0.7, side: THREE.DoubleSide })
|
||||||
: new THREE.MeshStandardMaterial({ color: DOEK_COLOR, 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 }),
|
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 })
|
||||||
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
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bouwt de volledige 3D-wand (in meters, lokaal assenstelsel: X = breedte
|
// Bouwt de volledige 3D-wand (in meters, lokaal assenstelsel: X = breedte
|
||||||
// gecentreerd, Y = hoogte vanaf onderkant onderregel, +Z = buitenzijde/zuid)
|
// gecentreerd, Y = hoogte vanaf onderkant onderregel, +Z = buitenzijde/zuid)
|
||||||
// en geeft daarnaast de volume-boekhouding terug voor de specificaties.
|
// 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.
|
// trasraam: { bottom, top } in mm (wand-coördinaten, zie trasraamFor) of null.
|
||||||
// skinExt: { left, right } in mm (zie cornerExt) of null.
|
// skinExt: { left, right } in mm (zie cornerExt) of null.
|
||||||
// facade: { u0, extStart, isEnd } (zie computeBattens) of null.
|
// facade: { u0, extStart, isEnd } (zie computeBattens) of null.
|
||||||
function build3D(CONFIG, calcData, opts) {
|
function build3D(CONFIG, calcData, opts) {
|
||||||
const { woodMaterial, spaanplaatMaterial, doekMaterial, cementplaatMaterial, isolatieMaterial, battenMaterial } = opts.materials;
|
const { woodMaterial, spaanplaatMaterial, doekMaterial, cementplaatMaterial, battenMaterial } = opts.materials;
|
||||||
const isolatieVisible = opts.isolatieVisible !== false;
|
|
||||||
const buitenMateriaal = opts.buitenMateriaal || 'vochtdoek';
|
const buitenMateriaal = opts.buitenMateriaal || 'vochtdoek';
|
||||||
const spaanplaatVisible = opts.spaanplaatVisible !== false;
|
const spaanplaatVisible = opts.spaanplaatVisible !== false;
|
||||||
const buitenVisible = opts.buitenVisible !== false;
|
const buitenVisible = opts.buitenVisible !== false;
|
||||||
@@ -692,48 +691,7 @@ window.WallModel = (() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// D2. ISOLATIE — vult elk vak tussen twee opeenvolgende stijlen (vol of
|
// (Isolatie wordt niet getekend; de spouwinhoud zit in computeSpecs.)
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// E. VERTICALE PLATEN — gedeelde opbouw voor spaanplaat (binnenzijde,
|
// E. VERTICALE PLATEN — gedeelde opbouw voor spaanplaat (binnenzijde,
|
||||||
// altijd) EN cement-gebonden platen (buitenzijde, als die gekozen is):
|
// altijd) EN cement-gebonden platen (buitenzijde, als die gekozen is):
|
||||||
@@ -912,6 +870,8 @@ window.WallModel = (() => {
|
|||||||
const spaanBuiten = buildVerticalBoardSkin(spaanplaatMaterial, 'front', buitenVisible);
|
const spaanBuiten = buildVerticalBoardSkin(spaanplaatMaterial, 'front', buitenVisible);
|
||||||
spaanplaatBuitenAreaM2 = spaanBuiten.areaM2;
|
spaanplaatBuitenAreaM2 = spaanBuiten.areaM2;
|
||||||
spaanplaatBuitenVolumeM3 = spaanBuiten.volumeM3;
|
spaanplaatBuitenVolumeM3 = spaanBuiten.volumeM3;
|
||||||
|
} else if (buitenMateriaal === 'geen') {
|
||||||
|
// Naadzijde: geen beplating (het deel van de andere unit staat ertegen).
|
||||||
} else if (buitenMateriaal === 'cementplaat') {
|
} else if (buitenMateriaal === 'cementplaat') {
|
||||||
const cementResult = buildVerticalBoardSkin(cementplaatMaterial, 'front', buitenVisible, null,
|
const cementResult = buildVerticalBoardSkin(cementplaatMaterial, 'front', buitenVisible, null,
|
||||||
Object.assign(extM(0), { zOut: sThickM + 2 * skinGapM }));
|
Object.assign(extM(0), { zOut: sThickM + 2 * skinGapM }));
|
||||||
@@ -1104,6 +1064,8 @@ window.WallModel = (() => {
|
|||||||
buitenAreaM2 = stats.spaanplaatBuitenAreaM2;
|
buitenAreaM2 = stats.spaanplaatBuitenAreaM2;
|
||||||
buitenKg = stats.spaanplaatBuitenVolumeM3 * DENSITY_SPAANPLAAT_KGM3;
|
buitenKg = stats.spaanplaatBuitenVolumeM3 * DENSITY_SPAANPLAAT_KGM3;
|
||||||
buitenVolumeM3 = stats.spaanplaatBuitenVolumeM3;
|
buitenVolumeM3 = stats.spaanplaatBuitenVolumeM3;
|
||||||
|
} else if (buitenMateriaal === 'geen') {
|
||||||
|
buitenAreaM2 = 0; buitenKg = 0; buitenVolumeM3 = 0;
|
||||||
} else if (buitenMateriaal === 'cementplaat') {
|
} else if (buitenMateriaal === 'cementplaat') {
|
||||||
buitenAreaM2 = stats.cementplaatAreaM2;
|
buitenAreaM2 = stats.cementplaatAreaM2;
|
||||||
buitenKg = stats.cementplaatVolumeM3 * DENSITY_CEMENTPLAAT_KGM3;
|
buitenKg = stats.cementplaatVolumeM3 * DENSITY_CEMENTPLAAT_KGM3;
|
||||||
|
|||||||
+2
-5
@@ -1231,16 +1231,13 @@ function mountWallEditor(container, callbacks) {
|
|||||||
|
|
||||||
let spaanplaatVisible = true;
|
let spaanplaatVisible = true;
|
||||||
let buitenVisible = true;
|
let buitenVisible = true;
|
||||||
let isolatieVisible = true;
|
|
||||||
// Weergave-filter (core/view-filter.js): beplating binnen/buiten + isolatie.
|
// Weergave-filter (core/view-filter.js): beplating binnen/buiten + isolatie.
|
||||||
createViewFilter(document.getElementById('wall-toolbar-3d'), [
|
createViewFilter(document.getElementById('wall-toolbar-3d'), [
|
||||||
{ key: 'binnen', label: 'Beplating binnenzijde' },
|
{ key: 'binnen', label: 'Beplating binnenzijde' },
|
||||||
{ key: 'buiten', label: 'Beplating buitenzijde' },
|
{ key: 'buiten', label: 'Beplating buitenzijde' }
|
||||||
{ key: 'isolatie', label: 'Isolatie' }
|
|
||||||
], (v) => {
|
], (v) => {
|
||||||
spaanplaatVisible = v.binnen;
|
spaanplaatVisible = v.binnen;
|
||||||
buitenVisible = v.buiten;
|
buitenVisible = v.buiten;
|
||||||
isolatieVisible = v.isolatie;
|
|
||||||
update3DScene();
|
update3DScene();
|
||||||
});
|
});
|
||||||
// Omgeving (core/skybox.js: geen/dag/nacht); de wand staat op de tegelvloer.
|
// Omgeving (core/skybox.js: geen/dag/nacht); de wand staat op de tegelvloer.
|
||||||
@@ -1259,7 +1256,7 @@ function mountWallEditor(container, callbacks) {
|
|||||||
function update3DScene() {
|
function update3DScene() {
|
||||||
if (wallGroup3D) scene.remove(wallGroup3D);
|
if (wallGroup3D) scene.remove(wallGroup3D);
|
||||||
const result = WallModel.build3D(CONFIG, calcData, {
|
const result = WallModel.build3D(CONFIG, calcData, {
|
||||||
materials: wallMaterials, buitenMateriaal, cladding, trasraam, spaanplaatVisible, buitenVisible, isolatieVisible
|
materials: wallMaterials, buitenMateriaal, cladding, trasraam, spaanplaatVisible, buitenVisible
|
||||||
});
|
});
|
||||||
wallGroup3D = result.group;
|
wallGroup3D = result.group;
|
||||||
lastWallStats = result.stats;
|
lastWallStats = result.stats;
|
||||||
|
|||||||
Reference in New Issue
Block a user