Dach 3D: echte Materialdicke (solide Slabs) statt papierdünner Flächen

roof.thickness war im 3D bislang ein totes Feld — Dächer waren einseitige
Flächen. emitRoofs baut jede Dachfläche jetzt als soliden Slab: Oberseite +
um thickness entlang der Flächennormalen (Newell) nach unten versetzte
Unterseite + umlaufende Seitenwände. Giebel bleiben dünne Endfüllung.
Basis für die kommende Dach-Schichtlogik. +1 Test (Unterseite unter der
Oberfläche); Walm-Zähltest auf Slab-Dreiecke aktualisiert. 667/667 grün.
Visuelle 3D-Abnahme in der Tauri-App durch den Nutzer.
This commit is contained in:
2026-07-10 01:58:15 +02:00
parent bfb80b363b
commit e99bb2413f
2 changed files with 75 additions and 4 deletions
+14 -3
View File
@@ -1254,13 +1254,24 @@ describe("emitRoofs (Dachflächen + Giebel als Meshes)", () => {
expect(roofMeshes.length).toBe(1);
const m = roofMeshes[0];
expect(m.positions.every((v) => Number.isFinite(v))).toBe(true);
// Walm hat KEINE Giebel (gables: []); 2 Quader (je 2 Dreiecke) + 2 Dreiecke
// (je 1 Dreieck) = 6 Dreiecke = 18 Indizes.
expect(m.indices.length).toBe(18);
// Walm hat KEINE Giebel (gables: []). Jede Fläche ist jetzt ein SOLIDER Slab
// (Oberseite + Unterseite + Seitenwände): Quader-Fläche (4 Ecken) = 2+2+8 =
// 12 Dreiecke, Dreiecks-Fläche (3 Ecken) = 1+1+6 = 8 Dreiecke.
// 2 Quader × 12 + 2 Dreiecke × 8 = 40 Dreiecke = 120 Indizes.
expect(m.indices.length).toBe(120);
const vcount = m.positions.length / 3;
expect(m.indices.every((idx) => idx >= 0 && idx < vcount)).toBe(true);
});
it("Dach-Dicke: Slab erzeugt eine Unterseite unter der Oberfläche (thickness > 0)", () => {
const project: Project = { ...sampleProject, roofs: [baseRoof] };
const m = projectToModel3d(project).meshes.filter(isRoofMesh)[0];
const heights = m.positions.filter((_, i) => i % 3 === 2);
// Sattel 6×4, 30°, Traufe 3: Traufe-OK bei z=3, First höher. Die Unterseite
// liegt um ~thickness (0.3, senkrecht) tiefer -> min height klar unter 3.
expect(Math.min(...heights)).toBeLessThan(3 - 0.2);
});
it("Projekt ohne Dächer emittiert keine Roof-Meshes (Rückwärtskompatibilität)", () => {
// Explizit leeres roofs-Feld -> keine Roof-Meshes.
const project: Project = { ...sampleProject, roofs: [] };