Natives 2D/3D live: Webview pusht Szene/Waende per Tauri-Command

Die nativen wgpu-Fenster spiegeln jetzt das LIVE-Modell statt des
eingefrorenen JSON-Snapshots: die Webview schiebt bei jeder Aenderung
(debounced 200 ms) den sichtbaren Plan (planToRenderScene) bzw. die
Projekt-Waende (projectToWalls3d) per push_native_scene/push_native_walls
an einen EventLoopProxy<UserEvent> der winit-Loop. Ansicht wird nur neu
eingepasst, solange im Fenster noch nicht gepannt/gezoomt/orbitiert
wurde; die JSON-Assets bleiben Start-Fallback. Ohne Tauri: No-op.
This commit is contained in:
2026-07-02 20:10:38 +02:00
parent 1811e82005
commit 01424c1e22
4 changed files with 228 additions and 20 deletions
+62
View File
@@ -0,0 +1,62 @@
// Live-Spiegelung in die nativen wgpu-Fenster (src-tauri/src/native.rs): schiebt
// die AKTUELL sichtbare 2D-Szene bzw. die geflachten 3D-Wände debounced per
// Tauri-Command an die native Event-Loop. Es wird EXAKT dieselbe Ableitung
// benutzt wie für die Browser-Ansicht (planToRenderScene auf dem sichtbaren
// Plan, projectToWalls3d aufs Projekt) — 1:1-Parität inkl. Türschwenk und
// Fenster-Symbolen (Text bleibt bewusst dem Browser-Overlay überlassen).
//
// Im reinen Browser (kein Tauri) sind beide Funktionen No-ops: erkannt wird
// Tauri an `window.__TAURI_INTERNALS__`; Fehler beim Invoke werden still
// geschluckt (console.debug), damit die App nie daran scheitert.
import type { Plan } from "./generatePlan";
import type { Project } from "../model/types";
import { planToRenderScene } from "./toRenderScene";
import { projectToWalls3d } from "./toWalls3d";
/** Sammel-Fenster: Modelländerungen werden gebündelt gepusht (nicht je Frame). */
const DEBOUNCE_MS = 200;
/** Läuft die App in einer Tauri-Webview? (Sonst: reiner Browser, kein Invoke.) */
function isTauri(): boolean {
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
}
/** Geguardeter Invoke (Muster wie src/compute): nie werfen, Fehler nur debuggen. */
async function invokeNative(cmd: string, args: Record<string, unknown>): Promise<void> {
try {
// @ts-ignore optionales Paket
const mod = await import(/* @vite-ignore */ "@tauri-apps/api/core").catch(() => null);
if (!mod || typeof (mod as any).invoke !== "function") return;
await (mod as any).invoke(cmd, args);
} catch (e) {
console.debug(`nativeSync: ${cmd} fehlgeschlagen`, e);
}
}
let sceneTimer: ReturnType<typeof setTimeout> | undefined;
let wallsTimer: ReturnType<typeof setTimeout> | undefined;
/**
* Debounced: den sichtbaren Plan (dasselbe Objekt wie in der PlanView) als
* render2d-Szene ans native 2D-Fenster schieben. No-op ohne Tauri.
*/
export function pushNativeScene(plan: Plan): void {
if (!isTauri()) return;
clearTimeout(sceneTimer);
sceneTimer = setTimeout(() => {
void invokeNative("push_native_scene", { scene: planToRenderScene(plan) });
}, DEBOUNCE_MS);
}
/**
* Debounced: die Wände des Projekts (alle Geschosse, gestapelt) als
* render3d-WallInputs ans native 3D-Fenster schieben. No-op ohne Tauri.
*/
export function pushNativeWalls(project: Project): void {
if (!isTauri()) return;
clearTimeout(wallsTimer);
wallsTimer = setTimeout(() => {
void invokeNative("push_native_walls", { walls: projectToWalls3d(project) });
}, DEBOUNCE_MS);
}