| 216 | } |
| 217 | |
| 218 | function draw() { |
| 219 | ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); |
| 220 | |
| 221 | // Pi Safe Range (0-3.3V) |
| 222 | ctx.fillStyle = "rgba(0, 255, 157, 0.04)"; |
| 223 | const s0 = toScreen(0, 0).y, |
| 224 | sMax = toScreen(0, VOLTAGE_LIMIT).y; |
| 225 | ctx.fillRect(0, sMax, window.innerWidth, s0 - sMax); |
| 226 | |
| 227 | // GRID LINES |
| 228 | ctx.strokeStyle = "#1a1d21"; |
| 229 | ctx.lineWidth = 1; |
| 230 | for (let i = 0; i <= 10; i++) { |
| 231 | let x = (i / 10) * window.innerWidth; |
| 232 | ctx.beginPath(); |
| 233 | ctx.moveTo(x, 0); |
| 234 | ctx.lineTo(x, window.innerHeight); |
| 235 | ctx.stroke(); |
| 236 | } |
| 237 | for (let i = 0; i <= 10; i++) { |
| 238 | let yVal = |
| 239 | state.viewport.yMin + |
| 240 | (i * (state.viewport.yMax - state.viewport.yMin)) / 10; |
| 241 | let yPos = toScreen(0, yVal).y; |
| 242 | ctx.beginPath(); |
| 243 | ctx.moveTo(0, yPos); |
| 244 | ctx.lineTo(window.innerWidth, yPos); |
| 245 | ctx.stroke(); |
| 246 | } |
| 247 | |
| 248 | // Axes |
| 249 | ctx.strokeStyle = "#30363d"; |
| 250 | ctx.lineWidth = 2; |
| 251 | const axOrigin = toScreen(0, 0); |
| 252 | ctx.beginPath(); |
| 253 | ctx.moveTo(0, axOrigin.y); |
| 254 | ctx.lineTo(window.innerWidth, axOrigin.y); |
| 255 | ctx.stroke(); |
| 256 | |
| 257 | // The Signal Curve |
| 258 | if (state.points.length) { |
| 259 | ctx.strokeStyle = "#00ff9d"; |
| 260 | ctx.lineWidth = 2.5; |
| 261 | ctx.lineJoin = "round"; |
| 262 | ctx.beginPath(); |
| 263 | state.points.forEach((p, i) => { |
| 264 | const s = toScreen(p.x, p.y); |
| 265 | if (i === 0 || isNaN(p.y) || !isFinite(p.y)) ctx.moveTo(s.x, s.y); |
| 266 | else ctx.lineTo(s.x, s.y); |
| 267 | }); |
| 268 | ctx.stroke(); |
| 269 | } |
| 270 | |
| 271 | // Scanning Cursor |
| 272 | ctx.strokeStyle = "#fff"; |
| 273 | ctx.setLineDash([5, 5]); |
| 274 | const scanX = state.scanPos * window.innerWidth; |
| 275 | ctx.beginPath(); |