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
@@ -39,14 +39,16 @@ window.LightingModel = (() => {
|
||||
calculateSegmentPositions(len, min, max).forEach(p => out.push({ x: A.x + (B.x - A.x) * p / len, z: A.z + (B.z - A.z) * p / len }));
|
||||
}
|
||||
|
||||
function enforceMinSpacing(positions, min) {
|
||||
// Te dicht bij elkaar: samen naar het midden, tenzij daar een opening zit (dan vervalt de tweede).
|
||||
function enforceMinSpacing(positions, min, blockedAt) {
|
||||
let pts = positions.slice().sort((a, b) => a - b);
|
||||
let changed = true;
|
||||
while (changed && pts.length > 1) {
|
||||
changed = false;
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
if (pts[i + 1] - pts[i] < min - 0.5) {
|
||||
pts.splice(i, 2, (pts[i] + pts[i + 1]) / 2);
|
||||
const mid = (pts[i] + pts[i + 1]) / 2;
|
||||
pts.splice(i, 2, blockedAt && blockedAt(mid) ? pts[i] : mid);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
@@ -55,6 +57,27 @@ window.LightingModel = (() => {
|
||||
return pts;
|
||||
}
|
||||
|
||||
// Naast openingen: op openingOffset van elke opening; is de tussenruimte daarvoor te klein, dan één lamp in
|
||||
// het midden ervan (of geen, als die tegen de kozijnen zou hangen). Posities in u (mm vanaf de linkerkant).
|
||||
function besideOpenings(cfg, s) {
|
||||
const W = cfg.wallWidth, off = s.openingOffset, clear = cfg.studWidth + 150;
|
||||
const ops = cfg.openings.map(op => [op.x, op.x + op.w]).sort((p, q) => p[0] - q[0]);
|
||||
const out = [];
|
||||
ops.forEach(([x0, x1], i) => {
|
||||
const next = ops[i + 1];
|
||||
if (i === 0) { const u = Math.max(x0 - off, s.endOffset); if (x0 - u >= clear) out.push(u); }
|
||||
if (next) {
|
||||
const gap = next[0] - x1;
|
||||
if (gap >= 2 * off + 1000) out.push(x1 + off, next[0] - off);
|
||||
else if (gap >= 2 * clear) out.push((x1 + next[0]) / 2);
|
||||
} else {
|
||||
const u = Math.min(x1 + off, W - s.endOffset);
|
||||
if (u - x1 >= clear) out.push(u);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function freeIntervals(lo, hi, blocked) {
|
||||
const free = [];
|
||||
let cursor = lo;
|
||||
@@ -113,7 +136,7 @@ window.LightingModel = (() => {
|
||||
};
|
||||
if (s.mode === 'gevel') {
|
||||
runs.forEach(run => {
|
||||
const blocked = [];
|
||||
const blocked = [], openings = [];
|
||||
const pos = [];
|
||||
// Delen achter een andere wand (bijv. een stuk dat de naad in loopt) zijn geen zichtbare gevel.
|
||||
segs.filter(o => !run.segs.includes(o)).forEach(o => {
|
||||
@@ -126,26 +149,29 @@ window.LightingModel = (() => {
|
||||
const cfg = sg.piece.config, sW = cfg.studWidth;
|
||||
const low = Math.min(cfg.wallHeightLeft, cfg.wallHeightRight) < H + 100;
|
||||
if (sg.override === 'geen' || low) { blocked.push([sg.s0, sg.s1]); return; }
|
||||
if (sg.override === 'openingen') {
|
||||
blocked.push([sg.s0, sg.s1]);
|
||||
cfg.openings.forEach(op => [op.x - s.openingOffset, op.x + op.w + s.openingOffset]
|
||||
.filter(u => u >= 0 && u <= cfg.wallWidth).forEach(u => pos.push(uToS(sg, u))));
|
||||
return;
|
||||
}
|
||||
const own = [];
|
||||
cfg.openings.filter(op => op.y - sW <= H && WallModel.openingTopMax(op) + sW >= H).forEach(op => {
|
||||
const a = uToS(sg, op.x - sW), b = uToS(sg, op.x + op.w + sW);
|
||||
blocked.push([Math.min(a, b), Math.max(a, b)]);
|
||||
own.push([Math.min(a, b), Math.max(a, b)]);
|
||||
});
|
||||
openings.push(...own);
|
||||
if (sg.override === 'openingen') {
|
||||
blocked.push([sg.s0, sg.s1]);
|
||||
besideOpenings(cfg, s).forEach(u => pos.push(uToS(sg, u)));
|
||||
return;
|
||||
}
|
||||
blocked.push(...own);
|
||||
});
|
||||
// Automatisch: per vrij wandstuk gelijk verdeeld, midden in gelijke delen van ca. lampMax (één lamp
|
||||
// midden tussen twee ramen), niet tegen de kozijnen of de hoek aan.
|
||||
freeIntervals(run.s0, run.s1, blocked).forEach(([a, b]) => {
|
||||
if (b - a < s.minWallWidth) return;
|
||||
const first = a + s.endOffset, last = b - s.endOffset;
|
||||
pos.push(first);
|
||||
calculateSegmentPositions(last - first, s.lampMin, s.lampMax).forEach(p => pos.push(first + p));
|
||||
pos.push(last);
|
||||
const k = Math.max(1, Math.round((b - a) / s.lampMax));
|
||||
for (let i = 0; i < k; i++) pos.push(a + (b - a) * (i + 0.5) / k);
|
||||
});
|
||||
// Min. h.o.h. geldt over alle lampen van de gevel samen (ook overrides en de grens tussen wanden).
|
||||
enforceMinSpacing(pos, s.lampMin).forEach(v => addLamp(run, v));
|
||||
const inOpening = (v) => openings.some(([a, b]) => v > a && v < b);
|
||||
enforceMinSpacing(pos, s.lampMin, inOpening).filter(v => !inOpening(v)).forEach(v => addLamp(run, v));
|
||||
});
|
||||
units.forEach(u => u.walls.forEach(w => w.pieces.forEach(p => p.lamps.sort((a, b) => a.u - b.u))));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user