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.
89 lines
3.9 KiB
TypeScript
89 lines
3.9 KiB
TypeScript
// Freie-2D-Geometrie-Domäne der 3D-Ableitung: zeigt jede 2D-Zeichnung
|
|
// (line/polyline/rect) als dünnes Boden-Ribbon-Mesh knapp über ihrer
|
|
// Geschossebene — die "2D-Referenz im 3D", die der drei.js-Fallback
|
|
// (`addDrawing2DLines` in Viewport3D.tsx) zeigt.
|
|
|
|
import type { Drawing2D, Project, Vec2 } from "../../model/types";
|
|
import { flattenCategories, getFloor } from "../../model/types";
|
|
import { DRAWING_LINE_FALLBACK_RGB, hexToRgb, type RMesh, type RRgb } from "./shared";
|
|
|
|
/** Halbe Linienbreite der 2D-Boden-Spuren im 3D (Meter) — liest sich als dünne Linie. */
|
|
const DRAWING_LINE_HALF_WIDTH = 0.008;
|
|
/** Höhen-Offset über der Geschossebene gegen Z-Fighting (wie addDrawing2DLines in Viewport3D.tsx). */
|
|
const DRAWING_LINE_ELEVATION_EPS = 0.01;
|
|
|
|
/**
|
|
* Strecken eines 2D-Zeichenelements für die Boden-Spur im 3D — exakt dieselbe
|
|
* Teilmenge wie `drawing2DSegments` im drei.js-Fallback (`Viewport3D.tsx`):
|
|
* line/polyline/rect. Kreis/Bogen/Text bleiben aussen vor (keine neue
|
|
* Fähigkeit gegenüber dem Fallback, nur Parität).
|
|
*/
|
|
function drawingSegments3d(d: Drawing2D): [Vec2, Vec2][] {
|
|
const g = d.geom;
|
|
const out: [Vec2, Vec2][] = [];
|
|
if (g.shape === "line") out.push([g.a, g.b]);
|
|
else if (g.shape === "polyline") {
|
|
for (let i = 0; i < g.pts.length - 1; i++) out.push([g.pts[i], g.pts[i + 1]]);
|
|
if (g.closed && g.pts.length > 2) out.push([g.pts[g.pts.length - 1], g.pts[0]]);
|
|
} else if (g.shape === "rect") {
|
|
const c1 = g.min, c3 = g.max;
|
|
const c2 = { x: g.max.x, y: g.min.y }, c4 = { x: g.min.x, y: g.max.y };
|
|
out.push([c1, c2], [c2, c3], [c3, c4], [c4, c1]);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** Modellfarbe einer 2D-Zeichnung (color → LineStyle → Kategorie), analog `drawing2DColor`. */
|
|
function drawingColor3d(project: Project, d: Drawing2D): RRgb {
|
|
if (d.color) return hexToRgb(d.color, DRAWING_LINE_FALLBACK_RGB);
|
|
const ls = d.lineStyleId ? project.lineStyles.find((l) => l.id === d.lineStyleId) : undefined;
|
|
if (ls?.color) return hexToRgb(ls.color, DRAWING_LINE_FALLBACK_RGB);
|
|
const cat = flattenCategories(project.layers).find((c) => c.code === d.categoryCode);
|
|
return cat?.color ? hexToRgb(cat.color, DRAWING_LINE_FALLBACK_RGB) : DRAWING_LINE_FALLBACK_RGB;
|
|
}
|
|
|
|
/**
|
|
* Emittiert je 2D-Zeichnung ein dünnes Ribbon-Mesh knapp über ihrer
|
|
* Geschossebene — die "2D-Referenz im 3D" (z. B. ein Boden-Raster), die der
|
|
* drei.js-Fallback (`addDrawing2DLines`) zeigt, bisher aber im nativen
|
|
* render3d/wgpu-Pfad fehlte (`RModel3d` kannte nur walls/slabs/meshes).
|
|
* `positions` in MODELL-Metern (x, y, z=Höhe) wie `emitOpeningGlass`; render3d
|
|
* rendert Kontext-Meshes ohnehin doppelseitig (`append_context_mesh`), daher
|
|
* genügt ein flaches Ribbon (kein Quader) pro Strecke.
|
|
*/
|
|
export function emitDrawingLines(project: Project): RMesh[] {
|
|
const out: RMesh[] = [];
|
|
for (const d of project.drawings2d) {
|
|
const segs = drawingSegments3d(d);
|
|
if (segs.length === 0) continue;
|
|
let baseZ = 0;
|
|
try {
|
|
baseZ = getFloor(project, d.levelId).baseElevation ?? 0;
|
|
} catch {
|
|
// Geschoss nicht (mehr) auflösbar: bei UK 0 zeichnen (Fallback).
|
|
}
|
|
const elev = baseZ + DRAWING_LINE_ELEVATION_EPS;
|
|
const positions: number[] = [];
|
|
const indices: number[] = [];
|
|
for (const [a, b] of segs) {
|
|
const dx = b.x - a.x;
|
|
const dy = b.y - a.y;
|
|
const len = Math.hypot(dx, dy);
|
|
if (len < 1e-9) continue;
|
|
const nx = (-dy / len) * DRAWING_LINE_HALF_WIDTH;
|
|
const ny = (dx / len) * DRAWING_LINE_HALF_WIDTH;
|
|
const base = positions.length / 3;
|
|
positions.push(
|
|
a.x + nx, a.y + ny, elev,
|
|
a.x - nx, a.y - ny, elev,
|
|
b.x - nx, b.y - ny, elev,
|
|
b.x + nx, b.y + ny, elev,
|
|
);
|
|
indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
|
|
}
|
|
if (positions.length === 0) continue;
|
|
out.push({ positions, indices, kind: "imported", color: drawingColor3d(project, d) });
|
|
}
|
|
return out;
|
|
}
|