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:
co-authored by
Claude Opus 5.5
parent
b137a458d4
commit
86fcd57b96
+44
-4
@@ -327,6 +327,7 @@ function mountHouseEditor(container, callbacks) {
|
||||
const roofMaterials = RoofModel.createMaterials();
|
||||
const lightingMaterials = LightingModel.createMaterials();
|
||||
const sandwichMaterials = SandwichModel.createMaterials();
|
||||
const postMaterials = PostModel.createMaterials();
|
||||
const elektraMaterials = ElektraModel.createMaterials();
|
||||
let houseGroup = null;
|
||||
let cameraFitted = CameraMemory.restore(overviewCamKey, camera, controls);
|
||||
@@ -975,6 +976,12 @@ function mountHouseEditor(container, callbacks) {
|
||||
ctx.beginPath(); ctx.arc(q.x, q.y, 3, 0, Math.PI * 2); ctx.fill();
|
||||
});
|
||||
}
|
||||
ctx.setLineDash([]);
|
||||
ctx.fillStyle = '#a07c54'; ctx.strokeStyle = '#e8d3b0'; ctx.lineWidth = 1;
|
||||
parts.posts.posts.forEach(pp => {
|
||||
const a = toPx(pp.x - pp.size / 2, pp.z - pp.size / 2), sz = pp.size * scale;
|
||||
ctx.fillRect(a.x, a.y, sz, sz); ctx.strokeRect(a.x, a.y, sz, sz);
|
||||
});
|
||||
ctx.restore();
|
||||
|
||||
ctx.strokeStyle = '#ffe9a8'; ctx.lineWidth = 1.5;
|
||||
@@ -1238,6 +1245,12 @@ function mountHouseEditor(container, callbacks) {
|
||||
UI.range('hs-fascia-h', 'Hoogte', r.fasciaHeight, { min: 150, max: 500, step: 10 }) +
|
||||
sel('hs-fascia-color', 'Kleur', Object.fromEntries(Object.entries(RoofModel.FASCIA_COLORS).map(([k, c]) => [k, c.label])), r.fasciaColor) },
|
||||
{ label: 'Daktrim', html: num('hs-trim-h', 'Hoogte', r.trimHeight, { step: 5, hint: 'Bij units zonder boeidelen' }) },
|
||||
{ label: 'Palen overkapping', html:
|
||||
toggle('hs-posts', 'Palen', r.posts.enabled, { hint: `Onder een dak dat minstens de min. uitkraging buiten de vloer steekt (veranda); per unit: Overkapping. Nu ${parts ? parts.posts.count : 0} st.` }) +
|
||||
(r.posts.enabled ? sel('hs-posts-wood', 'Hout', Object.fromEntries(Object.entries(PostModel.WOODS).map(([k, w]) => [k, w.label])), r.posts.wood) +
|
||||
UI.range('hs-posts-size', 'Maat', r.posts.size, { min: 100, max: 300, step: 10, hint: 'Vierkant, mm' }) +
|
||||
num('hs-posts-span', 'Max. h.o.h.', r.posts.maxSpan, { step: 100, hint: 'Grootste afstand tussen palen (of paal en gevel)' }) +
|
||||
num('hs-posts-min', 'Min. uitkraging', r.posts.minExt, { step: 100, hint: 'Kleinere overstekken krijgen geen palen' }) : '') },
|
||||
{ label: 'Daken per unit', html: `<div data-live="roof-list" class="rb-cols"></div>` });
|
||||
return groups;
|
||||
},
|
||||
@@ -1252,6 +1265,11 @@ function mountHouseEditor(container, callbacks) {
|
||||
UI.onRange('hs-fascia-h', v => { if (v > 0) r.fasciaHeight = v; }, after);
|
||||
UI.onSel('hs-fascia-color', v => { r.fasciaColor = v; }, rerender);
|
||||
UI.onNum('hs-trim-h', v => { r.trimHeight = v; }, after, pos);
|
||||
UI.onToggle('hs-posts', v => { r.posts.enabled = v; }, rerender);
|
||||
UI.onSel('hs-posts-wood', v => { r.posts.wood = v; }, rerender);
|
||||
UI.onRange('hs-posts-size', v => { if (v > 0) r.posts.size = v; }, after);
|
||||
UI.onNum('hs-posts-span', v => { if (v >= 1000) r.posts.maxSpan = v; }, after, pos);
|
||||
UI.onNum('hs-posts-min', v => { if (v >= 400) r.posts.minExt = v; }, after, pos);
|
||||
const list = () => {
|
||||
const el = body.querySelector('[data-live="roof-list"]');
|
||||
if (!el || !parts) return;
|
||||
@@ -1393,7 +1411,8 @@ function mountHouseEditor(container, callbacks) {
|
||||
groups.push({ label: 'Meterkast', html: num('he-mk-w', 'Breedte', e.meterkast.w, { step: 10 }) + num('he-mk-h', 'Hoogte', e.meterkast.h, { step: 10 }) +
|
||||
num('he-mk-d', 'Diepte', e.meterkast.d, { step: 10 }) + num('he-mk-y', 'Onderkant', e.meterkast.y, { step: 10, hint: 'Vanaf bovenkant vloer; zo dicht mogelijk bij de voordeur' }) });
|
||||
groups.push({ label: 'Punten', html: num('he-sw-h', 'Schakelaar', e.switchHeight, { step: 10, hint: 'Hart, vanaf bovenkant vloer' }) +
|
||||
num('he-wcd-h', 'Wcd', e.wcdHeight, { step: 10, hint: 'Hart, vanaf bovenkant vloer' }) + num('he-wcd-n', 'Wcd per unit', e.wcdPerUnit, { step: 1, hint: 'Dubbele wcd' }) });
|
||||
num('he-wcd-h', 'Wcd', e.wcdHeight, { step: 10, hint: 'Hart, vanaf bovenkant vloer' }) + num('he-wcd-n', 'Wcd per unit', e.wcdPerUnit, { step: 1, hint: 'Dubbele wcd' }) +
|
||||
sel('he-plate', 'Kleur', Object.fromEntries(Object.entries(ElektraModel.PLATE_COLORS).map(([k, c]) => [k, c.label])), e.plateColor || 'wit', { hint: 'Schakelaars, wcd\'s en vloerdozen' }) });
|
||||
groups.push({ label: 'Apparaten', html: Object.entries(ElektraModel.APPLIANCES).map(([k, A]) =>
|
||||
check(`he-app-${k}`, `${A.label} (${A.code})`, !!e.apparaten[k])).join('') });
|
||||
const nManual = Object.keys(e.place).length;
|
||||
@@ -1423,6 +1442,7 @@ function mountHouseEditor(container, callbacks) {
|
||||
UI.onNum('he-sw-h', v => { e.switchHeight = v; }, after, pos);
|
||||
UI.onNum('he-wcd-h', v => { e.wcdHeight = v; }, after, pos);
|
||||
UI.onNum('he-wcd-n', v => { e.wcdPerUnit = Math.max(0, Math.round(v)); }, after);
|
||||
UI.onSel('he-plate', v => { e.plateColor = v; }, rerender);
|
||||
UI.onNum('he-slack', v => { e.slack = v; }, after);
|
||||
UI.onNum('he-res', v => { e.reserve = v; }, after);
|
||||
UI.onNum('he-mk-res', v => { e.meterkastReserve = v; }, after);
|
||||
@@ -1860,6 +1880,9 @@ function mountHouseEditor(container, callbacks) {
|
||||
(pu && pu.roof.unitRoof.ridgeBeam || u.roof.steelRidge === false && ['zadel', 'wolf'].includes(shapeNow)
|
||||
? toggle('hp-steel-ridge', 'Stalen nokbalk', u.roof.steelRidge !== false, { hint: pu && pu.roof.unitRoof.ridgeBeam ? `${pu.roof.unitRoof.ridgeBeam.profile.name}, ${Math.round(pu.roof.unitRoof.ridgeBeam.steelKg)} kg; uit = verwijderen` : 'Uit = verwijderd' }) : '') +
|
||||
toggle('hp-roof-follow', 'Maat volgt vloer', u.roof.followFloor) });
|
||||
if (u.roof.followFloor) groups.push({ label: 'Overkapping', html:
|
||||
['f', 'r', 'b', 'l'].map(k => num('hp-ext-' + k, { f: 'Noord', r: 'Oost', b: 'Zuid', l: 'West' }[k], (u.roof.extend || {})[k] || 0,
|
||||
{ hint: k === 'f' ? `Dak verlengen voorbij de gevel (veranda), max. ${HouseModel.EXTEND_MAX} mm; palen volgen vanzelf` : '' })).join('') });
|
||||
if (!u.roof.followFloor) groups.push({ label: 'Dakmaat', html:
|
||||
num('hp-roof-w', 'Breedte', u.roof.deck.floor.width, { hint: 'West–oost' }) + num('hp-roof-d', 'Diepte', u.roof.deck.floor.depth, { hint: 'Noord–zuid' }) +
|
||||
num('hp-roof-ox', 'Versch. oost', u.roof.offsetX || 0) + num('hp-roof-oz', 'Versch. zuid', u.roof.offsetZ || 0) });
|
||||
@@ -1897,6 +1920,9 @@ function mountHouseEditor(container, callbacks) {
|
||||
UI.onToggle('hp-fascia', v => { u.roof.fascia = v; }, rerender);
|
||||
UI.onToggle('hp-steel-ridge', v => { u.roof.steelRidge = v; }, rerender);
|
||||
UI.onToggle('hp-roof-follow', v => { u.roof.followFloor = v; }, rerender);
|
||||
['f', 'r', 'b', 'l'].forEach(k => UI.onNum('hp-ext-' + k, v => {
|
||||
u.roof.extend = Object.assign({ f: 0, r: 0, b: 0, l: 0 }, u.roof.extend, { [k]: Math.max(0, Math.min(HouseModel.EXTEND_MAX, v)) });
|
||||
}, after));
|
||||
UI.onNum('hp-roof-w', v => { if (v >= 500) u.roof.deck.floor.width = v; }, after);
|
||||
UI.onNum('hp-roof-d', v => { if (v >= 500) u.roof.deck.floor.depth = v; }, after);
|
||||
UI.onNum('hp-roof-ox', v => { u.roof.offsetX = v; }, after);
|
||||
@@ -2124,6 +2150,12 @@ function mountHouseEditor(container, callbacks) {
|
||||
rows.push(['Boeideel-spots', e.spots, 'st.'], ['Gevellampen', totals.lamps.total, 'st.'], ['Binnenspots', totals.lamps.indoor, 'st.']);
|
||||
cards.push(UI.specCard('Dakrand & verlichting', 'lightbulb', rows));
|
||||
}
|
||||
if (totals.posts) {
|
||||
const pp = totals.posts;
|
||||
cards.push(UI.specCard('Overkapping', 'columns', [
|
||||
[`${pp.wood} palen ${pp.size}×${pp.size} mm`, pp.count, 'st.'],
|
||||
['Lengte totaal', f(pp.lengthM, 2), 'm'], ['Hout', f(pp.m3, 3), 'm³'], ['Gewicht', f(pp.kg), 'kg'], ['Paalvoeten', pp.count, 'st.']]));
|
||||
}
|
||||
// TIJDELIJK (verwijderen voor productie): zaagmachine-overzicht.
|
||||
const sw = totals.saw, cs = cost.saw;
|
||||
cards.push(UI.specCard('Zaagmachine (tijdelijk)', 'scissors', [
|
||||
@@ -2166,7 +2198,7 @@ function mountHouseEditor(container, callbacks) {
|
||||
const painted = state.settings.interiorPaint && BuildStages.shows(view3D.stage, 'afwerking');
|
||||
wallMaterials.spaanplaatMaterial.color.set(painted ? state.settings.interiorColor : SPAANPLAAT_COLOR);
|
||||
roofMaterials.ceilingMaterial.color.set(state.settings.ceilingColor || '#f4f3ef');
|
||||
parts = HouseModel.computeParts(state, { wallMaterials, floorMaterials, roofMaterials, lightingMaterials, sandwichMaterials, elektraMaterials, stage: view3D.stage, lampenVisible: view3D.lampen, elektraVisible: view3D.elektra });
|
||||
parts = HouseModel.computeParts(state, { wallMaterials, floorMaterials, roofMaterials, lightingMaterials, sandwichMaterials, postMaterials, elektraMaterials, stage: view3D.stage, lampenVisible: view3D.lampen, elektraVisible: view3D.elektra });
|
||||
reportDirty = true;
|
||||
|
||||
if (houseGroup) { scene.remove(houseGroup); disposeGroup(houseGroup); }
|
||||
@@ -2200,6 +2232,7 @@ function mountHouseEditor(container, callbacks) {
|
||||
houseGroup.add(u.roof.group);
|
||||
});
|
||||
if (parts.roofEdgeGroup) houseGroup.add(parts.roofEdgeGroup);
|
||||
if (parts.postsGroup) houseGroup.add(parts.postsGroup);
|
||||
}
|
||||
if (parts.sandwichGroup) houseGroup.add(parts.sandwichGroup);
|
||||
if (parts.lightingGroup && !section) houseGroup.add(parts.lightingGroup);
|
||||
@@ -2255,7 +2288,7 @@ function mountHouseEditor(container, callbacks) {
|
||||
renderNav(parts);
|
||||
}
|
||||
|
||||
const SIGN = { ahead: 4000, aside: 1800, poleH: 2200, poleW: 80, plateW: 1500, plateD: 30 };
|
||||
const SIGN = { ahead: 4000, aside: 1800, clear: 1200, poleH: 2200, poleW: 80, plateW: 1500, plateD: 30 };
|
||||
let signPos = null;
|
||||
let signMaterial = null;
|
||||
const signPoleMat = new THREE.MeshStandardMaterial({ color: 0x2b2b2b, roughness: 0.6, metalness: 0.3 });
|
||||
@@ -2284,7 +2317,14 @@ function mountHouseEditor(container, callbacks) {
|
||||
function buildLogoSign(groundY) {
|
||||
const { d, n } = frontDoor();
|
||||
const t = { x: n.z, z: -n.x };
|
||||
signPos = { x: d.x + n.x * SIGN.ahead + t.x * SIGN.aside, z: d.z + n.z * SIGN.ahead + t.z * SIGN.aside };
|
||||
// Voordeur in een binnenhoek: opzij kan het bord onder een andere unit vallen, dan verder opzij of de andere kant op.
|
||||
const at = (a) => ({ x: d.x + n.x * SIGN.ahead + t.x * a, z: d.z + n.z * SIGN.ahead + t.z * a });
|
||||
const free = (q) => !parts.rects.some(r => !r.error && q.x > r.x0 - SIGN.clear && q.x < r.x1 + SIGN.clear && q.z > r.z0 - SIGN.clear && q.z < r.z1 + SIGN.clear);
|
||||
signPos = at(SIGN.aside);
|
||||
for (let a = SIGN.aside; a <= 30000; a += 500) {
|
||||
const q = [at(a), at(-a)].find(free);
|
||||
if (q) { signPos = q; break; }
|
||||
}
|
||||
const toM = 0.001, plateH = SIGN.plateW / 3;
|
||||
const g = new THREE.Group();
|
||||
const pole = new THREE.Mesh(new THREE.BoxGeometry(SIGN.poleW * toM, SIGN.poleH * toM, SIGN.poleW * toM), signPoleMat);
|
||||
|
||||
Reference in New Issue
Block a user