1var OUTLIER_THRESHOLD = 0.05;
2var MARGIN_SIZE = 0.10;
3
4Heatmap.prototype.calculate = function(data) {
5  this.cleanUpData(data);
6  this.calculateBounds();
7  this.calculateHeatmap();
8
9  this.drawTraces = [];
10  for (var i = 0; i < data.length; ++i)
11    this.drawTraces.push(false);
12};
13
14Heatmap.prototype.cleanUpData = function(data) {
15  // Data, indexed by revision and trace.
16  this.traces = {};
17
18  for (var bot in data) {
19    var bot_data = data[bot];
20    for (var run_index = 0; run_index < bot_data.length; ++run_index) {
21      var run_data = bot_data[run_index];
22      if (run_data == null)
23        continue
24      var stories_data = run_data['user_story_runs'];
25      for (var story_index = 0; story_index < stories_data.length; ++story_index) {
26        var story_data = stories_data[story_index];
27        var story_name = story_data['user_story'];
28        if (story_name == 'summary')
29          continue
30
31        values = story_data['values'];
32
33        var index = bot_data.length - run_index - 1;
34        if (!this.traces[index])
35          this.traces[index] = {};
36        this.traces[index][story_index] = values;
37      }
38    }
39  }
40
41  this.revisions = Object.keys(this.traces).sort();
42};
43
44Heatmap.prototype.calculateBounds = function() {
45  var values = [];
46  for (var revision in this.traces)
47    for (var trace in this.traces[revision])
48      for (var value of this.traces[revision][trace])
49        values.push(value);
50
51  // Exclude OUTLIER_THRESHOLD% of the points.
52  values.sort(function(a, b) {return a - b});
53  this.min = percentile(values, OUTLIER_THRESHOLD / 2);
54  this.max = percentile(values, -OUTLIER_THRESHOLD / 2);
55
56  // Ease bounds by adding margins.
57  var margin = (this.max - this.min) * MARGIN_SIZE;
58  this.min -= margin;
59  this.max += margin;
60};
61
62Heatmap.prototype.calculateHeatmap = function() {
63  this.data = {};
64  for (var revision in this.traces) {
65    for (var trace in this.traces[revision]) {
66      for (var value of this.traces[revision][trace]) {
67        var bucket = this.findBucket(value);
68
69        if (this.data[revision] == null)
70          this.data[revision] = {};
71        if (this.data[revision][bucket] == null)
72          this.data[revision][bucket] = [];
73        this.data[revision][bucket].push(trace);
74      }
75    }
76  }
77};
78
79Heatmap.prototype.findBucket = function(value) {
80  var bucket = Math.floor(mapRange(value, this.min, this.max, 0, this.resolution));
81  return constrain(bucket, 0, this.resolution - 1);
82};
83