From f98c962ba339f071690b38c1520a4f2dab90f791 Mon Sep 17 00:00:00 2001 From: Karim Date: Sat, 11 Jul 2026 13:11:58 +0200 Subject: [PATCH] =?UTF-8?q?Schiebefenster-=C3=96ffnungssymbol=20(DIN-Pfeil?= =?UTF-8?q?)=20in=20Ansicht=20und=203D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schiebeflügel hatten bisher kein Öffnungssymbol (nur Dreh/Kipp/Drehkipp). DIN zeichnet für Schiebeelemente keinen Anschlag-Winkel, sondern einen Pfeil in Laufrichtung — ergänzt in der 2D-Ansicht (toElevation) und im 3D-fein (toWalls3d), Richtung aus der Griffseite. Test deckt Schiebe/Fest/Drehkipp ab. --- src/plan/toElevation.test.ts | 45 ++++++++++++++++++++++++++++++++++++ src/plan/toElevation.ts | 16 +++++++++++++ src/plan/toWalls3d.ts | 11 +++++++++ 3 files changed, 72 insertions(+) diff --git a/src/plan/toElevation.test.ts b/src/plan/toElevation.test.ts index 649722f..64a486f 100644 --- a/src/plan/toElevation.test.ts +++ b/src/plan/toElevation.test.ts @@ -353,6 +353,51 @@ describe("toElevation — Öffnungen: Verdeckung + Reveal-Schatten", () => { expect(symbols.length).toBeGreaterThan(0); }); + it("Öffnungssymbolik je Art: Schiebe=Pfeil (3 Linien), Fest=keine, Drehkipp=4", () => { + const base = elevationProject({ overhang: false }); + const mkProject = (kind: "schiebe" | "fest" | "drehkipp"): Project => + ({ + ...base, + windowTypes: [ + { + id: "wt", + name: "F", + kind, + wingCount: 1, + glazing: "zweifach", + frameWidth: 0.06, + defaultSillHeight: 0.9, + defaultWidth: 1, + defaultHeight: 1.2, + }, + ], + openings: [ + { + id: "op", + type: "opening", + hostWallId: "NEAR", + categoryCode: "21", + kind: "window", + typeId: "wt", + position: 1.5, + width: 1, + height: 1.2, + sillHeight: 0.9, + }, + ], + }) as unknown as Project; + const symCount = (kind: "schiebe" | "fest" | "drehkipp"): number => { + const p = mkProject(kind); + const plan = generateElevationPlan(p, p.drawingLevels[1])!; + return plan.primitives.filter( + (pr) => pr.kind === "line" && (pr as { cls?: string }).cls === "elevation-opening-symbol", + ).length; + }; + expect(symCount("schiebe")).toBe(3); // Pfeil: Schaft + 2 Spitzen + expect(symCount("fest")).toBe(0); // Festverglasung ohne Symbol + expect(symCount("drehkipp")).toBe(4); // Dreh (2) + Kipp (2) + }); + it("Reveal-Schatten: shadows=true erzeugt Öffnungs-Schattenbänder, false keine", () => { const project = openingProject(); const level = project.drawingLevels[1]; diff --git a/src/plan/toElevation.ts b/src/plan/toElevation.ts index e1ca6a9..2ae0d6b 100644 --- a/src/plan/toElevation.ts +++ b/src/plan/toElevation.ts @@ -799,6 +799,22 @@ function collectOpenings(project: Project, frame: ElevationFrame): ProjectedOpen [quadAt(pr.pts, gB, gz0), apex], ); } + if (kind === "schiebe") { + // Schiebeflügel: KEIN Dreieck (keine Bandseite), sondern ein Pfeil in + // Laufrichtung (horizontal) auf halber Glashöhe — die Griffseite + // (hingeSide) bestimmt die Pfeilrichtung. Klassische DIN-Konvention. + const zc = (gz0 + gz1) / 2; + const toRight = (sd.hingeSide ?? "left") === "left"; // Griff links ⇒ schiebt nach rechts + const tail = toRight ? gA : gB; + const head = toRight ? gB : gA; + const barb = (head - tail) * 0.22; // Pfeilspitzen-Rücksprung (Glasbreite-Anteil) + const headPt = quadAt(pr.pts, head, zc); + pr.symbol.push( + [quadAt(pr.pts, tail, zc), headPt], // Schaft + [quadAt(pr.pts, head - barb, gz0 + (gz1 - gz0) * 0.32), headPt], // obere Spitze + [quadAt(pr.pts, head - barb, gz0 + (gz1 - gz0) * 0.68), headPt], // untere Spitze + ); + } } // Fensterbank (Sims, VW): Bank + Tropfkante als zwei Bänder unter dem // Rahmen, seitlich leicht überstehend. Default AN (sillBoard fehlt), diff --git a/src/plan/toWalls3d.ts b/src/plan/toWalls3d.ts index c9f2f56..721b942 100644 --- a/src/plan/toWalls3d.ts +++ b/src/plan/toWalls3d.ts @@ -2298,6 +2298,17 @@ function frameMeshesForOpening( pushSymbol(gA, gbz, apexU, gtz); pushSymbol(gB, gbz, apexU, gtz); } + if (kind === "schiebe") { + // Schiebe: horizontaler Pfeil in Laufrichtung (Griff links ⇒ nach rechts). + const zc = (gbz + gtz) / 2; + const toRight = (s.hingeSide ?? "left") === "left"; + const tail = toRight ? gA : gB; + const head = toRight ? gB : gA; + const barb = (head - tail) * 0.22; + pushSymbol(tail, zc, head, zc); // Schaft + pushSymbol(head - barb, gbz + (gtz - gbz) * 0.32, head, zc); // obere Spitze + pushSymbol(head - barb, gbz + (gtz - gbz) * 0.68, head, zc); // untere Spitze + } } }