From aa4205bf0b1214bc1f014a39446c3b19a923d330 Mon Sep 17 00:00:00 2001 From: Karim Date: Sat, 4 Jul 2026 23:17:18 +0200 Subject: [PATCH] T-Stoss: materialgleicher Nah-Putz verschmilzt ohne L-Trennnaht (TS+Rust) Die L-Seitenlinie am T-Stoss wird nur noch gezeichnet, wenn der getrimmte Abzweig-Putz materialFREMD zum durchgehenden Nah-Putz der Durchgangswand ist. Bei materialgleichem Putz (z. B. iw-Innenputz auf iw-Innenputz) fuellt der Nah-Putz die Putzflanken durchgehend (spanCutout schneidet nur die Kernbreite) -> durchgehendes Putz-L ohne Naht. Tests entsprechend gezogen. --- src-tauri/geometry/src/lib.rs | 37 ++++++++++++++++++------- src/model/joins.test.ts | 16 +++++------ src/model/joins.ts | 14 +++++++++- src/plan/generatePlan.tjunction.test.ts | 24 +++++++--------- 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src-tauri/geometry/src/lib.rs b/src-tauri/geometry/src/lib.rs index e629194..56471f7 100644 --- a/src-tauri/geometry/src/lib.rs +++ b/src-tauri/geometry/src/lib.rs @@ -228,6 +228,18 @@ fn apply_layer_cuts( // Rueckgrat-Nahflaeche ab (nicht an der Wandachse). In einem Block, damit die // mutable Ausleihe von `result[branch_wall_idx]` vor dem Zugriff auf die // Durchgangswand unten endet. + // Nah-Putz-Komponenten der Durchgangswand (dieselbe sign-Seite wie + // near_plaster oben): ist die getrimmte Abzweig-Schicht materialgleich mit + // einer davon, fuellt der Nah-Putz den Abzweig-Putz an dieser Stelle + // durchgehend (spanCutout schneidet nur die KERN-Breite aus, nicht die + // schmalen Putzflanken) -> Putz-L OHNE Trennnaht, also KEINE L-Seitenlinie. + let near_plaster_comps: Vec<&str> = through_layers + .iter() + .enumerate() + .filter(|(i, _)| if sign > 0.0 { *i > backbone_idx } else { *i < backbone_idx }) + .map(|(_, l)| l.component_id.as_str()) + .collect(); + { let side_line = merge_cut; let (face_arr, side_arr) = match branch_end { @@ -238,7 +250,13 @@ fn apply_layer_cuts( face_arr[i] = if merged[i] { Some(merge_cut) } else { Some(face_cut) }; let adj_merged = !merged[i] && ((i > 0 && merged[i - 1]) || (i + 1 < n && merged[i + 1])); - side_arr[i] = if adj_merged { Some(side_line) } else { None }; + let merges_with_near_plaster = + near_plaster_comps.contains(&branch_layers[i].component_id.as_str()); + side_arr[i] = if adj_merged && !merges_with_near_plaster { + Some(side_line) + } else { + None + }; } } @@ -936,8 +954,8 @@ mod tests { // Wie t_junction_branch_gets_face_cut, aber alle drei Waende vom // W9-artigen Wandtyp: der Backstein-Kern (Index 1) sticht durch den // Nah-Putz und stoppt an der Kern-Nahflaeche (y=0.06), die beiden - // Innenputz-Schichten (Index 0/2) werden getrimmt und bekommen - // zusaetzlich die seitliche L-Linie an derselben Flaeche. + // Innenputz-Schichten (Index 0/2) werden getrimmt. Da der Nah-Putz der + // Durchgangswand materialgleich ist, entsteht KEINE L-Trennnaht. let input = JoinInput { walls: vec![ w_iw("A", 0.0, 0.0, 5.0, 0.0), @@ -972,13 +990,12 @@ mod tests { let l2 = lc.start[2].expect("Schicht 2 getrimmt"); assert!((l0.point.y - 0.075).abs() < 1e-9); assert!((l2.point.y - 0.075).abs() < 1e-9); - // ... UND die seitliche L-Linie an der Rueckgrat-Nahflaeche (y=0.06, - // parallel zur x-Achse). - let s0 = lc.start_side[0].expect("Schicht 0 hat L-Seitenlinie"); - let s2 = lc.start_side[2].expect("Schicht 2 hat L-Seitenlinie"); - assert!(dist(s0, Vec2 { x: 5.0, y: 0.06 }) < 1e-9); - assert!(dist(s0, Vec2 { x: 0.0, y: 0.06 }) < 1e-9); - assert!(dist(s2, Vec2 { x: 5.0, y: 0.06 }) < 1e-9); + // ... aber KEINE seitliche L-Linie: der Innenputz der Durchgangswand ist + // materialgleich und fuellt den getrimmten Abzweig-Putz durchgehend + // (Putz-L ohne Trennnaht). Nur bei materialFREMDEM Nah-Putz stuende hier + // eine L-Linie. + assert!(lc.start_side[0].is_none()); + assert!(lc.start_side[2].is_none()); // Die kollineare Durchgangswand bleibt bei den layerCuts unangetastet. let ca = find(&out, "A"); diff --git a/src/model/joins.test.ts b/src/model/joins.test.ts index 11f932f..ddcf28b 100644 --- a/src/model/joins.test.ts +++ b/src/model/joins.test.ts @@ -267,7 +267,7 @@ function iwWall(id: string, start: Vec2, end: Vec2): Wall { } describe("computeJoins — materialbewusster T-Stoss (layerCuts)", () => { - it("W9-artiger T-Knoten: Backstein-Kern des Abzweigs sticht bis zur Rückgrat-Nahfläche durch, Putz wird getrimmt + L-Seitenlinie an derselben Fläche", () => { + it("W9-artiger T-Knoten: Backstein-Kern des Abzweigs sticht bis zur Rückgrat-Nahfläche durch, Putz wird getrimmt — materialgleicher Nah-Putz ⇒ KEINE L-Trennnaht", () => { // Durchgangswand WA1 (0,0)→(5,0) / WA2 (5,0)→(10,0), Abzweig WB (5,0)→(5,3), // alle vom selben Wandtyp "iw" (Innenputz/Backstein/Innenputz, T=0.15). const proj = layeredWallProject( @@ -303,13 +303,13 @@ describe("computeJoins — materialbewusster T-Stoss (layerCuts)", () => { expect(lc.start[2]).not.toBeNull(); expect(distToLine(lc.start[0]!, { x: 5, y: 0.075 })).toBeCloseTo(0, 6); expect(distToLine(lc.start[2]!, { x: 5, y: 0.075 })).toBeCloseTo(0, 6); - // ... UND bekommen die zusätzliche L-Seitenlinie an der Rückgrat-Nahfläche - // (y = 0.06, parallel zur Durchgangsachse) — dort endet der durchstechende - // Kern. - expect(lc.startSide[0]).not.toBeNull(); - expect(lc.startSide[2]).not.toBeNull(); - expect(distToLine(lc.startSide[0]!, { x: 5, y: 0.06 })).toBeCloseTo(0, 6); - expect(distToLine(lc.startSide[0]!, { x: 0, y: 0.06 })).toBeCloseTo(0, 6); + // ... aber KEINE L-Seitenlinie: der Nah-Putz der Durchgangswand ist + // materialgleicher Innenputz und füllt den getrimmten Abzweig-Putz an den + // Flanken durchgehend (der spanCutout schneidet nur die Kernbreite aus). + // Es entsteht ein durchgehendes Putz-L ohne Trennnaht. Nur bei material- + // FREMDEM Nah-Putz stünde hier eine L-Linie. + expect(lc.startSide[0]).toBeNull(); + expect(lc.startSide[2]).toBeNull(); // Die kollinearen Hälften der Durchgangswand bleiben bei den layerCuts // unangetastet (keine Pro-Schicht-Cuts für sie). diff --git a/src/model/joins.ts b/src/model/joins.ts index 38e90a1..35d131b 100644 --- a/src/model/joins.ts +++ b/src/model/joins.ts @@ -421,11 +421,23 @@ function applyLayerCuts( // Nachbarschicht kappt ihre Breite. const sideLine: Line = mergeCut; + // Materialien des durchgehenden Nah-Putzes der Durchgangswand (dieselbe + // sign-Seite wie die nearPlaster-Summe oben). Ist die getrimmte Abzweig-Schicht + // materialgleich mit einer davon, füllt der Nah-Putz den Abzweig-Putz an dieser + // Stelle durchgehend (der spanCutout schneidet nur die KERN-Breite aus, nicht + // die schmalen Putzflanken) → Putz-L OHNE Trennnaht, also KEINE L-Seitenlinie. + const nearPlasterComps = new Set(); + for (let i = 0; i < throughWt.layers.length; i++) { + if (sign > 0 ? i > backboneIdx : i < backboneIdx) + nearPlasterComps.add(throughWt.layers[i].componentId); + } + for (let i = 0; i < n; i++) { faceArr[i] = merged[i] ? mergeCut : faceCut; const adjMerged = !merged[i] && ((i > 0 && merged[i - 1]) || (i < n - 1 && merged[i + 1])); - sideArr[i] = adjMerged ? sideLine : null; + const mergesWithNearPlaster = nearPlasterComps.has(branchWt.layers[i].componentId); + sideArr[i] = adjMerged && !mergesWithNearPlaster ? sideLine : null; } // Durchgangswand-Aussparung (Phase 1c): den Nah-Putz der Durchgangswand über diff --git a/src/plan/generatePlan.tjunction.test.ts b/src/plan/generatePlan.tjunction.test.ts index 8664ea6..dfef548 100644 --- a/src/plan/generatePlan.tjunction.test.ts +++ b/src/plan/generatePlan.tjunction.test.ts @@ -6,8 +6,9 @@ * • Der Backstein-Kern des Abzweigs sticht durch den Nah-Putz der Durchgangs- * wand und stoppt an deren Kern-Nahfläche (kein Durchlaufen bis zur Achse, * kein Überlappen) — "durchstechender Kern". - * • Die Innenputz-Schichten des Abzweigs enden weiterhin an der Nahfläche - * UND bekommen eine zusätzliche seitliche L-Linie (cls "layer-joint-l"). + * • Die Innenputz-Schichten des Abzweigs enden an der Nahfläche. Ist der + * Nah-Putz der Durchgangswand materialgleich, füllt er die Flanken durchgehend + * → durchgehendes Putz-L OHNE Trennnaht (keine "layer-joint-l"-Linie). */ import { describe, it, expect } from "vitest"; @@ -96,22 +97,17 @@ describe("generatePlan — materialbewusster T-Stoss (Poché-Bänder)", () => { expect(brick.pts[3].y).toBeCloseTo(0.06, 6); }); - it("zeichnet die L-Seitenlinie nur für die getrimmten Putzschichten, nicht für den durchlaufenden Kern", () => { + it("zeichnet KEINE L-Seitenlinie, wenn der getrimmte Putz materialgleich mit dem durchgehenden Nah-Putz der Durchgangswand ist (durchgehendes Putz-L ohne Naht)", () => { const plan = generatePlan(tJunctionProject(), "eg", visible, undefined, "mittel"); const lLines = plan.primitives.filter( (p): p is Extract => p.kind === "line" && p.cls === "layer-joint-l", ); - // Genau zwei L-Linien (eine je Innenputz-Schicht); der Backstein-Kern - // bekommt keine (er verschmilzt, kein Sprung im Material). - expect(lLines.length).toBe(2); - - // Jede L-Linie verläuft an der Rückgrat-Nahfläche (y=0.06, parallel zur - // x-Achse) und spannt genau die Breite EINER Putzschicht (0.015 m) auf. - for (const l of lLines) { - expect(l.a.y).toBeCloseTo(0.06, 6); - expect(l.b.y).toBeCloseTo(0.06, 6); - expect(Math.abs(l.a.x - l.b.x)).toBeCloseTo(0.015, 6); - } + // Alle drei Wände sind vom selben iw-Typ: der Innenputz des Abzweigs trifft + // an der Flanke auf den materialgleichen Innenputz der Durchgangswand (der + // spanCutout schneidet nur die Kernbreite aus, nicht die Putzflanken). Der + // Putz läuft dort durchgehend ums Eck (L) — eine Trennnaht würde eine falsche + // Materialgrenze suggerieren. Daher KEINE L-Linie. + expect(lLines.length).toBe(0); }); it("bricht die Nah-Putz-Schicht der Durchgangswand am Durchstoss auf (Phase 1c)", () => {