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
+49 -3
View File
@@ -25,6 +25,7 @@ import type {
} from "../model/types";
import { commitTransform } from "../tools/transform";
import type { CopyMode, TransformOp, TransformSelection } from "../tools/transform";
import { uniqueId } from "../tools/types";
import { centroid as roomCentroid } from "../geometry/roomArea";
import { t } from "../i18n";
import type { StoreApi } from "./store";
@@ -1417,6 +1418,18 @@ function resizeGeom(
const { ax, ay, sx, sy } = scaleAround(box, w, h, anchor);
return { ...g, at: { x: ax + (g.at.x - ax) * sx, y: ay + (g.at.y - ay) * sy } };
}
case "image": {
const box = bbox2([g.min, g.max]);
const { ax, ay, sx, sy } = scaleAround(box, w, h, anchor);
const f = (p: Vec2): Vec2 => ({ x: ax + (p.x - ax) * sx, y: ay + (p.y - ay) * sy });
const a = f(g.min);
const b = f(g.max);
return {
...g,
min: { x: Math.min(a.x, b.x), y: Math.min(a.y, b.y) },
max: { x: Math.max(a.x, b.x), y: Math.max(a.y, b.y) },
};
}
default:
return g;
}
@@ -1432,7 +1445,7 @@ export function drawingVertices(d: import("../model/types").Drawing2D): Vec2[] {
const g = d.geom;
if (g.shape === "line") return [g.a, g.b];
if (g.shape === "polyline") return [...g.pts];
if (g.shape === "rect") {
if (g.shape === "rect" || g.shape === "image") {
return [
g.min,
{ x: g.max.x, y: g.min.y },
@@ -1445,6 +1458,38 @@ export function drawingVertices(d: import("../model/types").Drawing2D): Vec2[] {
return [];
}
/**
* Fügt ein Bild-Zeichenelement (eingebettete Data-URL, z. B. Foto/Logo oder
* gerasterte PDF-Seite) achsparallel bei `[at .. at+size]` an — analog zu den
* Zeichenwerkzeugen, aber ohne Command (Datei-Auswahl ist async, s. App.tsx
* Bild-/PDF-Import). Direkt selektiert zurückgegeben, damit Griffe/Objekt-Info
* sofort zum Verschieben/Skalieren bereitstehen.
*/
export function addImageDrawing2d(
project: Project,
levelId: string,
categoryCode: string,
src: string,
at: Vec2,
widthM: number,
heightM: number,
): { project: Project; id: string } {
const id = uniqueId("dr2d");
const d: Drawing2D = {
id,
type: "drawing2d",
levelId,
categoryCode,
geom: {
shape: "image",
min: at,
max: { x: at.x + widthM, y: at.y + heightM },
src,
},
};
return { project: { ...project, drawings2d: [...project.drawings2d, d] }, id };
}
/**
* Verschiebt den Griff `index` des selektierten Elements (2D-Element ODER Wand)
* auf `pt` (immutabel). Beim Rechteck bleibt die Achsparallelität erhalten.
@@ -1469,7 +1514,7 @@ function moveGrip(
const pts = g.pts.map((p, i) => (i === index ? pt : p));
return { ...d, geom: { ...g, pts } };
}
if (g.shape === "rect") {
if (g.shape === "rect" || g.shape === "image") {
const corners = [
g.min,
{ x: g.max.x, y: g.min.y },
@@ -1518,6 +1563,7 @@ function moveElementBy(
case "polyline":
return { ...d, geom: { ...g, pts: g.pts.map(mv) } };
case "rect":
case "image":
return { ...d, geom: { ...g, min: mv(g.min), max: mv(g.max) } };
case "circle":
case "arc":
@@ -1573,7 +1619,7 @@ function moveEdge(
const pts = g.pts.map((p, i) => (i === aIndex || i === bIndex ? mv(p) : p));
return { ...d, geom: { ...g, pts } };
}
if (g.shape === "rect") {
if (g.shape === "rect" || g.shape === "image") {
// Ecken in derselben Reihenfolge wie drawingVertices, die zwei Kanten-
// Ecken verschieben, dann bbox neu bilden (achsparallel halten).
const corners = [