Erster Schritt der Rust-Kern-Migration: die Join-Geometrie wird per wasm-pack zu
WASM gebaut (build:geometry) und exportiert compute_joins_json (JSON rein/raus).
Damit kann das TS-Frontend kuenftig die EINE Rust-Implementierung aufrufen statt
des TS-Duplikats. Crate wie render2d/render3d aus dem Workspace excludet, bleibt
per Pfad-Dep fuer den nativen Host nutzbar. Verhalten unveraendert (noch nicht
verdrahtet). geometry cargo test 8/8, cad-tauri cargo check ok.
Die L-Seitenlinie am T-Stoss wird nur noch gezeichnet, wenn der getrimmte
Abzweig-Putz materialFREMD zum durchgehenden Nah-Putz der Durchgangswand ist.
Bei materialgleichem Putz (z. B. iw-Innenputz auf iw-Innenputz) fuellt der
Nah-Putz die Putzflanken durchgehend (spanCutout schneidet nur die Kernbreite)
-> durchgehendes Putz-L ohne Naht. Tests entsprechend gezogen.
Bisher zerlegte emitWall eine Wand um jede Oeffnung in Pfeiler/Bruestung/Sturz-
Teilquader -> sichtbare Segment-Naehte im 3D, Oeffnung war semantisch kein Loch,
und versetzt uebereinanderliegende Fenster liessen sich gar nicht abbilden. Neu:
im 3D-Pfad (layered=true) EIN RWall pro Schicht-Band ueber die volle Achse mit
allen Oeffnungen als rechteckige holes; render3d stanzt sie per achsparalleler
Rechteck-Gitter-Zerlegung der Langseiten aus und setzt 4 Laibungsquads je Loch.
holes und openings schliessen sich im Emitter gegenseitig aus. Tuer = Loch bis
zum Wand-zBottom. Schnitt-Pfad (layered=false) segmentiert unveraendert weiter
(wallSegmentOwners bleibt synchron, toSection.ts unangetastet).
Zusatz: trimWallTopForCeilings (Deckentrim, e4b8df6) gilt jetzt NUR im 3D-Pfad
(layered=true, Z-Fighting-Vermeidung) und wird im Schnitt-Pfad uebersprungen -
der Schnitt braucht die volle Wandhoehe fuer seine schichtweise Prioritaets-
Subtraktion (Kontrolle von Schichteinzug/Bodenaufbau am Wand-Decken-Anschluss).
RWall.holes / WallInput.holes additiv (#[serde(default)]). Pflicht-Testfaelle:
zwei ueberlappende, hoehenversetzte Fenster in einem Koerper; Tuer-Loch bis Boden
ohne untere Laibung. cargo test 56 gruen, vitest 230 (+3), build:engine3d + tsc
sauber.
Am materialbewussten T-Stoss wurde die Durchgangswand bisher nur uebermalt
(Zeichenreihenfolge), nicht geometrisch ausgeschnitten: ihr Nah-Putz-Band, die
Schichtfugen- und die Nahflaechenlinie liefen weiter ueber die Durchstoss-Breite
des Abzweig-Backsteins. Neu liefert computeJoins erstmals auch Cuts fuer die
DURCHGANGSWAND: WallCuts.spanCutouts (Achsen-Intervall = Projektion der Abzweig-
Kernbreite, Quer-Offsetzone = Nahflaeche bis Rueckgrat-Nahflaeche = Nah-Putz-
Tiefe aus Phase 1b). addWallPoche splittet betroffene Schicht-Baender entlang der
Achse in Teil-Baender vor/nach dem Intervall, unterbricht die Schichtfugen im
Merge-Bereich und macht die Nahflaechen-Umrisskante ueber dem Durchstoss
strokelos (separate Segmente davor/danach) — keine Trennlinie an der
Verschmelzungsflaeche, der T-Stoss liest als EIN Join. Rust-Paritaet additiv
(span_cutouts), Aggregat-startCut/endCut unveraendert -> parity gruen.
vitest 227 (+5), cargo 8 (+1), tsc sauber.
Der 3D-Viewport kann das Modell jetzt live an einer vertikalen Ebene
aufschneiden (Overlay-Button 'Schnittebene', MVP: Ebene durch die
Modellmitte, Blick +Y):
- Globals um section_plane vec4 erweitert (xyz=Normale, w=-n*p; Enable
in mode.y); MESH_/GRID_WGSL discarden Fragmente vor der Ebene
(world_pos als neuer Varying) - Flaechen, Kanten, Grid, Highlight.
- section_fill.rs (neu): build_cut_caps ruft cut_section und trianguliert
jedes (u,v)-Rechteck zurueck in Weltkoordinaten ([x,y,z,u,v]).
- CAP_WGSL: prozedurale 45-Grad-Diagonalschraffur aus (u,v) in Modell-
Metern (fwidth-AA), Tinte auf Papier wie die 2D-Konvention; eigene
cap_pipeline (CullMode::None, Depth-Bias Richtung Kamera gegen
Z-Fighting), gezeichnet nach den Flaechen, nicht geclippt.
- web.rs cached walls/slabs; set_section_plane(active, p, n) aktualisiert
Uniform + Caps; set_model baut Caps bei aktivem Schnitt neu.
- useWasm3dRenderer.setSectionPlane + Toggle in Wasm3DViewport.
Bekannte Luecken (dokumentiert): Oeffnungen im 3D-Solid nicht ausgespart
(cut_section spart aus -> Mismatch bei Schnitt durchs Fenster; wird mit
dem Boolean-Loecher-Slice geloest), nur vertikale Ebenen, ein globales
Muster (per-Bauteil = Stufe 2). cargo 56 Tests + naga-WGSL-Validierung,
222 vitest gruen, WASM neu gebaut (pkg3d gitignored).
Der verschmelzende Abzweig-Kern (Backstein) lief seit Phase 1 bis zur
ACHSE der Durchgangswand und ueberlappte deren Koerper. Jetzt stoppt er
an der Nahflaeche des Durchgangs-Rueckgrats: mergeCut bei
offT + sign*(tT/2 - nearPlaster), wobei nearPlaster die Summe der
Durchgangs-Schichten zwischen Nahseite und Rueckgrat-Kern ist. Die
L-Seitenlinien der Putzschichten liegen ebenfalls auf dieser Tiefe.
Naht zwischen den beiden Kernen entfaellt ueber die bestehenden
Mechanismen (stroke:none am Fuellband, noStrokeEdges an den Stirnkanten,
Zeichenreihenfolge bricht die Nahflaechenlinie auf) - kein
generatePlan-Umbau noetig. Rust-Paritaet in geometry/lib.rs analog
(Aggregat-Cuts unveraendert, parity.test gruen). 216 Tests + cargo 7/7.
Am T-Stoss wird der Abzweig nicht mehr naiv ueber die volle Dicke an der
Durchgangswand-Flaeche gekappt. Neu (joinPriority-basiert):
- resolveJoinPriority(a,b): merge/coexist/trim je nach Prioritaet+Komponente.
- WallCuts.layerCuts (additiv): pro Schicht eine Cut-Linie + optionale L-Seiten-
linie. Gleiche/hoehere Prioritaet wie der Durchgangs-Backbone -> kein Cut
(Schicht laeuft durch = Merge); schwaechere Schicht -> Nahflaechen-Cut + L.
- addWallPoche nutzt layerCuts pro Schicht (Fallback: alte Aggregat-Cuts);
neue 'layer-joint-l'-Linie als L-Rueckschnitt.
- Rust-Paritaet in geometry/lib.rs additiv gespiegelt (LayerInput serde default,
Aggregat-Cuts bit-identisch -> parity.test gruen).
Einschichtige/nicht-passende T-Stoesse pixel-identisch. +8 Tests (216 gesamt),
cargo 7/7.
compute_joins behandelt jetzt wie die TS-Referenz auch T-Knoten (3 Enden,
kollineares Paar = Durchgang, Abzweig an dessen Flaeche geschnitten) und
Mittelspannen-Stoesse (freies Ende trifft Wandseite). miter_line/L-Ecke
bit-identisch. Rust-Tests aktualisiert (t_junction_branch_gets_face_cut,
mid_span_tee_free_end_hits_wall_side). cargo test + parity.test.ts gruen.
Ein selektiertes Bauteil wird jetzt im 3D-Viewport mit einer orangen
Umrisslinie markiert, die dank No-Depth-Pipeline auch hinter Wänden
durchscheint (klare Selektions-Rückmeldung). Zusammen mit Objekt-Info
(numerisch) und Attribute-Panel ist die 3D-Auswahl damit vollständig.
- Engine: set_highlight_lines(vertices) + highlight_pipeline
(LineList, depth_compare Always, kein Depth-Write), als letzter
Draw-Call obenauf. Reuse der Grid-Linien-Infrastruktur.
- TS: selectionHighlightLines() baut die Quader-/Prisma-Kanten der
Auswahl in Akzentfarbe; App berechnet sie per useMemo aus der
Store-Auswahl und reicht sie durch.
- Neue set_render_style(style)-Methode: shaded/white/wireframe/hidden
(textured fällt vorerst auf shaded zurück, bis die Textur-Pipeline
steht). Voll durchverdrahtet vom TopBar-Darstellungs-Dropdown zum
Nordstern-Viewport (setRenderStyle statt nur setWhiteMode).
- Feature-Edge-Extraktion (edges.rs): Kanten aus dem Mesh, dedupliziert
+ Crease-Erkennung (nur Silhouette/Knickkanten, keine Triangulierungs-
Diagonalen) → sauberes Architektur-Drahtgitter. Reuse der Grid-
LineList-Pipeline.
- wireframe = nur Kanten; hidden = flach-weisse Flächen (Depth-Bias) +
sichtbare Kanten obenauf (klassischer Hidden-Line-Look).
- Grundschattierung: dezentes Gegenlicht (Fill-Light), damit abgewandte
Flächen nicht mehr in Schwarz absaufen.
- Boden-Grid-Fläche auf y = baseElevation des aktiven Geschosses,
ein-/ausschaltbar (Overlay-Button im Viewport, State in viewSlice
grid3dVisible). Eigene LineList-Pipeline in der Engine (grid.rs,
GRID_WGSL, geteilte View-Projection/Depth mit der Mesh-Pipeline),
neue set_ground_grid(visible, elevation, extent, spacing)-Methode.
Minor-Linien dezent, jede 5. als Major betont.
- Kamera-Steuerung überarbeitet (vorher nur Mitte=Orbit, Pan auf
Shift+Mitte versteckt): Links=Orbit, Mitte/Rechts=Pan (bewegen),
Rad=Zoom zum Cursor. Laptop-freundlich, konsistent mit der
2D-Plan-Navigation (dort Mitte=Pan).
Bisher zeigte nur three.js importierte Gebaeude/DXF-Meshes und das
Terrain-TIN (Project.context). Neuer MeshInput-Typ + Kontext-Mesh-Pfad
in render3d (Flat-Normalen, doppelseitig gegen unbekanntes Winding),
projectToModel3d speist project.context jetzt in beide Renderer
(nativ + WASM) ein. Nebenbei: fehlendes layers-Feld in demo_walls()
behoben, das den native3d-Feature-Build zuvor schon brach.
CameraPreset::Iso war faelschlich als Perspective definiert; three.js
zeigt bei Iso bereits korrekt orthografisch (parallele Projektion, wie
bei einer echten Isometrie definiert). Front/Top/Side/Iso sind jetzt
konsistent orthografisch, nur Perspective bleibt perspektivisch.
Der Rust-Port der Wandecken-Gehrung nutzte weiter die Distanz-Heuristik und
hatte damit denselben Spitzwinkel-Bug wie der TS-Pfad vor a2c8fcd. Die
Paarung erfolgt jetzt vorzeichenbasiert ueber die Achs-Auslaufrichtungen
(dot(n, d)) — Aussen mit Aussen, Innen mit Innen. Rechte/stumpfe Ecken
bleiben bit-identisch; native und WASM/TS-Renderpfad stimmen bei spitzen
Ecken wieder ueberein. Neuer Spitzwinkel-Test.
Der Schnitt-Extraktor schnitt die Wand-Cut-Polygone bisher gegen
achsparallele Einzelwand-Rechtecke ohne Eckverschneidung — an einer
Wandecke ueberlappten dadurch zwei Querschnitte. mesh.rs exponiert jetzt
den gemitterten Fussabdruck (wall_mitered_footprints, dieselbe Gehrung wie
das 3D-Mesh) als geteilte Wahrheit; section.rs bildet die Cut-Polygone
gegen diesen Fussabdruck. Die achsparallele Naeherung fuer Verdeckung/
Projektionskanten bleibt bewusst unveraendert.
mesh.rs berechnet die Ecken-Verschneidung jetzt selbst (portiert aus dem
Web-Kern computeJoins/clippedBand): Wandenden werden per Rundungs-Key
gruppiert, ueber Hoehen-Ueberlappung geclustert (gestapelte Geschosse mit
gleichem Grundriss verschneiden sich NICHT), und Zweier-Cluster ueber die
gemeinsame Gehrungslinie geschnitten. T-/X-Stoesse und freie Enden bleiben
stumpf, wie im Web-Kern. Endkappen-Normalen geometrisch aus dem Kreuzprodukt.
WallInput bekommt optional layers (WallLayer{thickness,color}, serde-default,
rueckwaertskompatibel): jede Schicht wird als eigenes Band ueber dieselbe
gehrte Grundflaeche extrudiert und materialgefaerbt. toWalls3d.ts loest die
WallType-Schichten je Wand auf (hexToRgb) und reicht sie durch.
39/39 Rust-Tests (7 neue: Gehrung, T-Stoss, freies Ende, Schichten, Kombi).
stroke_polyline tesselliert Segmente jetzt einzeln (Butt-Enden) und
setzt an Innenknoten Fächer-Bögen auf der Aussenseite sowie an offenen
Enden Halbkreis-Kappen. Fächerdichte 18°/Schritt, identisch zur
WebGL2-Darstellung. Geschlossene Ringe: Bögen an allen Knoten, keine
Kappen; offene Polylinien inkl. Strich-Segmente: Kappen an den Enden.
Die Kamera-Presets liefen zuvor als Viewport-Overlay mit eigenen JS-yaw/pitch-
Winkeln, rein perspektivisch (Iso falsch, keine Parallelprojektion). Jetzt
haengt der Engine-Viewport am app-weiten view3d-State (TopBar-CameraMenu, wie
three.js) und nutzt die vorhandene Rust-preset_camera:
- web.rs: set_view_preset(preset) + model_bounds/fit; Front/Top/Side echt
orthografisch, Iso/Perspektive perspektivisch - wie das native Fenster.
default_fov() pub(crate), damit Fit denselben FOV nutzt.
- useWasm3dRenderer: applyViewPreset; render(null) ueberschreibt die
Preset-Kamera nicht.
- Wasm3DViewport: konsumiert view3d/renderMode als Props (Overlay + lokaler
Umschalter entfernt, wasm3dOverlay.css geloescht); Orbit bleibt frei
(Preset = Sprung, kein Lock), three.js-Verhalten gespiegelt.
- Viewport3D-Switcher reicht view3d/renderMode an den Engine-Viewport durch
(kein three.js-Szenencode angefasst).
Der Engine-3D-Viewport (render3d/wgpu) war reines Ansehen (Orbit/Zoom). Jetzt:
- Kamera-Presets als Overlay: Top/Unten/Vorne/Hinten/Links/Rechts/Iso.
Richtungen exakt wie der three.js-Pfad (applyView3d); framen die aktuelle
Modell-Bounding-Box (fitTargetDist) und springen auf den Preset-Winkel,
danach Orbit/Pan/Zoom weiter frei. Rein JS.
- Render-Modus Schattiert/Weiss: mode-Uniform in Globals (WGSL/Rust),
Weiss = beleuchtetes Clay-Grau; wasm-Binding set_render_mode_white,
durchgereicht via useWasm3dRenderer.
- Overlay-CSS self-contained (wasm3dOverlay.css), Theme-aware.
three.js unangetastet. Auswahl/Grips/Zeichnen + Wireframe/HLR bewusst noch
offen (naechste Scheibe).
Multisampled Farbtarget (SAMPLE_COUNT=4) + resolve_target in die Surface-View je
Frame; Depth-Textur-sample_count synchron auf 4; MSAA-/Depth-Textur werden bei
Resize gemeinsam neu erzeugt; multisample.count=4 auf der Render-Pipeline. Nur
gpu.rs — mesh/section/openings unberührt.
Gates: cargo check (window) + wasm32 --features web grün, cargo test 32/32,
--features render 33/33.
- openings.rs (neu): split_range_by_voids() — die bisher nur in section.rs
liegende Pfeiler/Brüstung/Sturz-Zerlegung, herausgezogen als koordinaten-
neutraler Helfer. section.rs und mesh.rs teilen jetzt EIN Öffnungsmodell.
- section.rs: wall_cut_rectangles delegiert den Sortier-/Rechteck-Teil an
openings::split_range_by_voids (plane-spezifisches Clipping bleibt lokal).
- mesh.rs: extrude_wall emittiert nicht mehr einen Vollblock je Wand, sondern
Teilkörper (Pfeiler in voller Höhe + Brüstung/Sturz je Öffnung) → echtes Loch;
Tür (sill==0) lässt die Brüstung weg und reicht bis zum Boden. Ohne Öffnung
exakt EIN Block, byte-identische Geometrie zu vorher (Alt-Tests unverändert grün).
Gates: cargo test 32/32 (29+3 neue), wasm32 --features web check grün, nativ grün.
- 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.
glyphon 0.11 ist die neueste zu wgpu 29 passende Version (wgpu 30 existiert
bereits, glyphon pinnt aber ^29). Das 22er-Limit maxInterStageShaderComponents
wird von 29 nicht mehr in requiredLimits gesendet — src/engine/requestDeviceShim.ts
komplett entfernt, beide Hook-Importstellen (useWasmPlanRenderer, useWasm3dRenderer)
angepasst. pollster 0.3→0.4, naga 22→29 mitgezogen. Alle Draw-/Text-Pfade
(draw_sequence, glyphon ColorMode::Web, widthScreen-Polylinien, Headless/Golden)
unverändert funktionsfähig.
Verifiziert: cargo check nativ (render2d/render3d/src-tauri) grün, cargo test
render2d 17/17 + Golden bit-exakt, render3d 29/29, wasm32 --features web für
beide grün, npx tsc -b + npm run build grün.
WallInput bekommt openings (from/to/sill/height, serde-default). Der Cut
durch eine Öffnung liefert mehrere einfache Rechtecke (Brüstung, Sturz,
Leibungen) statt Lochpolygon — kompatibel zu render2d::FillPolygon. Der
Verdeckungstest behandelt Öffnungen als Durchblick (u-Zuordnung exakt im
Elevationsfall, konservativ undurchsichtig bei kantennaher Ausrichtung);
Fensterrahmen-Kanten werden als projizierte Kanten ausgegeben. Tests
26 auf 29, Beweis-SVG mit Fenster + dahinterliegender Wand neu generiert
(sichtbares Band exakt im Fensterausschnitt). mesh.rs (3D-Vollkoerper)
beruecksichtigt Öffnungen weiterhin nicht — dokumentierte Luecke.
HeadlessRenderer (Feature headless) baut Instance/Adapter/Device ohne
Surface (Vulkan, kein Display-Server); render_to_image rendert in eine
Rgba8Unorm-Texture (non-sRGB wie ColorMode::Web) und liest mit 256-Byte-
Row-Padding zurueck. Draw-Code unveraendert geteilt mit dem Fenster-Pfad.
CLI-Bin render_png (Demo-Szene, 990x630); Demo additiv um 45-Grad-
Schraffur, Tuerblatt und Schwenkbogen erweitert, damit das Golden alle
Pfade abdeckt. Golden-Test mit eingechecktem Referenzbild: 0/623700 Pixel
Abweichung, bit-exakt reproduzierbar; bei Abweichung Diff-Dump nach
target/. Doku in docs/design/engine-headless.md.
Analytische Eigen-Engine-Alternative zum OCCT-HLR-Spike: Schnittebene
(Punkt+Normale) gegen extrudierte Fussabdruck-Prismen; Cut-Polygone in
(u,v)-Schnittkoordinaten mit Komponenten-Referenz (spaetere Schraffur),
Projektion der dahinterliegenden Kanten mit Verdeckungstest, getrennt
visible/hidden. SectionOutput serde-serialisierbar (Meter). 26 Tests,
Example section_svg schreibt docs/welle-c-hlr-spike/section-engine-proof.svg
(L-Wand+Bodenplatte: Poché, durchgezogene sichtbare, gestrichelte verdeckte
Kanten). Offene Punkte in docs/design/engine-section-pipeline.md.
Feature web (wasm-bindgen) + cdylib analog render2d; WebModelRenderer
mit Canvas-Surface, set_model (walls/slabs wie der native Push) und
set_camera. Projektion liefert bereits [0,1]-Clip-Z, math.rs unveraendert.
wgpu-22-requestDevice-Shim in src/engine/requestDeviceShim.ts geteilt.
Neuer Hook useWasm3dRenderer + Wasm3DViewport (Orbit/Pan/Zoom wie three.js-
Sicht); Viewport3D dispatcht per ?engine=wasm bzw. localStorage, three.js
bleibt Default. Build-Script build:engine3d (wasm-pack, src/engine/pkg3d).
Verifiziert headful per scripts/probe-engine3d.mjs (37 % Geometrie-Pixel);
headless praesentiert Chromium keine WebGPU-Frames (auch bei render2d).
Integrations-Spike: die native 2D-Engine (src-tauri/render2d) laeuft jetzt als
WASM/WebGPU-Canvas in der App-UI, mit derselben Szene (planToRenderScene) und
ViewBox wie der WebGL2-Pfad; das SVG-Overlay (Text/Griffe/Auswahl) bleibt drueber.
- render2d: neues Feature "web" (wasm-bindgen/web-sys), Fassade WebPlanRenderer
(new/set_scene/set_view_box/set_paper_scale/resize/render) auf Canvas-Surface.
Geteilte GPU-Schicht (gpu.rs), native Fensterschicht (winit) unberuehrt.
- Font: cosmic-text findet auf wasm keine Systemfonts -> Inter-Bytes eingebettet
(assets/Inter.ttf), Renderer.load_font + text_family aus der geladenen Familie.
- Frontend: useWasmPlanRenderer (gleiche Schnittstelle wie useGlPlanRenderer);
PlanView waehlt die Engine per ?engine=wasm + navigator.gpu, sonst WebGL2/SVG.
- Build: npm-Script build:engine (wasm-pack, target web -> src/engine/pkg,
gitignored). chromium-shell.sh mit WebGPU-Flags (--enable-unsafe-webgpu/Vulkan).
- Kompat-Shim: wgpu 22 sendet das spec-entfernte Limit maxInterStageShaderComponents,
das neuere Chromium ablehnen -> im Hook vor requestDevice entfernt.
Verifiziert: cargo test -p render2d gruen (18), tsc gruen, wasm-Build gruen;
Headless-Chromium (?engine=wasm) zeichnet den Grundriss (Waende/Schraffur/
Tuerschwenk/Treppe/Raumtext) im WASM-Viewport, Fallback ohne Flag unveraendert.
Bögen im nativen 2D-wgpu-Renderer werden nicht mehr zoomabhängig in Segmente
zerlegt, sondern per SDF-Fragment-Shader (ARC_WGSL) mathematisch exakt rund
gerendert — bei jeder Zoomstufe ein echter Kreis, kein Vieleck, ohne
Neu-Tessellierung.
- compile_scene sammelt je Bogen EINE analytische Instanz (ArcInstanceData,
Bildschirm-Raum-Parameter + Dash in Modell-Metern), zoom-invariant.
- Eigene Arc-Pipeline (ein Frame-Uniform, Quad je Instanz aus vertex_index):
radiale Kante, Butt-Cap-Winkelclamp (beide Sweep-Vorzeichen) und Dash
(Bogenlänge modulo Muster) analytisch antialiased; Strichbreite mit derselben
mm->px-Formel wie die Linien.
- tessellate_arc + Zoom-Retessellierungs-Cache (last_scene/arc_px_per_m/
maybe_retessellate) entfernt; upload_scene ohne px_per_m.
- Tests auf die neue Semantik umgeschrieben (Winkel-Parität, Bounding-Box,
Dash-Mapping), ARC_WGSL per naga validiert.
Papier-mm-Strichmuster (dash) wurden bisher komplett ignoriert und immer
durchgezogen gerendert — im nativen 2D-Fenster gab es dafür bislang gar keinen
Mechanismus. `split_dash` portiert den bereits für Schraffuren bewährten
`applyDashRuns`-Algorithmus (glPlanHatch.ts) nach Rust und zerlegt einen
Linienzug in seine "an"-Teilstücke; die Phase läuft dabei über bereits
verkettete Zeichnungszüge UND über die neu adaptiv tessellierten Bögen
hinweg durch, sodass z. B. der gestrichelte Türschwenk-Bogen gleichmäßig
gestrichelt bleibt statt an jedem Segment neu anzusetzen.
Bögen (Türschwenke) wurden bisher einmalig in JS in eine feste Facettenzahl
zerlegt; beim Hineinzoomen wurden die Facetten sichtbar. Die Zerlegung
(`tessellate_arc`) wandert nach Rust und läuft jetzt zoomabhängig anhand einer
Sehnenabweichungs-Toleranz (Sagitta ≤0.3 Geräte-px), Segmentzahl auf 8..512
geklemmt. Die native GPU-Szene bekommt dafür einen eigenen `Arc`-Primitiv-Typ
(unvortessellliert); der Renderer merkt sich die zuletzt hochgeladene Szene und
tessellliert Bögen automatisch neu, sobald sich der Zoom seit dem letzten
Upload um mehr als Faktor 1.3 verändert hat.
Deckenplatten (SlabInput) werden im render3d aus dem Grundriss-Umriss per
Ear-Clipping trianguliert und über die Deckendicke extrudiert (Deckel/Boden/
Mantel mit robust nach außen orientierten Normalen). Payload erweitert auf
{ walls, slabs } — blanke Wand-Arrays bleiben kompatibel. Beleuchtung auf
hemisphärisches Ambient (Himmel/Boden) + Directional-Sonne umgestellt, dezente
Kantenbetonung, hellerer Hintergrund (#f5f5f5). Beispiel-Geschossdecke im EG.
Raumstempel-Text rendert jetzt im nativen wgpu-Fenster mit echten
TrueType-Glyphen (Inter, Systemfonts via cosmic-text) ueber einen
GPU-Glyphen-Atlas — im selben 4x-MSAA-Pass nach der Geometrie.
Schriftgroesse in Papier-mm (gleiche Formel wie Strichbreiten, skaliert
mit dem Zoom); die Bridge flacht Rich-Text-Stempel + Zusatzzeilen
zeilenweise auf serde-kompatible texts ab (Layout wie der SVG-Pfad).
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.
Beide Pipelines (Fill/Line) rendern mit sample_count=4 in eine lazily verwaltete
Multisample-Textur (ensure_msaa, Muster analog render3d::ensure_depth) und
resolven in die Surface-View. Kantige Haarlinien im nativen 2D-Viewport werden
dadurch knackscharf; Renderer-API unveraendert.
- native.rs: EINE winit-EventLoop hostet 2D- und 3D-Fenster (winit erlaubt nur
eine Loop pro Prozess) — loest den RecreationAttempt-Panic zweier Loops; ersetzt
native2d.rs/native3d.rs. Feature-gegated (native2d/native3d, einzeln oder zusammen).
- render2d/render3d laden das ECHTE Modell aus assets/native2d_scene.json bzw.
native3d_walls.json (Demo-Szene als Fallback); initialer Ausschnitt/Kamera aus
den Modell-Grenzen gerahmt, initialer Redraw + gesetzte Fenstergroesse.
- TS-Konverter toRenderScene/toWalls3d + scripts/dump-native-scene erzeugen die
JSON aus sampleProject/generatePlan (npm run dump:native).
- render2d: Scene.polylines fuer zusammenhaengende Umriss-/Zeichnungslaeufe →
Gehrung statt Stumpfkappen an Wandecken/2D-Geometrien; MITER_LIMIT 4→8
(deckungsgleich mit SVG stroke-miterlimit:8 und WebGL2).
- native3d-Feature + render3d-Pfad-Dep in der Tauri-Crate.
Spawns a GTK-free winit window with its own wgpu surface from Tauri's
setup hook on a background thread, sidestepping the WebKitGTK surface
contention on Wayland. Reuses render2d's renderer via a shared demo
module. Opt-in behind the native2d cargo feature; default build
unaffected.
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).