From 78e58af463c6b5a09d85d6014bbdba0b3d8a605a Mon Sep 17 00:00:00 2001 From: Robert Konigsberg Date: Wed, 22 Feb 2012 09:05:49 -0500 Subject: [PATCH] custom circles, first part. --- dygraph-canvas.js | 20 +++++++++---- dygraph-options-reference.js | 14 ++++++++- dygraph-utils.js | 69 ++++++++++++++++++++++++++++++++++++++++++++ dygraph.js | 10 ++++--- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 5e6766d..caa4a37 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -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; } diff --git a/dygraph-options-reference.js b/dygraph-options-reference.js index 92846ee..94461a5 100644 --- a/dygraph-options-reference.js +++ b/dygraph-options-reference.js @@ -41,7 +41,13 @@ Dygraph.OPTIONS_REFERENCE = // "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 = // "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: [ {name: 'series', yval: y-value}, … ]" }, + "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"], diff --git a/dygraph-utils.js b/dygraph-utils.js index 8b9dfd9..0ae3f08 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -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. +}; diff --git a/dygraph.js b/dygraph.js index 66432f7..f03defc 100644 --- a/dygraph.js +++ b/dygraph.js @@ -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(); -- 2.7.4