Laminaat leggen
by PhilQ
HTML
<svg id="floor" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="-50 -50 460.5 693.5" fill="none"></svg>
<div id="controls">
<div>
<div><label for="floor_w">Vloer (lengte):</label><input id="floor_w" type="number" value="593.5" step="0.1" min="1" /></div>
<div><label for="floor_h">Vloer (breedte):</label><input id="floor_h" type="number" value="360.5" step="0.1" min="1" /></div>
<div><label for="margin">Ruimte rondom:</label><input id="margin" type="number" value="1" step="0.1" min="0" /></div>
</div>
<div>
<div><label for="laminaat_w">Laminaat (lengte):</label><input id="laminaat_w" type="number" value="138" step="0.1" min="1" /></div>
<div><label for="laminaat_h">Laminaat (breedte):</label><input id="laminaat_h" type="number" value="19.3" step="0.1" min="1" /></div>
</div>
<div>
<div><label for="min_w">Minimale lengte:</label><input id="min_w" type="number" value="40" step="1" min="1" /></div>
<div><label for="min_h">Minimale breedte:</label><input id="min_h" type="number" value="6" step="1" min="1" /></div>
</div>
<div>
<input type="button" id="go" value="Go!" />
</div>
<div>
<select id="pick_pattern">
<option value="-">-</option>
</select>
<div class="left"><input id="show_widths" type="checkbox" /><label for="show_widths">Toon lengtes</label></div>
</div>
<div>
<select id="pick_room">
<option value="-">Kies een ruimte...</option>
<option value="483.5x200.5">Keuken</option>
<option value="593.5x360.5">Woonkamer</option>
<option value="310.5x360.5">Werkkamer</option>
<option value="406.5x200.5">Slaapkamer 2</option>
<option value="406.5x258.5">Slaapkamer 1</option>
</select>
</div>
</div>
CSS
body {
background-color: #eee;
}
svg {
position: fixed;
inset: 0;
}
svg g rect:first-child {
/* stroke: black; */
}
body:not(.show_widths) svg g text {
opacity: 0;
}
svg g text:hover,
svg g rect:hover + text {
opacity: 1 !important;
}
#controls {
position: fixed;
top: 0px;
height: auto;
left: 0px;
right: 0px;
z-index: 100;
padding: 20px;
background-color: #ccc;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}
#controls > div {
display: flex;
flex-direction: column;
gap: 5px;
}
#controls > div > div {
display: flex;
flex-direction: row;
gap: 5px;
justify-content: space-between;
}
#controls > div > div.left {
justify-content: flex-start;
}
#controls > div > div.right {
justify-content: flex-end;
}
input[type=button] {
height: 46px;
}
JavaScript
function laminaatLayout(
floorWidth = 0,
floorHeight = 0,
panelWidth = 0,
panelHeight = 0,
minimalWidth = 30,
minimalHeight = 6,
startShift = 0,
rowShifts = [],
) {
const pieces = [];
const wastedPieces = [];
let fullPanelsUsed = 0;
let curX = 0;
let curY = 0;
let remainder = 0;
let smallestEnd = null;
let smallestStart = null;
let firstPiece = panelWidth;
let repeatingRows = null;
while (curY < floorHeight) {
// Pick new panel
if (remainder === 0) {
fullPanelsUsed++;
remainder = panelWidth;
}
// Start of new row
if (curX === 0) {
let rowShift = 0;
if (rowShifts.length > 0) {
const idx = Math.round(curY / panelHeight) % rowShifts.length;
rowShift += rowShifts[idx] + startShift;
// Discard remainder if too short
if (remainder < panelWidth + rowShift) {
wastedPieces.push(remainder);
fullPanelsUsed++;
remainder = panelWidth;
} else {
rowShift = panelWidth + rowShift - remainder;
}
} else if (remainder === panelWidth) {
// Start of new cycle
rowShift += startShift;
}
if (rowShift < 0) {
// Trim beginning of start piece
wastedPieces.push(-rowShift);
remainder += rowShift;
}
}
if (curX + remainder >= floorWidth) {
// Overshoot or exactly-to-edge panel
const offcut = curX + remainder - floorWidth;
pieces.push({
x: curX,
y: curY,
width: remainder - offcut,
height: panelHeight,
});
if (smallestEnd === null) {
smallestEnd = remainder - offcut;
}
remainder = offcut;
curX = 0;
curY += panelHeight;
// Discard any invalid start offcuts
if (remainder < minimalWidth) {
if (repeatingRows === null) {
repeatingRows = curY /...