v0.6.3: modellen Waal (12,85 × 7 m, flauw zadeldak dat aan de oostkop 1,5 m doorloopt als veranda), Maas (9,2 × 9,2 m, woningdak met inpandige veranda in de noordwesthoek) en Rijn (L-vorm 13 × 10 m, zadeldak als twee lessenaars op de naad, vleugel met plat dak, steenstrips met schoorsteenstrook, kookeiland, logeerkamer, spots in de boeidelen); overkapping per unit (dak verlengen voorbij de gevel) met palen (PostModel: hout, maat, h.o.h., op de plattegrond/PDF en in de kosten); kleur schakelaars/wcd's (wit, antraciet, zwart); gevellampen naast openingen en per vrij wandstuk gelijk verdeeld, nooit in een opening; logobord niet meer onder een unit bij een voordeur in een binnenhoek

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-10-11 14:26:06 +02:00
co-authored by Claude Opus 5.5
parent b137a458d4
commit 86fcd57b96
14 changed files with 586 additions and 35 deletions
+309 -5
View File
@@ -306,7 +306,7 @@ window.HouseModel = (() => {
Object.assign(S.facade, { type: 'stuc', stucColor: 'wit' });
Object.assign(S.trasraam, { enabled: true, height: 600, finish: 'R510' });
S.roof.fasciaColor = 'zwart';
S.lighting.mode = 'gevel';
S.lighting.mode = 'spots';
S.lighting.direction = 'both';
if (S.elektra && S.elektra.apparaten) Object.assign(S.elektra.apparaten, { koken: true, oven: true, vaatwasser: true });
@@ -368,11 +368,298 @@ window.HouseModel = (() => {
return st;
}
// Model Waal (examples/ontwerp_waal.pdf): 12,85 × 7 m, twee units van 3,5 m met één flauw zadeldak over de
// woning (nok west–oost op de naad). Het dak loopt aan de oostkop 1,5 m door als veranda (10,5 m²) op drie
// eiken palen (PostModel). Geen naadwand: slaapkamer en woonkamer/keuken lopen over beide units door; bij de
// naad staat aan elke kant een binnenwand (badkamer/keuken | hal). Alles buitenwerks vanaf de noordwesthoek.
function createWaal() {
const st = newModelState('Waal');
const S = st.settings, LO = 2800, HI = 3400, W = 12850, D = 3500;
Object.assign(S.facade, { type: 'rabat', rabatColor: 'grijs' });
S.cladding = 'verticaal';
Object.assign(S.trasraam, { enabled: true, height: 600, finish: 'R510' });
Object.assign(S.roof, { fasciaColor: 'wit', overhang: RoofModel.OVERHANG_MAX, fasciaHeight: 350 });
S.roof.posts = PostModel.normalizeSettings({ enabled: true, size: 200, wood: 'eiken' });
S.lighting.mode = 'spots';
S.lighting.direction = 'both';
if (S.elektra && S.elektra.apparaten) Object.assign(S.elektra.apparaten, { koken: true, oven: true, vaatwasser: true });
const A = createUnitEntry('A', null, { width: W, depth: D, height: LO, windowOn: [] });
const B = createUnitEntry('B', { to: 'A', side: 'achter', align: 'start', offset: 0 }, { width: W, depth: D, height: LO, windowOn: [] });
st.units.push(A, B);
setHouseRoof(st, true);
Object.assign(S.roof.house, { form: { type: 'zadel', ridge: 'x', low: LO, high: HI } });
[A, B].forEach(u => { u.roof.extend = { f: 0, r: 1500, b: 0, l: 0 }; });
const { K, binnen, ops, iw, room, fx } = modelKit(st, LO, 90);
// Noord 'right' en zuid 'left' = vanaf de westhoek; west 'left' en oost 'right' = vanaf de noordkant van de wand.
ops(A.unit.walls.voor, [
['raam', 800, 500, 1600, 'right', 3490, K('raam')],
['raam', 800, 1100, 1000, 'right', 6530, K('raam')],
['raam', 1400, 1100, 1000, 'right', 8510, K('raam')]]);
ops(A.unit.walls.links, [['raam', 500, 1400, 700, 'left', 1170, K('raam')]]);
ops(B.unit.walls.links, [['raam', 500, 1400, 700, 'left', 390, K('raam')], ['raam', 1400, 1100, 1000, 'left', 1570, K('raam')]]);
// Oostkop onder de veranda: schuifdeur bij de keuken, openslaande deuren bij de woonkamer.
ops(A.unit.walls.rechts, [['deur', 1800, 2100, 0, 'right', 880,
K('deur', { type: 'schuifdeur', leaves: 2, mount: 'opbouw', side: 'binnen', glas: true })]]);
ops(B.unit.walls.rechts, [
['deur', 900, 2100, 0, 'right', 860, K('deur', { leaf: 'rechts', hinge: 'rechts', swing: 'buiten', volglas: true })],
['deur', 900, 2100, 0, 'right', 1760, K('deur', { leaf: 'links', hinge: 'links', swing: 'buiten', volglas: true })]]);
ops(B.unit.walls.achter, [
['raam', 500, 1400, 700, 'left', 3530, K('raam')],
['voordeur', 1100, 2100, 0, 'left', 4310, K('deur', { hinge: 'links', swing: 'buiten', glas: true })],
...[7130, 8230, 10470, 11570].map(x => ['raam', 500, 2100, 0, 'left', x, K('raam')])]);
B.lighting.walls.achter = 'openingen';
const DH = 2115;
// Noord–zuid: 'buiten' = west, 'left' = vanaf de noordkant; west–oost: 'buiten' = zuid, 'left' = vanaf de westkant.
iw(3235, 190, 3235, 3430, [['deur', 980, 2130, 'left', 1530, // slaapkamer | badkamer
Object.assign(binnen(), { type: 'schuifdeur', leaves: 1, mount: 'opbouw', side: 'buiten', dir: 'links' })]]);
iw(5325, 190, 5325, 3430); // badkamer | keuken
iw(3280, 3510, 6535, 3510, [], 160); // badkamer, keuken | hal (over de naad)
iw(3235, 3590, 3235, 4655, [['deur', 930, DH, 'left', 75, binnen({ hinge: 'links', swing: 'buiten' })]]); // hal → slaapkamer
iw(6490, 3590, 6490, 6810, [['deur', 930, DH, 'left', 72, binnen({ hinge: 'links', swing: 'binnen' })]]); // hal, entree | woonkamer
iw(190, 4700, 6445, 4700, [ // slaapkamer, hal | berging, toilet, entree
['deur', 930, DH, 'left', 120, binnen({ hinge: 'links', swing: 'binnen' })],
['deur', 930, DH, 'left', 4200, binnen({ hinge: 'rechts', swing: 'buiten' })]]);
iw(2248, 4745, 2248, 5819); // berging | toilet
iw(2203, 5864, 3607, 5864); // toilet | berging, entree
iw(2938, 5909, 2938, 6810); // berging | entree
iw(3562, 4745, 3562, 5819, [['deur', 930, DH, 'left', 70, binnen({ hinge: 'links', swing: 'binnen' })]]); // toilet → entree
const plafond = { ceiling: LO };
room('A', 9500, 1800, 'woonkeuken', 'Woonkamer/keuken', plafond);
room('A', 1500, 1800, 'slaapkamer', 'Slaapkamer', plafond);
room('A', 4300, 2200, 'badkamer', '', plafond);
room('B', 4800, 600, 'hal', 'Hal', plafond);
room('B', 2900, 1800, 'toilet', '', plafond);
room('B', 4700, 2700, 'hal', 'Entree', plafond);
room('B', 1200, 2400, 'berging', 'Berging', plafond);
const zB = (z) => z - D;
fx('kledingkast', 'A', 1690, 490, 0, { w: 3000 });
fx('bed', 'A', 1240, 2770, 270, { w: 1600, d: 2100 }, { nachtkastjes: false });
fx('douche', 'A', 4585, 640, 90, { w: 900, d: 1390 }, { glas: 'rechts', glasLengte: 700 });
fx('wastafel', 'A', 5050, 1920, 90, { w: 800 });
fx('toilet', 'A', 3790, 3085, 180, { w: 1000 });
fx('keuken', 'A', 5670, 1810, 270, { w: 3240 }, { front: 'antraciet', hogeKast: 'links', spoel: false, vaatwasser: false, kookplaat: true, kookplaatPlek: 0.45 });
fx('keuken', 'A', 7025, 490, 0, { w: 2110 }, { front: 'antraciet', hogeKast: 'geen', spoelbak: 0.5, kookplaat: false });
fx('eettafel', 'A', 10230, 1730, 0, { w: 2000, d: 1100 }, { vorm: 'ovaal', stoelen: 6 });
fx('bank', 'B', 9820, zB(5365), 90, { w: 2500, d: 900 }, { chaise: 'rechts', chaiseLengte: 1300 });
fx('fauteuil', 'B', 8420, zB(3910), 330);
fx('salontafel', 'B', 8180, zB(5180), 0, { w: 800, d: 800 }, { vorm: 'rond' });
fx('tvmeubel', 'B', 6735, zB(5815), 270, { w: 1500, d: 400 });
fx('kledingkast', 'B', 6145, zB(5777), 90, { w: 2060 });
fx('toilet', 'B', 2638, zB(5312), 270, { w: 1000 });
fx('boiler', 'B', 1810, zB(4970), 0);
fx('wasmachine', 'B', 2500, zB(6510), 180);
computeParts(st, { skip3D: true });
return st;
}
// Model Maas (examples/ontwerp_maas.pdf): vierkant 9,2 × 9,2 m onder één flauw zadeldak (nok west–oost op de
// naad). Twee units van 4,6 m diep: noord 6,7 m breed (oost uitgelijnd), zuid 9,2 m. De overkapping van het
// woningdak maakt van de noordwesthoek een inpandige veranda (2,5 × 4,6 m) op eiken palen. Maten buitenwerks vanaf
// de noordwesthoek van de woning; unit A begint 2,5 m oostelijker (X = interieur-coördinaten).
function createMaas() {
const st = newModelState('Maas');
const S = st.settings, LO = 2800, HI = 3500, D = 4600, V = 2500;
Object.assign(S.facade, { type: 'rabat', rabatColor: 'naturel' });
S.cladding = 'verticaal';
S.trasraam.enabled = false;
Object.assign(S.roof, { fasciaColor: 'antraciet', overhang: 300, fasciaHeight: 300 });
S.roof.posts = PostModel.normalizeSettings({ enabled: true, size: 200, wood: 'eiken' });
S.lighting.mode = 'spots';
S.lighting.direction = 'both';
if (S.elektra && S.elektra.apparaten) Object.assign(S.elektra.apparaten, { koken: true, oven: true, vaatwasser: true });
const A = createUnitEntry('A', null, { width: 9200 - V, depth: D, height: LO, windowOn: [] });
const B = createUnitEntry('B', { to: 'A', side: 'achter', align: 'end', offset: 0 }, { width: 9200, depth: D, height: LO, windowOn: [] });
st.units.push(A, B);
setHouseRoof(st, true);
Object.assign(S.roof.house, { form: { type: 'zadel', ridge: 'x', low: LO, high: HI }, cover: true });
const { K, binnen, ops, iw, room, fx } = modelKit(st, LO, 90);
// Noord 'right' en zuid 'left' = vanaf de westhoek; west 'left' en oost 'right' = vanaf de noordkant van de wand.
ops(A.unit.walls.voor, [
['raam', 800, 1500, 600, 'right', 970, K('raam')],
['raam', 800, 1500, 600, 'right', 2715, K('raam')],
['raam', 800, 1100, 1000, 'right', 4815, K('raam')]]);
// Westgevel onder de veranda: openslaande deuren met een vast zijlicht aan beide kanten (samen 3000 × 2100).
ops(A.unit.walls.links, [
['raam', 600, 2100, 0, 'left', 770, K('raam')],
['deur', 900, 2100, 0, 'left', 1370, K('deur', { leaf: 'rechts', hinge: 'links', swing: 'buiten', volglas: true })],
['deur', 900, 2100, 0, 'left', 2270, K('deur', { leaf: 'links', hinge: 'rechts', swing: 'buiten', volglas: true })],
['raam', 600, 2100, 0, 'left', 3170, K('raam')]]);
ops(B.unit.walls.voor, [['raam', 1100, 1500, 600, 'right', 315, K('raam')]]);
ops(B.unit.walls.links, [['raam', 800, 1500, 600, 'left', 200, K('raam')]]);
ops(B.unit.walls.rechts, [['raam', 800, 1100, 1000, 'right', 210, K('raam')]]);
ops(B.unit.walls.achter, [
['raam', 500, 700, 1400, 'left', 3525, K('raam')],
['voordeur', 1100, 2100, 0, 'left', 5675, K('deur', { hinge: 'links', swing: 'buiten' })],
['raam', 500, 1500, 600, 'left', 8375, K('raam')]]);
ensureSeamWalls(st);
const seam = st.interiorWalls.find(w => w.seam);
if (seam) {
const tA = WallModel.thicknessOf(WallModel.configFromState(A.unit.walls.links));
seam.wall.openings = [{ id: 1, type: 'deur', w: 930, h: 2115, y: 0, anchor: 'left', offset: Math.round(5790 - V - tA),
fill: binnen({ hinge: 'rechts', swing: 'binnen' }) }];
}
const DH = 2115, X = (x) => x - V;
// Noord–zuid: 'buiten' = west, 'left' = vanaf de noordkant; west–oost: 'buiten' = zuid, 'left' = vanaf de westkant.
iw(X(3250), 4700, X(3250), 9010, [ // slaapkamer | hal, toilet, badkamer
['deur', 930, DH, 'left', 70, binnen({ hinge: 'links', swing: 'binnen' })],
['deur', 980, 2130, 'left', 2780, Object.assign(binnen(), { type: 'schuifdeur', leaves: 1, mount: 'opbouw', side: 'buiten', dir: 'links' })]]);
iw(X(3295), 5740, X(5620), 5740, [['deur', 930, DH, 'left', 665, binnen({ hinge: 'links', swing: 'binnen' })]]); // hal | toilet, schacht
iw(X(5075), 5785, X(5075), 6865); // toilet | schacht
iw(X(3295), 6910, X(5530), 6910); // toilet, schacht | badkamer
iw(X(5575), 5785, X(5575), 9010); // schacht, badkamer | entree
iw(X(6865), 4700, X(6865), 9010, [ // entree | logeerkamer, berging
['deur', 930, DH, 'left', 70, binnen({ hinge: 'links', swing: 'buiten' })],
['deur', 930, DH, 'left', 3185, binnen({ hinge: 'rechts', swing: 'buiten' })]]);
iw(X(6910), 6915, X(9010), 6915); // logeerkamer | berging
const plafond = { ceiling: LO };
room('A', 2500, 2000, 'woonkeuken', 'Woonkamer/keuken');
room('B', 1500, 2000, 'slaapkamer', 'Slaapkamer', plafond);
room('B', 4100, 1700, 'toilet');
room('B', 4000, 3300, 'badkamer');
room('B', 6200, 2500, 'hal', 'Entree', plafond);
room('B', 7900, 1100, 'slaapkamer', 'Logeerkamer', plafond);
room('B', 7900, 3300, 'berging', 'Berging', plafond);
const zB = (z) => z - D;
fx('keuken', 'A', X(7620), 490, 0, { w: 1580 }, { front: 'antraciet', hogeKast: 'geen', spoelbak: 0.58, kookplaat: false });
fx('keuken', 'A', X(8710), 2290, 90, { w: 4200 }, { front: 'antraciet', hogeKast: 'rechts', spoel: false, vaatwasser: false, kookplaat: true, kookplaatPlek: 0.43 });
fx('eettafel', 'A', X(5860), 1090, 0, { w: 1000, d: 1000 }, { vorm: 'ovaal', stoelen: 4 });
fx('bank', 'A', X(4020), 3770, 180, { w: 2550, d: 900 }, { chaise: 'rechts', chaiseLengte: 1300 });
fx('salontafel', 'A', X(4200), 2710, 0, { w: 800, d: 800 }, { vorm: 'rond' });
fx('fauteuil', 'A', X(3480), 1460, 330);
fx('bed', 'B', 1240, zB(6520), 270, { w: 1600, d: 2100 }, { nachtkastjes: false });
fx('kledingkast', 'B', 1700, zB(8710), 180, { w: 3000 });
fx('toilet', 'B', 3600, zB(6325), 270, { w: 1000 });
fx('wastafel', 'B', 3760, zB(7205), 0, { w: 800 });
fx('toilet', 'B', 3760, zB(8700), 180, { w: 1000 });
fx('douche', 'B', 5090, zB(8265), 180, { w: 900, d: 1500 }, { glas: 'rechts', glasLengte: 700 });
fx('bed', 'B', 7960, zB(6430), 90, { w: 900, d: 2100 }, { nachtkastjes: false });
fx('wasmachine', 'B', 7280, zB(7260), 0);
fx('boiler', 'B', 8700, zB(7260), 0);
computeParts(st, { skip3D: true });
return st;
}
// Model Rijn (examples/ontwerp_rijn.pdf): L-vorm 13 × 10 m, hoofdvolume 13 × 6,54 m (twee units van 3,27 m) met
// een vleugel van 4,2 × 3,46 m aan de zuidwestkant. Flauw zadeldak over het hoofdvolume als twee lessenaars die
// op de naad de nok vormen (een woningdak zou ook de vleugel overspannen); de vleugel heeft een plat dak op
// goothoogte. Alleen een naadwand naar de vleugel; de 160 mm wand langs de nok is een gewone binnenwand.
// Steenstrips rondom, in de oostgevel een donkere strook waar de schoorsteen staat. Maten buitenwerks vanaf de
// noordwesthoek, zoals de tekening (de steenstrips komen daar in de configurator buiten de units nog bij).
function createRijn() {
const st = newModelState('Rijn');
const S = st.settings, LO = 2800, HI = 3500, D = 3270, BRICK = 'R752', SCHOORSTEEN = 'R777';
Object.assign(S.facade, { type: 'steen', stone: BRICK });
Object.assign(S.trasraam, { enabled: true, height: 500, finish: 'R669' });
Object.assign(S.roof, { fasciaColor: 'wit', overhang: 300, fasciaHeight: 350 });
S.lighting.mode = 'spots';
S.lighting.direction = 'both';
if (S.elektra && S.elektra.apparaten) Object.assign(S.elektra.apparaten, { koken: true, oven: true, vaatwasser: true });
const A = createUnitEntry('A', null, { width: 13000, depth: D, height: LO, windowOn: [] });
const B = createUnitEntry('B', { to: 'A', side: 'achter', align: 'start', offset: 0 }, { width: 13000, depth: D, height: LO, windowOn: [] });
const C = createUnitEntry('C', { to: 'B', side: 'achter', align: 'start', offset: 0 }, { width: 4200, depth: 3460, height: LO, windowOn: [] });
st.units.push(A, B, C);
UnitModel.applyRoofForm(A.unit, { type: 'lessenaar', dir: 'achter', low: LO, high: HI });
UnitModel.applyRoofForm(B.unit, { type: 'lessenaar', dir: 'voor', low: LO, high: HI });
const { K, binnen, ops, iw, room, fx } = modelKit(st, LO, 90);
// Noord 'right' en zuid 'left' = vanaf de westhoek; west 'left' en oost 'right' = vanaf de noordkant van de wand.
ops(A.unit.walls.voor, [
['raam', 500, 1400, 700, 'right', 1045, K('raam')],
['raam', 500, 1400, 700, 'right', 3630, K('raam')],
['raam', 1200, 500, 1600, 'right', 4760, K('raam')],
['raam', 1400, 1400, 700, 'right', 7560, K('raam')]]);
ops(A.unit.walls.rechts, [['raam', 1800, 2100, 0, 'right', 785, K('raam')]]);
ops(B.unit.walls.rechts, [
['deur', 900, 2100, 0, 'right', 730, K('deur', { leaf: 'rechts', hinge: 'rechts', swing: 'buiten', volglas: true })],
['deur', 900, 2100, 0, 'right', 1630, K('deur', { leaf: 'links', hinge: 'links', swing: 'buiten', volglas: true })]]);
ops(B.unit.walls.links, [['raam', 1400, 1100, 1000, 'left', 465, K('raam')]]);
ops(B.unit.walls.achter, [
['raam', 1100, 2100, 0, 'left', 4680, K('raam')],
['raam', 1800, 2100, 0, 'left', 10490, K('raam')]]);
ops(C.unit.walls.links, [['raam', 500, 700, 1400, 'left', 365, K('raam')]]);
ops(C.unit.walls.rechts, [['voordeur', 1100, 2100, 0, 'right', 70, K('deur', { hinge: 'links', swing: 'buiten', glas: true })]]);
ops(C.unit.walls.achter, [['raam', 1400, 1100, 1000, 'left', 1505, K('raam')]]);
// Schoorsteen midden in de oostgevel (z 2870–3670). u loopt van buiten gezien van links (zuid) naar rechts.
A.facade.walls.rechts = { type: 'steen', stone: SCHOORSTEEN };
A.unit.walls.rechts.splits = [{ u: 400, facade: { type: 'steen', stone: BRICK } }];
B.unit.walls.rechts.splits = [{ u: 2870, facade: { type: 'steen', stone: SCHOORSTEEN } }];
const DH = 2115;
// Noord–zuid: 'buiten' = west, 'left' = vanaf de noordkant; west–oost: 'buiten' = zuid, 'left' = vanaf de westkant.
iw(190, 3266, 6513, 3266, [['deur', 930, DH, 'left', 2800, binnen({ hinge: 'links', swing: 'buiten' })]], 160); // slaapkamer, badkamer | keuken (langs de nok)
iw(4380, 190, 4380, 3186, [['deur', 980, 2130, 'left', 1136, // slaapkamer | badkamer
Object.assign(binnen(), { type: 'schuifdeur', leaves: 1, mount: 'opbouw', side: 'buiten', dir: 'links' })]]);
iw(6466, 190, 6466, 3186); // badkamer | woonkamer
iw(5800, 1135, 6421, 1135); // muurtje naast de douche
iw(2762, 3346, 2762, 6440, [['deur', 930, DH, 'left', 104, binnen({ hinge: 'links', swing: 'buiten' })]]); // berging | keuken
iw(1880, 6640, 1880, 7655, [['deur', 930, DH, 'left', 40, binnen({ hinge: 'links', swing: 'binnen' })]]); // toilet | entree
iw(190, 7700, 3107, 7700, [['deur', 930, DH, 'left', 1820, binnen({ hinge: 'links', swing: 'buiten' })]]); // toilet, entree | logeerkamer
iw(3062, 7745, 3062, 8335); // kapstoknis entree
iw(3107, 8290, 4010, 8290); // kapstoknis | kast logeerkamer
ensureSeamWalls(st, 'C');
const seam = st.interiorWalls.find(w => w.seam);
if (seam) {
const tC = WallModel.thicknessOf(WallModel.configFromState(C.unit.walls.links));
seam.wall.openings = [{ id: 1, type: 'deur', w: 930, h: DH, y: 0, anchor: 'left', offset: Math.round(2915 - tC),
fill: binnen({ hinge: 'links', swing: 'buiten' }) }];
}
const plafond = { ceiling: LO };
room('A', 9500, 1500, 'woonkeuken', 'Woonkamer/keuken');
room('A', 2600, 1700, 'slaapkamer', 'Slaapkamer', plafond);
room('A', 5400, 2000, 'badkamer', '', plafond);
room('B', 1400, 1500, 'berging', 'Berging', plafond);
room('C', 1000, 500, 'toilet');
room('C', 2400, 500, 'hal', 'Entree');
room('C', 1500, 2300, 'slaapkamer', 'Logeerkamer');
const zB = (z) => z - D, zC = (z) => z - 2 * D;
fx('kledingkast', 'A', 493, 1688, 270, { w: 2990 });
fx('bed', 'A', 2615, 1240, 0, { w: 1600, d: 2100 }, { nachtkastjes: false });
fx('douche', 'A', 5725, 640, 0, { w: 1400, d: 900 }, { glas: 'links', glasLengte: 900 });
fx('wastafel', 'A', 6191, 2000, 90, { w: 800 });
fx('toilet', 'A', 4998, 2680, 180);
fx('bank', 'A', 11195, 800, 0, { w: 3000, d: 1000 }, { chaise: 'rechts', chaiseLengte: 2100 });
fx('fauteuil', 'A', 8880, 1720, 315);
fx('salontafel', 'A', 10410, 2280, 0, { w: 800, d: 800 }, { vorm: 'rond' });
fx('keuken', 'B', 4945, zB(4350), 90, { w: 2000 }, { front: 'antraciet', hogeKast: 'geen', spoelbak: 0.15, kookplaat: true, kookplaatPlek: 0.85 });
fx('kookeiland', 'B', 5545, zB(4350), 270, { w: 2000, d: 600 }, { front: 'antraciet', kookplaat: false });
fx('keuken', 'B', 3110, zB(5080), 270, { w: 1200 }, { front: 'antraciet', hogeKast: 'rechts', spoel: false, vaatwasser: false });
fx('eettafel', 'B', 10010, zB(4990), 0, { w: 2000, d: 1100 }, { vorm: 'ovaal', stoelen: 6 });
fx('wasmachine', 'B', 620, zB(6140), 180);
fx('wasmachine', 'B', 1450, zB(6140), 180); // droger
fx('boiler', 'B', 2350, zB(6215), 180);
fx('toilet', 'C', 535, zC(7150), 270);
fx('bed', 'C', 650, zC(8745), 0, { w: 900, d: 2000 }, { nachtkastjes: false });
fx('kledingkast', 'C', 3703, zC(9072), 90, { w: 1450 });
computeParts(st, { skip3D: true });
return st;
}
// Standaardmodellen: Bestand › Snel nieuw en het venster Nieuw. De sleutel is de template.
const MODELS = {
amstel: { label: 'Woning Amstel', icon: 'house-heart', create: createAmstel },
wetering: { label: 'Woning Wetering', icon: 'house-door', create: createWetering },
ijssel: { label: 'Woning IJssel', icon: 'houses', create: createIJssel }
ijssel: { label: 'Woning IJssel', icon: 'houses', create: createIJssel },
waal: { label: 'Woning Waal', icon: 'house-add', create: createWaal },
maas: { label: 'Woning Maas', icon: 'house-check', create: createMaas },
rijn: { label: 'Woning Rijn', icon: 'house-gear', create: createRijn }
};
function createDefaultState(template) {
@@ -598,7 +885,9 @@ window.HouseModel = (() => {
ws.materials = Object.assign({}, ws.materials, facadeMaterials(state, facadeFor(state, u, side)));
});
if (u.roof.followFloor) {
const e = cover[u.id] || { l: 0, r: 0, f: 0, b: 0 };
// Overkapping van het woningdak plus de eigen verlenging (veranda) van deze unit.
const c = cover[u.id] || {}, x = u.roof.extend || {};
const e = Object.fromEntries(['l', 'r', 'f', 'b'].map(k => [k, (c[k] || 0) + (x[k] || 0)]));
u.roof.deck.floor.width = u.unit.floor.floor.width + e.l + e.r;
u.roof.deck.floor.depth = u.unit.floor.floor.depth + e.f + e.b;
u.roof.offsetX = (e.r - e.l) / 2;
@@ -610,6 +899,11 @@ window.HouseModel = (() => {
applyRoofJoins(state);
}
const EXTEND_MAX = 4000;
function normalizeExtend(e) {
return Object.fromEntries(['f', 'r', 'b', 'l'].map(k => [k, Math.max(0, Math.min(EXTEND_MAX, Math.round(Number((e || {})[k]) || 0)))]));
}
const houseRoofOn = (state) => !!(state.settings.roof.house && state.settings.roof.house.enabled && state.settings.roof.house.form);
const ROOF_HEIGHT_KEYS = ['heightAll', 'heightLeft', 'heightRight', 'heightMid', 'topPts'];
@@ -1338,6 +1632,10 @@ window.HouseModel = (() => {
const elektraGroup = build ? ElektraModel.build3D(elektra, o.elektraMaterials, stage) : null;
if (elektraGroup) elektraGroup.visible = o.elektraVisible !== false && shows('elektra');
const groundY = units.length ? Math.max(...units.map(u => u.layout.floorTop)) - (state.settings.groundLevel || 0) : 0;
const posts = PostModel.compute(state.settings.roof.posts, units, groundY);
const postsGroup = build ? PostModel.build3D(posts, o.postMaterials) : null;
const sandwich = state.settings.sandwich && units.length
? SandwichModel.compute(rects, Math.max(...units.map(u => u.layout.floorTop))) : null;
const sandwichGroup = build && sandwich ? SandwichModel.build3D(sandwich, o.sandwichMaterials || SandwichModel.createMaterials(), {
@@ -1350,7 +1648,7 @@ window.HouseModel = (() => {
const fixturesGroup = build ? FixtureModel.build3D(fixtures) : null;
if (fixturesGroup) fixturesGroup.visible = shows('afwerking');
return { rects, seams, overlaps: overlappingPairs(rects), units, interior, roofEdge, roofEdgeGroup, lighting, lightingGroup, elektra, elektraGroup, sandwich, sandwichGroup, rooms, roomsGroup, fixtures, fixturesGroup };
return { rects, seams, overlaps: overlappingPairs(rects), units, interior, roofEdge, roofEdgeGroup, lighting, lightingGroup, elektra, elektraGroup, sandwich, sandwichGroup, posts, postsGroup, rooms, roomsGroup, fixtures, fixturesGroup };
}
function allWallPieces(parts) {
@@ -1489,6 +1787,7 @@ window.HouseModel = (() => {
kg: u.floorSpecs.totalKg + u.roof.specs.totalKg + u.walls.reduce((a, w) => a + w.pieces.reduce((b, p) => b + p.specs.totalKg, 0), 0)
}));
if (parts.interior.length) weights.push({ label: 'Binnenwanden', element: 'BW', kg: parts.interior.reduce((a, p) => a + p.specs.totalKg, 0) });
if (parts.posts.count) weights.push({ label: 'Palen overkapping', element: 'PAAL', kg: parts.posts.kg });
const isolatie = parts.units.map(u => ({
label: `Unit ${u.id}`, element: u.id,
@@ -1568,6 +1867,10 @@ window.HouseModel = (() => {
return { unit: u.id, name: rb.profile.name, kgM: rb.profile.kgM, lengthM: rb.length / 1000, kg: rb.steelKg, fillM3: rb.fillM3, fillKg: rb.fillKg,
util: rb.util, defl: rb.defl, deflMax: rb.deflMax, ok: rb.ok, below: rb.below, loadKNm: rb.loadKNm };
}),
posts: parts.posts.count ? {
count: parts.posts.count, size: parts.posts.size, wood: (PostModel.WOODS[parts.posts.wood] || {}).label || '',
lengthM: parts.posts.lengthMM / 1000, m3: parts.posts.m3, kg: parts.posts.kg
} : null,
roofEdge: parts.roofEdge ? {
fasciaLengthM: parts.roofEdge.fasciaLengthMM / 1000,
fasciaHeight: parts.roofEdge.fasciaHeight,
@@ -1659,6 +1962,7 @@ window.HouseModel = (() => {
if (!u.roof) { u.roof = RoofModel.createDefaultRoof(u.unit.floor); delete u.roof.fascia; }
if (u.roof.fascia === undefined) u.roof.fascia = oldRoof.edge !== 'daktrim';
if (!RoofModel.COVERINGS[u.roof.covering]) u.roof.covering = 'dakleer';
u.roof.extend = normalizeExtend(u.roof.extend);
u.lighting = { walls: (u.lighting && u.lighting.walls) || {} };
u.facade = { walls: (u.facade && u.facade.walls) || {} };
if (!hadFacade) Object.entries(u.unit.walls).forEach(([side, ws]) => {
@@ -1672,7 +1976,7 @@ window.HouseModel = (() => {
ALIGN_LABELS, SIDE_LABELS, DEFAULT_INTERIOR_THICKNESS, FACADE_TYPES, facadeFor, facadeMaterials,
clone, createEmptyProject, createDefaultSettings, createUnitEntry, applyGlobalSettings, nextUnitId, MODELS, createDefaultState, unitRects, computeSeams, overlappingPairs,
interiorWallDefaults, setThickness, thicknessOf, interiorLayout, ensureSeamWalls, syncSeamWalls, missingSeamWalls,
houseRoofShape, houseRoofFits, setHouseRoof, coverExtensions,
houseRoofShape, houseRoofFits, setHouseRoof, coverExtensions, EXTEND_MAX,
computeParts, allWallPieces, computeTotals, computeDimensions, dimensionRows, TRANSPORT, TRANSPORT_CLASSES, computeTransport, validateState, fromUnitDesign, normalize
};
})();