Schnitt/Ansicht: SectionOutput aus render3d an die Zeichenebenen anbinden

- render3d/web.rs: GPU-freies WASM-Binding cut_section_json(model_json, p, n)
  → JSON {cutPolygons, visibleEdges, hiddenEdges} in (u,v)-Metern.
- src/plan/toSection.ts (neu): SectionOutput-Typen, sectionPlaneFromLevel()
  leitet die Schnittebene aus linePoints/directionSign ab, computeSection()
  flacht über projectToModel3d ab und ruft das Binding.
- src/engine/engine3d.ts (neu): geteilter memoisierter pkg3d-Loader, damit
  3D-Viewport und Schnittpfad EINE mod.default()-Init teilen.
- generatePlan.ts: generateSectionPlan() übersetzt SectionOutput in
  bestehende Primitive (Cut = polygon mit Poché-Schraffur, sichtbare Kante =
  line solid, verdeckte Kante = line dashed) — keine neue Primitive-Art nötig.
- App.tsx: SectionPlanView ersetzt den Stub für Ebenen vom Typ Schnitt/Ansicht;
  rendert dasselbe PlanView, mit lokalisierten Hinweisen (lädt/keine Linie/Fehler).
- i18n de/en: section.*-Schlüssel.
- Öffnungen erscheinen korrekt (Brüstung/Sturz-Teilrechtecke) über die
  projectToModel3d-Teilkörper-Zerlegung.

Gates: tsc -b 0, build grün (WASM = eigener Lazy-Chunk), render3d cargo test 29/29,
wasm32 --features web check grün.
This commit is contained in:
2026-07-03 18:37:49 +02:00
parent 3402e6ac96
commit 056ca2c288
8 changed files with 514 additions and 30 deletions
+36
View File
@@ -25,6 +25,7 @@ use wasm_bindgen::prelude::*;
use web_sys::HtmlCanvasElement;
use crate::gpu::Renderer;
use crate::section::{cut_section, SectionPlane};
use crate::types::{Camera, Projection, SlabInput, WallInput};
/// JSON-Eingabe des Modells: Waende (bereits in Teilquader zerlegt, siehe
@@ -37,6 +38,41 @@ struct ModelInput {
slabs: Vec<SlabInput>,
}
/// Berechnet einen Schnitt/eine Ansicht (`section::cut_section`) gegen das Modell
/// und gibt das `SectionOutput` als JSON-String zurueck. GPU-frei — reine
/// Prismen-Arithmetik (siehe `section.rs`); braucht KEINEN `WebModelRenderer`
/// (kein WebGPU-Kontext), nur das geladene WASM-Modul. Aufruf aus JS:
///
/// ```js
/// const json = mod.cut_section_json(
/// JSON.stringify({ walls, slabs }), // gleiches Format wie set_model
/// px, py, pz, // Punkt auf der Schnittebene (world-Meter, Y-up)
/// nx, ny, nz, // Ebenennormale = Blickrichtung (wird normalisiert)
/// );
/// const out = JSON.parse(json); // { cutPolygons, visibleEdges, hiddenEdges }
/// ```
///
/// Koordinaten der Ausgabe: (u, v) in Metern — `u` horizontal entlang der
/// Ebene, `v = world.y` (absolute Hoehe). Siehe `section.rs`-Moduldoc.
#[wasm_bindgen]
#[allow(clippy::too_many_arguments)]
pub fn cut_section_json(
model_json: &str,
px: f32,
py: f32,
pz: f32,
nx: f32,
ny: f32,
nz: f32,
) -> Result<String, JsValue> {
let model: ModelInput = serde_json::from_str(model_json)
.map_err(|e| JsValue::from_str(&format!("Modell parsen: {e}")))?;
let plane = SectionPlane::new([px, py, pz], [nx, ny, nz]);
let out = cut_section(&plane, &model.walls, &model.slabs);
serde_json::to_string(&out)
.map_err(|e| JsValue::from_str(&format!("SectionOutput serialisieren: {e}")))
}
/// Der Browser-Renderer: haelt Surface + Device/Queue + den geteilten `Renderer`
/// und die aktuelle Kamera. Wird per `WebModelRenderer.new(canvas)` aus JS erzeugt.
#[wasm_bindgen]