diff --git a/API.txt b/API.txt index b2ace0a..7714723 100644 --- a/API.txt +++ b/API.txt @@ -524,6 +524,7 @@ Customizing the data series points: { radius: number + symbol: "circle" or function } bars: { @@ -590,6 +591,26 @@ connected with a straight (possibly diagonal) line or with first a horizontal and then a vertical line. Note that this transforms the data by adding extra points. +For points, you can specify the radius and the symbol. The only +built-in symbol type is circles, for other types you can use a plugin +or define them yourself by specifying a callback: + + function cross(ctx, x, y, radius, shadow) { + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + } + +The parameters are the drawing context, x and y coordinates of the +center of the point, a radius which corresponds to what the circle +would have used and whether the call is to draw a shadow (due to +limited canvas support, shadows are currently faked through extra +draws). It's good practice to ensure that the area covered by the +symbol is the same as for the circle with the given radius, this +ensures that all symbols have approximately the same visual weight. + "shadowSize" is the default size of shadows in pixels. Set it to 0 to remove shadows. diff --git a/NEWS.txt b/NEWS.txt index f21e95a..17428b7 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -64,6 +64,9 @@ Changes: ensure that they appear next to each other rather than in between, at the expense of possibly awkward tick steps (sponsored by Flight Data Services, www.flightdataservices.com). +- Support for customizing the point type through a callback when + plotting points and new symbol plugin with some predefined point + types (sponsored by Utility Data Corporation). - New hooks: drawSeries diff --git a/examples/index.html b/examples/index.html index 2406e3e..75b2fc4 100644 --- a/examples/index.html +++ b/examples/index.html @@ -32,6 +32,7 @@

Various features:

diff --git a/examples/symbols.html b/examples/symbols.html new file mode 100644 index 0000000..6b02417 --- /dev/null +++ b/examples/symbols.html @@ -0,0 +1,49 @@ + + + + + Flot Examples + + + + + + + +

Flot Examples

+ +
+ +

Various point types. Circles are built-in. For other + point types, you can define a little callback function to draw the + symbol; some common ones are available in the symbol plugin.

+ + + + + diff --git a/jquery.flot.js b/jquery.flot.js index 3c4d8f0..86e5c89 100644 --- a/jquery.flot.js +++ b/jquery.flot.js @@ -87,7 +87,8 @@ radius: 3, lineWidth: 2, // in pixels fill: true, - fillColor: "#ffffff" + fillColor: "#ffffff", + symbol: "circle" // or callback }, lines: { // we don't put in show: false so we can see @@ -1899,16 +1900,23 @@ } function drawSeriesPoints(series) { - function plotPoints(datapoints, radius, fillStyle, offset, circumference, axisx, axisy) { + function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { var points = datapoints.points, ps = datapoints.pointsize; - + for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) continue; ctx.beginPath(); - ctx.arc(axisx.p2c(x), axisy.p2c(y) + offset, radius, 0, circumference, false); + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + if (symbol == "circle") + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + else + symbol(ctx, x, y, radius, shadow); + ctx.closePath(); + if (fillStyle) { ctx.fillStyle = fillStyle; ctx.fill(); @@ -1922,25 +1930,26 @@ var lw = series.points.lineWidth, sw = series.shadowSize, - radius = series.points.radius; + radius = series.points.radius, + symbol = series.points.symbol; if (lw > 0 && sw > 0) { // draw shadow in two steps var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; - plotPoints(series.datapoints, radius, null, w + w/2, Math.PI, - series.xaxis, series.yaxis); + plotPoints(series.datapoints, radius, null, w + w/2, true, + series.xaxis, series.yaxis, symbol); ctx.strokeStyle = "rgba(0,0,0,0.2)"; - plotPoints(series.datapoints, radius, null, w/2, Math.PI, - series.xaxis, series.yaxis); + plotPoints(series.datapoints, radius, null, w/2, true, + series.xaxis, series.yaxis, symbol); } ctx.lineWidth = lw; ctx.strokeStyle = series.color; plotPoints(series.datapoints, radius, - getFillStyle(series.points, series.color), 0, 2 * Math.PI, - series.xaxis, series.yaxis); + getFillStyle(series.points, series.color), 0, false, + series.xaxis, series.yaxis, symbol); ctx.restore(); } @@ -2382,9 +2391,16 @@ var pointRadius = series.points.radius + series.points.lineWidth / 2; octx.lineWidth = pointRadius; octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString(); - var radius = 1.5 * pointRadius; + var radius = 1.5 * pointRadius, + x = axisx.p2c(x), + y = axisy.p2c(y); + octx.beginPath(); - octx.arc(axisx.p2c(x), axisy.p2c(y), radius, 0, 2 * Math.PI, false); + if (series.points.symbol == "circle") + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + else + series.points.symbol(octx, x, y, radius, false); + octx.closePath(); octx.stroke(); } diff --git a/jquery.flot.symbol.js b/jquery.flot.symbol.js new file mode 100644 index 0000000..a32fe31 --- /dev/null +++ b/jquery.flot.symbol.js @@ -0,0 +1,70 @@ +/* +Flot plugin that adds some extra symbols for plotting points. + +The symbols are accessed as strings through the standard symbol +choice: + + series: { + points: { + symbol: "square" // or "diamond", "triangle", "cross" + } + } + +*/ + +(function ($) { + function processRawData(plot, series, datapoints) { + // we normalize the area of each symbol so it is approximately the + // same as a circle of the given radius + + var handlers = { + square: function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + diamond: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y - size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x - size, y); + }, + triangle: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) + var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); + var height = size * Math.sin(Math.PI / 3); + ctx.moveTo(x - size/2, y + height/2); + ctx.lineTo(x + size/2, y + height/2); + if (!shadow) { + ctx.lineTo(x, y - height/2); + ctx.lineTo(x - size/2, y + height/2); + } + }, + cross: function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + } + } + + var s = series.points.symbol; + if (handlers[s]) + series.points.symbol = handlers[s]; + } + + function init(plot) { + plot.hooks.processDatapoints.push(processRawData); + } + + $.plot.plugins.push({ + init: init, + name: 'symbols', + version: '1.0' + }); +})(jQuery);