3D: mehrschichtige Wände zeigen ihren Schichtaufbau

Statt eines Vollkörpers in der repräsentativen Farbe wird jede
Materiallage der Wand als eigener extrudierter Teilquader in ihrer
Component-Farbe emittiert (quer zur Wanddicke gestapelt, zentriert um
die Achse). Gilt auch für die Öffnungs-Teilquader (Pfeiler/Brüstung/
Sturz). Jede Schicht-Box trägt die wallId der Ursprungswand → Klick-
Auswahl + Highlight umfassen weiter die ganze Wand.
This commit is contained in:
2026-07-04 12:40:25 +02:00
parent 018fef56bf
commit 00c90857ad
2 changed files with 148 additions and 56 deletions
+45 -11
View File
@@ -24,12 +24,14 @@ describe("selectionHighlightLines", () => {
expect(lines.length).toBe(0);
});
it("eine Wand ohne Öffnungen -> genau 12 Kanten (ein Quader)", () => {
it("eine Wand ohne Öffnungen -> je Materiallage ein Quader (4·12 Kanten)", () => {
// W2 (EG, Ost) trägt keine Tür/kein Fenster im Sample-Projekt (nur W1/W3
// haben Öffnungen, s. sampleProject.ts) -> ein einziger Teilquader, also
// exakt 12 Kanten.
// haben Öffnungen, s. sampleProject.ts) -> ein Achsen-Teilquader. Wandtyp
// "aw" hat 4 Schichten (render-ext/insulation/brick/render-int), die
// TS-seitig je als eigene Box gestapelt werden (Option B) -> 4 Quader =
// 4·12 Kanten. (Früher: 1 Vollkörper = 12 Kanten.)
const lines = selectionHighlightLines(sampleProject, ["W2"], [], RGB);
expect(lines.length).toBe(12 * VERTICES_PER_EDGE * FLOATS_PER_VERTEX);
expect(lines.length).toBe(4 * 12 * VERTICES_PER_EDGE * FLOATS_PER_VERTEX);
});
it("eine Wand mit Farbwerten in jedem Vertex", () => {
@@ -70,16 +72,48 @@ describe("selectionHighlightLines", () => {
});
describe("Bauteil-/Materialfarbe statt Einheits-Grau (Wände + Decken)", () => {
it("Wand -> Farbe der dicksten Schicht (aw: insulation 0.16 m, #ffffff)", () => {
// Wandtyp "aw" (s. sampleProject): render-ext 0.02, insulation 0.16,
// brick 0.15, render-int 0.015 -> dickste Schicht = insulation (#ffffff).
it("Wand -> je Materiallage eine Box in ihrer Component-Farbe (Option B)", () => {
// Wandtyp "aw" (s. sampleProject) hat 4 Schichten mit eigenen Farben:
// render-ext 0.02 #d8d2c7, insulation 0.16 #ffffff,
// brick 0.15 #8c5544, render-int 0.015 #efece6 (Σ = 0.345 m).
// W2 ist öffnungslos -> genau ein Achsen-Teilquader, also 4 Schicht-Boxen.
const walls = projectToWalls3d(sampleProject);
const w2 = walls.filter((w) => w.wallId === "W2");
expect(w2.length).toBeGreaterThan(0);
expect(w2.length).toBe(4);
// Alle Schicht-Boxen tragen die gemeinsame wallId (Klick selektiert ganze Wand)
// und sind Vollkörper (layers leer -> render3d zeichnet in `color`).
for (const seg of w2) {
expect(seg.color[0]).toBeCloseTo(1, 6);
expect(seg.color[1]).toBeCloseTo(1, 6);
expect(seg.color[2]).toBeCloseTo(1, 6);
expect(seg.wallId).toBe("W2");
expect(seg.layers.length).toBe(0);
}
// Die vier Materialfarben kommen genau einmal vor.
const hex = (c: [number, number, number]) =>
c.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
const colors = w2.map((s) => hex(s.color)).sort();
expect(colors).toEqual(["8c5544", "d8d2c7", "efece6", "ffffff"]);
// Die Schichtdicken summieren zur Gesamt-Wanddicke (keine Lücken/Overlaps).
const totalT = w2.reduce((s, seg) => s + seg.thickness, 0);
expect(totalT).toBeCloseTo(0.345, 6);
});
it("Schicht-Offsets kacheln die Wanddicke lückenlos um die Achse", () => {
// W2-Achse (5,0)->(5,4): u=(0,1), Normale n=(1,0) -> der Schicht-Versatz
// wirkt rein auf x (Box-Start.x = 5 + offset). Sortiert man die Boxen nach
// Versatz, müssen sich benachbarte Flächen exakt berühren und der Stapel
// symmetrisch um die Achse liegen: min = -T/2, max = +T/2.
const w2 = projectToWalls3d(sampleProject).filter((w) => w.wallId === "W2");
const bands = w2
.map((s) => ({ offset: s.start[0] - 5, t: s.thickness }))
.sort((a, b) => a.offset - b.offset);
const T = bands.reduce((s, b) => s + b.t, 0);
expect(bands[0].offset - bands[0].t / 2).toBeCloseTo(-T / 2, 6);
expect(bands[bands.length - 1].offset + bands[bands.length - 1].t / 2).toBeCloseTo(T / 2, 6);
for (let i = 1; i < bands.length; i++) {
// Oberkante der Vorlage == Unterkante der nächsten (keine Lücke/Overlap).
expect(bands[i - 1].offset + bands[i - 1].t / 2).toBeCloseTo(bands[i].offset - bands[i].t / 2, 6);
}
});