fix(rover): clip heatmap to drive-radius circle on render

This commit is contained in:
Graham McIntire 2026-04-25 17:35:43 -05:00
parent c1eabb788f
commit 083c393bc8
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59

View file

@ -200,13 +200,15 @@ export const RoverMap: Partial<RoverMapHook> = {
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<RoverMapHook> = {
}
}
// 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))
}
}
}