summary refs log tree commit diff
path: root/ascii-town-heatmap.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ascii-town-heatmap.rs')
-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();