custom circles, first part.
authorRobert Konigsberg <konigsberg@google.com>
Wed, 22 Feb 2012 14:05:49 +0000 (09:05 -0500)
committerRobert Konigsberg <konigsberg@google.com>
Wed, 22 Feb 2012 14:05:49 +0000 (09:05 -0500)
dygraph-canvas.js
dygraph-options-reference.js
dygraph-utils.js
dygraph.js

index 5e6766d..caa4a37 100644 (file)
@@ -833,11 +833,17 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() {
     var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
 
     // setup graphics context
+    // TODO(konigsberg): This function has ctx and context. Clarify the difference.
     context.save();
     var pointSize = this.dygraph_.attr_("pointSize", setName);
     prevX = null;
     prevY = null;
     var drawPoints = this.dygraph_.attr_("drawPoints", setName);
+    var drawPointCallback = this.dygraph_.attr_("drawPointCallback", setName);
+    if (!drawPointCallback) {
+      drawPointCallback = Dygraph.Circles.DEFAULT;
+    }
+    var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
     for (j = firstIndexInSet; j < afterLastIndexInSet; j++) {
       point = points[j];
       if (isNullOrNaN(point.canvasy)) {
@@ -885,14 +891,18 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() {
         }
 
         if (drawPoints || isIsolated) {
-          ctx.beginPath();
-          ctx.fillStyle = color;
-          ctx.arc(point.canvasx, point.canvasy, pointSize,
-                  0, 2 * Math.PI, false);
-          ctx.fill();
+          pointsOnLine.push([point.canvasx, point.canvasy]);
         }
       }
     }
+    for (var idx = 0; idx < pointsOnLine.length; idx++) {
+      var cb = pointsOnLine[idx];
+      ctx.save();
+      drawPointCallback(
+          this.dygraph_, setName, ctx, cb[0], cb[1], color, pointSize);
+      ctx.restore();
+
+    }
     firstIndexInSet = afterLastIndexInSet;
   }
 
index 92846ee..94461a5 100644 (file)
@@ -41,7 +41,13 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "default": "false",
     "labels": ["Data Line display"],
     "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."
+    "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 drawPointCallback."
+  },
+  "drawPointCallback": {
+    "default": "null",
+    "labels": ["Data Line display"],
+    "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)",
+    "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color."
   },
   "height": {
     "default": "320",
@@ -81,6 +87,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "type": "function(event, x, points,row)",
     "description": "When set, this callback gets called every time a new point is highlighted. The parameters are the JavaScript mousemove event, the x-coordinate of the highlighted points and an array of highlighted points: <code>[ {name: 'series', yval: y-value}, &hellip; ]</code>"
   },
+  "drawHighlightCallback": {
+    "default": "null",
+    "labels": ["Data Line display"],
+    "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)",
+    "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color."
+  },
   "includeZero": {
     "default": "false",
     "labels": ["Axis display"],
index 8b9dfd9..0ae3f08 100644 (file)
@@ -693,7 +693,9 @@ Dygraph.isPixelChangingOptionList = function(labels, attrs) {
     'clickCallback': true,
     'digitsAfterDecimal': true,
     'drawCallback': true,
+    'drawHighlightCallback': true,
     'drawPoints': true,
+    'drawPointCallback': true,
     'drawXGrid': true,
     'drawYGrid': true,
     'fillAlpha': true,
@@ -775,3 +777,70 @@ Dygraph.isPixelChangingOptionList = function(labels, attrs) {
 
   return requiresNewPoints;
 };
+
+
+Dygraph.RegularConvex = function(sides, rotation) {
+  this.sides = sides;
+  this.rotation = rotation ? rotation : 0;
+  this.delta = Math.PI * 2 / sides;
+}
+
+Dygraph.RegularConvex.prototype.draw = function(ctx, cx, cy, radius, angleAdjustment) {
+ ctx.beginPath();
+ var first = true;
+ if (!angleAdjustment) angleAdjustment = 0;
+ var angle = this.rotation + angleAdjustment;
+
+ var x = cx + (Math.sin(angle) * radius);
+ var y = cy + (-Math.cos(angle) * radius);
+ ctx.moveTo(x, y);
+
+ for (var idx = 0; idx < this.sides; idx++) {
+   angle = (idx == this.sides - 1) ? this.rotation : (angle + this.delta);
+   var x = cx + (Math.sin(angle) * radius);
+   var y = cy + (-Math.cos(angle) * radius);
+   ctx.lineTo(x, y);
+ }
+ ctx.stroke();
+ ctx.closePath();
+}
+
+Dygraph.DrawPolygon_ = function(sides, rotation, ctx, cx, cy, color, radius, angleAdjustment) {
+  ctx.lineWidth = 1;
+  ctx.strokeStyle = color;
+  new Dygraph.RegularConvex(sides, rotation).draw(ctx, cx, cy, radius, angleAdjustment);
+}
+
+Dygraph.Circles = {
+  DEFAULT : function(g, name, ctx, canvasx, canvasy, color, radius) {
+    ctx.beginPath();
+    ctx.fillStyle = color;
+    ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
+    ctx.fill();
+  },
+  TRIANGLE : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(3, Math.PI / 3, ctx, cx, cy, color, radius);
+  },
+  SQUARE : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(4, Math.PI / 4, ctx, cx, cy, color, radius);
+  },
+  DIAMOND : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(4, Math.PI / 4, ctx, cx, cy, color, radius, Math.PI / 8);
+  },
+  PENTAGON : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(5, Math.PI / 5, ctx, cx, cy, color, radius);
+  },
+  HEXAGON : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(6, Math.PI / 6, ctx, cx, cy, color, radius);
+  },
+  CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
+    ctx.beginPath();
+    ctx.lineStyle = color;
+    ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
+    ctx.fill();
+  },
+  STAR : function(g, name, ctx, cx, cy, color, radius) {
+    Dygraph.DrawPolygon_(5, 2 * Math.PI / 5, ctx, cx, cy, color, radius);
+  }
+  // TODO: plus, x.
+};
index 66432f7..f03defc 100644 (file)
@@ -1655,10 +1655,12 @@ Dygraph.prototype.updateSelection_ = function() {
       if (!Dygraph.isOK(pt.canvasy)) continue;
 
       var circleSize = this.attr_('highlightCircleSize', pt.name);
-      ctx.beginPath();
-      ctx.fillStyle = this.plotter_.colors[pt.name];
-      ctx.arc(canvasx, pt.canvasy, circleSize, 0, 2 * Math.PI, false);
-      ctx.fill();
+      var callback = this.attr_("drawHighlightCallback", pt.name);
+      if (!callback) {
+        callback = Dygraph.Circles.DEFAULT;
+      }
+      callback(this.g, pt.name, ctx, canvasx, pt.canvasy,
+          this.plotter_.colors[pt.name], circleSize);
     }
     ctx.restore();