Flood fill to improve performance
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::projection::OrthoProjection;
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use font8x8::{UnicodeFonts, BASIC_FONTS};
|
use font8x8::{UnicodeFonts, BASIC_FONTS};
|
||||||
use plotters::prelude::*;
|
use plotters::prelude::*;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
const IMG_WIDTH: u32 = 900;
|
const IMG_WIDTH: u32 = 900;
|
||||||
@@ -118,32 +119,58 @@ pub fn render_frame(
|
|||||||
|
|
||||||
map_area.fill(&LAND_BG).context("fill map area")?;
|
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() {
|
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];
|
let cover = cloud_cover[i];
|
||||||
if cover.is_nan() {
|
if cover.is_nan() {
|
||||||
continue; // missing value (bitmap) — keep background
|
continue;
|
||||||
}
|
}
|
||||||
let color = match cloud_color(cover) {
|
let Some((px, py)) = proj.project(lats[i] as f64, lons[i] as f64) else { continue };
|
||||||
Some(c) => c,
|
let Some((col, row)) = proj.to_pixel(px, py, map_w, map_h) else { continue };
|
||||||
None => continue, // clear sky — keep land background
|
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
|
// BFS flood fill.
|
||||||
for dy in 0..2i32 {
|
const DIRS: [(i32, i32); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
|
||||||
for dx in 0..2i32 {
|
while let Some((col, row)) = queue.pop_front() {
|
||||||
let c = col + dx;
|
let cover = grid[row as usize * grid_w + col as usize];
|
||||||
let r = row + dy;
|
for (dc, dr) in DIRS {
|
||||||
if c >= 0 && c < map_w as i32 && r >= 0 && r < map_h as i32 {
|
let nc = col + dc;
|
||||||
map_area.draw_pixel((c, r), &color).ok();
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user