Fenster: Flügelstoss-Blöcke separat von Laibungs-Enden steuerbar (sashLine statt frameLine)

windowSymbol trug beide Blocktypen (Laibungs-Enden UND Flügelstoss-Marken)
im selben meetingMarks-Array, dadurch übersteuerte frameLine auch die Marke
zwischen den Flügeln — Nutzer wollte diese separat regelbar. Aufgeteilt in
jambMarks (Laibungs-Enden, zählt zu "Rahmen") und meetingMarks (Flügelstoss,
zählt zu "Fenster"/sashLine).
This commit is contained in:
2026-07-12 18:51:09 +02:00
parent a51fd212d3
commit 814e9a8656
3 changed files with 43 additions and 11 deletions
+18 -6
View File
@@ -256,9 +256,18 @@ export interface WindowSymbol {
* Flügel-Trennung ein kleines Rechteck-Polygon auf der Glasebene, das den * Flügel-Trennung ein kleines Rechteck-Polygon auf der Glasebene, das den
* Anschlag der zwei zusammenstossenden Flügelprofile darstellt. Bei „mittel" * Anschlag der zwei zusammenstossenden Flügelprofile darstellt. Bei „mittel"
* ein Quadrat je Stoss, bei „fein" zwei (die beiden Stulpprofile). „grob": * ein Quadrat je Stoss, bei „fein" zwei (die beiden Stulpprofile). „grob":
* leer. Der Aufrufer zeichnet sie als dünne Umriss-Rechtecke. * leer. Zählt zur "Fenster"-Linien-Kategorie (Flügel/Sprossen), NICHT zum
* Blendrahmen — separat von {@link jambMarks} steuerbar. Der Aufrufer
* zeichnet sie als dünne Umriss-Rechtecke.
*/ */
meetingMarks: Vec2[][]; meetingMarks: Vec2[][];
/**
* Blendrahmen-Querschnittsblöcke an den beiden Laibungs-Enden (ein Block je
* Seite, unabhängig von Flügelanzahl/Detailgrad) — Teil des Blendrahmens,
* zählt zur "Rahmen"-Linien-Kategorie (im Unterschied zu {@link meetingMarks},
* die an inneren Flügelstössen sitzen und zur "Fenster"-Kategorie zählen).
*/
jambMarks: Vec2[][];
} }
/** /**
@@ -401,11 +410,12 @@ export function windowSymbol(
const mullionLines: [Vec2, Vec2][] = []; const mullionLines: [Vec2, Vec2][] = [];
const sashFrames: Vec2[][] = []; const sashFrames: Vec2[][] = [];
const meetingMarks: Vec2[][] = []; const meetingMarks: Vec2[][] = [];
const jambMarks: Vec2[][] = [];
// grob (1:100): nur der Rahmenkasten + EINE Glaslinie über die ganze Öffnung. // grob (1:100): nur der Rahmenkasten + EINE Glaslinie über die ganze Öffnung.
if (detail === "grob") { if (detail === "grob") {
glassLines.push([add(jambStart, scale(n, gMidBase)), add(jambEnd, scale(n, gMidBase))]); glassLines.push([add(jambStart, scale(n, gMidBase)), add(jambEnd, scale(n, gMidBase))]);
return { frame, glassLines, mullionLines, sashFrames, sashOpeningLines: [], meetingMarks }; return { frame, glassLines, mullionLines, sashFrames, sashOpeningLines: [], meetingMarks, jambMarks };
} }
const fein = detail === "fein"; const fein = detail === "fein";
@@ -428,8 +438,10 @@ export function windowSymbol(
]; ];
}; };
// Laibungs-Pfosten an beiden Enden (Blendrahmen-Querschnitt an der Wand- // Laibungs-Pfosten an beiden Enden (Blendrahmen-Querschnitt an der Wand-
// öffnung) — EIN Block je Seite, unabhängig vom Detailgrad. // öffnung) — EIN Block je Seite, unabhängig vom Detailgrad. Teil des
meetingMarks.push(frameBlock(fw / 2, fw / 2), frameBlock(axisWidth - fw / 2, fw / 2)); // Blendrahmens (jambMarks), NICHT der Flügelstoss-Marken (meetingMarks) —
// separat steuerbar (Nutzer-Feedback).
jambMarks.push(frameBlock(fw / 2, fw / 2), frameBlock(axisWidth - fw / 2, fw / 2));
// Flügel-Trennlinien (Mittelpfosten) an den inneren Segmentgrenzen + // Flügel-Trennlinien (Mittelpfosten) an den inneren Segmentgrenzen +
// Stulp-Block: mittel 1, fein 2 (die beiden zusammenstossenden Profile) — // Stulp-Block: mittel 1, fein 2 (die beiden zusammenstossenden Profile) —
@@ -475,7 +487,7 @@ export function windowSymbol(
]); ]);
} }
} }
return { frame, glassLines, mullionLines, sashFrames, sashOpeningLines: [], meetingMarks }; return { frame, glassLines, mullionLines, sashFrames, sashOpeningLines: [], meetingMarks, jambMarks };
} }
// ── Alt-Pfad (kein Typ auflösbar) ──────────────────────────────────────── // ── Alt-Pfad (kein Typ auflösbar) ────────────────────────────────────────
@@ -506,5 +518,5 @@ export function windowSymbol(
mullionLines.push([add(onAxis, scale(n, pOuter)), add(onAxis, scale(n, pInner))]); mullionLines.push([add(onAxis, scale(n, pOuter)), add(onAxis, scale(n, pInner))]);
} }
} }
return { frame, glassLines, mullionLines, sashFrames: [], sashOpeningLines: [], meetingMarks: [] }; return { frame, glassLines, mullionLines, sashFrames: [], sashOpeningLines: [], meetingMarks: [], jambMarks: [] };
} }
+10 -2
View File
@@ -2304,13 +2304,21 @@ function addOpeningSymbol(
} }
} }
} }
// Laibungs-Blöcke an den Öffnungsenden (Blendrahmen-Querschnitt) — Teil des
// Blendrahmens, zählt zu "Rahmen".
for (const mark of sym.jambMarks) {
for (let i = 0; i < mark.length; i++) {
out.push({ kind: "line", a: mark[i], b: mark[(i + 1) % mark.length], cls: "window-stulp", weightMm: frameHairline, color: frameColor, greyed, openingId: o.id });
}
}
// Stulp-Mittelquadrat je Flügel-Junction (SIA-Signatur): mittel = EIN Quadrat, // Stulp-Mittelquadrat je Flügel-Junction (SIA-Signatur): mittel = EIN Quadrat,
// fein = ZWEI anliegende Rechtecke (die beiden Stulp-Stäbe der Flügel) — // fein = ZWEI anliegende Rechtecke (die beiden Stulp-Stäbe der Flügel) —
// bereits tiefenkorrekt (inset-/insetFace-bewusst) in windowSymbol berechnet. // bereits tiefenkorrekt (inset-/insetFace-bewusst) in windowSymbol berechnet.
// Zählt als "Rahmen" (Laibungs-/Stossblock des Blendrahmens). // Sitzt zwischen den Flügeln, zählt zu "Fenster" (separat vom Blendrahmen
// steuerbar — Nutzer-Feedback).
for (const mark of sym.meetingMarks) { for (const mark of sym.meetingMarks) {
for (let i = 0; i < mark.length; i++) { for (let i = 0; i < mark.length; i++) {
out.push({ kind: "line", a: mark[i], b: mark[(i + 1) % mark.length], cls: "window-stulp", weightMm: frameHairline, color: frameColor, greyed, openingId: o.id }); out.push({ kind: "line", a: mark[i], b: mark[(i + 1) % mark.length], cls: "window-stulp", weightMm: sashWeight, color: sashColor, greyed, openingId: o.id });
} }
} }
// Glaslinien (Anzahl je Detailgrad/Verglasung, s. glassCount). // Glaslinien (Anzahl je Detailgrad/Verglasung, s. glassCount).
+15 -3
View File
@@ -341,10 +341,12 @@ describe("generatePlan — Linien-Übersteuerung je Kategorie (Opening.frameLine
for (const l of stulp) expect(l.kind === "line" && l.weightMm).toBeCloseTo(0.02, 6); for (const l of stulp) expect(l.kind === "line" && l.weightMm).toBeCloseTo(0.02, 6);
}); });
it("frameLine übersteuert Farbe+Stärke der Stulp-/Laibungsblöcke (window-stulp)", () => { it("frameLine übersteuert Farbe+Stärke der Laibungs-Blöcke (window-stulp, einflügliges Fenster ohne Flügelstoss)", () => {
// Einflüglig -> nur jambMarks (Laibungs-Enden, "Rahmen"), keine
// Flügelstoss-Blöcke -> eindeutig zuordenbar.
const p = project( const p = project(
{ ...baseWindow, typeId: "wt1", frameLine: { color: "#ff0000", weight: 0.5 } }, { ...baseWindow, typeId: "wt1", frameLine: { color: "#ff0000", weight: 0.5 } },
[twoSashType], [baseWindowType],
); );
const stulp = linesOfClass(p, "window-stulp", "mittel"); const stulp = linesOfClass(p, "window-stulp", "mittel");
expect(stulp.length).toBeGreaterThan(0); expect(stulp.length).toBeGreaterThan(0);
@@ -354,7 +356,7 @@ describe("generatePlan — Linien-Übersteuerung je Kategorie (Opening.frameLine
} }
}); });
it("sashLine übersteuert Farbe+Stärke des Flügelrahmens (window-sash, fein)", () => { it("sashLine übersteuert Farbe+Stärke des Flügelrahmens (window-sash, fein) UND der Flügelstoss-Blöcke (window-stulp an der Junction) — NICHT die Laibungs-Enden", () => {
const p = project( const p = project(
{ ...baseWindow, typeId: "wt1", sashLine: { color: "#00ff00", weight: 0.3 } }, { ...baseWindow, typeId: "wt1", sashLine: { color: "#00ff00", weight: 0.3 } },
[twoSashType], [twoSashType],
@@ -365,6 +367,16 @@ describe("generatePlan — Linien-Übersteuerung je Kategorie (Opening.frameLine
expect(l.kind === "line" && l.color).toBe("#00ff00"); expect(l.kind === "line" && l.color).toBe("#00ff00");
expect(l.kind === "line" && l.weightMm).toBeCloseTo(0.3, 6); expect(l.kind === "line" && l.weightMm).toBeCloseTo(0.3, 6);
} }
// Öffnung 1.2 m breit ab x=1.0 -> Junction bei x=1.6 (Mitte), Laibungs-
// Enden bei x≈1.03/2.17 (fw/2 vom Rand). Nur die Junction-Blöcke (Mitte)
// reagieren auf sashLine; die Laibungs-Enden bleiben beim Default.
const stulp = linesOfClass(p, "window-stulp", "mittel");
const junction = stulp.filter((l) => l.kind === "line" && l.a.x > 1.3 && l.a.x < 1.9);
const jamb = stulp.filter((l) => l.kind === "line" && (l.a.x < 1.3 || l.a.x > 1.9));
expect(junction.length).toBeGreaterThan(0);
expect(jamb.length).toBeGreaterThan(0);
for (const l of junction) expect(l.kind === "line" && l.color).toBe("#00ff00");
for (const l of jamb) expect(l.kind === "line" && l.color).toBe("#1a1a1a");
}); });
it("sillLineStyle übersteuert Farbe+Stärke der Auf-/Untersicht-Linie (window-sill)", () => { it("sillLineStyle übersteuert Farbe+Stärke der Auf-/Untersicht-Linie (window-sill)", () => {