Donner SVG 0.8.0-pre
SVG editor and embeddable C⁠+⁠+⁠20 engine.
Loading...
Searching...
No Matches
GeodeStrokeTolerance.h File Reference

Device-aware curve-flattening tolerance for stroke-outline generation. More...

#include <algorithm>
#include <cmath>
#include "donner/base/Transform.h"
Include dependency graph for GeodeStrokeTolerance.h:

Namespaces

namespace  donner
 Top-level Donner namespace, which is split into different sub-namespaces such as donner::svg and donner::css.

Functions

double donner::geode::MaxAbsScaleFactor (const Transform2d &transform)
 Largest factor by which transform can stretch a vector, i.e.
double donner::geode::StrokeFlattenScaleBucket (double scale)
 Round scale up to the next power of two, with a floor of 1.
double donner::geode::StrokeFlattenToleranceFor (const Transform2d &deviceFromLocal)
 Path-local curve-flattening tolerance for stroke geometry that will be drawn with deviceFromLocal.

Variables

constexpr double donner::geode::kStrokeFlattenDevicePixels = 0.25
 Target flattening chord error for stroke outlines, in device pixels. Matches the historical path-local default so unscaled rendering is unchanged.
constexpr double donner::geode::kMinStrokeFlattenTolerance = 1e-4
 Lower clamp for the derived path-local tolerance. Bounds tessellation work (and recursion depth) at absurd zoom levels. The device-pixel guarantee holds up to a scale of kStrokeFlattenDevicePixels / kMinStrokeFlattenTolerance.
constexpr double donner::geode::kMaxStrokeFlattenTolerance = kStrokeFlattenDevicePixels
 Upper clamp for the derived path-local tolerance: the historical path-local default. The derivation only ever REFINES flattening, never coarsens it.

Detailed Description

Device-aware curve-flattening tolerance for stroke-outline generation.

Path::strokeToFill flattens curves to line segments before offsetting them, and the tolerance it takes is expressed in the path's OWN coordinate space. A renderer that submits geometry in document space and lets the GPU apply the view transform therefore gets a polygon that was tessellated once at document scale and then magnified: at any meaningful zoom a circle (4 kappa cubics) degenerates into a visibly faceted segment chain.

The fix is to derive the path-local tolerance from the transform the geometry is actually rasterized with, so the chord error stays bounded in DEVICE pixels no matter what the view scale is. Curves rendering as visible line segments is a correctness violation, not a cosmetic one, so this derivation is mandatory for every renderer-side stroke call site.

The derived scale is quantized to power-of-two buckets so that a continuous zoom gesture does not invalidate the stroke cache on every frame: within a bucket the tolerance is bit-identical, and crossing a bucket boundary re-derives (and, via the cache key, re-flattens) exactly once.

Function Documentation

◆ MaxAbsScaleFactor()

double donner::geode::MaxAbsScaleFactor ( const Transform2d & transform)
inline

Largest factor by which transform can stretch a vector, i.e.

the maximum singular value of its 2x2 linear part.

Using the maximum singular value (rather than, say, the average of the axis scales) is what makes the device-space bound a guarantee: for any local-space error vector e, |M e| <= sigmaMax * |e|, so bounding the local chord error by target / sigmaMax bounds the device chord error by target.

Parameters
transformTransform whose linear part is measured. Translation is ignored.

◆ StrokeFlattenScaleBucket()

double donner::geode::StrokeFlattenScaleBucket ( double scale)
inline

Round scale up to the next power of two, with a floor of 1.

Quantizing before deriving the tolerance keeps the tolerance stable across a continuous zoom, so the stroke cache is rebuilt on bucket crossings only. Rounding UP (never down) keeps the derived tolerance conservative: the flattening is at least as fine as the exact scale demands.

The floor of 1 is what makes the derivation refine-only - see kMaxStrokeFlattenTolerance. Minified geometry keeps the path-local default rather than being tessellated more coarsely.

Parameters
scaleLinear scale factor. Non-finite and non-positive inputs fall back to 1.0 (a degenerate transform paints nothing, so any bucket works).

◆ StrokeFlattenToleranceFor()

double donner::geode::StrokeFlattenToleranceFor ( const Transform2d & deviceFromLocal)
inline

Path-local curve-flattening tolerance for stroke geometry that will be drawn with deviceFromLocal.

The result keeps the flattening chord error under kStrokeFlattenDevicePixels device pixels for every scale up to kStrokeFlattenDevicePixels / kMinStrokeFlattenTolerance, and is clamped to [kMinStrokeFlattenTolerance, kMaxStrokeFlattenTolerance].

Parameters
deviceFromLocalTransform applied to the geometry at draw time.

Variable Documentation

◆ kMaxStrokeFlattenTolerance

double donner::geode::kMaxStrokeFlattenTolerance = kStrokeFlattenDevicePixels
inlineconstexpr

Upper clamp for the derived path-local tolerance: the historical path-local default. The derivation only ever REFINES flattening, never coarsens it.

Coarsening minified geometry would technically stay inside the device-pixel budget, but it trades output quality for CPU on content that is not the defect this derivation exists to fix, and it perturbs the rendering of every downscaled document (filter sub-renders, pattern tiles, thumbnails). Refining only keeps the change strictly additive.