2D-Engine-Parität: WASM-Pfad rendert deckungsgleich zum SVG-Referenzpfad

Sechs Lücken geschlossen: Text-Massstab vom Geometrie-Massstab entkoppelt
(set_text_scale, spiegelt SVG-Referenzskala); glyphon auf ColorMode::Web
(Text war linear-konvertiert zu dunkel); z-basierte Maler-Reihenfolge
(interleavte draw_sequence statt fills-vor-lines, Alt-Szenen unverändert);
CSS-Klassenfarben/-Opacities in toRenderScene gespiegelt (Türschwenk etc.);
greyed-Dimmung 0.3 auf allen Primitiven; Dämmschraffur am Modell-Ursprung
verankert (userSpaceOnUse), exakte Bézier-Wellenform statt Sinus und
kachelgekoppelte Strichbreite (widthScreen-Modus). Probe
scripts/probe-engine-parity.mjs vergleicht ?gl=0 gegen ?engine=wasm;
Rest-Diff nur AA/Glyphen-Rasterung. cargo 17/17, vitest 94/94, Builds grün.
This commit is contained in:
2026-07-03 08:17:01 +02:00
parent 98994c96aa
commit 23410f9b9e
11 changed files with 470 additions and 156 deletions
+15 -3
View File
@@ -475,11 +475,23 @@ export const PlanView = forwardRef<PlanViewHandle, PlanViewProps>(
const paperScaleForGl = (v: ViewBox): number =>
paperScaleRef.current ?? scaleFromView(v, svgRef.current) ?? 100;
// Referenz-Nenner (1:N) NUR für die Text-Schriftgröße der Engine — spiegelt
// EXAKT die SVG-Formel (case "text" unten, gleiche `print`-Bedingung): im
// normalen Anzeigemodus fest auf die Referenzskala 1:100 (Raumstempel bleibt
// lesbar, unabhängig vom zufälligen Einpass-Massstab der Ansicht), erst im
// Druck-Modus der echte gemessene `paperScale`. BEWUSST NICHT `paperScaleForGl`
// (das dort verwendete live abgeleitete N wäre für Text willkürlich/unlesbar
// und würde vom SVG-Referenzpfad abweichen — siehe Doku in `gpu::Renderer`).
const textScaleForGl = (): number => {
const print = !hairline && paperScale != null && paperScale > 0;
return print ? paperScale! : 100;
};
// GL-Geometrie beim Plan-Wechsel neu tessellieren + sofort neu zeichnen.
useEffect(() => {
if (!useGpuRenderer) return;
updateGeometry(plan);
renderGl(viewRef.current, paperScaleForGl(viewRef.current));
renderGl(viewRef.current, paperScaleForGl(viewRef.current), textScaleForGl());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [useGpuRenderer, plan, updateGeometry, renderGl]);
@@ -487,7 +499,7 @@ export const PlanView = forwardRef<PlanViewHandle, PlanViewProps>(
// keine Re-Tessellierung. Auch bei paperScale-Wechsel neu (Strichbreiten).
useEffect(() => {
if (useGpuRenderer) {
renderGl(view, paperScaleForGl(view));
renderGl(view, paperScaleForGl(view), textScaleForGl());
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [useGpuRenderer, renderGl, view, paperScale]);
@@ -1065,7 +1077,7 @@ export const PlanView = forwardRef<PlanViewHandle, PlanViewProps>(
if (!v || !v0) return;
viewRef.current = v; // abgeleitete Reads (Snap-Toleranz etc.) frisch halten
applySvgTransform(v0, v); // SVG per GPU-Composit (kein Re-Raster)
renderGl(v, paperScaleForGl(v)); // GL exakt auf V (Strich in echten Papier-mm)
renderGl(v, paperScaleForGl(v), textScaleForGl()); // GL exakt auf V (Strich in echten Papier-mm)
};
/** Pan-/Zoom-Ziel imperativ anwenden (rAF-gedrosselt), ohne React-Render. */
const enqueuePan = (v: ViewBox) => {