diff options
author | Daniel Schadt <kingdread@gmx.de> | 2023-03-11 19:38:16 +0100 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2023-03-11 19:38:16 +0100 |
commit | 4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992 (patch) | |
tree | 5fc46f69c5ddc78d974f3b93e83af0eacf1b59d1 /src/renderer/mod.rs | |
parent | 718d4fbf8b6e85ab808b11143b0657e75096ef73 (diff) | |
download | hittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.tar.gz hittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.tar.bz2 hittekaart-4e8ce5bbaf5aa71f7e00e7a131fc6b25e623c992.zip |
abstract away tile rendering logic
This is in prepration for the tilehunt mode, where we want to render
tiles differently.
Diffstat (limited to 'src/renderer/mod.rs')
-rw-r--r-- | src/renderer/mod.rs | 90 |
1 files changed, 90 insertions, 0 deletions
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<u8>, +} + +/// 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<Coordinates>], tick: Sender<()>) -> Result<Self::Prepared>; + + /// 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<RenderedTile>) -> 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<u64>; +} + +/// 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<R: Renderer, F: FnMut() -> Result<()>>( + zoom: u32, + tracks: &[Vec<Coordinates>], + mut tick: F, +) -> Result<R::Prepared> { + 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<R: Renderer, F: FnMut(RenderedTile) -> 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() + }) +} |