Flood fill to improve performance

This commit is contained in:
Schuwi
2026-03-06 21:41:16 +01:00
parent a9eed7f378
commit 4ee33a882d

View File

@@ -2,6 +2,7 @@ use crate::projection::OrthoProjection;
use anyhow::{Context, Result};
use font8x8::{UnicodeFonts, BASIC_FONTS};
use plotters::prelude::*;
use std::collections::VecDeque;
use std::path::Path;
const IMG_WIDTH: u32 = 900;
@@ -118,32 +119,58 @@ pub fn render_frame(
map_area.fill(&LAND_BG).context("fill map area")?;
// Plot grid points
// Nearest-neighbour fill via multi-source BFS.
//
// Each data point seeds one pixel in a flat grid. BFS then propagates each
// value outward to all 4-connected NaN neighbours, so every reachable pixel
// ends up holding the value of the closest source point (by Manhattan distance).
// Complexity: O(map_w × map_h) — ~410 k array ops, no hashing.
let grid_w = map_w as usize;
let grid_h = map_h as usize;
let mut grid = vec![f32::NAN; grid_w * grid_h];
let mut queue: VecDeque<(i32, i32)> = VecDeque::new();
// Seed the grid with projected data points.
for i in 0..lats.len() {
let lat = lats[i] as f64;
let lon = lons[i] as f64;
let Some((px, py)) = proj.project(lat, lon) else { continue };
let Some((col, row)) = proj.to_pixel(px, py, map_w, map_h) else { continue };
let cover = cloud_cover[i];
if cover.is_nan() {
continue; // missing value (bitmap) — keep background
continue;
}
let color = match cloud_color(cover) {
Some(c) => c,
None => continue, // clear sky — keep land background
};
let Some((px, py)) = proj.project(lats[i] as f64, lons[i] as f64) else { continue };
let Some((col, row)) = proj.to_pixel(px, py, map_w, map_h) else { continue };
let idx = row as usize * grid_w + col as usize;
if grid[idx].is_nan() {
grid[idx] = cover;
queue.push_back((col, row));
}
}
// Paint a 2×2 block to avoid gaps between grid points
for dy in 0..2i32 {
for dx in 0..2i32 {
let c = col + dx;
let r = row + dy;
if c >= 0 && c < map_w as i32 && r >= 0 && r < map_h as i32 {
map_area.draw_pixel((c, r), &color).ok();
}
// BFS flood fill.
const DIRS: [(i32, i32); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
while let Some((col, row)) = queue.pop_front() {
let cover = grid[row as usize * grid_w + col as usize];
for (dc, dr) in DIRS {
let nc = col + dc;
let nr = row + dr;
if nc < 0 || nc >= map_w as i32 || nr < 0 || nr >= map_h as i32 {
continue;
}
let nidx = nr as usize * grid_w + nc as usize;
if grid[nidx].is_nan() {
grid[nidx] = cover;
queue.push_back((nc, nr));
}
}
}
// Paint the filled grid.
for row in 0..map_h as i32 {
for col in 0..map_w as i32 {
let cover = grid[row as usize * grid_w + col as usize];
if let Some(color) = cloud_color(cover) {
map_area.draw_pixel((col, row), &color).ok();
}
// NaN or clear sky: keep the land background
}
}