1Heatmap.prototype.draw = function() {
2  this.drawHeatmap();
3  for (var i = 0; i < this.drawTraces.length; ++i)
4    if (this.drawTraces[i])
5      this.drawTrace(i, 1);
6};
7
8Heatmap.prototype.drawHeatmap = function() {
9  this.context.clearRect(0, 0, this.w, this.h);
10
11  this.context.save();
12  this.context.scale(this.w / this.revisions.length, this.h / this.resolution);
13
14  var counts = [];
15  for (var time = 0; time < this.revisions.length; ++time) {
16    var revision = this.revisions[time];
17    for (var bucket in this.data[revision]) {
18      counts.push(this.data[revision][bucket].length);
19    }
20  }
21  counts.sort(function(a, b) {return a - b});
22  var cutoff = percentile(counts, 0.9);
23  if (cutoff < 2)
24    cutoff = 2;
25
26  for (var time = 0; time < this.revisions.length; ++time) {
27    var revision = this.revisions[time];
28    for (var bucket in this.data[revision]) {
29      var count = this.data[revision][bucket].length;
30
31      // Calculate average color across all traces in bucket.
32      var r = 0, g = 0, b = 0;
33      for (var i = 0; i < this.data[revision][bucket].length; ++i) {
34        var trace = this.data[revision][bucket][i];
35        r += nthColor(trace)[0];
36        g += nthColor(trace)[1];
37        b += nthColor(trace)[2];
38      }
39      r /= count, g /= count, b /= count;
40      var brightness = mapRange(count / cutoff, 0, 1, 2, 0.5);
41
42      // Draw!
43      this.context.fillStyle = calculateColor(r, g, b, 1, brightness);
44      this.context.fillRect(time, bucket, 1, 1);
45    }
46  }
47
48  this.context.restore();
49};
50
51Heatmap.prototype.drawTrace = function(trace, opacity) {
52  this.drawTraceLine(trace, 4, calculateColor(255, 255, 255, opacity, 1));
53  var color = calculateColor(nthColor(trace)[0], nthColor(trace)[1], nthColor(trace)[2], opacity, 1);
54  this.drawTraceLine(trace, 2, color);
55};
56
57Heatmap.prototype.drawTraceLine = function(trace, width, color) {
58  var revisionWidth = this.w / this.revisions.length;
59
60  this.context.save();
61  this.context.lineJoin = 'round';
62  this.context.lineWidth = width;
63  this.context.strokeStyle = color;
64  this.context.translate(revisionWidth / 2, 0);
65  this.context.beginPath();
66
67  var started = false;
68  for (var time = 0; time < this.revisions.length; ++time) {
69    var values = this.traces[this.revisions[time]][trace];
70    var sum = 0;
71    for (var value of values)
72      sum += value;
73    var value = sum / values.length;
74
75    var bucket = mapRange(value, this.min, this.max, 0, this.h);
76    if (started) {
77      this.context.lineTo(revisionWidth * time, bucket);
78    } else {
79      this.context.moveTo(revisionWidth * time, bucket);
80      started = true;
81    }
82  }
83
84  this.context.stroke();
85  this.context.restore();
86}
87
88Heatmap.prototype.scaleCanvas = function() {
89  this.canvas.width = this.canvas.clientWidth * window.devicePixelRatio;
90  this.canvas.height = this.canvas.clientHeight * window.devicePixelRatio;
91  this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
92
93  this.w = this.canvas.clientWidth, this.h = this.canvas.clientHeight;
94
95  // Flip canvas.
96  this.context.scale(1, -1);
97  this.context.translate(0, -this.h);
98};
99