summaryrefslogtreecommitdiff
path: root/timings/js/plotter.js
diff options
context:
space:
mode:
Diffstat (limited to 'timings/js/plotter.js')
-rw-r--r--timings/js/plotter.js336
1 files changed, 336 insertions, 0 deletions
diff --git a/timings/js/plotter.js b/timings/js/plotter.js
new file mode 100644
index 00000000..86fb2304
--- /dev/null
+++ b/timings/js/plotter.js
@@ -0,0 +1,336 @@
+/*
+ Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+*/
+
+// Collection of classes used to plot data in a <canvas>. Create a Plotter()
+// to generate a plot.
+
+// vertical marker for columns
+function Marker(color) {
+ var m = document.createElement("DIV");
+ m.setAttribute("class", "plot-cursor");
+ m.style.backgroundColor = color;
+ m.style.opacity = "0.3";
+ m.style.position = "absolute";
+ m.style.left = "-2px";
+ m.style.top = "-2px";
+ m.style.width = "0px";
+ m.style.height = "0px";
+ return m;
+}
+
+/**
+ * HorizontalMarker class
+ * Create a horizontal marker at the indicated mouse location.
+ * @constructor
+ *
+ * @param canvasRect {Object} The canvas bounds (in client coords).
+ * @param clientY {Number} The vertical mouse click location that spawned
+ * the marker, in the client coordinate space.
+ * @param yValue {Number} The plotted value corresponding to the clientY
+ * click location.
+ */
+function HorizontalMarker(canvasRect, clientY, yValue) {
+ // Add a horizontal line to the graph.
+ var m = document.createElement("DIV");
+ m.setAttribute("class", "plot-baseline");
+ m.style.backgroundColor = HorizontalMarker.COLOR;
+ m.style.opacity = "0.3";
+ m.style.position = "absolute";
+ m.style.left = canvasRect.offsetLeft;
+ var h = HorizontalMarker.HEIGHT;
+ m.style.top = (clientY - h/2).toFixed(0) + "px";
+ m.style.width = canvasRect.width + "px";
+ m.style.height = h + "px";
+ this.markerDiv_ = m;
+
+ this.value = yValue;
+}
+
+HorizontalMarker.HEIGHT = 5;
+HorizontalMarker.COLOR = "rgb(0,100,100)";
+
+// Remove the horizontal line from the graph.
+HorizontalMarker.prototype.remove_ = function() {
+ this.markerDiv_.parentNode.removeChild(this.markerDiv_);
+}
+
+/**
+ * Plotter class
+ * @constructor
+ *
+ * Draws a chart using CANVAS element. Takes array of lines to draw with
+ * deviations values for each data sample.
+ *
+ * @param {Array} clNumbers list of clNumbers for each data sample.
+ * @param {Array} plotData list of arrays that represent individual lines.
+ * The line itself is an Array of value and stdd.
+ * @param {Array} dataDescription list of data description for each line
+ * in plotData.
+ * @units {string} units name of measurement used to describe plotted data.
+ *
+ * Example of the plotData:
+ * [
+ * [line 1 data],
+ * [line 2 data]
+ * ].
+ * Line data looks like [[point one], [point two]].
+ * And individual points are [value, deviation value]
+ */
+function Plotter(clNumbers, plotData, dataDescription, units, resultNode) {
+ this.clNumbers_ = clNumbers;
+ this.plotData_ = plotData;
+ this.dataDescription_ = dataDescription;
+ this.resultNode_ = resultNode;
+ this.units_ = units;
+ this.coordinates = new Coordinates(plotData);
+
+ // A color palette that's unambigous for normal and color-deficient viewers.
+ // Values are (red, green, blue) on a scale of 255.
+ // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf
+ this.colors = [[0, 114, 178], // blue
+ [230, 159, 0], // orange
+ [0, 158, 115], // green
+ [204, 121, 167], // purplish pink
+ [86, 180, 233], // sky blue
+ [213, 94, 0], // dark orange
+ [0, 0, 0], // black
+ [240, 228, 66] // yellow
+ ];
+}
+
+/**
+ * Does the actual plotting.
+ */
+Plotter.prototype.plot = function() {
+ var canvas = this.canvas();
+ this.coordinates_div_ = this.coordinates_();
+ this.ruler_div_ = this.ruler();
+ // marker for the result-point that the mouse is currently over
+ this.cursor_div_ = new Marker("rgb(100,80,240)");
+ // marker for the result-point for which details are shown
+ this.marker_div_ = new Marker("rgb(100,100,100)");
+ var ctx = canvas.getContext("2d");
+ for (var i = 0; i < this.plotData_.length; i++)
+ this.plotLine_(ctx, this.nextColor(i), this.plotData_[i]);
+
+ this.resultNode_.appendChild(canvas);
+ this.resultNode_.appendChild(this.coordinates_div_);
+
+ this.resultNode_.appendChild(this.ruler_div_);
+ this.resultNode_.appendChild(this.cursor_div_);
+ this.resultNode_.appendChild(this.marker_div_);
+ this.attachEventListeners(canvas);
+ this.canvasRectangle = {
+ "offsetLeft": canvas.offsetLeft,
+ "offsetTop": canvas.offsetTop,
+ "width": canvas.offsetWidth,
+ "height": canvas.offsetHeight
+ };
+};
+
+Plotter.prototype.drawDeviationBar_ = function(context, strokeStyles, x, y,
+ deviationValue) {
+ context.strokeStyle = strokeStyles;
+ context.lineWidth = 1.0;
+ context.beginPath();
+ context.moveTo(x, (y + deviationValue));
+ context.lineTo(x, (y - deviationValue));
+ context.moveTo(x, (y - deviationValue));
+ context.closePath();
+ context.stroke();
+};
+
+Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data) {
+ ctx.strokeStyle = strokeStyles;
+ ctx.lineWidth = 2.0;
+ ctx.beginPath();
+ var initial = true;
+ var deviationData = [];
+ for (var i = 0; i < data.length; i++) {
+ var x = this.coordinates.xPoints(i);
+ var value = data[i][0];
+ var stdd = data[i][1];
+ var y = 0.0;
+ var err = 0.0;
+ if (isNaN(value)) {
+ // Re-set 'initial' if we're at a gap in the data.
+ initial = true;
+ } else {
+ y = this.coordinates.yPoints(value);
+ // We assume that the stdd will only be NaN (missing) when the value is.
+ if (parseFloat(value) != 0.0)
+ err = y * parseFloat(stdd) / parseFloat(value);
+ if (initial)
+ initial = false;
+ else
+ ctx.lineTo(x, y);
+ }
+
+ ctx.moveTo(x, y);
+ deviationData.push([x, y, err])
+ }
+ ctx.closePath();
+ ctx.stroke();
+
+ for (var i = 0; i < deviationData.length; i++) {
+ this.drawDeviationBar_(ctx, strokeStyles, deviationData[i][0],
+ deviationData[i][1], deviationData[i][2]);
+ }
+};
+
+Plotter.prototype.attachEventListeners = function(canvas) {
+ var self = this;
+ canvas.parentNode.addEventListener(
+ "mousemove", function(evt) { self.onMouseMove_(evt); }, false);
+ this.cursor_div_.addEventListener(
+ "click", function(evt) { self.onMouseClick_(evt); }, false);
+};
+
+Plotter.prototype.updateRuler_ = function(evt) {
+ var r = this.ruler_div_;
+ r.style.left = this.canvasRectangle.offsetLeft + "px";
+
+ r.style.top = this.canvasRectangle.offsetTop + "px";
+ r.style.width = this.canvasRectangle.width + "px";
+ var h = evt.clientY - this.canvasRectangle.offsetTop;
+ if (h > this.canvasRectangle.height)
+ h = this.canvasRectangle.height;
+ r.style.height = h + "px";
+};
+
+Plotter.prototype.updateCursor_ = function() {
+ var c = this.cursor_div_;
+ c.style.top = this.canvasRectangle.offsetTop + "px";
+ c.style.height = this.canvasRectangle.height + "px";
+ var w = this.canvasRectangle.width / this.clNumbers_.length;
+ var x = (this.canvasRectangle.offsetLeft +
+ w * this.current_index_).toFixed(0);
+ c.style.left = x + "px";
+ c.style.width = w + "px";
+};
+
+
+Plotter.prototype.onMouseMove_ = function(evt) {
+ var canvas = evt.currentTarget.firstChild;
+ var positionX = evt.clientX - this.canvasRectangle.offsetLeft;
+ var positionY = evt.clientY - this.canvasRectangle.offsetTop;
+
+ this.current_index_ = this.coordinates.dataSampleIndex(positionX);
+ var yValue = this.coordinates.yValue(positionY);
+
+ this.coordinates_td_.innerHTML =
+ "r" + this.clNumbers_[this.current_index_] + ": " +
+ this.plotData_[0][this.current_index_][0].toFixed(2) + " " +
+ this.units_ + " +/- " +
+ this.plotData_[0][this.current_index_][1].toFixed(2) + " " +
+ yValue.toFixed(2) + " " + this.units_;
+
+ // If there is a horizontal marker, also display deltas relative to it.
+ if (this.horizontal_marker_) {
+ var baseline = this.horizontal_marker_.value;
+ var delta = yValue - baseline
+ var fraction = delta / baseline; // allow division by 0
+
+ var deltaStr = (delta >= 0 ? "+" : "") + delta.toFixed(0) + " " +
+ this.units_;
+ var percentStr = (fraction >= 0 ? "+" : "") +
+ (fraction * 100).toFixed(3) + "%";
+
+ this.baseline_deltas_td_.innerHTML = deltaStr + ": " + percentStr;
+ }
+
+ this.updateRuler_(evt);
+ this.updateCursor_();
+};
+
+Plotter.prototype.onMouseClick_ = function(evt) {
+ // Shift-click controls the horizontal reference line.
+ if (evt.shiftKey) {
+ if (this.horizontal_marker_) {
+ this.horizontal_marker_.remove_();
+ }
+
+ var canvasY = evt.clientY - this.canvasRectangle.offsetTop;
+ this.horizontal_marker_ = new HorizontalMarker(this.canvasRectangle,
+ evt.clientY, this.coordinates.yValue(canvasY));
+
+ // Insert before cursor node, otherwise it catches clicks.
+ this.cursor_div_.parentNode.insertBefore(
+ this.horizontal_marker_.markerDiv_, this.cursor_div_);
+ } else {
+ var index = this.current_index_;
+ var m = this.marker_div_;
+ var c = this.cursor_div_;
+ m.style.top = c.style.top;
+ m.style.left = c.style.left;
+ m.style.width = c.style.width;
+ m.style.height = c.style.height;
+ if ("onclick" in this) {
+ var this_x = this.clNumbers_[index];
+ var prev_x = index > 0 ? (parseInt(this.clNumbers_[index-1]) + 1) :
+ this_x;
+ this.onclick(prev_x, this_x);
+ }
+ }
+};
+
+Plotter.prototype.canvas = function() {
+ var canvas = document.createElement("CANVAS");
+ canvas.setAttribute("id", "_canvas");
+ canvas.setAttribute("class", "plot");
+ canvas.setAttribute("width", this.coordinates.widthMax);
+ canvas.setAttribute("height", this.coordinates.heightMax);
+ return canvas;
+};
+
+Plotter.prototype.ruler = function() {
+ ruler = document.createElement("DIV");
+ ruler.setAttribute("class", "plot-ruler");
+ ruler.style.borderBottom = "1px dotted black";
+ ruler.style.position = "absolute";
+ ruler.style.left = "-2px";
+ ruler.style.top = "-2px";
+ ruler.style.width = "0px";
+ ruler.style.height = "0px";
+ return ruler;
+};
+
+Plotter.prototype.coordinates_ = function() {
+ var coordinatesDiv = document.createElement("DIV");
+ var table_html =
+ "<table border=0 width='100%'><tbody><tr>" +
+ "<td colspan=2 class='legend'>Legend: ";
+ for (var i = 0; i < this.dataDescription_.length; i++) {
+ if (i > 0)
+ table_html += ", ";
+ table_html += "<span class='legend_item' style='color:" +
+ this.nextColor(i) + "'>" + this.dataDescription_[i] + "</span>";
+ }
+ table_html += "</td></tr><tr>" +
+ "<td class='plot-coordinates'><i>move mouse over graph</i></td>" +
+ "<td align=right style='color: " + HorizontalMarker.COLOR +
+ "'><i>Shift-click to place baseline</i></td>" +
+ "</tr></tbody></table>";
+ coordinatesDiv.innerHTML = table_html;
+
+ var tr = coordinatesDiv.firstChild.firstChild.childNodes[1];
+ this.coordinates_td_ = tr.childNodes[0];
+ this.baseline_deltas_td_ = tr.childNodes[1];
+
+ return coordinatesDiv;
+};
+
+Plotter.prototype.nextColor = function(i) {
+ var index = i % this.colors.length;
+ return "rgb(" + this.colors[index][0] + "," +
+ this.colors[index][1] + "," +
+ this.colors[index][2] + ")";
+};
+
+Plotter.prototype.log = function(val) {
+ document.getElementById('log').appendChild(
+ document.createTextNode(val + '\n'));
+};