| Heatmap.prototype.draw = function() { |
| this.drawHeatmap(); |
| for (var i = 0; i < this.drawTraces.length; ++i) |
| if (this.drawTraces[i]) |
| this.drawTrace(i, 1); |
| }; |
| |
| Heatmap.prototype.drawHeatmap = function() { |
| this.context.clearRect(0, 0, this.w, this.h); |
| |
| this.context.save(); |
| this.context.scale(this.w / this.revisions.length, this.h / this.resolution); |
| |
| var counts = []; |
| for (var time = 0; time < this.revisions.length; ++time) { |
| var revision = this.revisions[time]; |
| for (var bucket in this.data[revision]) { |
| counts.push(this.data[revision][bucket].length); |
| } |
| } |
| counts.sort(function(a, b) {return a - b}); |
| var cutoff = percentile(counts, 0.9); |
| if (cutoff < 2) |
| cutoff = 2; |
| |
| for (var time = 0; time < this.revisions.length; ++time) { |
| var revision = this.revisions[time]; |
| for (var bucket in this.data[revision]) { |
| var count = this.data[revision][bucket].length; |
| |
| // Calculate average color across all traces in bucket. |
| var r = 0, g = 0, b = 0; |
| for (var i = 0; i < this.data[revision][bucket].length; ++i) { |
| var trace = this.data[revision][bucket][i]; |
| r += nthColor(trace)[0]; |
| g += nthColor(trace)[1]; |
| b += nthColor(trace)[2]; |
| } |
| r /= count, g /= count, b /= count; |
| var brightness = mapRange(count / cutoff, 0, 1, 2, 0.5); |
| |
| // Draw! |
| this.context.fillStyle = calculateColor(r, g, b, 1, brightness); |
| this.context.fillRect(time, bucket, 1, 1); |
| } |
| } |
| |
| this.context.restore(); |
| }; |
| |
| Heatmap.prototype.drawTrace = function(trace, opacity) { |
| this.drawTraceLine(trace, 4, calculateColor(255, 255, 255, opacity, 1)); |
| var color = calculateColor(nthColor(trace)[0], nthColor(trace)[1], nthColor(trace)[2], opacity, 1); |
| this.drawTraceLine(trace, 2, color); |
| }; |
| |
| Heatmap.prototype.drawTraceLine = function(trace, width, color) { |
| var revisionWidth = this.w / this.revisions.length; |
| |
| this.context.save(); |
| this.context.lineJoin = 'round'; |
| this.context.lineWidth = width; |
| this.context.strokeStyle = color; |
| this.context.translate(revisionWidth / 2, 0); |
| this.context.beginPath(); |
| |
| var started = false; |
| for (var time = 0; time < this.revisions.length; ++time) { |
| var values = this.traces[this.revisions[time]][trace]; |
| var sum = 0; |
| for (var value of values) |
| sum += value; |
| var value = sum / values.length; |
| |
| var bucket = mapRange(value, this.min, this.max, 0, this.h); |
| if (started) { |
| this.context.lineTo(revisionWidth * time, bucket); |
| } else { |
| this.context.moveTo(revisionWidth * time, bucket); |
| started = true; |
| } |
| } |
| |
| this.context.stroke(); |
| this.context.restore(); |
| } |
| |
| Heatmap.prototype.scaleCanvas = function() { |
| this.canvas.width = this.canvas.clientWidth * window.devicePixelRatio; |
| this.canvas.height = this.canvas.clientHeight * window.devicePixelRatio; |
| this.context.scale(window.devicePixelRatio, window.devicePixelRatio); |
| |
| this.w = this.canvas.clientWidth, this.h = this.canvas.clientHeight; |
| |
| // Flip canvas. |
| this.context.scale(1, -1); |
| this.context.translate(0, -this.h); |
| }; |