aa4205bf0b
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.
397 lines
17 KiB
TypeScript
397 lines
17 KiB
TypeScript
/**
|
||
* Unit-Tests für die Wand-Gehrung (computeJoins / miterLine):
|
||
* • Rechtwinklige L-Ecke: die Gehrung liegt auf der 45°-Diagonalen und trifft
|
||
* die korrekten Fläche×Fläche-Apexe.
|
||
* • SPITZE L-Ecke (Öffnungswinkel < 90°): die Gehrung muss weiterhin Außen mit
|
||
* Außen und Innen mit Innen paaren. Die frühere Distanz-Heuristik kippte hier
|
||
* und lieferte eine um 90° verdrehte Gehrung (Kreuz-Overlap der Poché-Bänder);
|
||
* dieser Test fängt den Regress.
|
||
* • T-Knoten (drei Wandenden am selben Punkt): das abzweigende Ende bekommt
|
||
* einen Schnitt auf der zugewandten Fläche der Durchgangswand, die beiden
|
||
* kollinearen Enden bleiben ungeschnitten.
|
||
* • Mittelspannen-T-Stoß: ein freies Ende, das mitten auf die Seite einer
|
||
* anderen Wand trifft, bekommt einen Schnitt auf deren Fläche; ohne Treffer
|
||
* bleibt es rechtwinklig (null).
|
||
*/
|
||
|
||
import { describe, it, expect } from "vitest";
|
||
import { computeJoins, resolveJoinPriority } from "./joins";
|
||
import { cross, len, sub } from "./geometry";
|
||
import type { Line } from "./geometry";
|
||
import type { Component, Project, Vec2, Wall } from "./types";
|
||
|
||
/** Abstand eines Punktes P von der unendlichen Geraden `line`. */
|
||
function distToLine(line: Line, p: Vec2): number {
|
||
return Math.abs(cross(line.dir, sub(p, line.point))) / len(line.dir);
|
||
}
|
||
|
||
/** Minimalprojekt aus zwei Wänden mit einem Wandtyp gegebener Gesamtdicke. */
|
||
function twoWallProject(w1: Wall, w2: Wall, thickness: number): Project {
|
||
return {
|
||
id: "t",
|
||
name: "T",
|
||
lineStyles: [],
|
||
hatches: [],
|
||
components: [{ id: "c", name: "C", color: "#ccc", hatchId: "none", joinPriority: 10 }],
|
||
wallTypes: [
|
||
{ id: "aw", name: "AW", layers: [{ componentId: "c", thickness }] },
|
||
],
|
||
drawingLevels: [
|
||
{ id: "eg", name: "EG", kind: "floor", visible: true, locked: false, floorHeight: 2.6, cutHeight: 1.0, baseElevation: 0 },
|
||
],
|
||
layers: [{ code: "20", name: "Wände", color: "#0a0a0a", lw: 0.5, visible: true, locked: false }],
|
||
walls: [w1, w2],
|
||
doors: [],
|
||
openings: [],
|
||
ceilings: [],
|
||
stairs: [],
|
||
rooms: [],
|
||
drawings2d: [],
|
||
context: [],
|
||
};
|
||
}
|
||
|
||
/** Minimalprojekt aus drei Wänden mit einem Wandtyp gegebener Gesamtdicke. */
|
||
function threeWallProject(
|
||
w1: Wall,
|
||
w2: Wall,
|
||
w3: Wall,
|
||
thickness: number,
|
||
): Project {
|
||
const proj = twoWallProject(w1, w2, thickness);
|
||
proj.walls = [w1, w2, w3];
|
||
return proj;
|
||
}
|
||
|
||
function wall(id: string, start: Vec2, end: Vec2): Wall {
|
||
return {
|
||
id,
|
||
type: "wall",
|
||
floorId: "eg",
|
||
categoryCode: "20",
|
||
start,
|
||
end,
|
||
wallTypeId: "aw",
|
||
height: 2.6,
|
||
};
|
||
}
|
||
|
||
describe("computeJoins — Gehrung an der L-Ecke", () => {
|
||
it("legt die rechtwinklige Gehrung auf die 45°-Diagonale (Fläche×Fläche-Apexe)", () => {
|
||
// W1 (0,0)→(5,0), W2 (5,0)→(5,4); Knoten (5,0). T=0.4, half=0.2.
|
||
const proj = twoWallProject(
|
||
wall("W1", { x: 0, y: 0 }, { x: 5, y: 0 }),
|
||
wall("W2", { x: 5, y: 0 }, { x: 5, y: 4 }),
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
const cut = joins.get("W1")!.endCut;
|
||
expect(cut).not.toBeNull();
|
||
// Außen-Apex (5.2,−0.2) und Innen-Apex (4.8,0.2) liegen auf der Gehrung;
|
||
// Richtung ist die 45°-Diagonale.
|
||
expect(distToLine(cut!, { x: 5.2, y: -0.2 })).toBeCloseTo(0, 6);
|
||
expect(distToLine(cut!, { x: 4.8, y: 0.2 })).toBeCloseTo(0, 6);
|
||
expect(Math.abs(cut!.dir.x)).toBeCloseTo(Math.abs(cut!.dir.y), 6);
|
||
});
|
||
|
||
it("paart bei SPITZEM Winkel Außen-mit-Außen und Innen-mit-Innen (kein Kreuz-Overlap)", () => {
|
||
// Symmetrische V-Ecke, Knoten (0,0), Öffnungswinkel 60° (spitz), nach oben.
|
||
// Beide Wandkörper laufen unter ±30° zur +y-Achse aus:
|
||
// gA = (−sin30, cos30) = (−0.5, √3/2), gB = (+0.5, √3/2).
|
||
// W_A: end im Knoten (start = gA·2), W_B: start im Knoten (end = gB·2).
|
||
const s3 = Math.sqrt(3) / 2;
|
||
const proj = twoWallProject(
|
||
wall("WA", { x: -1, y: 2 * s3 }, { x: 0, y: 0 }),
|
||
wall("WB", { x: 0, y: 0 }, { x: 1, y: 2 * s3 }),
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
// Beide Wände teilen dieselbe Gehrung; WA endet im Knoten, WB startet dort.
|
||
const cut = joins.get("WA")!.endCut ?? joins.get("WB")!.startCut;
|
||
expect(cut).not.toBeNull();
|
||
|
||
// Korrekt: Außen-Apex (0,−0.4) und Innen-Apex (0,0.4) → vertikale Gehrung
|
||
// (Richtung parallel zur y-Achse). Die buggy Distanz-Heuristik lieferte
|
||
// stattdessen eine HORIZONTALE Gehrung (Apexe (±0.23,0)) → hier rot.
|
||
expect(distToLine(cut!, { x: 0, y: -0.4 })).toBeCloseTo(0, 6);
|
||
expect(distToLine(cut!, { x: 0, y: 0.4 })).toBeCloseTo(0, 6);
|
||
// Gehrung vertikal: dir.x ≈ 0 (die falsche, horizontale Gehrung hätte dir.y ≈ 0).
|
||
const dir = cut!.dir;
|
||
expect(Math.abs(dir.x) / len(dir)).toBeCloseTo(0, 6);
|
||
});
|
||
});
|
||
|
||
describe("computeJoins — T-Knoten (drei Wandenden am selben Punkt)", () => {
|
||
it("schneidet nur das abzweigende Ende, auf der zugewandten Fläche der Durchgangswand", () => {
|
||
// Durchgangswand in zwei Hälften WA1 (0,0)→(5,0) und WA2 (5,0)→(10,0),
|
||
// Abzweig WB (5,0)→(5,3) nach oben. T=0.4 → halbe Dicke 0.2.
|
||
const proj = threeWallProject(
|
||
wall("WA1", { x: 0, y: 0 }, { x: 5, y: 0 }),
|
||
wall("WA2", { x: 5, y: 0 }, { x: 10, y: 0 }),
|
||
wall("WB", { x: 5, y: 0 }, { x: 5, y: 3 }),
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
|
||
// Die kollinearen Hälften der Durchgangswand bleiben ungeschnitten.
|
||
expect(joins.get("WA1")!.endCut).toBeNull();
|
||
expect(joins.get("WA2")!.startCut).toBeNull();
|
||
|
||
// Der Abzweig bekommt einen Schnitt auf der ihm zugewandten (oberen)
|
||
// Fläche der Durchgangswand: die horizontale Gerade y = 0.2.
|
||
const cut = joins.get("WB")!.startCut;
|
||
expect(cut).not.toBeNull();
|
||
expect(distToLine(cut!, { x: 5, y: 0.2 })).toBeCloseTo(0, 6);
|
||
expect(distToLine(cut!, { x: 0, y: 0.2 })).toBeCloseTo(0, 6);
|
||
// Gehrungsgerade horizontal (parallel zur Durchgangsachse).
|
||
expect(Math.abs(cut!.dir.y) / len(cut!.dir)).toBeCloseTo(0, 6);
|
||
});
|
||
});
|
||
|
||
describe("computeJoins — Mittelspannen-T-Stoß (Ende trifft Wandseite)", () => {
|
||
it("schneidet ein freies Ende, das auf die Seite einer anderen Wand trifft, entlang deren Fläche", () => {
|
||
// Durchgangswand W (0,0)→(10,0), T=0.4 → halbe Dicke 0.2. Abzweig WB2
|
||
// trifft mit seinem freien Ende (4, 0.2) genau auf die obere Fläche.
|
||
const proj = threeWallProject(
|
||
wall("W", { x: 0, y: 0 }, { x: 10, y: 0 }),
|
||
wall("WB2", { x: 4, y: 3 }, { x: 4, y: 0.2 }),
|
||
wall("Filler", { x: 100, y: 100 }, { x: 100, y: 103 }), // unbeteiligt
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
|
||
const cut = joins.get("WB2")!.endCut;
|
||
expect(cut).not.toBeNull();
|
||
expect(distToLine(cut!, { x: 0, y: 0.2 })).toBeCloseTo(0, 6);
|
||
expect(distToLine(cut!, { x: 5, y: 0.2 })).toBeCloseTo(0, 6);
|
||
expect(Math.abs(cut!.dir.y) / len(cut!.dir)).toBeCloseTo(0, 6);
|
||
|
||
// Die Wand W selbst bleibt an ihren (freien) Enden ungeschnitten.
|
||
expect(joins.get("W")!.startCut).toBeNull();
|
||
expect(joins.get("W")!.endCut).toBeNull();
|
||
// Das obere, unbeteiligte Ende von WB2 bleibt ebenfalls ungeschnitten.
|
||
expect(joins.get("WB2")!.startCut).toBeNull();
|
||
});
|
||
|
||
it("lässt ein freies Ende rechtwinklig (null), wenn keine Wand nah genug getroffen wird", () => {
|
||
// Gleiche Anordnung, aber Abzweig-Ende bei y=0.5 — außerhalb der
|
||
// Wandfläche (Dicke/2 + Toleranz = 0.201).
|
||
const proj = twoWallProject(
|
||
wall("W", { x: 0, y: 0 }, { x: 10, y: 0 }),
|
||
wall("WB3", { x: 4, y: 3 }, { x: 4, y: 0.5 }),
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
expect(joins.get("WB3")!.endCut).toBeNull();
|
||
});
|
||
});
|
||
|
||
describe("resolveJoinPriority — Ordnungslogik zweier Bauteile am Stoss", () => {
|
||
const brick: Component = { id: "brick", name: "Backstein", color: "#8c5544", hatchId: "none", joinPriority: 50 };
|
||
const brick2: Component = { id: "brick", name: "Backstein (Kopie)", color: "#000", hatchId: "none", joinPriority: 50 };
|
||
const render: Component = { id: "render", name: "Putz", color: "#efece6", hatchId: "none", joinPriority: 10 };
|
||
const insulation: Component = { id: "insulation", name: "Dämmung", color: "#fff", hatchId: "none", joinPriority: 20 };
|
||
const concrete: Component = { id: "concrete", name: "Beton", color: "#9aa0a6", hatchId: "none", joinPriority: 100 };
|
||
|
||
it("verschmilzt bei gleicher Priorität UND gleicher Komponente", () => {
|
||
expect(resolveJoinPriority(brick, brick2)).toBe("merge");
|
||
});
|
||
|
||
it("koexistiert bei gleicher Priorität, aber verschiedener Komponente", () => {
|
||
// Zwei fiktive Komponenten mit zufällig gleicher Priorität, aber
|
||
// unterschiedlicher Identität — keine eindeutige Rangfolge.
|
||
const other: Component = { id: "other-30", name: "X", color: "#123", hatchId: "none", joinPriority: 20 };
|
||
expect(resolveJoinPriority(insulation, other)).toBe("coexist");
|
||
});
|
||
|
||
it("lässt die höhere Priorität gewinnen (die niedrigere wird getrimmt)", () => {
|
||
expect(resolveJoinPriority(render, brick)).toBe("trim-a"); // a=Putz verliert
|
||
expect(resolveJoinPriority(brick, render)).toBe("trim-b"); // b=Putz verliert
|
||
expect(resolveJoinPriority(concrete, brick)).toBe("trim-b");
|
||
expect(resolveJoinPriority(brick, concrete)).toBe("trim-a");
|
||
});
|
||
});
|
||
|
||
/**
|
||
* Minimalprojekt mit einem mehrschichtigen, W9-artigen Wandtyp
|
||
* (Innenputz/Backstein-Kern/Innenputz, wie `sampleProject`s "iw") — für die
|
||
* materialbewusste T-Stoss-Erweiterung (layerCuts).
|
||
*/
|
||
function layeredWallProject(w1: Wall, w2: Wall, w3: Wall): Project {
|
||
return {
|
||
id: "t",
|
||
name: "T",
|
||
lineStyles: [],
|
||
hatches: [],
|
||
components: [
|
||
{ id: "render-int", name: "Innenputz", color: "#efece6", hatchId: "none", joinPriority: 10 },
|
||
{ id: "brick", name: "Backstein", color: "#8c5544", hatchId: "none", joinPriority: 50 },
|
||
],
|
||
wallTypes: [
|
||
{
|
||
id: "iw",
|
||
name: "Innenwand",
|
||
layers: [
|
||
{ componentId: "render-int", thickness: 0.015 },
|
||
{ componentId: "brick", thickness: 0.12 },
|
||
{ componentId: "render-int", thickness: 0.015 },
|
||
],
|
||
},
|
||
],
|
||
drawingLevels: [
|
||
{ id: "eg", name: "EG", kind: "floor", visible: true, locked: false, floorHeight: 2.6, cutHeight: 1.0, baseElevation: 0 },
|
||
],
|
||
layers: [{ code: "20", name: "Wände", color: "#0a0a0a", lw: 0.5, visible: true, locked: false }],
|
||
walls: [w1, w2, w3],
|
||
doors: [],
|
||
openings: [],
|
||
ceilings: [],
|
||
stairs: [],
|
||
rooms: [],
|
||
drawings2d: [],
|
||
context: [],
|
||
};
|
||
}
|
||
|
||
function iwWall(id: string, start: Vec2, end: Vec2): Wall {
|
||
return {
|
||
id,
|
||
type: "wall",
|
||
floorId: "eg",
|
||
categoryCode: "20",
|
||
start,
|
||
end,
|
||
wallTypeId: "iw",
|
||
height: 2.6,
|
||
};
|
||
}
|
||
|
||
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 — 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(
|
||
iwWall("WA1", { x: 0, y: 0 }, { x: 5, y: 0 }),
|
||
iwWall("WA2", { x: 5, y: 0 }, { x: 10, y: 0 }),
|
||
iwWall("WB", { x: 5, y: 0 }, { x: 5, y: 3 }),
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
const cuts = joins.get("WB")!;
|
||
|
||
// Aggregat-Cut bleibt unverändert (bit-identisch zum bisherigen Verhalten).
|
||
expect(cuts.startCut).not.toBeNull();
|
||
expect(distToLine(cuts.startCut!, { x: 5, y: 0.075 })).toBeCloseTo(0, 6);
|
||
|
||
// layerCuts ist gesetzt (mindestens eine Schicht verschmilzt).
|
||
expect(cuts.layerCuts).toBeDefined();
|
||
const lc = cuts.layerCuts!;
|
||
expect(lc.start.length).toBe(3);
|
||
|
||
// Schicht 1 (Backstein-Kern, Index 1) verschmilzt: Cut an der Nahfläche des
|
||
// Durchgangs-Rückgrats — NICHT null (Achse) und NICHT die Aggregat-Nahfläche
|
||
// (0.075). Durchgangswand-Halbdicke 0.075 minus Nah-Putz 0.015 → y = 0.06.
|
||
expect(lc.start[1]).not.toBeNull();
|
||
expect(distToLine(lc.start[1]!, { x: 5, y: 0.06 })).toBeCloseTo(0, 6);
|
||
expect(distToLine(lc.start[1]!, { x: 0, y: 0.06 })).toBeCloseTo(0, 6);
|
||
// Der Kern läuft NICHT bis zur Achse (y=0) durch.
|
||
expect(distToLine(lc.start[1]!, { x: 5, y: 0 })).not.toBeCloseTo(0, 6);
|
||
expect(lc.startSide[1]).toBeNull();
|
||
|
||
// Schichten 0 und 2 (Innenputz) werden getrimmt: derselbe Nahflächen-Cut
|
||
// wie das Aggregat...
|
||
expect(lc.start[0]).not.toBeNull();
|
||
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);
|
||
// ... 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).
|
||
expect(joins.get("WA1")!.layerCuts).toBeUndefined();
|
||
expect(joins.get("WA2")!.layerCuts).toBeUndefined();
|
||
|
||
// Die als Durchgang gewählte Wandhälfte bekommt eine Durchgangswand-Aussparung
|
||
// (Phase 1c): über die Backstein-Kernbreite (0.12) im Nah-Putz-Bereich
|
||
// (Offset 0.06…0.075). Welche Hälfte den Durchgang bildet, hängt von der
|
||
// Knoten-Reihenfolge ab — wir prüfen die, die den Cutout trägt.
|
||
const throughCuts = joins.get("WA1")!.spanCutouts ?? joins.get("WA2")!.spanCutouts;
|
||
expect(throughCuts).toBeDefined();
|
||
expect(throughCuts!.length).toBe(1);
|
||
const sc = throughCuts![0];
|
||
// Achsen-Intervall: Kernbreite 0.12, zentriert am Knoten (x=5 → [4.94,5.06]).
|
||
expect(sc.to - sc.from).toBeCloseTo(0.12, 6);
|
||
// offA = Nahfläche (0.075), offB = Rückgrat-Nahfläche (0.06).
|
||
expect(sc.offA).toBeCloseTo(0.075, 6);
|
||
expect(sc.offB).toBeCloseTo(0.06, 6);
|
||
// Der Abzweig selbst trägt keine Aussparung (die gehört der Durchgangswand).
|
||
expect(joins.get("WB")!.spanCutouts).toBeUndefined();
|
||
// L-Ecke bzw. Wände ohne T bleiben cutout-frei (die kollineare Nicht-Durchgangs-
|
||
// Hälfte hat keinen Cutout).
|
||
expect(
|
||
joins.get("WA1")!.spanCutouts === undefined ||
|
||
joins.get("WA2")!.spanCutouts === undefined,
|
||
).toBe(true);
|
||
});
|
||
|
||
it("Mittelspannen-T-Stoss: die Durchgangswand bekommt einen spanCutout mit korrekter Kernbreite und Nah-Putz-Offsetzone", () => {
|
||
// Realer W9-Fall: Durchgangswand als EINE Wand W (0,0)→(10,0), Abzweig WB
|
||
// trifft mit seinem freien Ende (4,0) mittig auf ihre Seite. Beide iw.
|
||
const proj = layeredWallProject(
|
||
iwWall("W", { x: 0, y: 0 }, { x: 10, y: 0 }),
|
||
iwWall("WB", { x: 4, y: 3 }, { x: 4, y: 0 }),
|
||
iwWall("Filler", { x: 100, y: 100 }, { x: 100, y: 103 }), // unbeteiligt
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
|
||
const wCuts = joins.get("W")!;
|
||
expect(wCuts.spanCutouts).toBeDefined();
|
||
expect(wCuts.spanCutouts!.length).toBe(1);
|
||
const sc = wCuts.spanCutouts![0];
|
||
// Intervall um x=4, Breite = Kernbreite 0.12 → [3.94, 4.06].
|
||
expect(sc.from).toBeCloseTo(3.94, 6);
|
||
expect(sc.to).toBeCloseTo(4.06, 6);
|
||
expect(sc.offA).toBeCloseTo(0.075, 6);
|
||
expect(sc.offB).toBeCloseTo(0.06, 6);
|
||
// Der Abzweig und die unbeteiligte Wand tragen keine Aussparung.
|
||
expect(joins.get("WB")!.spanCutouts).toBeUndefined();
|
||
expect(joins.get("Filler")!.spanCutouts).toBeUndefined();
|
||
});
|
||
|
||
it("L-Ecke und einfacher (nicht materialbewusster) Stoss bekommen KEINEN spanCutout", () => {
|
||
// Reine L-Ecke zweier Wände: keine Durchgangswand, kein Durchstoss.
|
||
const proj = twoWallProject(
|
||
wall("L1", { x: 0, y: 0 }, { x: 5, y: 0 }),
|
||
wall("L2", { x: 5, y: 0 }, { x: 5, y: 4 }),
|
||
0.4,
|
||
);
|
||
const joins = computeJoins(proj, proj.walls);
|
||
expect(joins.get("L1")!.spanCutouts).toBeUndefined();
|
||
expect(joins.get("L2")!.spanCutouts).toBeUndefined();
|
||
});
|
||
|
||
it("setzt layerCuts NICHT, wenn kein Bauteil des Abzweigs mit dem Rückgrat der Durchgangswand übereinstimmt", () => {
|
||
// Gleiche Anordnung, aber der Abzweig ist eine reine Einschicht-Wand ohne
|
||
// Materialübereinstimmung zum Backstein-Rückgrat → Default-Verhalten.
|
||
const proj = layeredWallProject(
|
||
iwWall("WA1", { x: 0, y: 0 }, { x: 5, y: 0 }),
|
||
iwWall("WA2", { x: 5, y: 0 }, { x: 10, y: 0 }),
|
||
iwWall("WB", { x: 5, y: 0 }, { x: 5, y: 3 }),
|
||
);
|
||
proj.wallTypes.push({
|
||
id: "solo",
|
||
name: "Solo",
|
||
layers: [{ componentId: "render-int", thickness: 0.1 }],
|
||
});
|
||
proj.walls[2] = { ...proj.walls[2], wallTypeId: "solo" };
|
||
const joins = computeJoins(proj, proj.walls);
|
||
expect(joins.get("WB")!.layerCuts).toBeUndefined();
|
||
expect(joins.get("WB")!.startCut).not.toBeNull(); // Aggregat-Cut bleibt normal
|
||
});
|
||
});
|