1507c2bf00
ResourceManager.tsx, generatePlan.ts und toWalls3d.ts sind nach Bauteil- Domäne in src/ui/resourceManager/, src/plan/generatePlan/ und src/plan/toWalls3d/ aufgeteilt (reine Verschiebung, keine Verhaltens- änderung). App.tsx verliert weitere Handler-Gruppen an eigene Hooks (useExportHandlers, useImportHandling, useKeyboardShortcuts) sowie Hit-Testing an src/viewport/planHitTest.ts. Unbenutzte Rust-geometry-Crate (durch WASM-Joins abgelöst) sowie der alte OCCT/HLR-Schnitt-Spike (hlr.ts/occt.ts, vite occt-Aliases) entfernt. Neu: PDF-Import als Rasterbild (src/io/pdfImport.ts) + gemeinsamer Bild-Asset-Helfer (src/io/imageAsset.ts) für Plan- und Layout-Bilder. tsc -b sauber, vitest 891/891 grün.
186 lines
5.7 KiB
TypeScript
186 lines
5.7 KiB
TypeScript
// Treppen-Domäne des Grundriss-Generators (DOSSIER/SIA): Tritte, Lauflinie mit
|
|
// Auf-/Abpfeil, Zwischenpodest und Aussenlinie — mit dem Grundriss-Schnitt
|
|
// (Tritte unter der Schnitthöhe durchgezogen, darüber gestrichelt/verdeckt +
|
|
// SIA-Bruchlinie).
|
|
|
|
import type { Project, Stair, Vec2 } from "../../model/types";
|
|
import { stairVerticalExtent } from "../../model/wall";
|
|
import { stairCut, stairGeometry, stairOutline } from "../../geometry/stair";
|
|
import type { SpiralOutlineSegments } from "../../geometry/stair";
|
|
import { NO_HATCH, POCHE_STROKE, type Primitive } from "./shared";
|
|
|
|
/**
|
|
* Zeichnet die Wendel-Aussenlinie: Innen-/Aussenbogen als Arc-Primitive plus zwei
|
|
* radiale Schliesslinien. Wird von `addStairSymbol` aufgerufen wenn `shape === "spiral"`.
|
|
*/
|
|
function _addSpiralOutline(
|
|
out: Primitive[],
|
|
seg: SpiralOutlineSegments,
|
|
stroke: string,
|
|
lwMm: number,
|
|
greyed: boolean,
|
|
stairId: string,
|
|
): void {
|
|
// Radiale Schliesslinien (Startwinkel + Endwinkel).
|
|
for (const [a, b] of seg.lines) {
|
|
out.push({ kind: "line", a, b, cls: "stair-outline", weightMm: lwMm, color: stroke, greyed, stairId });
|
|
}
|
|
// Innen- und Aussenbogen.
|
|
for (const arc of seg.arcs) {
|
|
const from: Vec2 = { x: arc.cx + arc.r * Math.cos(arc.a0), y: arc.cy + arc.r * Math.sin(arc.a0) };
|
|
const to: Vec2 = { x: arc.cx + arc.r * Math.cos(arc.a1), y: arc.cy + arc.r * Math.sin(arc.a1) };
|
|
out.push({
|
|
kind: "arc",
|
|
center: { x: arc.cx, y: arc.cy },
|
|
from,
|
|
to,
|
|
r: arc.r,
|
|
cls: "stair-outline",
|
|
weightMm: lwMm,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Treppen-Symbol im Grundriss (DOSSIER/SIA):
|
|
* • Tritte als Rechtecke: die Tritt-Trennlinien werden gezeichnet — unter der
|
|
* Schnittebene DURCHGEZOGEN (sichtbar), darüber GESTRICHELT (verdeckt).
|
|
* • Der erste Tritt oberhalb der Schnittebene trägt die diagonale SIA-Bruchlinie
|
|
* (Doppelstrich quer über den Lauf).
|
|
* • Zwischenpodest (L/Wendel) als Umriss-Polygon.
|
|
* • Lauflinie (Lauflinie) mit Auf-/Abpfeil (Auf-/Abpfeil) und Stufenanzahl-Label.
|
|
* Alle Primitive tragen `stairId` für die Auswahl.
|
|
*/
|
|
export function addStairSymbol(
|
|
out: Primitive[],
|
|
project: Project,
|
|
stair: Stair,
|
|
greyed: boolean,
|
|
lwMm: number,
|
|
cutHeight: number,
|
|
): void {
|
|
const { zBottom, zTop } = stairVerticalExtent(project, stair);
|
|
const totalRise = zTop - zBottom;
|
|
const geo = stairGeometry(stair, totalRise);
|
|
const cut = stairCut(geo, cutHeight);
|
|
const below = new Set(cut.belowIndices);
|
|
const stroke = stair.color ?? POCHE_STROKE;
|
|
const stairId = stair.id;
|
|
|
|
// Umriss + Tritt-Trennlinien je Stufe. Der letzte Punkt jedes Tritt-Rechtecks
|
|
// liefert die vordere Kante (Auftritt); wir zeichnen jede Tritt-Kante.
|
|
for (const tr of geo.treads) {
|
|
const isBelow = below.has(tr.index);
|
|
// Tritt-Rechteck als reine Umrisslinie (keine Füllung), durchgezogen wenn
|
|
// unter der Schnittebene, sonst gestrichelt (verdeckt).
|
|
out.push({
|
|
kind: "polygon",
|
|
pts: tr.pts,
|
|
fill: "none",
|
|
stroke,
|
|
strokeWidthMm: lwMm * 0.7,
|
|
hatch: NO_HATCH,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
// Zusätzliche Auftrittskante (vordere Kante der Stufe) etwas kräftiger.
|
|
const front: [Vec2, Vec2] = [tr.pts[1], tr.pts[2]];
|
|
out.push({
|
|
kind: "line",
|
|
a: front[0],
|
|
b: front[1],
|
|
cls: "stair-tread",
|
|
weightMm: lwMm * (isBelow ? 1.0 : 0.6),
|
|
dash: isBelow ? null : [0.12, 0.08],
|
|
color: stroke,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
|
|
// Zwischenpodest (L/Wendel-Auge): Umriss-Polygon.
|
|
if (geo.landing && geo.landing.length >= 3) {
|
|
out.push({
|
|
kind: "polygon",
|
|
pts: geo.landing,
|
|
fill: "none",
|
|
stroke,
|
|
strokeWidthMm: lwMm * 0.7,
|
|
hatch: NO_HATCH,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
|
|
// SIA-Bruchlinie (Doppelstrich diagonal über den Schnitt-Tritt).
|
|
if (cut.breakLine) {
|
|
for (const [a, b] of cut.breakLine) {
|
|
out.push({
|
|
kind: "line",
|
|
a,
|
|
b,
|
|
cls: "stair-break",
|
|
weightMm: lwMm,
|
|
color: stroke,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Lauflinie (Lauflinie) — kräftig, durchgezogen.
|
|
for (let i = 0; i < geo.runLine.length - 1; i++) {
|
|
out.push({
|
|
kind: "line",
|
|
a: geo.runLine[i],
|
|
b: geo.runLine[i + 1],
|
|
cls: "stair-run",
|
|
weightMm: lwMm,
|
|
color: stroke,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
// Auf-/Abpfeil an der Spitze der Lauflinie. "filled" = gefülltes Dreieck
|
|
// (SIA-Vollpfeil), sonst der offene Winkelpfeil (zwei Linien, Default).
|
|
const [h1, tip, h2] = geo.arrow.head;
|
|
if (stair.arrowStyle === "filled") {
|
|
out.push({
|
|
kind: "polygon",
|
|
pts: [h1, tip, h2],
|
|
fill: stroke,
|
|
stroke,
|
|
strokeWidthMm: lwMm,
|
|
hatch: NO_HATCH,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
} else {
|
|
out.push({ kind: "line", a: h1, b: tip, cls: "stair-arrow", weightMm: lwMm, color: stroke, greyed, stairId });
|
|
out.push({ kind: "line", a: h2, b: tip, cls: "stair-arrow", weightMm: lwMm, color: stroke, greyed, stairId });
|
|
}
|
|
|
|
// Aussenlinie (Outline) der Treppe — schliessendes Umriss-Polygon (gerade/L)
|
|
// bzw. Innen-/Aussenbogen + radiale Schliesslinien (Wendel).
|
|
// Entspricht `_aussen_gerade` / `_aussen_l_polygon` / `_aussen_wendel` im
|
|
// Rhino-Plugin. Stets durchgezogen, volle Strichstärke.
|
|
const outline = stairOutline(stair, totalRise);
|
|
if (outline.polygon && outline.polygon.length >= 3) {
|
|
out.push({
|
|
kind: "polygon",
|
|
pts: outline.polygon,
|
|
fill: "none",
|
|
stroke,
|
|
strokeWidthMm: lwMm,
|
|
hatch: NO_HATCH,
|
|
greyed,
|
|
stairId,
|
|
});
|
|
}
|
|
if (outline.spiral) {
|
|
_addSpiralOutline(out, outline.spiral, stroke, lwMm, greyed, stairId);
|
|
}
|
|
}
|