(point, cssColor, sizeCssPx)
| 106 | }; |
| 107 | |
| 108 | const prepare: HighlightRenderer['prepare'] = (point, cssColor, sizeCssPx) => { |
| 109 | assertNotDisposed(); |
| 110 | |
| 111 | if (!Number.isFinite(point.centerDeviceX) || !Number.isFinite(point.centerDeviceY)) { |
| 112 | throw new Error('HighlightRenderer.prepare: point center must be finite.'); |
| 113 | } |
| 114 | if (!Number.isFinite(point.canvasWidth) || !Number.isFinite(point.canvasHeight) || point.canvasWidth <= 0 || point.canvasHeight <= 0) { |
| 115 | throw new Error('HighlightRenderer.prepare: canvasWidth/canvasHeight must be positive finite numbers.'); |
| 116 | } |
| 117 | if (!isFiniteScissor(point.scissor)) { |
| 118 | throw new Error('HighlightRenderer.prepare: scissor must be finite.'); |
| 119 | } |
| 120 | if (!Number.isFinite(sizeCssPx) || sizeCssPx < 0) { |
| 121 | throw new Error('HighlightRenderer.prepare: size must be a finite non-negative number.'); |
| 122 | } |
| 123 | |
| 124 | const dprRaw = point.devicePixelRatio; |
| 125 | const dpr = Number.isFinite(dprRaw) && dprRaw > 0 ? dprRaw : 1; |
| 126 | const baseRadiusDevicePx = sizeCssPx * dpr; |
| 127 | |
| 128 | // Slightly larger than the implied "normal" point size. |
| 129 | const radius = Math.max(1, baseRadiusDevicePx * 1.5); |
| 130 | const thickness = Math.max(1, Math.round(Math.max(2, radius * 0.25))); |
| 131 | |
| 132 | const seriesRgba = parseCssColorToRgba01(cssColor) ?? DEFAULT_RGBA; |
| 133 | const ringRgba = brighten(seriesRgba, 1.25); |
| 134 | const useDarkOutline = luminance(seriesRgba) > 0.7; |
| 135 | const outlineRgba: readonly [number, number, number, number] = useDarkOutline ? [0, 0, 0, 0.9] : [1, 1, 1, 0.9]; |
| 136 | |
| 137 | const buf = new ArrayBuffer(12 * 4); |
| 138 | new Float32Array(buf).set([ |
| 139 | point.centerDeviceX, |
| 140 | point.centerDeviceY, |
| 141 | radius, |
| 142 | thickness, |
| 143 | ringRgba[0], |
| 144 | ringRgba[1], |
| 145 | ringRgba[2], |
| 146 | 1.0, |
| 147 | outlineRgba[0], |
| 148 | outlineRgba[1], |
| 149 | outlineRgba[2], |
| 150 | outlineRgba[3], |
| 151 | ]); |
| 152 | writeUniformBuffer(device, uniformBuffer, buf); |
| 153 | |
| 154 | lastCanvasWidth = point.canvasWidth; |
| 155 | lastCanvasHeight = point.canvasHeight; |
| 156 | |
| 157 | // Clamp scissor to valid canvas bounds (defensive). |
| 158 | const x0 = clampInt(Math.floor(point.scissor.x), 0, Math.max(0, point.canvasWidth)); |
| 159 | const y0 = clampInt(Math.floor(point.scissor.y), 0, Math.max(0, point.canvasHeight)); |
| 160 | const x1 = clampInt(Math.ceil(point.scissor.x + point.scissor.w), 0, Math.max(0, point.canvasWidth)); |
| 161 | const y1 = clampInt(Math.ceil(point.scissor.y + point.scissor.h), 0, Math.max(0, point.canvasHeight)); |
| 162 | lastScissor = { x: x0, y: y0, w: Math.max(0, x1 - x0), h: Math.max(0, y1 - y0) }; |
| 163 | |
| 164 | hasPrepared = true; |
| 165 | }; |
nothing calls this directly
no test coverage detected