God-Files aufgeteilt (ResourceManager/generatePlan/toWalls3d/App.tsx-Handler), Totcode entfernt (geometry-Crate, OCCT/HLR-Spike), PDF/Bild-Import ergänzt.
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.
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user