fix(linetype): render dotted and dash-dot patterns (#149)

A dot in an LTYPE pattern is a zero-length element. The pattern resolver
encoded it as a tiny `0.01 * scale` world-length dash, which had two bad
effects: it was sub-pixel at normal zoom so the dash LOD saw the pattern's
smallest feature drop below one pixel and collapsed the whole line to
solid, and at larger LTSCALE it left only invisible sub-pixel dots between
big gaps. Either way dotted and dash-dot linetypes were never visible;
only plain dashed (no zero elements) worked.

Keep dots as literal 0.0 so they no longer drag `min_elem` down, and
render each as a fixed ~1.5 px mark in the wire shader (pixel-snapped via
world_per_pixel, mirroring the hatch shader's dot handling). The shader
tells a real dot from trailing array padding by element count — the index
of the last non-zero element plus one — so no producer needs a sentinel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-25 17:37:24 +03:00
commit fcb97b12da
2 changed files with 39 additions and 14 deletions

View file

@ -889,17 +889,22 @@ pub(crate) fn resolve_pattern(
return solid;
}
// Keep dots (element length exactly 0) as 0.0 so the shader can render
// them as a fixed ~1 px mark; trailing array slots stay 0.0 padding and
// the shader tells the two apart by position (a 0.0 before the last
// non-zero element is a dot, trailing 0.0s are padding). The old code
// encoded dots as `0.01 * scale` — a tiny world-length dash that went
// sub-pixel at normal zoom and dragged the pattern's `min_elem` below one
// pixel, so the dash LOD collapsed dotted / dash-dot lines to solid (or,
// at larger LTSCALE, left only invisible sub-pixel dots between big
// gaps). (#149)
let mut pat = [0.0f32; 8];
let mut pat_len = 0.0f32;
for (i, el) in lt.elements.iter().take(8).enumerate() {
let raw = el.length as f32 * scale;
let encoded = if raw == 0.0 {
0.01 * scale.max(0.01)
} else {
raw
};
pat[i] = encoded;
pat_len += encoded.abs();
// positive = dash, negative = gap, exactly 0 = dot.
let v = el.length as f32 * scale;
pat[i] = v;
pat_len += v.abs();
}
if pat_len < 1e-6 {
return solid;