From 3d66dc89675baa9945d0a7bc2a61ef9e9cf4e3b6 Mon Sep 17 00:00:00 2001 From: Karim Date: Sun, 12 Jul 2026 18:07:09 +0200 Subject: [PATCH] =?UTF-8?q?Fenster-Grundriss:=20Rahmen-/Stulp-Bl=C3=B6cke?= =?UTF-8?q?=20=C3=BCber=20volle=20Rahmentiefe,=20plus=20Laibungs-Bl=C3=B6c?= =?UTF-8?q?ke=20an=20den=20Enden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Stulp-Marken an Flügelstössen waren als kleines, von der Rahmentiefe unabhängiges Quadrat gezeichnet statt als echter Profilquerschnitt über die ganze Rahmentiefe (aussen bis innen). Zusätzlich fehlten die entsprechenden Blendrahmen-Querschnittsblöcke an den beiden Laibungs-Enden komplett — jetzt zeigt jedes Fenster dort einen Block, unabhängig von der Flügelanzahl. --- src/geometry/opening.ts | 37 ++++++++++++---------- src/plan/generatePlan.windowFields.test.ts | 24 ++++++++------ 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/geometry/opening.ts b/src/geometry/opening.ts index 22a623e..c230733 100644 --- a/src/geometry/opening.ts +++ b/src/geometry/opening.ts @@ -410,33 +410,38 @@ export function windowSymbol( const fein = detail === "fein"; const axisWidth = Math.hypot(jambEnd.x - jambStart.x, jambEnd.y - jambStart.y); - const axisDir = normalize(sub(jambEnd, jambStart)); const segs = sashSegments(spec.sashes, axisWidth); // Öffenbarer Flügel sitzt `reveal` tiefer im Blendrahmen (nur „fein"). const reveal = fein ? Math.min(bandDepth * 0.3, fw * 0.7) : 0; - // Halbe Kantenlänge eines Stulp-Quadrats (SIA-Marke), an die Rahmenbreite gekoppelt. - const markHalf = Math.min(fw * 0.6, bandDepth * 0.4); + // Profil-Block über die VOLLE Rahmentiefe (aussen bis innen), Breite `2·halfU` + // entlang der Achse, zentriert bei `uCenter` (Achsabstand ab jambStart) — der + // Rahmenquerschnitt, wie er im Grundriss-Schnitt sichtbar ist (Nutzer: + // „Rahmen aussen als zwei Flächen gleichbreit wie das Fenster [tief]"). + const frameBlock = (uCenter: number, halfU: number): Vec2[] => { + const a = along(jambStart, jambEnd, uCenter - halfU); + const b = along(jambStart, jambEnd, uCenter + halfU); + return [ + add(a, scale(n, frameOuterN)), + add(b, scale(n, frameOuterN)), + add(b, scale(n, frameInnerN)), + add(a, scale(n, frameInnerN)), + ]; + }; + // Laibungs-Pfosten an beiden Enden (Blendrahmen-Querschnitt an der Wand- + // öffnung) — EIN Block je Seite, unabhängig vom Detailgrad. + meetingMarks.push(frameBlock(fw / 2, fw / 2), frameBlock(axisWidth - fw / 2, fw / 2)); // Flügel-Trennlinien (Mittelpfosten) an den inneren Segmentgrenzen + - // Stulp-Quadrate: mittel 1, fein 2 (die beiden zusammenstossenden Profile). + // Stulp-Block: mittel 1, fein 2 (die beiden zusammenstossenden Profile) — + // je über die volle Rahmentiefe. for (let i = 0; i < segs.length - 1; i++) { const uPos = segs[i].to; const onAxis = along(jambStart, jambEnd, uPos); mullionLines.push([add(onAxis, scale(n, frameOuterN)), add(onAxis, scale(n, frameInnerN))]); - const center = add(onAxis, scale(n, gMidBase)); - const square = (offU: number): Vec2[] => { - const c = add(center, scale(axisDir, offU)); - return [ - add(add(c, scale(axisDir, -markHalf)), scale(n, -markHalf)), - add(add(c, scale(axisDir, markHalf)), scale(n, -markHalf)), - add(add(c, scale(axisDir, markHalf)), scale(n, markHalf)), - add(add(c, scale(axisDir, -markHalf)), scale(n, markHalf)), - ]; - }; if (fein) { - meetingMarks.push(square(-markHalf), square(markHalf)); + meetingMarks.push(frameBlock(uPos - fw / 2, fw / 2), frameBlock(uPos + fw / 2, fw / 2)); } else { - meetingMarks.push(square(0)); + meetingMarks.push(frameBlock(uPos, fw / 2)); } } diff --git a/src/plan/generatePlan.windowFields.test.ts b/src/plan/generatePlan.windowFields.test.ts index 05db541..609d1a2 100644 --- a/src/plan/generatePlan.windowFields.test.ts +++ b/src/plan/generatePlan.windowFields.test.ts @@ -214,31 +214,37 @@ describe("generatePlan — Stulp-/Flügelstoss-Marken (SIA fig. 37/38, window-st ], }; - it("mittel: EIN Stulp-Quadrat (4 Linien) je Flügel-Junction", () => { + // Jeder Block ist ein Rechteck über die VOLLE Rahmentiefe (4 Linien) — je EIN + // Block an JEDEM Laibungs-Ende (Blendrahmen-Querschnitt, immer vorhanden) + // plus die Flügelstoss-Blöcke an inneren Junctions (mittel 1, fein 2). + + it("mittel: 2 Laibungs-Blöcke (Enden) + EIN Stulp-Block je Flügel-Junction", () => { const p = project({ ...baseWindow, typeId: "wt1" }, [twoSashType]); - expect(stulpLineCount(p, "mittel")).toBe(4); + // 2 Laibungsenden + 1 Junction = 3 Blöcke × 4 Linien. + expect(stulpLineCount(p, "mittel")).toBe(12); }); - it("fein: ZWEI Stulp-Quadrate (8 Linien) je Flügel-Junction", () => { + it("fein: 2 Laibungs-Blöcke (Enden) + ZWEI Stulp-Blöcke je Flügel-Junction", () => { const p = project({ ...baseWindow, typeId: "wt1" }, [twoSashType]); - expect(stulpLineCount(p, "fein")).toBe(8); + // 2 Laibungsenden + 2 Junction-Blöcke = 4 Blöcke × 4 Linien. + expect(stulpLineCount(p, "fein")).toBe(16); }); - it("grob: KEINE Stulp-Marken", () => { + it("grob: KEINE Rahmen-Blöcke", () => { const p = project({ ...baseWindow, typeId: "wt1" }, [twoSashType]); expect(stulpLineCount(p, "grob")).toBe(0); }); - it("Alt-Fall (kein typeId, nur wingCount) erzeugt KEINE Stulp-Marken — keine echte Flügeltabelle", () => { + it("Alt-Fall (kein typeId, nur wingCount) erzeugt KEINE Rahmen-Blöcke — keine echte Flügeltabelle", () => { const p = project({ ...baseWindow, wingCount: 3 }); expect(stulpLineCount(p, "mittel")).toBe(0); expect(stulpLineCount(p, "fein")).toBe(0); }); - it("einflügliges Fenster (keine Junction) erzeugt KEINE Stulp-Marken", () => { + it("einflügliges Fenster (keine Junction) zeigt trotzdem die 2 Laibungs-Blöcke an den Enden", () => { const p = project({ ...baseWindow, typeId: "wt1" }, [baseWindowType]); - expect(stulpLineCount(p, "mittel")).toBe(0); - expect(stulpLineCount(p, "fein")).toBe(0); + expect(stulpLineCount(p, "mittel")).toBe(8); + expect(stulpLineCount(p, "fein")).toBe(8); }); });