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:
2026-08-19 22:45:41 +02:00
parent 06c0143d32
commit 1507c2bf00
85 changed files with 12461 additions and 11280 deletions
+47
View File
@@ -0,0 +1,47 @@
// Wand-Trefferprüfung im Modellraum fürs Grundriss-Kontextmenü — reine
// Geometrie-Helfer ohne Komponenten-Bezug, ausgelagert aus App.tsx.
import type { Project, Vec2 } from "../model/types";
import { getWallType, wallTypeThickness } from "../model/types";
import { wallCorners } from "../model/geometry";
/**
* Liefert die ID der obersten Wand, deren (gehrtes) Band den Modellpunkt
* enthält — gefiltert auf das aktive Geschoss + sichtbare Kategorien, in
* umgekehrter Wand-Reihenfolge (zuletzt gezeichnet zuerst). Spiegelt die
* Trefferlogik der PlanView, arbeitet aber direkt auf den Wand-Polygonen, da
* das Grundriss-Kontextmenü nur den Modellpunkt kennt. `null` bei Leerraum.
*/
export function hitTestWall(
project: Project,
floorId: string,
visibleCodes: Set<string>,
m: Vec2,
): string | null {
const walls = project.walls.filter(
(w) => w.floorId === floorId && visibleCodes.has(w.categoryCode),
);
for (let i = walls.length - 1; i >= 0; i--) {
const w = walls[i];
const thickness = wallTypeThickness(getWallType(project, w));
const corners = wallCorners(w.start, w.end, thickness);
if (pointInPoly(m, corners)) return w.id;
}
return null;
}
/** Punkt-in-Polygon (Ray-Casting) im Modellraum — wie in der PlanView. */
export function pointInPoly(pt: Vec2, poly: Vec2[]): boolean {
let inside = false;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const xi = poly[i].x;
const yi = poly[i].y;
const xj = poly[j].x;
const yj = poly[j].y;
const intersect =
yi > pt.y !== yj > pt.y &&
pt.x < ((xj - xi) * (pt.y - yi)) / (yj - yi) + xi;
if (intersect) inside = !inside;
}
return inside;
}