3d2d4d6321
Neuer GPU-Renderer fuer den Grundriss (src/plan/glPlan/): Earcut-Tessellierung (konkav-faehig), gehrte Linienzuege (Miter), echte Papier-mm-Strichbreiten im Massstab (repliziert den SVG-printStrokeVb-Pfad), Hybrid mit scharfem SVG-Text- Overlay. GPU ist der Standardpfad; der SVG-Renderer bleibt automatischer Fallback, falls WebGL2/Shader nicht verfuegbar sind. Imperativer Pan (rAF + CSS-transform) fuer fluessige Interaktion ohne React-Re-Render je Frame. Enthaelt zudem den bisher nicht committeten Arbeitsstand des Browser-BIM (Oeffnungen, Treppen, Raeume, Decken, DXF-Export, Materialbibliothek, Kontext- Import, Tauri-Compute-Boundary-PoC).
307 lines
9.0 KiB
Markdown
307 lines
9.0 KiB
Markdown
# Architektur-Pivot: Tauri + Rust-Backend (2026-07-01)
|
||
|
||
## Entscheidung
|
||
|
||
**Alte Welt:** Browser-CAD (React/Vite + WebGL/three.js)
|
||
**Neue Welt:** Desktop Tauri-App (React/Vite Frontend + Rust-Backend + **wgpu 3D-Rendering**)
|
||
|
||
**Grund:** Komplexe Möbel mit vielen Polygonen (100k+) + mehrere parallele rechenintensive Ops → WebGL/three.js wird zum Bottleneck. **wgpu** (low-level GPU-API auf Vulkan/Metal/DX12) + Rust-Compute skaliert native.
|
||
|
||
**Scope:** Desktop-only Release (Milestone 1). WASM-Fallback für Browser später wenn gebraucht.
|
||
|
||
---
|
||
|
||
## Post-Migration Stack
|
||
|
||
### Frontend (React/Vite — Komponenten + State, unverändert)
|
||
|
||
```
|
||
src/
|
||
App.tsx ← Shell-Komponente
|
||
compute/index.ts ← Compute-Boundary (neu)
|
||
model/types.ts ← Semantisches Modell
|
||
commands/ ← Befehlssystem
|
||
panels/ ← UI-Panels
|
||
plan/PlanView.tsx ← 2D-SVG-Rendering
|
||
viewport/Viewport3D.tsx ← three.js 3D-Display
|
||
ui/ ← Topbar, Dialogs, etc.
|
||
state/ ← Redux-Slices (project, selection, view, layout)
|
||
...
|
||
```
|
||
|
||
**Rolle:** User-Input-Handling, 2D/3D-Darstellung (Display-Layer), State-Management.
|
||
|
||
### Backend (Rust/Tauri — neu)
|
||
|
||
```
|
||
src-tauri/
|
||
src/
|
||
main.rs ← Tauri window + invoke-handler registration
|
||
geometry.rs ← compute_joins(), kernel2d(), etc.
|
||
parsers/
|
||
dwg.rs ← DXF/DWG-Geometrie-Parsing
|
||
dxf.rs
|
||
sia/
|
||
room_detection.rs ← detectRooms() (SIA-416)
|
||
...
|
||
Cargo.toml ← Dependencies (serde, tauri, …)
|
||
```
|
||
|
||
**Rolle:** Rechenintensive Ops, Geometrie-Kernel, Parsing, SIA-Raumerkennung.
|
||
|
||
### IPC: Tauri invoke (async, serde)
|
||
|
||
```typescript
|
||
// Frontend ruft Rust auf
|
||
const result = await invoke<JoinInfo[]>('compute_joins', { walls, joints });
|
||
|
||
// Rust bearbeitet + serialisiert Ergebnis
|
||
#[tauri::command]
|
||
async fn compute_joins(input: JoinInput) -> Result<JoinOutput, String> {
|
||
geometry::compute_joins(input).map_err(|e| e.to_string())
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Compute-Boundary (Key Design)
|
||
|
||
**Neue Datei:** `src/compute/index.ts` — einziger Eingang für rechenintensive Ops.
|
||
|
||
```typescript
|
||
// Beispiel-Schnittstellen
|
||
export async function computeJoins(walls: Wall[], joints: Array<[number, number]>): Promise<JoinInfo[]> { … }
|
||
export async function computeKernel2D(op: 'offset'|'trim', geom: Polyline, …): Promise<Polyline[]> { … }
|
||
export async function detectRooms(…): Promise<Room[]> { … }
|
||
export async function parseShapeFromDwg(…): Promise<DwgGeometry> { … }
|
||
```
|
||
|
||
**Hinter der Boundary:**
|
||
1. Versuche Tauri invoke zu Rust (`#[tauri::command]`)
|
||
2. Fallback auf lokale TS-Impl wenn Rust nicht verfügbar (während Migration)
|
||
|
||
**Effekt:** Aufrufort im Code bleibt stabil; Caller sieht nicht, ob Op schon in Rust ist oder noch TS.
|
||
|
||
**Migrationsfluss:**
|
||
```
|
||
1. TS-Impl existiert (z.B. src/model/joins.ts)
|
||
2. Neue Op in Compute-Boundary mit Invoke+Fallback
|
||
3. Parallel: Rust-Impl in src-tauri/src/geometry.rs
|
||
4. Tests: Rust-Output == TS-Output (Parität)
|
||
5. TS-Impl bleibt (Fallback, wird nicht entfernt bis Rust stable)
|
||
6. Eventuell: TS-Impl löschen wenn Rust bewährt
|
||
```
|
||
|
||
---
|
||
|
||
## Dev-Workflow (post-Tauri)
|
||
|
||
### Development
|
||
|
||
```bash
|
||
# Terminal 1: Vite dev-server
|
||
npm run dev # localhost:5173
|
||
|
||
# Terminal 2: Tauri dev
|
||
npm run tauri:dev # öffnet Tauri-Fenster, zeigt auf localhost:5173
|
||
# Rust hot-reload + TS hot-reload gleichzeitig
|
||
```
|
||
|
||
**Voraussetzungen:**
|
||
- Node.js + npm (wie heute)
|
||
- Rust + Cargo (neu)
|
||
- Tauri CLI: `npm install -D @tauri-apps/cli`
|
||
|
||
### Build
|
||
|
||
```bash
|
||
# Single command
|
||
npm run tauri:build
|
||
|
||
# Erzeugt:
|
||
# - Windows: src-tauri/target/release/cad.exe
|
||
# - macOS: src-tauri/target/release/bundle/macos/cad.app
|
||
# - Linux: src-tauri/target/release/bundle/deb/cad_*.deb (oder Flatpak)
|
||
```
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Rust-Unit-Tests
|
||
cargo test # in src-tauri/
|
||
|
||
# TS-Tests (unverändert)
|
||
npm run test
|
||
|
||
# Integration-Test: App starten + Aktion prüfen
|
||
npm run tauri:dev # manuell testen oder Puppeteer-Probe erweitern
|
||
```
|
||
|
||
---
|
||
|
||
## GPU-Strategy (für später)
|
||
|
||
**Milestone 1 (jetzt):** CPU-Ops in Rust (kernel2d, joins, parsing, SIA).
|
||
|
||
**Milestone 2 (später):** GPU-Compute via wgpu
|
||
- Tauri + wgpu Renderer (optional, nicht erforderlich)
|
||
- ODER drei.js bleibt, Rust handelt CPU-Ops, three.js handelt Display
|
||
- GPU-Heavy-Ops (z.B. große Boolean-Operationen) können in wgpu laufen, aber MVP braucht das nicht
|
||
|
||
**Aktueller Plan:** three.js bleibt für 3D-Display (skaliert ausreichend für Möbel-Geometrie mit Instancing + LOD).
|
||
|
||
---
|
||
|
||
## Migration Strategy: Ops nach Priorisierung
|
||
|
||
**Phase 1 (aktuell — Tauri-Shell + Proof-of-Concept):**
|
||
- [ ] `computeJoins` (Wand-Eckverbindungen) → Rust
|
||
- Gründe: klein, häufig, zeigt invoke-Flow
|
||
|
||
**Phase 2 (nächst):**
|
||
- [ ] `kernel2d` (Offset/Trim/Extend/Fillet) → Rust
|
||
- Gründe: Rechenlast ⭐⭐, Frequenz hoch
|
||
- [ ] DXF/DWG-Parser → Rust (Geometrie-Extraktion)
|
||
- Gründe: Rechenlast ⭐⭐, Frequenz mittel (Import-Dialog)
|
||
|
||
**Phase 3 (später):**
|
||
- [ ] `detectRooms` (SIA-416 Raumerkennung) → Rust
|
||
- Gründe: Rechenlast ⭐, async-freundlich
|
||
- [ ] `booleanOps` (Union/Differenz/Schnitt) → Rust
|
||
- Gründe: Rechenlast ⭐⭐, Frequenz gering (ad-hoc)
|
||
|
||
---
|
||
|
||
## Folgen für bestehenden Code
|
||
|
||
### Was ändert sich NICHT
|
||
|
||
- `src/model/types.ts` — semantisches Modell bleibt in TS (Frontend kennt es)
|
||
- `src/state/` — Redux-Store unverändert
|
||
- `src/ui/` — Komponenten unverändert
|
||
- `src/plan/PlanView.tsx` — SVG-Rendering unverändert
|
||
- `src/viewport/Viewport3D.tsx` — three.js-Rendering unverändert
|
||
- `src/commands/` — Befehlssystem unverändert
|
||
|
||
### Was ändert sich
|
||
|
||
- **Neue `src/compute/index.ts`** — alle rechenintensiven Ops laufen durch hier
|
||
- **Neue `src-tauri/`** — Rust-Backend
|
||
- **Vite-Config:** Tauri plugin hinzufügen
|
||
- **Package.json:** tauri scripts hinzufügen
|
||
- **Build-Prozess:** `npm run tauri:build` statt `npm run build`
|
||
|
||
### Was wird migriert (schrittweise)
|
||
|
||
- `src/model/joins.ts` → `src-tauri/src/geometry.rs` (Phase 1)
|
||
- `src/geometry/kernel2d.ts` → `src-tauri/src/geometry.rs` (Phase 2)
|
||
- `src/io/{dxfParser, dwgParser}.ts` → `src-tauri/src/parsers/` (Phase 2)
|
||
- `src/geometry/{roomArea, roomBoundary}.ts` → `src-tauri/src/sia/room_detection.rs` (Phase 3)
|
||
- `src/editors/booleanOps.ts` → `src-tauri/src/geometry.rs` (Phase 3)
|
||
|
||
**Wichtig:** TS-Versionen bleiben als Fallback (nicht gelöscht).
|
||
|
||
---
|
||
|
||
## Distribution (später)
|
||
|
||
### Desktop Binaries (post-Tauri)
|
||
|
||
- **Windows:** `.exe` (standalone executable)
|
||
- **macOS:** `.app` bundle (code-signed)
|
||
- **Linux:** `.deb` package ODER **Flatpak** (preferred)
|
||
- Flatpak = moderne WebKitGTK6 immer dabei, unabhängig von Distro-Alter
|
||
|
||
### Browser (wenn gebraucht)
|
||
|
||
- **WASM-Fallback** für `src/compute/` Ops (Rust → WASM via wasm-bindgen)
|
||
- Later-phase feature, nicht Milestone 1
|
||
|
||
---
|
||
|
||
## Technische Details
|
||
|
||
### Serialisierung (TS ↔ Rust)
|
||
|
||
**serde + serde_json** für Geometrie-Typen:
|
||
|
||
```rust
|
||
// Rust
|
||
#[derive(Serialize, Deserialize)]
|
||
pub struct Vec2 { pub x: f64, pub y: f64 }
|
||
|
||
#[derive(Serialize, Deserialize)]
|
||
pub struct Wall {
|
||
pub id: String,
|
||
pub start: Vec2,
|
||
pub end: Vec2,
|
||
// …
|
||
}
|
||
```
|
||
|
||
```typescript
|
||
// TS (type-safe invoke)
|
||
interface Vec2 { x: number; y: number }
|
||
interface Wall { id: string; start: Vec2; end: Vec2; /* … */ }
|
||
|
||
await invoke<JoinInfo[]>('compute_joins', { walls: Wall[] })
|
||
```
|
||
|
||
### Tauri Security (default)
|
||
|
||
- Invoke-Handler sind Rust-side validiert
|
||
- Whitelist-Makro (`#[tauri::command]`) registered nur explizit erlaubte Functions
|
||
- CORS/CSP Policy default secure
|
||
- Keine arbitrary-Script-Execution (native app)
|
||
|
||
---
|
||
|
||
## Abhängigkeiten (neu post-Tauri)
|
||
|
||
### Frontend (npm)
|
||
- Bestehende: react, vite, three.js, redux, etc.
|
||
- Neu: `@tauri-apps/api` (JS-SDK für invoke)
|
||
- Optional später: `@tauri-apps/cli` dev-dependency (bereits in package.json)
|
||
|
||
### Backend (Cargo)
|
||
```toml
|
||
[dependencies]
|
||
tauri = { version = "2", features = ["webkit2gtk-6.0"] } # GTK4
|
||
serde = { version = "1.0", features = ["derive"] }
|
||
serde_json = "1.0"
|
||
# später: wgpu, delaunator, opencascade-sys, etc.
|
||
```
|
||
|
||
### System
|
||
- Rust 1.70+
|
||
- GTK4 dev libraries (Linux only, auto-handled by Tauri)
|
||
- Xcode Command Line Tools (macOS, auto-checked)
|
||
|
||
---
|
||
|
||
## Next Steps (Koordination)
|
||
|
||
**Aufgabe für nächste Phase:**
|
||
→ Siehe `docs/design/tauri-migration-plan.md` (Schritt-für-Schritt, vier parallele Agents)
|
||
|
||
**HANDOVER.md:** wird aktualisiert nach Tauri-Shell stabil.
|
||
|
||
---
|
||
|
||
## FAQ
|
||
|
||
**Q: Läuft die App noch im Browser?**
|
||
A: Nein (Milestone 1). Desktop-only. WASM-Fallback für Browser später wenn gebraucht.
|
||
|
||
**Q: Was passiert mit dem existing TS-Code?**
|
||
A: Bleibt unverändert (außer neue Compute-Boundary). TS-Implementierungen = Fallback bis Rust stabil.
|
||
|
||
**Q: Muss ich Rust können um das Projekt zu verstehen?**
|
||
A: Nein. Frontend bleibt React/TS. Rust ist "blackbox" hinter invoke. Aber bei Rust-Bugs muss man rein.
|
||
|
||
**Q: Wann ist Tauri-Shell fertig?**
|
||
A: Nach den vier Agents (Schritt 1–4 in tauri-migration-plan.md), ~1–2 Wochen.
|
||
|
||
**Q: Kann ich lokal testen?**
|
||
A: Ja, `npm run tauri:dev` öffnet lokale App. Alles wie heute, nur Rust hinten dran.
|