Merge pull request #148 from klausw-g/edge-points
authorDan Vanderkam <danvdk@gmail.com>
Fri, 11 May 2012 21:47:47 +0000 (14:47 -0700)
committerDan Vanderkam <danvdk@gmail.com>
Fri, 11 May 2012 21:47:47 +0000 (14:47 -0700)
Add new option 'drawGapPoints'

auto_tests/tests/callback.js
dygraph-canvas.js
dygraph-options-reference.js
tests/isolated-points.html

index 4d0b172..6d3df1d 100644 (file)
@@ -123,6 +123,48 @@ CallbackTestCase.prototype.testDrawPointCallback_pointSize = function() {
 };
 
 /**
+ * Test that drawPointCallback is called for isolated points when
+ * drawPoints is false, and also for gap points if that's enabled.
+ */
+CallbackTestCase.prototype.testDrawPointCallback_isolated = function() {
+  var xvalues = [];
+
+  var g;
+  var callback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam) {
+    var dx = g.toDataXCoord(cx);
+    xvalues.push(dx);
+    Dygraph.Circles.DEFAULT.apply(this, arguments);
+  };
+
+  var graph = document.getElementById("graph");
+  var testdata = [[10, 2], [11, 3], [12, NaN], [13, 2], [14, NaN], [15, 3]];
+  var graphOpts = {
+      labels: ['X', 'Y'],
+      valueRange: [0, 4],
+      drawPoints : false,
+      drawPointCallback : callback,
+      pointSize : 8
+  };
+
+  // Test that isolated points get drawn
+  g = new Dygraph(graph, testdata, graphOpts);
+  assertEquals(2, xvalues.length);
+  assertEquals(13, xvalues[0]);
+  assertEquals(15, xvalues[1]);
+
+  // Test that isolated points + gap points get drawn when
+  // drawGapEdgePoints is set.  This should add one point at the right
+  // edge of the segment at x=11, but not at the graph edge at x=10.
+  xvalues = []; // Reset for new test
+  graphOpts.drawGapEdgePoints = true;
+  g = new Dygraph(graph, testdata, graphOpts);
+  assertEquals(3, xvalues.length);
+  assertEquals(11, xvalues[0]);
+  assertEquals(13, xvalues[1]);
+  assertEquals(15, xvalues[2]);
+};
+
+/**
  * This tests that when the function idxToRow_ returns the proper row and the onHiglightCallback
  * is properly called when the first series is hidden (setVisibility = false)
  *
index ff291e5..0c44e84 100644 (file)
@@ -661,11 +661,12 @@ DygraphCanvasRenderer.prototype._renderAnnotations = function() {
   }
 };
 
-DygraphCanvasRenderer.makeNextPointStep_ = function(connect, points, end) {
+DygraphCanvasRenderer.makeNextPointStep_ = function(
+    connect, points, start, end) {
   if (connect) {
     return function(j) {
-      while (++j < end) {
-        if (!(points[j].yval === null)) break;
+      while (++j + start < end) {
+        if (!(points[start + j].yval === null)) break;
       }
       return j;
     }
@@ -688,17 +689,22 @@ DygraphCanvasRenderer.prototype._drawStyledLine = function(
   var points = this.layout.points;
   var prevX = null;
   var prevY = null;
+  var nextY = null;
   var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
   if (!Dygraph.isArrayLike(strokePattern)) {
     strokePattern = null;
   }
+  var drawGapPoints = this.dygraph_.attr_('drawGapEdgePoints', setName);
 
-  var point;
+  var point, nextPoint;
   var next = DygraphCanvasRenderer.makeNextPointStep_(
-      this.attr_('connectSeparatedPoints'), points, afterLastIndexInSet);
+      this.attr_('connectSeparatedPoints'), points, firstIndexInSet,
+      afterLastIndexInSet);
   ctx.save();
-  for (var j = firstIndexInSet; j < afterLastIndexInSet; j = next(j)) {
-    point = points[j];
+  for (var j = 0; j < setLength; j = next(j)) {
+    point = points[firstIndexInSet + j];
+    nextY = (next(j) < setLength) ?
+        points[firstIndexInSet + next(j)].canvasy : null;
     if (isNullOrNaN(point.canvasy)) {
       if (stepPlot && prevX !== null) {
         // Draw a horizontal line to the start of the missing data
@@ -713,8 +719,15 @@ DygraphCanvasRenderer.prototype._drawStyledLine = function(
     } else {
       // 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 ||
-                                   isNullOrNaN(points[j+1].canvasy)));
+      var isIsolated = (!prevX && isNullOrNaN(nextY));
+      if (drawGapPoints) {
+        // Also consider a point to be is "isolated" if it's adjacent to a
+        // null point, excluding the graph edges.
+        if ((j > 0 && !prevX) ||
+            (next(j) < setLength && isNullOrNaN(nextY))) {
+          isIsolated = true;
+        }
+      }
       if (prevX === null) {
         prevX = point.canvasx;
         prevY = point.canvasy;
index 6a72616..97b410f 100644 (file)
@@ -43,6 +43,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "type": "boolean",
     "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>."
   },
+  "drawGapEdgePoints": {
+    "default": "false",
+    "labels": ["Data Line display"],
+    "type": "boolean",
+    "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities."
+  },
   "drawPointCallback": {
     "default": "null",
     "labels": ["Data Line display"],
index c09048d..f507fda 100644 (file)
       ],
       {
         labels: ["X", "S1", "S2" ],
+        S1: {
+          drawGapEdgePoints: true
+        },
+        pointSize: 4,
         showRoller: true
       }
     );