v0.4.10: wandhoogtes gekoppeld, middenpunt (nok), binnenspots langs naadwanden
- WallModel: optioneel middenpunt (heightMid, topLine), bovenregels in verstek; stijlen, openingen, beplating, doek, latwerk, BOM, 2D en PDF volgen de knik - wand-editor: schakelaar "Middenpunt (nok)" + nokhoogte, links/rechts = goothoogte - UnitModel.syncWallHeights: tegenoverliggende wand gespiegeld, haakse wanden vlak op de hoekhoogtes; wallPieces met nok per stuk - LightingModel.computeIndoor: raster vanaf binnenzijde eigen naadwanddeel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9bd7faeaa6
commit
9126588df7
+46
-21
@@ -65,13 +65,21 @@ const WALL_TEMPLATE = `
|
||||
<div><input type="number" id="inp-wall-h-all" value="2400" step="50" min="500" max="5000"><span class="unit">mm</span></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label for="inp-wall-h-left">Hoogte links</label>
|
||||
<label for="inp-wall-h-left" id="lbl-wall-h-left">Hoogte links</label>
|
||||
<div><input type="number" id="inp-wall-h-left" value="2400" step="50" min="500" max="5000"><span class="unit">mm</span></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label for="inp-wall-h-right">Hoogte rechts</label>
|
||||
<label for="inp-wall-h-right" id="lbl-wall-h-right">Hoogte rechts</label>
|
||||
<div><input type="number" id="inp-wall-h-right" value="2400" step="50" min="500" max="5000"><span class="unit">mm</span></div>
|
||||
</div>
|
||||
<div class="control-group" title="Middenpunt van de bovenrand verhogen (nok): links en rechts worden dan goothoogtes.">
|
||||
<label for="inp-wall-peak">Middenpunt (nok)</label>
|
||||
<div><label class="ui-switch"><input type="checkbox" id="inp-wall-peak"><span></span></label></div>
|
||||
</div>
|
||||
<div class="control-group" id="grp-wall-h-mid" style="display:none">
|
||||
<label for="inp-wall-h-mid">Nokhoogte</label>
|
||||
<div><input type="number" id="inp-wall-h-mid" value="3400" step="50" min="500" max="8000"><span class="unit">mm</span></div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<h1><i class="bi bi-columns-gap"></i> Stijlen</h1>
|
||||
@@ -543,6 +551,8 @@ function mountWallEditor(container, callbacks) {
|
||||
heightAll: parseFloat(document.getElementById('inp-wall-h-all').value),
|
||||
heightLeft: parseFloat(document.getElementById('inp-wall-h-left').value),
|
||||
heightRight: parseFloat(document.getElementById('inp-wall-h-right').value),
|
||||
// Middenpunt (nok); null = rechte bovenrand.
|
||||
heightMid: document.getElementById('inp-wall-peak').checked ? parseFloat(document.getElementById('inp-wall-h-mid').value) : null,
|
||||
studWidth: parseFloat(document.getElementById('inp-stud-w').value),
|
||||
studDepth: parseFloat(document.getElementById('inp-stud-d').value),
|
||||
plateThick: parseFloat(document.getElementById('inp-plate-w').value),
|
||||
@@ -566,6 +576,9 @@ function mountWallEditor(container, callbacks) {
|
||||
document.getElementById('inp-wall-h-all').value = state.wall.heightAll;
|
||||
document.getElementById('inp-wall-h-left').value = state.wall.heightLeft;
|
||||
document.getElementById('inp-wall-h-right').value = state.wall.heightRight;
|
||||
document.getElementById('inp-wall-peak').checked = state.wall.heightMid > 0;
|
||||
document.getElementById('inp-wall-h-mid').value = state.wall.heightMid > 0 ? state.wall.heightMid
|
||||
: Math.max(state.wall.heightLeft, state.wall.heightRight) + 800;
|
||||
document.getElementById('inp-stud-w').value = state.wall.studWidth;
|
||||
document.getElementById('inp-stud-d').value = state.wall.studDepth;
|
||||
document.getElementById('inp-plate-w').value = state.wall.plateThick;
|
||||
@@ -702,24 +715,35 @@ function mountWallEditor(container, callbacks) {
|
||||
const hLeftInput = document.getElementById('inp-wall-h-left');
|
||||
const hRightInput = document.getElementById('inp-wall-h-right');
|
||||
|
||||
const hMidInput = document.getElementById('inp-wall-h-mid');
|
||||
const peakOn = document.getElementById('inp-wall-peak').checked;
|
||||
|
||||
let hAll = Math.round(parseFloat(hAllInput.value) || 2400);
|
||||
let hLeft = Math.round(parseFloat(hLeftInput.value) || 2400);
|
||||
let hRight = Math.round(parseFloat(hRightInput.value) || 2900);
|
||||
let hMid = Math.round(parseFloat(hMidInput.value) || 0);
|
||||
|
||||
if (hAll !== lastBaseHeight) {
|
||||
const delta = hAll - lastBaseHeight;
|
||||
hLeft = Math.max(500, hLeft + delta);
|
||||
hRight = Math.max(500, hRight + delta);
|
||||
hMid = Math.max(500, hMid + delta);
|
||||
hLeftInput.value = hLeft;
|
||||
hRightInput.value = hRight;
|
||||
hMidInput.value = hMid;
|
||||
lastBaseHeight = hAll;
|
||||
}
|
||||
// Met nok: links/rechts zijn goothoogtes.
|
||||
document.getElementById('grp-wall-h-mid').style.display = peakOn ? '' : 'none';
|
||||
document.getElementById('lbl-wall-h-left').textContent = peakOn ? 'Goothoogte links' : 'Hoogte links';
|
||||
document.getElementById('lbl-wall-h-right').textContent = peakOn ? 'Goothoogte rechts' : 'Hoogte rechts';
|
||||
|
||||
// Afronding/fallbacks + ankerresolutie van de openingen: WallModel.
|
||||
CONFIG = WallModel.buildConfig({
|
||||
width: wallWidth,
|
||||
heightLeft: hLeft,
|
||||
heightRight: hRight,
|
||||
heightMid: peakOn ? hMid : null,
|
||||
studWidth: parseFloat(document.getElementById('inp-stud-w').value),
|
||||
studDepth: parseFloat(document.getElementById('inp-stud-d').value),
|
||||
plateThick: parseFloat(document.getElementById('inp-plate-w').value),
|
||||
@@ -827,10 +851,8 @@ function mountWallEditor(container, callbacks) {
|
||||
const hLeftMM = CONFIG.wallHeightLeft;
|
||||
const hRightMM = CONFIG.wallHeightRight;
|
||||
const halfWMM = wallWidthMM / 2;
|
||||
const slopeMM = (hRightMM - hLeftMM) / wallWidthMM;
|
||||
const topYAtMM = (x) => hLeftMM + (x + halfWMM) * slopeMM;
|
||||
const vertOffsetMM = CONFIG.plateThick * Math.sqrt(1 + slopeMM * slopeMM);
|
||||
const maxRoofMM = Math.max(hLeftMM, hRightMM);
|
||||
const tl = WallModel.topLine(CONFIG);
|
||||
const maxRoofMM = tl.maxY;
|
||||
|
||||
const scale = Math.min(w / (wallWidthMM * 1.3), h / (maxRoofMM * 1.3));
|
||||
const centerX = w / 2;
|
||||
@@ -882,8 +904,7 @@ function mountWallEditor(container, callbacks) {
|
||||
ctxFront.beginPath();
|
||||
ctxFront.moveTo(toCanvasX(-halfWMM), toCanvasY(0));
|
||||
ctxFront.lineTo(toCanvasX(halfWMM), toCanvasY(0));
|
||||
ctxFront.lineTo(toCanvasX(halfWMM), toCanvasY(hRightMM));
|
||||
ctxFront.lineTo(toCanvasX(-halfWMM), toCanvasY(hLeftMM));
|
||||
tl.pts.slice().reverse().forEach(p => ctxFront.lineTo(toCanvasX(p.x), toCanvasY(p.y)));
|
||||
ctxFront.closePath();
|
||||
ctxFront.stroke();
|
||||
|
||||
@@ -898,14 +919,13 @@ function mountWallEditor(container, callbacks) {
|
||||
ctxFront.fill();
|
||||
ctxFront.stroke();
|
||||
|
||||
ctxFront.beginPath();
|
||||
ctxFront.moveTo(toCanvasX(-halfWMM), toCanvasY(hLeftMM));
|
||||
ctxFront.lineTo(toCanvasX(halfWMM), toCanvasY(hRightMM));
|
||||
ctxFront.lineTo(toCanvasX(halfWMM), toCanvasY(hRightMM - vertOffsetMM));
|
||||
ctxFront.lineTo(toCanvasX(-halfWMM), toCanvasY(hLeftMM - vertOffsetMM));
|
||||
ctxFront.closePath();
|
||||
ctxFront.fill();
|
||||
ctxFront.stroke();
|
||||
WallModel.topPlateOutlines(tl).forEach(pts => {
|
||||
ctxFront.beginPath();
|
||||
pts.forEach((p, i) => (i ? ctxFront.lineTo : ctxFront.moveTo).call(ctxFront, toCanvasX(p.x), toCanvasY(p.y)));
|
||||
ctxFront.closePath();
|
||||
ctxFront.fill();
|
||||
ctxFront.stroke();
|
||||
});
|
||||
|
||||
// X/Y-assen (lijn + label), altijd binnen het canvas gehouden zodat er
|
||||
// niks buiten het zichtbare venster valt. Stijl: core/canvas-labels.js.
|
||||
@@ -920,20 +940,25 @@ function mountWallEditor(container, callbacks) {
|
||||
const yAxisRightX = Math.min(w - 10, toCanvasX(halfWMM) + 14);
|
||||
drawCanvasDim(ctxFront, yAxisRightX, toCanvasY(0), yAxisRightX, toCanvasY(hRightMM),
|
||||
'Y: ' + Math.round(hRightMM) + 'mm', Math.min(w - 4, yAxisRightX + 12), (toCanvasY(0) + toCanvasY(hRightMM)) / 2);
|
||||
if (tl.peak) {
|
||||
const pxC = toCanvasX(tl.peak.x);
|
||||
ctxFront.fillStyle = '#9ab';
|
||||
ctxFront.font = '11px sans-serif';
|
||||
ctxFront.textAlign = 'center';
|
||||
ctxFront.fillText('nok ' + Math.round(tl.peak.y) + 'mm', pxC, Math.max(12, toCanvasY(tl.peak.y) - 6));
|
||||
ctxFront.textAlign = 'start';
|
||||
}
|
||||
|
||||
const drawStudFront = (xMM, isJack) => {
|
||||
const xLeft = xMM - CONFIG.studWidth / 2;
|
||||
const xRight = xMM + CONFIG.studWidth / 2;
|
||||
const yTopLeft = topYAtMM(xLeft) - vertOffsetMM;
|
||||
const yTopRight = topYAtMM(xRight) - vertOffsetMM;
|
||||
const yBot = CONFIG.plateThick;
|
||||
ctxFront.fillStyle = isJack ? '#a37c45' : '#c29b62';
|
||||
ctxFront.strokeStyle = '#8a6836';
|
||||
ctxFront.beginPath();
|
||||
ctxFront.moveTo(toCanvasX(xLeft), toCanvasY(yBot));
|
||||
ctxFront.lineTo(toCanvasX(xRight), toCanvasY(yBot));
|
||||
ctxFront.lineTo(toCanvasX(xRight), toCanvasY(yTopRight));
|
||||
ctxFront.lineTo(toCanvasX(xLeft), toCanvasY(yTopLeft));
|
||||
tl.underPts(xLeft, xRight).reverse().forEach(p => ctxFront.lineTo(toCanvasX(p.x), toCanvasY(p.y)));
|
||||
ctxFront.closePath();
|
||||
ctxFront.fill();
|
||||
ctxFront.stroke();
|
||||
@@ -1128,7 +1153,7 @@ function mountWallEditor(container, callbacks) {
|
||||
const buitenThick = CONFIG.sheathingThick;
|
||||
const spaanThick = CONFIG.sheathingThick;
|
||||
const totalDepth = buitenThick + CONFIG.studDepth + spaanThick;
|
||||
const maxH = Math.max(CONFIG.wallHeightLeft, CONFIG.wallHeightRight);
|
||||
const maxH = WallModel.topLine(CONFIG).maxY;
|
||||
|
||||
const scale = Math.min((w * 0.45) / totalDepth, (h * 0.8) / maxH);
|
||||
const originX = w / 2 - (totalDepth * scale) / 2;
|
||||
|
||||
Reference in New Issue
Block a user