@@ -208,31 +208,31 @@ void main() {
208208 int totalChevrons = max(1, int(floor(lineWidthPx / chevronSpacingPx)));
209209 float bpSpacing = lineLengthBp / float(totalChevrons + 1);
210210
211- // Find the viewport bp range relative to line start
211+ // Find the viewport bp range relative to line start to window into visible chevrons
212212 float viewportStartBp = u_bpRangeX.x + u_bpRangeX.y - float(u_regionStart) - float(a_position.x);
213213 float viewportEndBp = viewportStartBp + u_bpRangeX.z;
214214
215215 // Compute which chevron indices are visible in the viewport
216- // chevron i is at bp position: bpSpacing * (i + 1)
217216 int firstVisible = max(0, int(floor(viewportStartBp / bpSpacing)) - 1);
218217 int lastVisible = min(totalChevrons - 1, int(ceil(viewportEndBp / bpSpacing)));
219218
220219 int globalChevronIndex = firstVisible + localChevronIndex;
221220
222221 // Discard if outside visible range or beyond total
223- if (globalChevronIndex > lastVisible || globalChevronIndex >= totalChevrons) {
222+ if (globalChevronIndex < 0 || globalChevronIndex > lastVisible || globalChevronIndex >= totalChevrons) {
224223 gl_Position = vec4(2.0, 2.0, 0.0, 1.0);
225224 v_color = vec4(0.0);
226225 return;
227226 }
228227
229- // Compute chevron clip-space position along the line
230- // Use line start as HP uint base , then add fractional bp offset in clip space
231- // to avoid uint truncation causing uneven spacing at high zoom
228+ // Compute chevron position using HP arithmetic to avoid float precision loss.
229+ // Split lineStart as uint (exact) , then add fractional offset to the lo component.
230+ // hpScaleLinear handles the (hi-hi) + (lo-lo) subtraction correctly.
232231 float chevronOffsetBp = bpSpacing * float(globalChevronIndex + 1);
233- uint lineAbsStart = a_position.x + u_regionStart;
234- vec2 splitStart = hpSplitUint(lineAbsStart);
235- float cx = hpToClipX(splitStart, u_bpRangeX) + chevronOffsetBp / u_bpRangeX.z * 2.0;
232+ uint lineStartAbs = a_position.x + u_regionStart;
233+ vec2 splitStart = hpSplitUint(lineStartAbs);
234+ vec2 splitChevron = vec2(splitStart.x, splitStart.y + chevronOffsetBp);
235+ float cx = hpToClipX(splitChevron, u_bpRangeX);
236236
237237 float yPx = floor(a_y - u_scrollY + 0.5) + 0.5;
238238 float cy = 1.0 - (yPx / u_canvasHeight) * 2.0;
@@ -241,37 +241,17 @@ void main() {
241241 float chevronWidth = 5.0 / u_canvasWidth * 2.0;
242242 float chevronHeight = 4.0 / u_canvasHeight * 2.0;
243243
244+ // Chevron shape: 4 vertices forming two lines (< or >)
245+ // vid 0,3 are the outer tips, vid 1,2 are the center point
246+ // a_direction flips the X to mirror between < and >
247+ float dir = a_direction;
244248 float sx, sy;
245- if (a_direction > 0.0) {
246- // > shape: point on right
247- if (vid == 0) {
248- sx = cx - chevronWidth * 0.5;
249- sy = cy + chevronHeight * 0.5;
250- } else if (vid == 1) {
251- sx = cx + chevronWidth * 0.5;
252- sy = cy;
253- } else if (vid == 2) {
254- sx = cx + chevronWidth * 0.5;
255- sy = cy;
256- } else {
257- sx = cx - chevronWidth * 0.5;
258- sy = cy - chevronHeight * 0.5;
259- }
249+ if (vid == 1 || vid == 2) {
250+ sx = cx + chevronWidth * 0.5 * dir;
251+ sy = cy;
260252 } else {
261- // < shape: point on left
262- if (vid == 0) {
263- sx = cx + chevronWidth * 0.5;
264- sy = cy + chevronHeight * 0.5;
265- } else if (vid == 1) {
266- sx = cx - chevronWidth * 0.5;
267- sy = cy;
268- } else if (vid == 2) {
269- sx = cx - chevronWidth * 0.5;
270- sy = cy;
271- } else {
272- sx = cx + chevronWidth * 0.5;
273- sy = cy - chevronHeight * 0.5;
274- }
253+ sx = cx - chevronWidth * 0.5 * dir;
254+ sy = (vid == 0) ? cy + chevronHeight * 0.5 : cy - chevronHeight * 0.5;
275255 }
276256
277257 gl_Position = vec4(sx, sy, 0.0, 1.0);
0 commit comments