a regression test plus general fallout from the toDomYCoord bug
authorDan Vanderkam <dan@dygraphs.com>
Fri, 13 May 2011 01:59:19 +0000 (21:59 -0400)
committerDan Vanderkam <dan@dygraphs.com>
Fri, 13 May 2011 01:59:19 +0000 (21:59 -0400)
auto_tests/misc/local.html
auto_tests/tests/CanvasAssertions.js
auto_tests/tests/simple_drawing.js
auto_tests/tests/to_dom_coords.js [new file with mode: 0644]
dygraph-canvas.js
dygraph.js

index 046a11e..6f0fd66 100644 (file)
@@ -24,6 +24,7 @@
   <script type="text/javascript" src="../tests/range_tests.js"></script>
   <script type="text/javascript" src="../tests/axis_labels.js"></script>
   <script type="text/javascript" src="../tests/multi_csv.js"></script>
+  <script type="text/javascript" src="../tests/to_dom_coords.js"></script>
 </head>
 <body>
   <div id='graph'></div>
index 1ef0573..8b52155 100644 (file)
@@ -85,6 +85,21 @@ CanvasAssertions.assertLineDrawn = function(proxy, p1, p2, attrs) {
       " and " + p2 + " with attributes " + toString(attrs));
 }
 
+/**
+ * Checks how many lines of the given color have been drawn.
+ * @return {Integer} The number of lines of the given color.
+ */
+CanvasAssertions.numLinesDrawn = function(proxy, color) {
+  var num_lines = 0;
+  for (var i = 0; i < proxy.calls__.length; i++) {
+    var call = proxy.calls__[i];
+    if (call.name == "lineTo" && call.properties.strokeStyle == color) {
+      num_lines++;
+    }
+  }
+  return num_lines;
+}
+
 CanvasAssertions.matchPixels = function(expected, actual) {
   // Expect array of two integers. Assuming the values are within one
   // integer unit of each other. This should be tightened down by someone
index 71f2fc1..7e9e6ef 100644 (file)
@@ -30,9 +30,9 @@ var SimpleDrawingTestCase = TestCase("simple-drawing");
 var _origFunc = Dygraph.getContext;
 SimpleDrawingTestCase.prototype.setUp = function() {
   document.body.innerHTML = "<div id='graph'></div>";
-  // Dygraph.getContext = function(canvas) {
-  //   return new Proxy(_origFunc(canvas));
-  // }
+  Dygraph.getContext = function(canvas) {
+    return new Proxy(_origFunc(canvas));
+  }
 };
 
 SimpleDrawingTestCase.prototype.tearDown = function() {
@@ -49,10 +49,10 @@ SimpleDrawingTestCase.prototype.testDrawSimpleRangePlusOne = function() {
 
   var graph = document.getElementById("graph");
   var g = new Dygraph(graph, ZERO_TO_FIFTY, opts);
-  // var htx = g.hidden_ctx_;
+  var htx = g.hidden_ctx_;
 
-  // CanvasAssertions.assertLineDrawn(htx, [56,300], [475,5.8], {
-  //   strokeStyle: "#008080",
-  //   lineWidth: 1
-  // });
+  CanvasAssertions.assertLineDrawn(htx, [56,300], [475,5.8], {
+    strokeStyle: "#008080",
+    lineWidth: 1
+  });
 }
diff --git a/auto_tests/tests/to_dom_coords.js b/auto_tests/tests/to_dom_coords.js
new file mode 100644 (file)
index 0000000..c70b704
--- /dev/null
@@ -0,0 +1,122 @@
+/** 
+ * @fileoverview Test cases for toDomCoords/toDataCoords
+ *
+ * @author danvk@google.com (Dan Vanderkam)
+ */
+
+var ToDomCoordsTestCase = TestCase("to-dom-coords");
+
+var _origFunc = Dygraph.getContext;
+ToDomCoordsTestCase.prototype.setUp = function() {
+  document.body.innerHTML = "<div id='graph'></div>";
+  Dygraph.getContext = function(canvas) {
+    return new Proxy(_origFunc(canvas));
+  }
+};
+
+ToDomCoordsTestCase.prototype.tearDown = function() {
+  Dygraph.getContext = _origFunc;
+};
+
+// Checks that toDomCoords and toDataCoords are inverses of one another.
+ToDomCoordsTestCase.prototype.checkForInverses = function(g) {
+  var x_range = g.xAxisRange();
+  var y_range = g.yAxisRange();
+  for (var i = 0; i <= 10; i++) {
+    var x = x_range[0] + i / 10.0 * (x_range[1] - x_range[0]);
+    for (var j = 0; j <= 10; j++) {
+      var y = y_range[0] + j / 10.0 * (y_range[1] - y_range[0]);
+      assertEquals(x, g.toDataXCoord(g.toDomXCoord(x)));
+      assertEquals(y, g.toDataYCoord(g.toDomYCoord(y)));
+    }
+  }
+}
+
+ToDomCoordsTestCase.prototype.testPlainChart = function() {
+  var opts = {
+    drawXAxis: false,
+    drawYAxis: false,
+    drawXGrid: false,
+    drawYGrid: false,
+    rightGap: 0,
+    valueRange: [0, 100],
+    dateWindow: [0, 100],
+    width: 400,
+    height: 400,
+    colors: ['#ff0000']
+  }
+
+  var graph = document.getElementById("graph");
+  g = new Dygraph(graph, [ [0,0], [100,100] ], opts);
+
+  assertEquals([0, 100], g.toDataCoords(0, 0));
+  assertEquals([0, 0], g.toDataCoords(0, 400));
+  assertEquals([100, 100], g.toDataCoords(400, 0));
+  assertEquals([100, 0], g.toDataCoords(400, 400));
+
+  this.checkForInverses(g);
+
+  var htx = g.hidden_ctx_;
+  assertEquals(1, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+}
+
+ToDomCoordsTestCase.prototype.testChartWithAxes = function() {
+  var opts = {
+    drawXAxis: true,
+    xAxisHeight: 50,
+    drawYAxis: true,
+    yAxisLabelWidth: 100,
+    axisTickSize: 0,
+    drawXGrid: false,
+    drawYGrid: false,
+    rightGap: 0,
+    valueRange: [0, 100],
+    dateWindow: [0, 100],
+    width: 500,
+    height: 450,
+    colors: ['#ff0000']
+  }
+
+  var graph = document.getElementById("graph");
+  g = new Dygraph(graph, [ [0,0], [100,100] ], opts);
+
+  assertEquals([0, 100], g.toDataCoords(100, 0));
+  assertEquals([0, 0], g.toDataCoords(100, 400));
+  assertEquals([100, 100], g.toDataCoords(500, 0));
+  assertEquals([100, 0], g.toDataCoords(500, 400));
+
+  this.checkForInverses(g);
+}
+
+ToDomCoordsTestCase.prototype.testChartWithAxesAndLabels = function() {
+  var opts = {
+    drawXAxis: true,
+    xAxisHeight: 50,
+    drawYAxis: true,
+    yAxisLabelWidth: 100,
+    axisTickSize: 0,
+    drawXGrid: false,
+    drawYGrid: false,
+    rightGap: 0,
+    valueRange: [0, 100],
+    dateWindow: [0, 100],
+    width: 500,
+    height: 500,
+    colors: ['#ff0000'],
+    ylabel: 'This is the y-axis',
+    xlabel: 'This is the x-axis',
+    xLabelHeight: 25,
+    title: 'This is the title of the chart',
+    titleHeight: 25
+  }
+
+  var graph = document.getElementById("graph");
+  g = new Dygraph(graph, [ [0,0], [100,100] ], opts);
+
+  assertEquals([0, 100], g.toDataCoords(100, 25));
+  assertEquals([0, 0], g.toDataCoords(100, 425));
+  assertEquals([100, 100], g.toDataCoords(500, 25));
+  assertEquals([100, 0], g.toDataCoords(500, 425));
+
+  this.checkForInverses(g);
+}
index 764bac1..4ea5ef9 100644 (file)
@@ -344,7 +344,11 @@ DygraphCanvasRenderer.prototype.computeArea_ = function() {
   area.w = this.width - area.x - this.attr_('rightGap');
   area.h = this.height;
   if (this.attr_('drawXAxis')) {
-    area.h -= this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize');
+    if (this.attr_('xAxisHeight')) {
+      area.h -= this.attr_('xAxisHeight');
+    } else {
+      area.h -= this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize');
+    }
   }
 
   // Shrink the drawing area to accomodate additional y-axes.
@@ -1006,6 +1010,10 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() {
     }
   }
 
+  var isNullOrNaN = function(x) {
+    return (x === null || isNaN(x));
+  };
+
   for (var i = 0; i < setCount; i++) {
     var setName = setNames[i];
     var color = this.colors[setName];
@@ -1021,7 +1029,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() {
     for (var j = 0; j < points.length; j++) {
       var point = points[j];
       if (point.name == setName) {
-        if (!Dygraph.isOK(point.canvasy)) {
+        if (isNullOrNaN(point.canvasy)) {
           if (stepPlot && prevX != null) {
             // Draw a horizontal line to the start of the missing data
             ctx.beginPath();
@@ -1037,7 +1045,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() {
           // A point is "isolated" if it is non-null but both the previous
           // and next points are null.
           var isIsolated = (!prevX && (j == points.length - 1 ||
-                                       !Dygraph.isOK(points[j+1].canvasy)));
+                                       isNullOrNaN(points[j+1].canvasy)));
 
           if (prevX === null) {
             prevX = point.canvasx;
index a976436..b5afc7c 100644 (file)
@@ -4199,6 +4199,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "type": "integer",
     "description": "Width, in pixels, of the x-axis labels."
   },
+  "xAxisHeight": {
+    "default": "(null)",
+    "labels": ["Axis display"],
+    "type": "integer",
+    "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize."
+  },
   "showLabelsOnHighlight": {
     "default": "true",
     "labels": ["Interactive Elements", "Legend"],
@@ -4416,7 +4422,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
   },
   "panEdgeFraction": {
     "default": "null",
-    "labels": ["Axis Display", "Interactive Elements"],
+    "labels": ["Axis display", "Interactive Elements"],
     "type": "float",
     "default": "null",
     "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% pased the edges of the displayed values. null means no bounds."
@@ -4507,7 +4513,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
   },
   "fillAlpha": {
     "default": "0.15",
-    "labels": ["Error bars", "Data Series Colors"],
+    "labels": ["Error Bars", "Data Series Colors"],
     "type": "float (0.0 - 1.0)",
     "description" : "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point."
   },
@@ -4592,6 +4598,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     var labels = op['labels'];
     if (typeof(labels) !== 'object') {
       warn('Option "' + k + '" is missing a "labels": [...] option');
+    } else {
       for (var i = 0; i < labels.length; i++) {
         if (!cats.hasOwnProperty(labels[i])) {
           warn('Option "' + k + '" has label "' + labels[i] +