diff --git a/src/plan/toElevation.test.ts b/src/plan/toElevation.test.ts index 64a486f..972b850 100644 --- a/src/plan/toElevation.test.ts +++ b/src/plan/toElevation.test.ts @@ -398,6 +398,52 @@ describe("toElevation — Öffnungen: Verdeckung + Reveal-Schatten", () => { expect(symCount("drehkipp")).toBe(4); // Dreh (2) + Kipp (2) }); + it("Kämpfer-Zeilen (mullionRows): teilen das Glasfeld horizontal (Glas + Trennlinien)", () => { + const base = elevationProject({ overhang: false }); + const mk = (mullionRows: number): Project => + ({ + ...base, + windowTypes: [ + { + id: "wt", + name: "F", + kind: "fest", + wingCount: 1, + glazing: "einfach", + frameWidth: 0.06, + mullionRows, + 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 glassCount = (rows: number): number => { + const p = mk(rows); + const plan = generateElevationPlan(p, p.drawingLevels[1])!; + // Glasflächen = weiss/blau gefüllte Polygone mit Kontur, keine Öffnung/Rahmen. + return plan.primitives.filter( + (pr) => pr.kind === "polygon" && pr.fill === "#9fc2d9", + ).length; + }; + expect(glassCount(1)).toBe(1); // fest, 1 Feld + expect(glassCount(3)).toBe(3); // in 3 Zeilen geteilt + }); + 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 2ae0d6b..bf801bd 100644 --- a/src/plan/toElevation.ts +++ b/src/plan/toElevation.ts @@ -772,8 +772,20 @@ function collectOpenings(project: Project, frame: ElevationFrame): ProjectedOpen const gB = fa1 - gU; const gz0 = fz + gV; const gz1 = fieldTop - gV; + // Kämpfer-/Sprossen-Zeilen (mullionRows): das Glasfeld wird in `rows` + // horizontale Scheiben geteilt (analog zum 3D, s. toWalls3d), mit einer + // Trennlinie je Zeilengrenze über die Glasbreite. 1 = ungeteilt (bisher). + const rows = Math.max(1, Math.round(wt?.mullionRows ?? 1)); if (gB - gA > 1e-6 && gz1 - gz0 > 1e-6) { - pr.glass.push(rect(pr.pts, gA, gB, gz0, gz1)); + for (let r = 0; r < rows; r++) { + const rz0 = gz0 + ((gz1 - gz0) * r) / rows + (r > 0 ? gV / 2 : 0); + const rz1 = gz0 + ((gz1 - gz0) * (r + 1)) / rows - (r < rows - 1 ? gV / 2 : 0); + if (rz1 - rz0 > 1e-6) pr.glass.push(rect(pr.pts, gA, gB, rz0, rz1)); + } + for (let r = 1; r < rows; r++) { + const rz = gz0 + ((gz1 - gz0) * r) / rows; + pr.divisions.push([quadAt(pr.pts, gA, rz), quadAt(pr.pts, gB, rz)]); + } } // Trennlinie zum nächsten Feld (ausser am Rand). if (fa1 < 1 - fa - 1e-6) {