Neuer Bezugspunkt: Projekt nach Kataster-/3D-Import auf praktischen Ursprung verschieben

Nach einem Standort-Import landet die Kontext-Geometrie dort, wo der gesuchte
Ort war — das kann weit vom bisherigen Modell-Ursprung liegen. Neuer Befehl
"georef" (Button in SitePanel) verschiebt das GESAMTE Projekt per Klick so,
dass der gewählte Punkt zum neuen Ursprung wird; geoAnchor wandert automatisch
mit, der reale LV95-Bezug bleibt exakt erhalten (rekonstruierbar, auch beim
späteren Export). Bewusst nur Translation, keine Rotation — mehrere Felder
(Roof.ridgeAxis, Column.rotation, Text-/Bild-Rotation) sind achsen-/winkel-
gebunden und bräuchten bei einer Drehung eigene Sorgfalt.
This commit is contained in:
2026-07-12 20:13:22 +02:00
parent 0ab3f70277
commit 5be49c5557
10 changed files with 474 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
// Ursprungs-Verschiebung des GESAMTEN Projekts (Neuer Bezugspunkt) — reine,
// verlustfreie Translation aller Modell-Koordinaten um einen festen Vektor
// (dx,dy). Genutzt, um nach einem Kataster-/3D-Import (der weit weg vom
// Modell-Ursprung landen kann, s. `Project.geoAnchor`) einen neuen, praktischen
// Bezugspunkt zu setzen: die komplette Zeichnung wandert relativ zueinander
// unverändert, nur ihre absoluten Koordinaten verschieben sich. `geoAnchor`
// (falls gesetzt) wandert MIT, sodass der LV95-Bezug exakt erhalten bleibt —
// beim Export ist die Verschiebung damit jederzeit rekonstruierbar.
//
// Bewusst NUR Translation (keine Rotation): mehrere Felder im Modell sind an
// die Achsen gebunden (z. B. `Roof.ridgeAxis: "x"|"y"`) und mehrere Winkel-Felder
// (Column.rotation, Text-/Bild-Rotation, Tür-Schwenk) müssten bei einer Drehung
// mitgeführt werden — eine Rotation ist ein eigener, grösserer Schritt.
//
// Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md).
import type {
ContextObject,
Contour,
Project,
Vec2,
} from "./types";
const shift = (p: Vec2, dx: number, dy: number): Vec2 => ({ x: p.x + dx, y: p.y + dy });
const shiftAll = (pts: Vec2[], dx: number, dy: number): Vec2[] => pts.map((p) => shift(p, dx, dy));
/** Kontur (Höhenlinie): Punkte + optionale Kreis-/Bogen-Mitte verschieben. */
function shiftContour(c: Contour, dx: number, dy: number): Contour {
return {
...c,
pts: shiftAll(c.pts, dx, dy),
curve: c.curve ? { ...c.curve, cx: c.curve.cx + dx, cy: c.curve.cy + dy } : undefined,
};
}
/**
* Kontext-Objekt (importiertes Mesh/Terrain/Konturen-Satz) verschieben.
* `positions` ist ein flaches [x,y,z=Höhe, …]-Array — nur x/y (Grundriss)
* verschieben, die Höhe (z) bleibt unverändert.
*/
function shiftContextObject(obj: ContextObject, dx: number, dy: number): ContextObject {
if (obj.type === "contourSet") {
return { ...obj, contours: obj.contours.map((c) => shiftContour(c, dx, dy)) };
}
const positions = obj.positions.slice();
for (let i = 0; i + 2 < positions.length; i += 3) {
positions[i] += dx;
positions[i + 1] += dy;
}
return { ...obj, positions };
}
/**
* Verschiebt SÄMTLICHE Modell-Koordinaten eines Projekts um den festen Vektor
* (dx,dy) — Wände, Decken, Dächer, Öffnungen-Host (folgt automatisch der Wand),
* Treppen, Räume, Stützen, Extrusionen, freie 2D-Geometrie, Kontext-Schicht
* (Import/Terrain/Konturen), Schnitt-/Ansichtslinien sowie den Geo-Anker
* (`geoAnchor.model`, damit der LV95-Bezug exakt erhalten bleibt). Richtungs-
* Vektoren (z. B. `Stair.dir`) und Winkel-Felder bleiben unangetastet — eine
* reine Translation ändert keine Orientierung.
*/
export function translateProject(project: Project, dx: number, dy: number): Project {
if (dx === 0 && dy === 0) return project;
return {
...project,
walls: project.walls.map((w) => ({
...w,
start: shift(w.start, dx, dy),
end: shift(w.end, dx, dy),
})),
ceilings: project.ceilings?.map((c) => ({
...c,
outline: shiftAll(c.outline, dx, dy),
openings: c.openings?.map((h) => shiftAll(h, dx, dy)),
})),
roofs: project.roofs?.map((r) => ({ ...r, outline: shiftAll(r.outline, dx, dy) })),
stairs: project.stairs?.map((s) => ({
...s,
start: shift(s.start, dx, dy),
center: s.center ? shift(s.center, dx, dy) : undefined,
})),
rooms: project.rooms?.map((r) => ({
...r,
boundary: shiftAll(r.boundary, dx, dy),
stampAnchor: r.stampAnchor ? shift(r.stampAnchor, dx, dy) : undefined,
})),
columns: project.columns?.map((c) => ({ ...c, position: shift(c.position, dx, dy) })),
extrudedSolids: project.extrudedSolids?.map((s) => ({
...s,
points: shiftAll(s.points, dx, dy),
circle: s.circle ? { ...s.circle, center: shift(s.circle.center, dx, dy) } : undefined,
})),
drawings2d: project.drawings2d.map((d) => {
const g = d.geom;
switch (g.shape) {
case "line":
return { ...d, geom: { ...g, a: shift(g.a, dx, dy), b: shift(g.b, dx, dy) } };
case "polyline":
return { ...d, geom: { ...g, pts: shiftAll(g.pts, dx, dy) } };
case "rect":
return { ...d, geom: { ...g, min: shift(g.min, dx, dy), max: shift(g.max, dx, dy) } };
case "circle":
return { ...d, geom: { ...g, center: shift(g.center, dx, dy) } };
case "arc":
return { ...d, geom: { ...g, center: shift(g.center, dx, dy) } };
case "text":
return { ...d, geom: { ...g, at: shift(g.at, dx, dy) } };
}
}),
context: project.context?.map((o) => shiftContextObject(o, dx, dy)),
drawingLevels: project.drawingLevels.map((lvl) =>
lvl.linePoints
? { ...lvl, linePoints: [shift(lvl.linePoints[0], dx, dy), shift(lvl.linePoints[1], dx, dy)] }
: lvl,
),
geoAnchor: project.geoAnchor
? { ...project.geoAnchor, model: shift(project.geoAnchor.model, dx, dy) }
: undefined,
};
}