summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-08-27 16:55:59 +0300
committerGitHub <noreply@github.com>2017-08-27 16:55:59 +0300
commit6bf855fc99e4a1a2841dd013c767ca12b4069d93 (patch)
treebb70bf17dc013e2cdcdcee168f34ec72ab34d84b
parent(no commit message) (diff)
downloadheatmap-6bf855fc99e4a1a2841dd013c767ca12b4069d93.tar.gz
heatmap-6bf855fc99e4a1a2841dd013c767ca12b4069d93.zip
-rw-r--r--ascii-town-heatmap.rs96
1 files changed, 26 insertions, 70 deletions
diff --git a/ascii-town-heatmap.rs b/ascii-town-heatmap.rs
index 4edc4d6..cae11ef 100644
--- a/ascii-town-heatmap.rs
+++ b/ascii-town-heatmap.rs
@@ -7,7 +7,7 @@ extern crate serde;
#[macro_use]
extern crate serde_derive;
-use std::collections::BTreeSet;
+use std::collections::{BTreeSet, HashMap};
use std::io;
#[derive(Debug, Deserialize)]
@@ -37,57 +37,35 @@ fn main() {
let mut ord_m = BTreeSet::new();
for result in csv::Reader::from_reader(io::stdin()).deserialize() {
let Tile {
- access_count: a,
- modify_count: m,
+ create_time,
+ modify_time,
+ access_count: mut a,
+ modify_count: mut m,
tile_x: x,
tile_y: y,
..
} = result.unwrap();
- // Ignore 0,0, the values are too large.
- if (x, y) != (0, 0) {
- ord_a.insert(a);
- ord_m.insert(m);
- }
-
// Handle old migrated values.
- if a == 0 && m > 0 {
- tiles[y][x] = (1, 0);
- } else {
- tiles[y][x] = (a, m);
+ if modify_time == create_time && m > 0 {
+ a += 1;
+ m = 0;
}
- }
- // Chunk the values by splitting them at the largest gaps.
- #[derive(PartialEq, Eq, PartialOrd, Ord)]
- struct Chunk {
- start: u64,
- }
- fn chunks(ord: BTreeSet<u64>, chunks: usize) -> Vec<Chunk> {
- let mut gaps = BTreeSet::new();
- let mut prev = 0;
- let mut run = 0;
- for &x in &ord {
- let gap = x - prev;
- if gap > 100 {
- // Favor gaps with longer runs before them.
- gaps.insert((gap + run * 2, x));
- run = 0;
- } else {
- run += 1;
- }
- prev = x;
- }
- let mut chunks: Vec<_> = gaps.iter()
- .rev()
- .take(chunks)
- .map(|&(_, start)| Chunk { start })
- .collect();
- chunks.sort();
- chunks
+ ord_a.insert(a);
+ ord_m.insert(m);
+
+ tiles[y][x] = (a, m);
}
- let chunks_a = chunks(ord_a, 15);
- let chunks_m = chunks(ord_m, 15);
+
+ // Normalize all values by mapping them to equally spaced values in [0, 1].
+ let normal_map = |ord: BTreeSet<u64>| -> HashMap<u64, f64> {
+ ord.iter().enumerate().map(|(i, &x)| {
+ (x, i as f64 / (ord.len() - 1) as f64)
+ }).collect()
+ };
+ let normal_a = normal_map(ord_a);
+ let normal_m = normal_map(ord_m);
// Compose the heatmap image in-memory from the 2D array.
let mut heatmap = image::ImageBuffer::new(TORUS_SZ * TILE_W, TORUS_SZ * TILE_H);
@@ -97,38 +75,16 @@ fn main() {
let yellow = hsl::HSL::from_rgb(&[255, 255, 0]).h;
for y in 0..TORUS_SZ {
for x in 0..TORUS_SZ {
- // Normalize a value to the [0, 1] interval based on chunks.
- fn chunk_normalize(chunks: &[Chunk], x: u64) -> f64 {
- if x == 0 {
- 0.0
- } else {
- // Find the chunk x is in.
- let i = chunks.iter().position(|c| c.start > x);
- let mut v = i.unwrap_or(chunks.len()) as f64;
-
- // Add the intra-chunk linear offset.
- if let Some(i) = i {
- let start = if i > 1 {
- chunks[i - 1].start
- } else {
- 0
- };
- v += (x - start) as f64 / (chunks[i].start - start) as f64;
- }
-
- // Normalize so all the chunks fit in [0, 1].
- (v / chunks.len() as f64).min(1.0)
- }
- }
// Get and normalize the values.
let (a, m) = tiles[y as usize][x as usize];
- let a = chunk_normalize(&chunks_a, a).powf(0.2);
- let m = chunk_normalize(&chunks_m, m).powf(0.2);
+ let a = normal_a[&a].powf(1.0 / 2.0);
+ let m = normal_m[&m].powf(1.0 / 3.0);
// access => luminosity
- let l = (a + m).min(1.0) * 0.7;
+ let l = ((a + m) * 0.5).min(0.7);
// modify => saturation + hue (grey -> red -> yellow)
- let h = red * (1.0 - m) + yellow * m;
+ let interp = |a, b, x| a * (1.0 - x) + b * x;
+ let h = interp(red, yellow, m.powf(4.0));
let s = m;
let (r, g, b) = hsl::HSL { h, s, l }.to_rgb();