2D-Bögen analytisch: exakter Kreis-Shader statt Segment-Tessellierung

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.
This commit is contained in:
2026-07-02 22:07:18 +02:00
parent 926dedca40
commit 2a2b3b294c
8 changed files with 588 additions and 172 deletions
+7 -6
View File
@@ -10,9 +10,10 @@
// lines:[{a,b,color,widthMm,dash}], texts:[{pos,content,sizeMm,color,align}] }
// mit Point = [x,y] (Meter) und Rgba = [r,g,b,a] (0..1).
//
// Bögen werden NICHT hier tessellliert (anders als früher): der Rust-Renderer
// zerlegt sie zoomabhängig (`tessellate::tessellate_arc`), damit sie bei jeder
// Vergrößerung glatt bleiben statt sichtbare Facetten zu zeigen. Strichmuster
// Bögen werden NICHT hier tessellliert: der Rust-Renderer zeichnet sie
// mathematisch exakt per SDF-Fragment-Shader (`shaders::ARC_WGSL`, eine
// analytische Instanz je Bogen) — bei jeder Vergrößerung ein echter Kreis statt
// eines Vielecks, ohne Neu-Tessellierung. Strichmuster
// (`dash`, mm Papier) werden ebenfalls unverändert durchgereicht — das geometrische
// Zerschneiden in Teilstücke (`applyDashRuns`-Algorithmus) passiert erst NACH der
// Bogen-Tessellierung in Rust (`tessellate::split_dash`), damit die Phase über die
@@ -328,9 +329,9 @@ export function planToRenderScene(plan: Plan): RScene {
} else if (p.kind === "arc") {
flushRun();
const col = toRgba(DEFAULT_LINE, 1) ?? [0.1, 0.1, 0.1, 1];
// Bogen unvortessellliert an Rust übergeben (`RArc`) — die zoomabhängige
// Zerlegung (glatte Rundung bei jeder Vergrößerung) und ein evtl. Strich-
// muster übernimmt `tessellate::tessellate_arc`/`split_dash` drüben.
// Bogen unvortessellliert an Rust übergeben (`RArc`) — die exakte runde
// Darstellung (analytischer SDF-Shader, `ARC_WGSL`) und ein evtl. Strich-
// muster übernimmt der native Renderer (`compile_scene`) drüben.
arcs.push({
center: [p.center.x, p.center.y],
from: [p.from.x, p.from.y],