Ansicht-Fenster im VW-Detail: Flügelrahmen, Sims mit Tropfkante, exakte Symbole

Nutzer-Referenz: drei ablesbare Konturen (Blendrahmen → Flügelprofil → Glas)
je Flügel; Fensterbank als zwei seitlich überstehende Bänder (Bank +
Tropfkante, Default an, aus bei sillBoard keine/innen); die DIN-Öffnungs-
symbole starten/enden jetzt EXAKT auf den Glasfeld-Ecken (Dreh: Spitze am
Bandseiten-Mittel des Glasfelds, Kipp: Spitze oben Mitte — wie VW; vorher
sassen die Anker auf der Flügelspanne statt dem Glasfeld). 737/737 grün.
This commit is contained in:
2026-07-11 02:44:56 +02:00
parent a612ae55cd
commit ee9aeda01e
+72 -10
View File
@@ -423,7 +423,11 @@ interface ProjectedOpening {
glass: Array<Array<[number, number]>>; glass: Array<Array<[number, number]>>;
/** Teilungslinien (Pfosten/Kämpfer) über den Rahmen. */ /** Teilungslinien (Pfosten/Kämpfer) über den Rahmen. */
divisions: Array<[[number, number], [number, number]]>; divisions: Array<[[number, number], [number, number]]>;
/** Öffnungssymbol (DIN-Ansicht): gestrichelte Linien zum Bandseiten-Mittel. */ /** Flügelrahmen-Rechtecke (Kontur je öffenbarem/festem Flügel). */
sashRects: Array<Array<[number, number]>>;
/** Fensterbank (Sims): Bank + Tropfkante als zwei Bänder unter dem Rahmen. */
sill: Array<Array<[number, number]>>;
/** Öffnungssymbol (DIN-Ansicht): Linien exakt auf den Glasfeld-Ecken. */
symbol: Array<[[number, number], [number, number]]>; symbol: Array<[[number, number], [number, number]]>;
/** Tür (Blatt-Fläche statt Glas, ausser Glas-Blatt). */ /** Tür (Blatt-Fläche statt Glas, ausser Glas-Blatt). */
isDoor: boolean; isDoor: boolean;
@@ -476,7 +480,7 @@ function projectOpeningRect(
if (depth <= EPS) continue; if (depth <= EPS) continue;
// pts-Reihenfolge: [unten-von, unten-bis, oben-bis, oben-von] — Basis für // pts-Reihenfolge: [unten-von, unten-bis, oben-bis, oben-von] — Basis für
// die (fa, fz)-Interpolation der Flügel-/Glasfelder in collectOpenings. // die (fa, fz)-Interpolation der Flügel-/Glasfelder in collectOpenings.
return { pts, depth, glass: [], divisions: [], symbol: [], isDoor: false }; return { pts, depth, glass: [], divisions: [], sashRects: [], sill: [], symbol: [], isDoor: false };
} }
return null; return null;
} }
@@ -575,29 +579,57 @@ function collectOpenings(project: Project, frame: ElevationFrame): ProjectedOpen
pr.divisions.push([quadAt(pr.pts, fm, fz), quadAt(pr.pts, fm, fieldTop)]); pr.divisions.push([quadAt(pr.pts, fm, fz), quadAt(pr.pts, fm, fieldTop)]);
continue; continue;
} }
pr.glass.push(rect(pr.pts, fa0 + fa * 0.3, fa1 - fa * 0.3, fz, fieldTop)); // Flügelrahmen (Kontur) + Glas darin eingerückt (VW: Blendrahmen →
// Flügelprofil → Glas als drei ablesbare Konturen).
pr.sashRects.push(rect(pr.pts, fa0, fa1, fz, fieldTop));
const spw = Math.min(0.06, Math.max(0.03, fw)); // Flügelprofil (m)
const gU = spw / Math.max(w, 1e-6);
const gV = spw / Math.max(h, 1e-6);
const gA = fa0 + gU;
const gB = fa1 - gU;
const gz0 = fz + gV;
const gz1 = fieldTop - gV;
if (gB - gA > 1e-6 && gz1 - gz0 > 1e-6) {
pr.glass.push(rect(pr.pts, gA, gB, gz0, gz1));
}
// Trennlinie zum nächsten Feld (ausser am Rand). // Trennlinie zum nächsten Feld (ausser am Rand).
if (fa1 < 1 - fa - 1e-6) { if (fa1 < 1 - fa - 1e-6) {
pr.divisions.push([quadAt(pr.pts, fa1, fz), quadAt(pr.pts, fa1, fieldTop)]); pr.divisions.push([quadAt(pr.pts, fa1, fz), quadAt(pr.pts, fa1, fieldTop)]);
} }
// Öffnungssymbol (DIN-Ansicht, gestrichelt): Spitze am Bandseiten-Mittel. // Öffnungssymbol (DIN): Linien starten/enden EXAKT auf den Glasfeld-
// Ecken (Nutzer-Report: Anker waren daneben). Dreh: Spitze am
// Bandseiten-Mittel des Glasfelds; Kipp: Spitze oben Mitte (VW).
const kind = sd.opening ?? wt?.kind ?? "fest"; const kind = sd.opening ?? wt?.kind ?? "fest";
if (kind === "dreh" || kind === "drehkipp") { if (kind === "dreh" || kind === "drehkipp") {
const hingeLeft = (sd.hingeSide ?? "left") === "left"; const hingeLeft = (sd.hingeSide ?? "left") === "left";
const apex = quadAt(pr.pts, hingeLeft ? fa0 : fa1, (fz + fieldTop) / 2); const apex = quadAt(pr.pts, hingeLeft ? gA : gB, (gz0 + gz1) / 2);
const farU = hingeLeft ? gB : gA;
pr.symbol.push( pr.symbol.push(
[quadAt(pr.pts, hingeLeft ? fa1 : fa0, fz), apex], [quadAt(pr.pts, farU, gz0), apex],
[quadAt(pr.pts, hingeLeft ? fa1 : fa0, fieldTop), apex], [quadAt(pr.pts, farU, gz1), apex],
); );
} }
if (kind === "kipp" || kind === "drehkipp") { if (kind === "kipp" || kind === "drehkipp") {
const apex = quadAt(pr.pts, (fa0 + fa1) / 2, fz); const apex = quadAt(pr.pts, (gA + gB) / 2, gz1);
pr.symbol.push( pr.symbol.push(
[quadAt(pr.pts, fa0, fieldTop), apex], [quadAt(pr.pts, gA, gz0), apex],
[quadAt(pr.pts, fa1, fieldTop), apex], [quadAt(pr.pts, gB, gz0), apex],
); );
} }
} }
// Fensterbank (Sims, VW): Bank + Tropfkante als zwei Bänder unter dem
// Rahmen, seitlich leicht überstehend. Default AN (sillBoard fehlt),
// aus bei "keine"/"innen".
const sb = wt?.sillBoard;
if (sb !== "keine" && sb !== "innen") {
const ov1 = 0.02 / Math.max(w, 1e-6);
const ov2 = 0.05 / Math.max(w, 1e-6);
const h1 = 0.035 / Math.max(h, 1e-6);
const h2 = 0.025 / Math.max(h, 1e-6);
pr.sill.push(rect(pr.pts, -ov1, 1 + ov1, -h1, 0));
pr.sill.push(rect(pr.pts, -ov2, 1 + ov2, -(h1 + h2), -h1));
}
// Oberlicht: eigenes Glasfeld + Kämpferlinie. // Oberlicht: eigenes Glasfeld + Kämpferlinie.
if (fTr > 0) { if (fTr > 0) {
pr.glass.push(rect(pr.pts, fa, 1 - fa, fTr + fz / 2, 1 - fz)); pr.glass.push(rect(pr.pts, fa, 1 - fa, fTr + fz / 2, 1 - fz));
@@ -969,6 +1001,36 @@ export function generateElevationPlan(
hatch: NO_HATCH, hatch: NO_HATCH,
}, },
}); });
// Sims (Bank + Tropfkante) — weiss gefüllt (verdeckt die Fassade), Kontur.
for (const sRect of op.sill) {
items.push({
depth: d,
seq: seq++,
prim: {
kind: "polygon",
pts: toVec(sRect),
fill: mono ? "#ffffff" : FRAME_FILL,
stroke: EDGE_INK,
strokeWidthMm: EDGE_MM,
hatch: NO_HATCH,
},
});
}
// Flügelrahmen-Konturen (zwischen Blendrahmen und Glas).
for (const sRect of op.sashRects) {
items.push({
depth: d,
seq: seq++,
prim: {
kind: "polygon",
pts: toVec(sRect),
fill: "none",
stroke: EDGE_INK,
strokeWidthMm: EDGE_MM,
hatch: NO_HATCH,
},
});
}
for (const g of op.glass) { for (const g of op.glass) {
items.push({ items.push({
depth: d, depth: d,