diff --git a/assets/js/rover_map_hook.ts b/assets/js/rover_map_hook.ts index cf928eb0..e87c8281 100644 --- a/assets/js/rover_map_hook.ts +++ b/assets/js/rover_map_hook.ts @@ -200,13 +200,15 @@ export const RoverMap: Partial = { for (const c of cells) { this.cellLookup.set(cellKey(c.lat, c.lon), c) } + // Update radius before (re)drawing so the per-pixel clip uses the + // new value, otherwise the first paint can leak past the circle. + this.setDriveRadius(drive_radius_km) if (!this.qualityLayer) { this.qualityLayer = this.buildQualityLayer() this.qualityLayer.addTo(this.map) } else { this.qualityLayer.redraw() } - this.setDriveRadius(drive_radius_km) }) this.handleEvent("focus_cell", ( @@ -338,27 +340,44 @@ export const RoverMap: Partial = { } } + // Clip the heatmap to the drive-radius circle: pixels farther + // than driveRadiusKm from home stay transparent so the overlay + // never extends past the dashed circle. + const cosLat = Math.cos(self.homeLat * Math.PI / 180) + const kmPerPxX = lonPerPx * 111.0 * Math.max(cosLat, 0.1) + const kmPerPxY = latPerPx * 111.0 + const homePxX = (self.homeLon - nw.lng) / lonPerPx + const homePxY = (nw.lat - self.homeLat) / latPerPx + const radiusKmSq = self.driveRadiusKm * self.driveRadiusKm + const img = ctx.createImageData(w, h) const data = img.data - for (let i = 0; i < w * h; i++) { - let bestAlpha = 0 - let bestRgb: [number, number, number] | null = null + for (let py = 0; py < h; py++) { + const dyKm = (py - homePxY) * kmPerPxY + for (let px = 0; px < w; px++) { + const dxKm = (px - homePxX) * kmPerPxX + if (dxKm * dxKm + dyKm * dyKm > radiusKmSq) continue - for (const tierColor of TIER_ORDER) { - const a = tierBuffers[tierColor][i] - if (a > 0.05 && a > bestAlpha) { - bestAlpha = a - bestRgb = TIER_RGB[tierColor] + const i = py * w + px + let bestAlpha = 0 + let bestRgb: [number, number, number] | null = null + + for (const tierColor of TIER_ORDER) { + const a = tierBuffers[tierColor][i] + if (a > 0.05 && a > bestAlpha) { + bestAlpha = a + bestRgb = TIER_RGB[tierColor] + } } - } - if (bestRgb && bestAlpha > 0) { - const o = i * 4 - data[o] = bestRgb[0] - data[o + 1] = bestRgb[1] - data[o + 2] = bestRgb[2] - data[o + 3] = Math.min(255, Math.round(bestAlpha * 0.35 * 255)) + if (bestRgb && bestAlpha > 0) { + const o = i * 4 + data[o] = bestRgb[0] + data[o + 1] = bestRgb[1] + data[o + 2] = bestRgb[2] + data[o + 3] = Math.min(255, Math.round(bestAlpha * 0.35 * 255)) + } } }