938d6421f9
Bei mehrschichtigen Wänden lässt sich neben Aussen/Mitte/Innen jetzt auch eine SCHICHTTRENNLINIE (Fuge zwischen zwei Schichten) als Referenzlinie wählen — z. B. die Achse auf die Innenkante der Aussenschale legen. - Neues Feld Wall.referenceOffset (freier Achsversatz entlang +n); wenn gesetzt, übersteuert es referenceLine. wallReferenceOffset bleibt die EINE Quelle → Grundriss, Schnitt und 3D erben das Verhalten automatisch. - Object-Info-Panel: das Referenzlinien-Dropdown listet zusätzlich je interne Fuge einen Eintrag (Kürzel der angrenzenden Schichten); Auswahl setzt den Versatz, Aussen/Mitte/Innen löscht ihn wieder. - selectionInfo berechnet die Fugen-Versätze (kumulierte Schichtdicken). +3 Tests. tsc + vitest grün.
137 lines
4.7 KiB
TypeScript
137 lines
4.7 KiB
TypeScript
// Unit-Tests für die Wand-/Decken-Resolver (wallVerticalExtent /
|
||
// ceilingVerticalExtent): reine Datenschicht, kein React/Store.
|
||
|
||
import { describe, it, expect } from "vitest";
|
||
import { ceilingVerticalExtent, wallReferenceOffset } from "./wall";
|
||
import type { Ceiling, Project, Wall } from "./types";
|
||
|
||
/** Minimalprojekt mit einem Geschoss (baseElevation 0, floorHeight 2.6) und
|
||
* einem einschichtigen Wandtyp (0.2 m), den die Decke als Aufbau nutzt. */
|
||
function makeProject(overrides?: Partial<Project>): Project {
|
||
return {
|
||
id: "t",
|
||
name: "T",
|
||
lineStyles: [],
|
||
hatches: [],
|
||
components: [{ id: "c", name: "C", color: "#ccc", hatchId: "none", joinPriority: 10 }],
|
||
wallTypes: [
|
||
{ id: "dt", name: "Decke", layers: [{ componentId: "c", thickness: 0.2 }] },
|
||
],
|
||
drawingLevels: [
|
||
{
|
||
id: "eg",
|
||
name: "EG",
|
||
kind: "floor",
|
||
visible: true,
|
||
locked: false,
|
||
floorHeight: 2.6,
|
||
cutHeight: 1.0,
|
||
baseElevation: 0,
|
||
},
|
||
{
|
||
id: "og",
|
||
name: "OG",
|
||
kind: "floor",
|
||
visible: true,
|
||
locked: false,
|
||
floorHeight: 2.6,
|
||
cutHeight: 1.0,
|
||
baseElevation: 2.8,
|
||
},
|
||
],
|
||
layers: [{ code: "30", name: "Decken", color: "#0a0a0a", lw: 0.5, visible: true, locked: false }],
|
||
walls: [],
|
||
doors: [],
|
||
openings: [],
|
||
ceilings: [],
|
||
stairs: [],
|
||
rooms: [],
|
||
drawings2d: [],
|
||
context: [],
|
||
...overrides,
|
||
};
|
||
}
|
||
|
||
function makeCeiling(overrides?: Partial<Ceiling>): Ceiling {
|
||
return {
|
||
id: "cl1",
|
||
type: "ceiling",
|
||
floorId: "eg",
|
||
categoryCode: "30",
|
||
outline: [
|
||
{ x: 0, y: 0 },
|
||
{ x: 4, y: 0 },
|
||
{ x: 4, y: 4 },
|
||
{ x: 0, y: 4 },
|
||
],
|
||
wallTypeId: "dt",
|
||
...overrides,
|
||
};
|
||
}
|
||
|
||
describe("ceilingVerticalExtent", () => {
|
||
it("ohne top/bottom: OK = Geschoss-Oberkante, UK = OK − Dicke (heutiges Verhalten)", () => {
|
||
const project = makeProject();
|
||
const ceiling = makeCeiling();
|
||
const { zBottom, zTop } = ceilingVerticalExtent(project, ceiling);
|
||
expect(zTop).toBeCloseTo(2.6); // baseElevation(0) + floorHeight(2.6)
|
||
expect(zBottom).toBeCloseTo(2.4); // zTop − thickness(0.2)
|
||
});
|
||
|
||
it("mit top: custom übersteuert die Oberkante; UK bleibt OK − Dicke", () => {
|
||
const project = makeProject();
|
||
const ceiling = makeCeiling({ top: { mode: "custom", z: 3.0 } });
|
||
const { zBottom, zTop } = ceilingVerticalExtent(project, ceiling);
|
||
expect(zTop).toBeCloseTo(3.0);
|
||
expect(zBottom).toBeCloseTo(2.8);
|
||
});
|
||
|
||
it("mit bottom: custom übersteuert die Unterkante — gewinnt über OK − Dicke", () => {
|
||
const project = makeProject();
|
||
const ceiling = makeCeiling({ bottom: { mode: "custom", z: 2.2 } });
|
||
const { zBottom, zTop } = ceilingVerticalExtent(project, ceiling);
|
||
expect(zTop).toBeCloseTo(2.6); // OK unverändert (Geschoss-Default)
|
||
expect(zBottom).toBeCloseTo(2.2); // UK-Override statt OK − Dicke (2.4)
|
||
});
|
||
|
||
it("mit top UND bottom: beide Overrides wirken unabhängig (Dicke wird ignoriert)", () => {
|
||
const project = makeProject();
|
||
const ceiling = makeCeiling({
|
||
top: { mode: "custom", z: 5.0 },
|
||
bottom: { mode: "custom", z: 4.5 },
|
||
});
|
||
const { zBottom, zTop } = ceilingVerticalExtent(project, ceiling);
|
||
expect(zTop).toBeCloseTo(5.0);
|
||
expect(zBottom).toBeCloseTo(4.5);
|
||
});
|
||
|
||
it("bottom: floor bindet die Unterkante an ein Geschoss (+ optionalem offset)", () => {
|
||
const project = makeProject();
|
||
const ceiling = makeCeiling({ bottom: { mode: "floor", floorId: "og", offset: 0.1 } });
|
||
const { zBottom, zTop } = ceilingVerticalExtent(project, ceiling);
|
||
expect(zTop).toBeCloseTo(2.6);
|
||
expect(zBottom).toBeCloseTo(2.9); // baseElevation(og)=2.8 + offset(0.1)
|
||
});
|
||
});
|
||
|
||
describe("wallReferenceOffset", () => {
|
||
const w = (extra: Partial<Wall>): Wall => ({ referenceLine: "center", ...extra } as Wall);
|
||
const T = 0.4; // Gesamtdicke → half = 0.2
|
||
|
||
it("bildet die benannten Linien ab (aussen/mitte/innen)", () => {
|
||
expect(wallReferenceOffset(w({ referenceLine: "left" }), T)).toBeCloseTo(0.2, 6);
|
||
expect(wallReferenceOffset(w({ referenceLine: "center" }), T)).toBeCloseTo(0, 6);
|
||
expect(wallReferenceOffset(w({ referenceLine: "right" }), T)).toBeCloseTo(-0.2, 6);
|
||
});
|
||
|
||
it("übersteuert die Referenzlinie mit einem freien Fugen-Versatz", () => {
|
||
// Fuge z. B. nach einer 0.05-Aussenschale: offset = half − 0.05 = 0.15.
|
||
expect(wallReferenceOffset(w({ referenceLine: "left", referenceOffset: 0.15 }), T)).toBeCloseTo(0.15, 6);
|
||
});
|
||
|
||
it("klemmt den Fugen-Versatz auf ±halbe Dicke", () => {
|
||
expect(wallReferenceOffset(w({ referenceOffset: 5 }), T)).toBeCloseTo(0.2, 6);
|
||
expect(wallReferenceOffset(w({ referenceOffset: -5 }), T)).toBeCloseTo(-0.2, 6);
|
||
});
|
||
});
|