From 4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Sat, 11 Mar 2023 19:38:16 +0100 Subject: abstract away tile rendering logic This is in prepration for the tilehunt mode, where we want to render tiles differently. --- src/renderer/mod.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/renderer/mod.rs (limited to 'src/renderer/mod.rs') diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs new file mode 100644 index 0000000..927c6ed --- /dev/null +++ b/src/renderer/mod.rs @@ -0,0 +1,90 @@ +//! Generic "tile rendering" methods. +use std::thread; + +use color_eyre::Result; +use crossbeam_channel::Sender; + +use super::gpx::Coordinates; + +pub mod heatmap; + +const CHANNEL_SIZE: usize = 30; + +/// Represents a fully rendered tile. +#[derive(Debug, Clone)] +pub struct RenderedTile { + /// The `x` coordinate of the tile. + pub x: u64, + /// The `y` coordinate of the tile. + pub y: u64, + /// The encoded (PNG) image data, ready to be saved to disk. + pub data: Vec, +} + +/// An object that is responsible for turning raw GPX tracks into a representation. +/// +/// This is done in two steps, preparation and actual rendering. This allows different feedback for +/// the user. +pub trait Renderer { + type Prepared: Send; + + /// Prepare the rendered data. + /// + /// The `tick` channel is used to provide user-feedback, for every finished track a tick should + /// be sent. + fn prepare(zoom: u32, tracks: &[Vec], tick: Sender<()>) -> Result; + + /// Actually produce the colored tiles, using the previously prepared data. + /// + /// The `saver` channel is used to send the finished tiles to a thread that is responsible for + /// saving them. + fn colorize(prepared: Self::Prepared, saver: Sender) -> Result<()>; + + /// Returns the tile count of the prepared data. + /// + /// This is used for the user interface, to scale progress bars appropriately. + fn tile_count(prepared: &Self::Prepared) -> Result; +} + +/// A convenience wrapper to call [`Renderer::prepare`]. +/// +/// This function takes the same arguments, but provides the ability to use a callback closure +/// instead of having to set up a channel. The callback is always called on the same thread. +pub fn prepare Result<()>>( + zoom: u32, + tracks: &[Vec], + mut tick: F, +) -> Result { + thread::scope(|s| { + let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE); + + let preparer = s.spawn(|| R::prepare(zoom, tracks, sender)); + + for _ in receiver { + tick()?; + } + + preparer.join().unwrap() + }) +} + +/// A convenience wrapper to call [`Renderer::colorize`]. +/// +/// This function takes the same arguments, but provides the ability to use a callback closure +/// instead of having to set up a channel. The callback is always called on the same thread. +pub fn colorize Result<()>>( + prepared: R::Prepared, + mut saver: F, +) -> Result<()> { + thread::scope(|s| { + let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE); + + let colorizer = s.spawn(|| R::colorize(prepared, sender)); + + for tile in receiver { + saver(tile)?; + } + + colorizer.join().unwrap() + }) +} -- cgit v1.2.3