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:
+45
-11
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user