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