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 7b9f72e79f
commit 6eead6d493
8 changed files with 588 additions and 172 deletions
+4 -5
View File
@@ -124,8 +124,8 @@ impl GpuState2d {
fn new(window: Arc<Window>, scene: &Scene, view_box: ViewBox) -> Self {
let (surface, device, queue, config) = configure_surface(&window, "2d.device");
let mut renderer = Renderer2d::new(&device, config.format);
let px_per_m = PX_PER_M * meet_scale(view_box, config.width as f32, config.height as f32);
renderer.upload_scene(&device, scene, px_per_m);
let _ = view_box; // Szene-Upload ist zoom-invariant (Boegen analytisch im Shader).
renderer.upload_scene(&device, scene);
Self { surface, device, queue, config, renderer, window }
}
@@ -696,9 +696,8 @@ impl ApplicationHandler<UserEvent> for App {
self.pending2d = Some(scene);
return;
};
let vb = *self.view_box.get_or_insert_with(initial_view_box);
let px_per_m = PX_PER_M * meet_scale(vb, state.config.width as f32, state.config.height as f32);
state.renderer.upload_scene(&state.device, &scene, px_per_m);
let _ = self.view_box.get_or_insert_with(initial_view_box);
state.renderer.upload_scene(&state.device, &scene);
if !self.nav2d {
self.view_box = Some(scene_view_box(&scene));
}