35299307d6
Bündelt den über mehrere Sessions gewachsenen, uncommitteten Stand in
einem Basis-Commit, damit Folge-Features isoliert darauf aufsetzen.
Verifikation: tsc --noEmit sauber, vitest 600/600 grün.
Enthalten (Details in PENDENZEN.md ✅-Liste / HANDOVER.md):
- truck-Integration: Profil-Extrusion + Verjüngung + Boolean-CSG (csgrs),
Crate src-tauri/trucksolid, Werkzeug `extrude`, ExtrudedSolid-Modell.
- kernel2d-Port nach Rust/WASM (Phasen 1–5, Diff-Harness).
- render3d 3D-Live-Schnitt = 2D-Schnitt: geschichteter Bodenaufbau,
Prioritäts-Verschneidung (section_boolean.rs), einstellbare
Schichttrennlinien, per-Hatch-Strichstärke, relativeToWall-Orientierung.
- Interop-Export IFC4/STL/OBJ (Loch-Ausschnitt wallMeshCut), Schnellexport.
- Projektdatei .obp + OS-Lock (lock.rs, LockConflictDialog).
- Layout-Blätter (Modell/Editor/Panel/PDF), Ausschnitte, Override-Engine,
Tragwerk-Stützen (Column), BIM-Tree-Panel.
- Bauteil-Typsystem (Tür/Fenster/Treppe-Typen), Betontreppe mit schräger
Laufplatte, Text-/Textbox-Werkzeug, Mess-Werkzeug, 2D/3D-Griffe für
Öffnungen/Treppen, Snap-Symbol-Restyle.
168 lines
6.2 KiB
TypeScript
168 lines
6.2 KiB
TypeScript
// Wand-Resolver: leitet aus den (optionalen) Wand-Attributen die konkreten
|
||
// geometrischen Größen ab — Versatz der Achse über die Dicke (Referenzlinie)
|
||
// und die vertikale Ausdehnung (UK..OK). Alle Defaults entsprechen exakt dem
|
||
// bisherigen Verhalten, sodass bestehende Wände unverändert rendern.
|
||
//
|
||
// Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md).
|
||
|
||
import type { Ceiling, Column, Project, Stair, VerticalAnchor, Wall } from "./types";
|
||
import { ceilingThickness, getFloor } from "./types";
|
||
|
||
/**
|
||
* Versatz der Schichtstapelung entlang der linken Normalen `+n` (leftNormal),
|
||
* sodass die Achse auf der gewünschten Wandfläche liegt. Die Schichten werden
|
||
* normalerweise symmetrisch von `−T/2` (außen, +n-Seite) bis `+T/2` (innen)
|
||
* gestapelt; dieser Versatz wird ZUSÄTZLICH auf jeden Schicht-Offset addiert.
|
||
*
|
||
* • "center" → 0 (Schichten symmetrisch um die Achse — Default).
|
||
* • "left" → +T/2 (Achse auf der +n-/„außen"-Fläche; Wand liegt auf −n).
|
||
* • "right" → −T/2 (Achse auf der −n-/„innen"-Fläche; Wand liegt auf +n).
|
||
*
|
||
* Die Schicht-Offsets laufen von −T/2 (= +n außen) nach +T/2 (= −n innen).
|
||
* Für "left" soll die −T/2-Kante (außen) auf der Achse liegen → +T/2 addieren,
|
||
* sodass die Schichten von 0…T (alle auf der −n-Seite) liegen.
|
||
*/
|
||
export function wallReferenceOffset(
|
||
wall: Wall,
|
||
totalThickness: number,
|
||
): number {
|
||
const ref = wall.referenceLine ?? "center";
|
||
const half = totalThickness / 2;
|
||
if (ref === "left") return +half;
|
||
if (ref === "right") return -half;
|
||
return 0;
|
||
}
|
||
|
||
/** Löst einen VerticalAnchor zu einem absoluten Z (Meter) auf oder `null`. */
|
||
function resolveAnchor(
|
||
project: Project,
|
||
anchor: VerticalAnchor | undefined,
|
||
): number | null {
|
||
if (!anchor) return null;
|
||
if (anchor.mode === "custom") return anchor.z;
|
||
// "floor": baseElevation des gebundenen Geschosses (+ optionalem Offset).
|
||
const floor = project.drawingLevels.find((z) => z.id === anchor.floorId);
|
||
if (!floor) return null;
|
||
return (floor.baseElevation ?? 0) + (anchor.offset ?? 0);
|
||
}
|
||
|
||
/**
|
||
* Vertikale Ausdehnung der Wand (absolute Z-Werte in Metern).
|
||
* • zBottom: aus `wall.bottom`, sonst baseElevation des Wand-Geschosses
|
||
* (= heutiges Verhalten).
|
||
* • zTop: aus `wall.top`, sonst `zBottom + wall.height` (= heute).
|
||
* Sind beide UK/OK ungesetzt, liefert dies exakt `[baseElevation, base+height]`.
|
||
*/
|
||
export function wallVerticalExtent(
|
||
project: Project,
|
||
wall: Wall,
|
||
): { zBottom: number; zTop: number } {
|
||
// Geschoss-baseElevation als Default-UK (heutiges Verhalten). Fehlt das
|
||
// Geschoss, 0 als sicherer Rückfall.
|
||
let floorBase = 0;
|
||
try {
|
||
floorBase = getFloor(project, wall.floorId).baseElevation ?? 0;
|
||
} catch {
|
||
floorBase = 0;
|
||
}
|
||
const zBottom = resolveAnchor(project, wall.bottom) ?? floorBase;
|
||
const zTop = resolveAnchor(project, wall.top) ?? zBottom + wall.height;
|
||
return { zBottom, zTop };
|
||
}
|
||
|
||
/**
|
||
* Vertikale Ausdehnung einer Decke (absolute Z-Werte in Metern).
|
||
* • zTop: aus `ceiling.top`, sonst Oberkante des Geschosses
|
||
* (baseElevation + floorHeight) — also bündig mit dem Wandkopf.
|
||
* • zBottom: aus `ceiling.bottom`, sonst zTop − Deckendicke (die Decke
|
||
* wächst nach UNTEN — heutiges Verhalten). Die `thickness` bleibt bei
|
||
* gesetztem `bottom` rein informativ (dient nicht mehr der UK-Ableitung).
|
||
* Fehlt das Geschoss, dient 0 als sicherer Rückfall für die Oberkante.
|
||
*/
|
||
export function ceilingVerticalExtent(
|
||
project: Project,
|
||
ceiling: Ceiling,
|
||
): { zBottom: number; zTop: number } {
|
||
let floorTop = 0;
|
||
try {
|
||
const floor = getFloor(project, ceiling.floorId);
|
||
floorTop = (floor.baseElevation ?? 0) + (floor.floorHeight ?? 0);
|
||
} catch {
|
||
floorTop = 0;
|
||
}
|
||
const zTop = resolveAnchor(project, ceiling.top) ?? floorTop;
|
||
const zBottom =
|
||
resolveAnchor(project, ceiling.bottom) ??
|
||
zTop - ceilingThickness(project, ceiling);
|
||
return { zBottom, zTop };
|
||
}
|
||
|
||
/**
|
||
* Vertikale Ausdehnung einer Treppe (absolute Z-Werte in Metern).
|
||
* • zBottom: OKFF (baseElevation) des zugehörigen Geschosses.
|
||
* • zTop: zBottom + Gesamt-Steighöhe. Fehlt `stair.totalRise`, gilt die
|
||
* Geschosshöhe (floorHeight) des Geschosses — so steigt die Treppe genau ins
|
||
* nächste Geschoss (OKFF → OKFF).
|
||
* Fehlt das Geschoss, dient 0 als sicherer Rückfall für die Unterkante.
|
||
*/
|
||
export function stairVerticalExtent(
|
||
project: Project,
|
||
stair: Stair,
|
||
): { zBottom: number; zTop: number } {
|
||
let base = 0;
|
||
let floorHeight = 0;
|
||
try {
|
||
const floor = getFloor(project, stair.floorId);
|
||
base = floor.baseElevation ?? 0;
|
||
floorHeight = floor.floorHeight ?? 0;
|
||
} catch {
|
||
base = 0;
|
||
floorHeight = 0;
|
||
}
|
||
const rise =
|
||
stair.totalRise != null && stair.totalRise > 0
|
||
? stair.totalRise
|
||
: floorHeight > 0
|
||
? floorHeight
|
||
: 2.6;
|
||
return { zBottom: base, zTop: base + rise };
|
||
}
|
||
|
||
/**
|
||
* Vertikale Ausdehnung einer Stütze (absolute Z-Werte in Metern) — dieselbe
|
||
* Regel wie {@link wallVerticalExtent}:
|
||
* • zBottom: aus `column.bottom`, sonst baseElevation des Geschosses.
|
||
* • zTop: aus `column.top`, sonst `zBottom + column.height`.
|
||
* Fehlt das Geschoss, dient 0 als sicherer Rückfall für die Unterkante.
|
||
*/
|
||
export function columnVerticalExtent(
|
||
project: Project,
|
||
column: Column,
|
||
): { zBottom: number; zTop: number } {
|
||
let floorBase = 0;
|
||
try {
|
||
floorBase = getFloor(project, column.floorId).baseElevation ?? 0;
|
||
} catch {
|
||
floorBase = 0;
|
||
}
|
||
const zBottom = resolveAnchor(project, column.bottom) ?? floorBase;
|
||
const zTop = resolveAnchor(project, column.top) ?? zBottom + column.height;
|
||
return { zBottom, zTop };
|
||
}
|
||
|
||
/**
|
||
* Das Geschoss DIREKT ÜBER dem Wand-Geschoss (in Dokumentreihenfolge der
|
||
* "floor"-Ebenen) oder `undefined`, wenn die Wand im obersten Geschoss liegt.
|
||
* Dient der „Verknüpfung zum oberen Geschoss" (OK an nächstes Geschoss binden).
|
||
*/
|
||
export function nextFloorAbove(
|
||
project: Project,
|
||
wall: Wall,
|
||
): { id: string; name: string } | undefined {
|
||
const floors = project.drawingLevels.filter((z) => z.kind === "floor");
|
||
const idx = floors.findIndex((z) => z.id === wall.floorId);
|
||
if (idx < 0) return undefined;
|
||
const above = floors[idx + 1];
|
||
return above ? { id: above.id, name: above.name } : undefined;
|
||
}
|