aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock22
-rw-r--r--hittekaart-py/Cargo.toml2
-rw-r--r--hittekaart/src/renderer/heatmap.rs41
3 files changed, 40 insertions, 25 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d67ed63..0a85c6b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1118,11 +1118,10 @@ dependencies = [
[[package]]
name = "pyo3"
-version = "0.24.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
+checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383"
dependencies = [
- "cfg-if",
"indoc",
"libc",
"memoffset",
@@ -1136,19 +1135,18 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
-version = "0.24.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
+checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f"
dependencies = [
- "once_cell",
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
-version = "0.24.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
+checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105"
dependencies = [
"libc",
"pyo3-build-config",
@@ -1156,9 +1154,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
-version = "0.24.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
+checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -1168,9 +1166,9 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
-version = "0.24.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
+checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf"
dependencies = [
"heck",
"proc-macro2",
diff --git a/hittekaart-py/Cargo.toml b/hittekaart-py/Cargo.toml
index 7e69ef8..915f467 100644
--- a/hittekaart-py/Cargo.toml
+++ b/hittekaart-py/Cargo.toml
@@ -9,6 +9,6 @@ name = "hittekaart_py"
crate-type = ["cdylib"]
[dependencies]
-pyo3 = "0.24.0"
+pyo3 = "0.26.0"
hittekaart = { path = "../hittekaart" }
rayon = "1.10.0"
diff --git a/hittekaart/src/renderer/heatmap.rs b/hittekaart/src/renderer/heatmap.rs
index 187c8f3..d2c2452 100644
--- a/hittekaart/src/renderer/heatmap.rs
+++ b/hittekaart/src/renderer/heatmap.rs
@@ -7,9 +7,10 @@
//! We then render the colored heatmap tiles in `Renderer::colorize()`, which provides us with
//! colorful PNG data.
use crossbeam_channel::Sender;
-use image::{ImageBuffer, Luma, Pixel, RgbaImage};
+use image::{ImageBuffer, Luma, Pixel, Rgba, RgbaImage};
use nalgebra::{vector, Vector2};
use rayon::iter::ParallelIterator;
+use std::iter;
use super::{
super::{
@@ -118,27 +119,40 @@ fn render_line<P: Pixel>(
fn merge_heat_counter(base: &mut HeatCounter, overlay: &HeatCounter) {
for (tx, ty, source) in overlay.enumerate_tiles() {
let target = base.tile_mut(tx, ty);
- for (x, y, source) in source.enumerate_pixels() {
- let target = target.get_pixel_mut(x, y);
- target[0] += source[0];
+ for (t, s) in target.iter_mut().zip(source.iter()) {
+ *t += *s;
}
}
}
-fn colorize_tile(tile: &ImageBuffer<Luma<u8>, Vec<u8>>, max: u32) -> RgbaImage {
- let gradient = colorgrad::yl_or_rd();
+fn colorize_tile(tile: &ImageBuffer<Luma<u8>, Vec<u8>>, lut: &[Rgba<u8>]) -> RgbaImage {
let mut result = ImageBuffer::from_pixel(tile.width(), tile.height(), [0, 0, 0, 0].into());
- for (x, y, pixel) in tile.enumerate_pixels() {
+ for (pixel, target) in tile.pixels().zip(result.pixels_mut()) {
if pixel[0] > 0 {
- let alpha = pixel[0] as f64 / max as f64;
- let color = gradient.at(1.0 - alpha);
- let target = result.get_pixel_mut(x, y);
- *target = color.to_rgba8().into();
+ *target = lut[usize::from(pixel[0])];
}
}
result
}
+fn prepare_lut(max: u8) -> Vec<Rgba<u8>> {
+ let gradient = colorgrad::yl_or_rd();
+ iter::once([0, 0, 0, 0].into())
+ .chain((1..=max).map(|count| {
+ let alpha = count as f64 / max as f64;
+ // If we simply use alpha here, we get a linear mapping, and a jump from 1 to 2
+ // repetitions is worth as much as a jump from 9 to 10. By transforming alpha via the
+ // cubic function given below, we give more weight to the "early" repetitions by having
+ // a steeper slope, and less weight to later repetitions.
+ // There is no science behind the power 3, others work as well (if you adjust the
+ // sign). It seemed like a decent trade-off though.
+ let alpha = (alpha - 1.0).powi(3) + 1.0;
+ let color = gradient.at(1.0 - alpha);
+ color.to_rgba8().into()
+ }))
+ .collect()
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Renderer;
@@ -194,10 +208,13 @@ impl super::Renderer for Renderer {
return Ok(());
}
+ // max is a u8, so at most we have 256 colors to compute. We can handle that.
+ let color_lut = prepare_lut(max);
+
layer
.into_parallel_tiles()
.try_for_each_with(tx, |tx, (tile_x, tile_y, tile)| {
- let colorized = colorize_tile(&tile, max.into());
+ let colorized = colorize_tile(&tile, &color_lut);
let data = layer::compress_png_as_bytes(&colorized)?;
tx.send(RenderedTile {
x: tile_x,