| 69 | }; |
| 70 | |
| 71 | const generateGridVertices = (gridArea: GridArea, horizontal: number, vertical: number): Float32Array => { |
| 72 | const { left, right, top, bottom, canvasWidth, canvasHeight } = gridArea; |
| 73 | // Be resilient: older call sites may omit/incorrectly pass DPR. Defaulting avoids hard crashes. |
| 74 | const devicePixelRatio = |
| 75 | Number.isFinite(gridArea.devicePixelRatio) && gridArea.devicePixelRatio > 0 ? gridArea.devicePixelRatio : 1; |
| 76 | |
| 77 | // Calculate plot area in device pixels using explicit DPR |
| 78 | const plotLeft = left * devicePixelRatio; |
| 79 | const plotRight = canvasWidth - right * devicePixelRatio; |
| 80 | const plotTop = top * devicePixelRatio; |
| 81 | const plotBottom = canvasHeight - bottom * devicePixelRatio; |
| 82 | |
| 83 | const plotWidth = plotRight - plotLeft; |
| 84 | const plotHeight = plotBottom - plotTop; |
| 85 | |
| 86 | // Total vertices: (horizontal + vertical) * 2 vertices per line |
| 87 | const totalLines = horizontal + vertical; |
| 88 | const vertices = new Float32Array(totalLines * 2 * 2); // 2 vertices * 2 floats per vertex |
| 89 | |
| 90 | let idx = 0; |
| 91 | |
| 92 | // Generate horizontal lines (constant Y, varying X) |
| 93 | for (let i = 0; i < horizontal; i++) { |
| 94 | // Calculate t parameter for even spacing |
| 95 | const t = horizontal === 1 ? 0.5 : i / (horizontal - 1); |
| 96 | const yDevice = plotTop + t * plotHeight; |
| 97 | |
| 98 | // Convert to clip space |
| 99 | const xClipLeft = (plotLeft / canvasWidth) * 2.0 - 1.0; |
| 100 | const xClipRight = (plotRight / canvasWidth) * 2.0 - 1.0; |
| 101 | const yClip = 1.0 - (yDevice / canvasHeight) * 2.0; // Flip Y-axis |
| 102 | |
| 103 | // First vertex (left edge) |
| 104 | vertices[idx++] = xClipLeft; |
| 105 | vertices[idx++] = yClip; |
| 106 | |
| 107 | // Second vertex (right edge) |
| 108 | vertices[idx++] = xClipRight; |
| 109 | vertices[idx++] = yClip; |
| 110 | } |
| 111 | |
| 112 | // Generate vertical lines (constant X, varying Y) |
| 113 | for (let i = 0; i < vertical; i++) { |
| 114 | // Calculate t parameter for even spacing |
| 115 | const t = vertical === 1 ? 0.5 : i / (vertical - 1); |
| 116 | const xDevice = plotLeft + t * plotWidth; |
| 117 | |
| 118 | // Convert to clip space |
| 119 | const xClip = (xDevice / canvasWidth) * 2.0 - 1.0; |
| 120 | const yClipTop = 1.0 - (plotTop / canvasHeight) * 2.0; // Flip Y-axis |
| 121 | const yClipBottom = 1.0 - (plotBottom / canvasHeight) * 2.0; // Flip Y-axis |
| 122 | |
| 123 | // First vertex (top edge) |
| 124 | vertices[idx++] = xClip; |
| 125 | vertices[idx++] = yClipTop; |
| 126 | |
| 127 | // Second vertex (bottom edge) |
| 128 | vertices[idx++] = xClip; |