From 64f61797d8cdf892981d9a14a84e97ed166ada7f Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 10 Jul 2026 00:35:26 +0200 Subject: [PATCH] =?UTF-8?q?Dach:=20Auswahl-Hervorhebung=20auch=20im=203D?= =?UTF-8?q?=20(Draht-Umriss=20der=20Dachfl=C3=A4chen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit selectionHighlightLines bekommt roofIds und emittiert die Kanten aller Dachflächen + Giebel (roofGeometry, Modell→World) als Akzent-Draht — ein selektiertes Dach leuchtet jetzt im wgpu-Viewport wie Wand/Decke. App reicht selectedRoofIds durch. +1 Test. tsc + vitest grün. --- src/App.tsx | 5 ++++- src/plan/toWalls3d.test.ts | 9 +++++++++ src/plan/toWalls3d.ts | 23 ++++++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5c10c18..d301df8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5984,7 +5984,8 @@ function Content({ selectedWallIds.length === 0 && selectedCeilingIds.length === 0 && selectedOpeningIds.length === 0 && - selectedStairIds.length === 0 + selectedStairIds.length === 0 && + selectedRoofIds.length === 0 ? null : selectionHighlightLines( project, @@ -5993,6 +5994,7 @@ function Content({ HIGHLIGHT_RGB, selectedOpeningIds, selectedStairIds, + selectedRoofIds, ), [ project, @@ -6000,6 +6002,7 @@ function Content({ selectedCeilingIds, selectedOpeningIds, selectedStairIds, + selectedRoofIds, ], ); diff --git a/src/plan/toWalls3d.test.ts b/src/plan/toWalls3d.test.ts index a067b26..011cb28 100644 --- a/src/plan/toWalls3d.test.ts +++ b/src/plan/toWalls3d.test.ts @@ -27,6 +27,15 @@ describe("selectionHighlightLines", () => { expect(lines.length).toBe(0); }); + it("ein Dach -> Draht-Kanten der Dachflächen/Giebel (nicht leer, mit Farbe)", () => { + // sampleProject enthält das Demo-Satteldach RF1. + const lines = selectionHighlightLines(sampleProject, [], [], RGB, [], [], ["RF1"]); + expect(lines.length).toBeGreaterThan(0); + // Farbe in jedem Vertex (erstes Vertex: [px,py,pz,r,g,b]). + expect(lines[3]).toBeCloseTo(RGB[0], 6); + expect(lines[5]).toBeCloseTo(RGB[2], 6); + }); + it("eine Wand ohne Öffnungen -> je Materiallage ein Quader, Kern-Band unter der Decke gespalten (5·12 Kanten)", () => { // W2 (EG, Ost) trägt keine Tür/kein Fenster im Sample-Projekt (nur W1/W3 // haben Öffnungen, s. sampleProject.ts) -> ein Achsen-Teilquader. Wandtyp diff --git a/src/plan/toWalls3d.ts b/src/plan/toWalls3d.ts index b5239fa..bc8c644 100644 --- a/src/plan/toWalls3d.ts +++ b/src/plan/toWalls3d.ts @@ -2254,12 +2254,14 @@ export function selectionHighlightLines( rgb: RRgb, openingIds: string[] = [], stairIds: string[] = [], + roofIds: string[] = [], ): Float32Array { if ( wallIds.length === 0 && ceilingIds.length === 0 && openingIds.length === 0 && - stairIds.length === 0 + stairIds.length === 0 && + roofIds.length === 0 ) { return new Float32Array(0); } @@ -2268,6 +2270,7 @@ export function selectionHighlightLines( const ceilingSet = new Set(ceilingIds); const openingSet = new Set(openingIds); const stairSet = new Set(stairIds); + const roofSet = new Set(roofIds); const out: number[] = []; const pushEdge = (a: RVec3, b: RVec3) => { out.push(a[0], a[1], a[2], rgb[0], rgb[1], rgb[2]); @@ -2332,5 +2335,23 @@ export function selectionHighlightLines( for (const o of openings) if (openingSet.has(o.openingId)) boxEdges(o); for (const s of slabs) if (ceilingSet.has(s.ceilingId)) prismEdges(s.outline, s.zBottom, s.zTop); for (const st of stairs) if (stairSet.has(st.stairId)) prismEdges(st.outline, st.zBottom, st.zTop); + // Dach: die Kanten aller Dachflächen + Giebel als Draht-Umriss (roofGeometry + // liefert 3D-Polygone in MODELL-Koordinaten [x, y, z=Höhe]; world = [x, z, y]). + if (roofSet.size > 0) { + for (const roof of project.roofs ?? []) { + if (!roofSet.has(roof.id)) continue; + const g = roofGeometry(roof, roofBaseElevation(project, roof)); + const polyEdges = (pts: readonly (readonly [number, number, number])[]) => { + const m = pts.length; + for (let i = 0, j = m - 1; i < m; j = i++) { + const a = pts[j]; + const b = pts[i]; + pushEdge([a[0], a[2], a[1]], [b[0], b[2], b[1]]); + } + }; + for (const pl of g.planes) polyEdges(pl.pts); + for (const gab of g.gables) polyEdges(gab); + } + } return new Float32Array(out); }