Screen-space miter join at degree-2 polyline joints (#790) — TSL counterpart
of GLSL_LINE_JOIN's luxarLineJoin, shared by the visual and picking line
factories so a pick footprint keeps matching the visible one.
Two segments meeting at a turn of angle theta leave an uncovered circular
sector of that angle on the OUTSIDE of the bend and double-cover a lens on
the inside. No per-endpoint intensity scalar can close the outer wedge —
nothing rasterises there to shade — so this rotates the quad's end edge onto
the shared miter edge and the two quads TILE.
That tiling is exact in exact arithmetic, not bit-exact in float32. The two
sides of a joint evaluate algebraically identical operands in a canonical
order, but they reach pixel space differently — the vertex stage scales the
NDC difference ndcEnd.sub(ndcStart) once, while this helper scales each
endpoint (sharedPx, farPx) and subtracts afterwards — so their miter
points agree only to float32 rounding, order 1e-5 px on the
test_line_joins fixture, which is at most one seam pixel once the
rasteriser quantises. tests/e2e/line-join-artifact.spec.ts quantifies it
and gates the bend bands accordingly. Subtracting in NDC and scaling
afterwards on both paths — here and in GLSL_LINE_JOIN — would make the two
sides bit-exact; that is a known, deliberately deferred change.
STRUCTURAL DIFFERENCE FROM THE GLSL TWIN, and it is deliberate: the join
STYLE is a build-time graph variant here (the caller simply does not call
this when the style is none), exactly as the line factories already treat
config.isOrtho, whereas GLSL keeps uLineJoin a runtime uniform so a
?lineJoin= override never recompiles a program. The parity harness compares
pixels, not mechanisms.
Emits real If blocks rather than select(): select() evaluates both
arms, which would pay the partner texel fetch and projection on every vertex
of every thin line and throw away the width gate's entire point.
Screen-space miter join at degree-2 polyline joints (#790) — TSL counterpart of
GLSL_LINE_JOIN'sluxarLineJoin, shared by the visual and picking line factories so a pick footprint keeps matching the visible one.Two segments meeting at a turn of angle theta leave an uncovered circular sector of that angle on the OUTSIDE of the bend and double-cover a lens on the inside. No per-endpoint intensity scalar can close the outer wedge — nothing rasterises there to shade — so this rotates the quad's end edge onto the shared miter edge and the two quads TILE.
That tiling is exact in exact arithmetic, not bit-exact in float32. The two sides of a joint evaluate algebraically identical operands in a canonical order, but they reach pixel space differently — the vertex stage scales the NDC difference
ndcEnd.sub(ndcStart)once, while this helper scales each endpoint (sharedPx,farPx) and subtracts afterwards — so their miter points agree only to float32 rounding, order 1e-5 px on thetest_line_joinsfixture, which is at most one seam pixel once the rasteriser quantises.tests/e2e/line-join-artifact.spec.tsquantifies it and gates the bend bands accordingly. Subtracting in NDC and scaling afterwards on both paths — here and inGLSL_LINE_JOIN— would make the two sides bit-exact; that is a known, deliberately deferred change.STRUCTURAL DIFFERENCE FROM THE GLSL TWIN, and it is deliberate: the join STYLE is a build-time graph variant here (the caller simply does not call this when the style is
none), exactly as the line factories already treatconfig.isOrtho, whereas GLSL keepsuLineJoina runtime uniform so a?lineJoin=override never recompiles a program. The parity harness compares pixels, not mechanisms.Emits real
Ifblocks rather thanselect():select()evaluates both arms, which would pay the partner texel fetch and projection on every vertex of every thin line and throw away the width gate's entire point.