fix(rover): clip heatmap to drive-radius circle on render
This commit is contained in:
parent
c1eabb788f
commit
083c393bc8
1 changed files with 35 additions and 16 deletions
|
|
@ -200,13 +200,15 @@ export const RoverMap: Partial<RoverMapHook> = {
|
||||||
for (const c of cells) {
|
for (const c of cells) {
|
||||||
this.cellLookup.set(cellKey(c.lat, c.lon), c)
|
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) {
|
if (!this.qualityLayer) {
|
||||||
this.qualityLayer = this.buildQualityLayer()
|
this.qualityLayer = this.buildQualityLayer()
|
||||||
this.qualityLayer.addTo(this.map)
|
this.qualityLayer.addTo(this.map)
|
||||||
} else {
|
} else {
|
||||||
this.qualityLayer.redraw()
|
this.qualityLayer.redraw()
|
||||||
}
|
}
|
||||||
this.setDriveRadius(drive_radius_km)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.handleEvent("focus_cell", (
|
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 img = ctx.createImageData(w, h)
|
||||||
const data = img.data
|
const data = img.data
|
||||||
|
|
||||||
for (let i = 0; i < w * h; i++) {
|
for (let py = 0; py < h; py++) {
|
||||||
let bestAlpha = 0
|
const dyKm = (py - homePxY) * kmPerPxY
|
||||||
let bestRgb: [number, number, number] | null = null
|
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 i = py * w + px
|
||||||
const a = tierBuffers[tierColor][i]
|
let bestAlpha = 0
|
||||||
if (a > 0.05 && a > bestAlpha) {
|
let bestRgb: [number, number, number] | null = null
|
||||||
bestAlpha = a
|
|
||||||
bestRgb = TIER_RGB[tierColor]
|
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) {
|
if (bestRgb && bestAlpha > 0) {
|
||||||
const o = i * 4
|
const o = i * 4
|
||||||
data[o] = bestRgb[0]
|
data[o] = bestRgb[0]
|
||||||
data[o + 1] = bestRgb[1]
|
data[o + 1] = bestRgb[1]
|
||||||
data[o + 2] = bestRgb[2]
|
data[o + 2] = bestRgb[2]
|
||||||
data[o + 3] = Math.min(255, Math.round(bestAlpha * 0.35 * 255))
|
data[o + 3] = Math.min(255, Math.round(bestAlpha * 0.35 * 255))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue