From 9a00c8e2f22738147e46fcfb8fd93c28166ea3ab Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 10 Jul 2026 18:46:40 +0200 Subject: [PATCH] =?UTF-8?q?Dach=202D:=20Ansichts-Poch=C3=A9=20der=20Dachau?= =?UTF-8?q?fsicht=20aus=20der=20Eindeckungs-Schicht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das Dach liegt über der Schnittebene und wird von oben gesehen → das Pick- Polygon der Traufe-Fläche trägt jetzt die Ansichts-Schraffur (viewHatchId) der ÄUSSERSTEN Dachtyp-Schicht (Eindeckung), analog dem Decken-Poché in der Aufsicht. Ohne Dachtyp/viewHatchId weiterhin weiss. +1 Test. 685/685 grün. --- src/plan/generatePlan.roof.test.ts | 27 +++++++++++++++++++++++++++ src/plan/generatePlan.ts | 30 ++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/plan/generatePlan.roof.test.ts b/src/plan/generatePlan.roof.test.ts index f05f1a0..e251a41 100644 --- a/src/plan/generatePlan.roof.test.ts +++ b/src/plan/generatePlan.roof.test.ts @@ -129,6 +129,33 @@ describe("generatePlan — Dächer (Dachaufsicht)", () => { expect(linesOf(withoutColor.primitives, "roof-eaves")[0].color).toBe("#8a4b32"); }); + it("Dachaufsicht-Poché: viewHatchId der äussersten Dachtyp-Schicht schraffiert das Pick-Polygon", () => { + const p: Project = { + ...baseProject([roof("R9", "sattel", { roofTypeId: "rt1" })]), + hatches: [ + { id: "diag", name: "Diagonal", pattern: "diagonal", scale: 1, angle: 45, color: "#1a1a1a" }, + ], + components: [ + { id: "tile", name: "Ziegel", color: "#a03a2a", hatchId: "", viewHatchId: "diag", joinPriority: 1 }, + ], + roofTypes: [{ id: "rt1", name: "Dach", layers: [{ componentId: "tile", thickness: 0.05 }] }], + }; + const polys = generatePlan(p, "eg", visible).primitives.filter( + (pr): pr is Extract => + pr.kind === "polygon" && (pr as { roofId?: string }).roofId === "R9", + ); + expect(polys.length).toBe(1); + expect(polys[0].hatch.pattern).toBe("diagonal"); + + // Ohne Dachtyp: keine Schraffur (Pattern "none"), Pick-Polygon bleibt. + const plain = generatePlan(baseProject([roof("R10", "sattel")]), "eg", visible).primitives.filter( + (pr): pr is Extract => + pr.kind === "polygon" && (pr as { roofId?: string }).roofId === "R10", + ); + expect(plain.length).toBe(1); + expect(plain[0].hatch.pattern).toBe("none"); + }); + it("Rückwärtskompatibilität: ohne Dächer (bzw. Dach auf anderem Geschoss) bleiben die Primitive unverändert", () => { const planNoField = generatePlan(baseProject(undefined), "eg", visible); const planEmpty = generatePlan(baseProject([]), "eg", visible); diff --git a/src/plan/generatePlan.ts b/src/plan/generatePlan.ts index 8ebeb58..92d3ad3 100644 --- a/src/plan/generatePlan.ts +++ b/src/plan/generatePlan.ts @@ -38,6 +38,7 @@ import { glazingPanesOf, openingLabel, openingsOfWall, + roofLayers, roomsOfFloor, sashesOfWindowType, stairLabel, @@ -763,7 +764,7 @@ export function generatePlan( for (const roof of roofs) { const greyed = categoryDisplay(roof.categoryCode).greyed; const category = catByCode.get(roof.categoryCode); - addRoof(primitives, roof, greyed, category); + addRoof(primitives, project, roof, greyed, category); } // Räume (SIA-416-Flächen) dieses Geschosses: transluzente Farbfüllung + Stempel @@ -2567,6 +2568,7 @@ function addCeilingPoche( */ function addRoof( out: Primitive[], + project: Project, roof: Roof, greyed: boolean, category: LayerCategory | undefined, @@ -2574,10 +2576,26 @@ function addRoof( const stroke = roof.color ?? category?.color ?? POCHE_STROKE; const geo = roofGeometry(roof, 0); - // Unsichtbares Pick-Polygon über der Traufe-Fläche — macht das Dach über die - // ganze Fläche per Links-Klick selektierbar (die Dachlinien allein wären - // schwer exakt zu treffen). fill/stroke "none" ⇒ zeichnet nichts; PlanView - // testet nur die `pts` (Punkt-in-Polygon), analog Öffnung/Decke. + // Ansichts-Poché der Dachaufsicht: das Dach liegt ÜBER der Schnittebene und + // wird von oben gesehen (nicht geschnitten) → Ansichts-Schraffur + // (viewHatchId) der ÄUSSERSTEN Schicht (Eindeckung) des Dachtyps, analog dem + // Decken-Poché (Aufsicht = erste Lage). Ohne Dachtyp/viewHatchId keine + // Schraffur (weiss) — die Fläche bleibt trotzdem das Pick-Polygon. + let viewHatch = NO_HATCH; + const firstLayer = roofLayers(project, roof)[0]; + if (firstLayer?.componentId) { + try { + const comp = getComponent(project, firstLayer.componentId); + if (comp.viewHatchId) viewHatch = resolveHatch(project, comp.viewHatchId); + } catch { + viewHatch = NO_HATCH; + } + } + + // Pick-Polygon über der Traufe-Fläche (macht das Dach über die ganze Fläche + // per Links-Klick selektierbar) — trägt jetzt zusätzlich die Ansichts- + // Schraffur der Eindeckung (fill bleibt "none": keine Volltonfüllung, nur + // Musterlinien). PlanView testet die `pts` (Punkt-in-Polygon). if (geo.eaves.length >= 3) { out.push({ kind: "polygon", @@ -2585,7 +2603,7 @@ function addRoof( fill: "none", stroke: "none", strokeWidthMm: 0, - hatch: NO_HATCH, + hatch: viewHatch, greyed, roofId: roof.id, });