Commit bad53abc authored by olau@iola.dk's avatar olau@iola.dk

Revamped internals to add support for extra axes, not just dual (sponsored by Flight Data Services)


git-svn-id: https://flot.googlecode.com/svn/trunk@251 1e0a6537-2640-0410-bfb7-f154510ff394
parent 6f969865
This diff is collapsed.
Flot x.x Flot x.x
-------- --------
API changes:
Multiple axes support. Code using dual axes should be changed from
using x2axis/y2axis in the options to using an array. For instance,
{
xaxis: { ... }, x2axis: { ... },
yaxis: { ... }, y2axis: { ... }
}
becomes
{
xaxes: [ { ... }, { ... } ],
yaxes: [ { ... }, { ... } ]
}
Note that if you're just using one axis, continue to use the
xaxis/yaxis directly (it now sets the default settings for the
arrays). Plugins touching the axes must be ported to take the extra
axes into account, a couple of helper functions have been added for
that purpose, check the source.
Changes: Changes:
- Support for specifying a bottom for each point for line charts when - Support for specifying a bottom for each point for line charts when
...@@ -15,9 +39,11 @@ Changes: ...@@ -15,9 +39,11 @@ Changes:
- Stacking plugin can stack horizontal bar charts. - Stacking plugin can stack horizontal bar charts.
- Navigate plugin now redraws the plot while panning instead of only - Navigate plugin now redraws the plot while panning instead of only
after the fact (can be disabled by setting the pan.frameRate option after the fact (can be disabled by setting the pan.frameRate option
to null). Issue 235. to null), raised by lastthemy (issue 235).
- Date formatter now accepts %0m and %0d to get a zero-padded month or - Date formatter now accepts %0m and %0d to get a zero-padded month or
day (issue raised by Maximillian Dornseif). day (issue raised by Maximillian Dornseif).
- Revamped internals to support an unlimited number of axes, not just
dual (sponsored by Flight Data Services, www.flightdataservices.com).
- New hooks: drawSeries - New hooks: drawSeries
Bug fixes: Bug fixes:
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<ul> <ul>
<li><a href="time.html">Plotting time series</a> and <a href="visitors.html">visitors per day with zooming and weekends</a> (with selection plugin)</li> <li><a href="time.html">Plotting time series</a> and <a href="visitors.html">visitors per day with zooming and weekends</a> (with selection plugin)</li>
<li><a href="dual-axis.html">Dual axis support</a></li> <li><a href="multiple-axes.html">Multiple axes</a></li>
<li><a href="thresholding.html">Thresholding the data</a> (with threshold plugin)</li> <li><a href="thresholding.html">Thresholding the data</a> (with threshold plugin)</li>
<li><a href="stacking.html">Stacked charts</a> (with stacking plugin)</li> <li><a href="stacking.html">Stacked charts</a> (with stacking plugin)</li>
<li><a href="percentiles.html">Using filled areas to plot percentiles</a> (with fillbetween plugin)</li> <li><a href="percentiles.html">Using filled areas to plot percentiles</a> (with fillbetween plugin)</li>
......
/* /*
Flot plugin for showing a crosshair, thin lines, when the mouse hovers Flot plugin for showing crosshairs, thin lines, when the mouse hovers
over the plot. over the plot.
crosshair: { crosshair: {
...@@ -19,10 +19,11 @@ The plugin also adds four public methods: ...@@ -19,10 +19,11 @@ The plugin also adds four public methods:
- setCrosshair(pos) - setCrosshair(pos)
Set the position of the crosshair. Note that this is cleared if Set the position of the crosshair. Note that this is cleared if
the user moves the mouse. "pos" should be on the form { x: xpos, the user moves the mouse. "pos" is in coordinates of the plot and
y: ypos } (or x2 and y2 if you're using the secondary axes), which should be on the form { x: xpos, y: ypos } (you can use x2/x3/...
is coincidentally the same format as what you get from a "plothover" if you're using multiple axes), which is coincidentally the same
event. If "pos" is null, the crosshair is cleared. format as what you get from a "plothover" event. If "pos" is null,
the crosshair is cleared.
- clearCrosshair() - clearCrosshair()
...@@ -69,10 +70,9 @@ The plugin also adds four public methods: ...@@ -69,10 +70,9 @@ The plugin also adds four public methods:
if (!pos) if (!pos)
crosshair.x = -1; crosshair.x = -1;
else { else {
var axes = plot.getAxes(); var o = plot.p2c(pos);
crosshair.x = Math.max(0, Math.min(o.left, plot.width()));
crosshair.x = Math.max(0, Math.min(pos.x != null ? axes.xaxis.p2c(pos.x) : axes.x2axis.p2c(pos.x2), plot.width())); crosshair.y = Math.max(0, Math.min(o.top, plot.height()));
crosshair.y = Math.max(0, Math.min(pos.y != null ? axes.yaxis.p2c(pos.y) : axes.y2axis.p2c(pos.y2), plot.height()));
} }
plot.triggerRedrawOverlay(); plot.triggerRedrawOverlay();
......
This diff is collapsed.
...@@ -64,7 +64,7 @@ Example API usage: ...@@ -64,7 +64,7 @@ Example API usage:
Here, "center" specifies where the center of the zooming should Here, "center" specifies where the center of the zooming should
happen. Note that this is defined in pixel space, not the space of the happen. Note that this is defined in pixel space, not the space of the
data points (you can use the c2p helpers on the axes in Flot to help data points (you can use the p2c helpers on the axes in Flot to help
you convert between these). you convert between these).
"amount" is the amount to zoom the viewport relative to the current "amount" is the amount to zoom the viewport relative to the current
...@@ -192,51 +192,49 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L ...@@ -192,51 +192,49 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
if (!args) if (!args)
args = {}; args = {};
var axes = plot.getAxes(), var c = args.center,
options = plot.getOptions(), amount = args.amount || plot.getOptions().zoom.amount,
c = args.center,
amount = args.amount ? args.amount : options.zoom.amount,
w = plot.width(), h = plot.height(); w = plot.width(), h = plot.height();
if (!c) if (!c)
c = { left: w / 2, top: h / 2 }; c = { left: w / 2, top: h / 2 };
var xf = c.left / w, var xf = c.left / w,
x1 = c.left - xf * w / amount,
x2 = c.left + (1 - xf) * w / amount,
yf = c.top / h, yf = c.top / h,
y1 = c.top - yf * h / amount, minmax = {
y2 = c.top + (1 - yf) * h / amount; x: {
min: c.left - xf * w / amount,
max: c.left + (1 - xf) * w / amount
},
y: {
min: c.top - yf * h / amount,
max: c.top + (1 - yf) * h / amount
}
};
function scaleAxis(min, max, name) { $.each(plot.getUsedAxes(), function(i, axis) {
var axis = axes[name], var opts = axis.options,
axisOptions = options[name]; min = minmax[axis.direction].min,
max = minmax[axis.direction].max
if (!axis.used)
return;
min = axis.c2p(min); min = axis.c2p(min);
max = axis.c2p(max); max = axis.c2p(max);
if (max < min) { // make sure min < max if (min > max) {
var tmp = min // make sure min < max
var tmp = min;
min = max; min = max;
max = tmp; max = tmp;
} }
var range = max - min, zr = axisOptions.zoomRange; var range = max - min, zr = opts.zoomRange;
if (zr && if (zr &&
((zr[0] != null && range < zr[0]) || ((zr[0] != null && range < zr[0]) ||
(zr[1] != null && range > zr[1]))) (zr[1] != null && range > zr[1])))
return; return;
axisOptions.min = min; opts.min = min;
axisOptions.max = max; opts.max = max;
} });
scaleAxis(x1, x2, 'xaxis');
scaleAxis(x1, x2, 'x2axis');
scaleAxis(y1, y2, 'yaxis');
scaleAxis(y1, y2, 'y2axis');
plot.setupGrid(); plot.setupGrid();
plot.draw(); plot.draw();
...@@ -246,49 +244,42 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L ...@@ -246,49 +244,42 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
} }
plot.pan = function (args) { plot.pan = function (args) {
var l = +args.left, t = +args.top, var delta = {
axes = plot.getAxes(), options = plot.getOptions(); x: +args.left,
y: +args.top
if (isNaN(l)) };
l = 0;
if (isNaN(t))
t = 0;
function panAxis(delta, name) { if (isNaN(delta.x))
var axis = axes[name], delta.x = 0;
axisOptions = options[name], if (isNaN(delta.y))
min, max; delta.y = 0;
if (!axis.used) $.each(plot.getUsedAxes(), function (i, axis) {
return; var opts = axis.options,
min, max, d = delta[axis.direction];
min = axis.c2p(axis.p2c(axis.min) + delta), min = axis.c2p(axis.p2c(axis.min) + d),
max = axis.c2p(axis.p2c(axis.max) + delta); max = axis.c2p(axis.p2c(axis.max) + d);
var pr = axisOptions.panRange; var pr = opts.panRange;
if (pr) { if (pr) {
// check whether we hit the wall // check whether we hit the wall
if (pr[0] != null && pr[0] > min) { if (pr[0] != null && pr[0] > min) {
delta = pr[0] - min; d = pr[0] - min;
min += delta; min += d;
max += delta; max += d;
} }
if (pr[1] != null && pr[1] < max) { if (pr[1] != null && pr[1] < max) {
delta = pr[1] - max; d = pr[1] - max;
min += delta; min += d;
max += delta; max += d;
} }
} }
axisOptions.min = min; opts.min = min;
axisOptions.max = max; opts.max = max;
} });
panAxis(l, 'xaxis');
panAxis(l, 'x2axis');
panAxis(t, 'yaxis');
panAxis(t, 'y2axis');
plot.setupGrid(); plot.setupGrid();
plot.draw(); plot.draw();
......
...@@ -8,20 +8,20 @@ The plugin defines the following options: ...@@ -8,20 +8,20 @@ The plugin defines the following options:
color: color color: color
} }
You enable selection support by setting the mode to one of "x", "y" or Selection support is enabled by setting the mode to one of "x", "y" or
"xy". In "x" mode, the user will only be able to specify the x range, "xy". In "x" mode, the user will only be able to specify the x range,
similarly for "y" mode. For "xy", the selection becomes a rectangle similarly for "y" mode. For "xy", the selection becomes a rectangle
where both ranges can be specified. "color" is color of the selection. where both ranges can be specified. "color" is color of the selection.
When selection support is enabled, a "plotselected" event will be emitted When selection support is enabled, a "plotselected" event will be
on the DOM element you passed into the plot function. The event emitted on the DOM element you passed into the plot function. The
handler gets one extra parameter with the ranges selected on the axes, event handler gets a parameter with the ranges selected on the axes,
like this: like this:
placeholder.bind("plotselected", function(event, ranges) { placeholder.bind("plotselected", function(event, ranges) {
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
// similar for yaxis, secondary axes are in x2axis // similar for yaxis - with multiple axes, the extra ones are in
// and y2axis if present // x2axis, x3axis, ...
}); });
The "plotselected" event is only fired when the user has finished The "plotselected" event is only fired when the user has finished
...@@ -37,17 +37,19 @@ The plugin allso adds the following methods to the plot object: ...@@ -37,17 +37,19 @@ The plugin allso adds the following methods to the plot object:
- setSelection(ranges, preventEvent) - setSelection(ranges, preventEvent)
Set the selection rectangle. The passed in ranges is on the same Set the selection rectangle. The passed in ranges is on the same
form as returned in the "plotselected" event. If the selection form as returned in the "plotselected" event. If the selection mode
mode is "x", you should put in either an xaxis (or x2axis) object, is "x", you should put in either an xaxis range, if the mode is "y"
if the mode is "y" you need to put in an yaxis (or y2axis) object you need to put in an yaxis range and both xaxis and yaxis if the
and both xaxis/x2axis and yaxis/y2axis if the selection mode is selection mode is "xy", like this:
"xy", like this:
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
setSelection will trigger the "plotselected" event when called. If setSelection will trigger the "plotselected" event when called. If
you don't want that to happen, e.g. if you're inside a you don't want that to happen, e.g. if you're inside a
"plotselected" handler, pass true as the second parameter. "plotselected" handler, pass true as the second parameter. If you
are using multiple axes, you can specify the ranges on any of those,
e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the
first one it sees.
- clearSelection(preventEvent) - clearSelection(preventEvent)
...@@ -135,21 +137,13 @@ The plugin allso adds the following methods to the plot object: ...@@ -135,21 +137,13 @@ The plugin allso adds the following methods to the plot object:
if (!selectionIsSane()) if (!selectionIsSane())
return null; return null;
var x1 = Math.min(selection.first.x, selection.second.x), var r = {}, c1 = selection.first, c2 = selection.second;
x2 = Math.max(selection.first.x, selection.second.x), $.each(plot.getAxes(), function (name, axis) {
y1 = Math.max(selection.first.y, selection.second.y), if (axis.used) {
y2 = Math.min(selection.first.y, selection.second.y); var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
var r = {}; }
var axes = plot.getAxes(); });
if (axes.xaxis.used)
r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
if (axes.x2axis.used)
r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
if (axes.yaxis.used)
r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
if (axes.y2axis.used)
r.y2axis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
return r; return r;
} }
...@@ -159,13 +153,12 @@ The plugin allso adds the following methods to the plot object: ...@@ -159,13 +153,12 @@ The plugin allso adds the following methods to the plot object:
plot.getPlaceholder().trigger("plotselected", [ r ]); plot.getPlaceholder().trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future // backwards-compat stuff, to be removed in future
var axes = plot.getAxes(); if (r.xaxis && r.yaxis)
if (axes.xaxis.used && axes.yaxis.used)
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
} }
function clamp(min, value, max) { function clamp(min, value, max) {
return value < min? min: (value > max? max: value); return value < min ? min: (value > max ? max: value);
} }
function setSelectionPos(pos, e) { function setSelectionPos(pos, e) {
...@@ -176,10 +169,10 @@ The plugin allso adds the following methods to the plot object: ...@@ -176,10 +169,10 @@ The plugin allso adds the following methods to the plot object:
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
if (o.selection.mode == "y") if (o.selection.mode == "y")
pos.x = pos == selection.first? 0: plot.width(); pos.x = pos == selection.first ? 0 : plot.width();
if (o.selection.mode == "x") if (o.selection.mode == "x")
pos.y = pos == selection.first? 0: plot.height(); pos.y = pos == selection.first ? 0 : plot.height();
} }
function updateSelection(pos) { function updateSelection(pos) {
...@@ -204,19 +197,55 @@ The plugin allso adds the following methods to the plot object: ...@@ -204,19 +197,55 @@ The plugin allso adds the following methods to the plot object:
} }
} }
// taken from markings support
function extractRange(ranges, coord) {
var axis, from, to, axes, key;
axes = plot.getUsedAxes();
for (i = 0; i < axes.length; ++i) {
axis = axes[i];
if (axis.direction == coord) {
key = coord + axis.n + "axis";
if (!ranges[key] && axis.n == 1)
key = coord + "axis"; // support x1axis as xaxis
if (ranges[key]) {
from = ranges[key].from;
to = ranges[key].to;
break;
}
}
}
// backwards-compat stuff - to be removed in future
if (!ranges[key]) {
axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
from = ranges[coord + "1"];
to = ranges[coord + "2"];
}
// auto-reverse as an added bonus
if (from != null && to != null && from > to) {
var tmp = from;
from = to;
to = tmp;
}
return { from: from, to: to, axis: axis };
}
function setSelection(ranges, preventEvent) { function setSelection(ranges, preventEvent) {
var axis, range, axes = plot.getAxes(); var axis, range, o = plot.getOptions();
var o = plot.getOptions();
if (o.selection.mode == "y") { if (o.selection.mode == "y") {
selection.first.x = 0; selection.first.x = 0;
selection.second.x = plot.width(); selection.second.x = plot.width();
} }
else { else {
axis = ranges["xaxis"]? axes["xaxis"]: (ranges["x2axis"]? axes["x2axis"]: axes["xaxis"]); range = extractRange(ranges, "x");
range = ranges["xaxis"] || ranges["x2axis"] || { from:ranges["x1"], to:ranges["x2"] }
selection.first.x = axis.p2c(Math.min(range.from, range.to)); selection.first.x = range.axis.p2c(range.from);
selection.second.x = axis.p2c(Math.max(range.from, range.to)); selection.second.x = range.axis.p2c(range.to);
} }
if (o.selection.mode == "x") { if (o.selection.mode == "x") {
...@@ -224,10 +253,10 @@ The plugin allso adds the following methods to the plot object: ...@@ -224,10 +253,10 @@ The plugin allso adds the following methods to the plot object:
selection.second.y = plot.height(); selection.second.y = plot.height();
} }
else { else {
axis = ranges["yaxis"]? axes["yaxis"]: (ranges["y2axis"]? axes["y2axis"]: axes["yaxis"]); range = extractRange(ranges, "y");
range = ranges["yaxis"] || ranges["y2axis"] || { from:ranges["y1"], to:ranges["y2"] }
selection.first.y = axis.p2c(Math.min(range.from, range.to)); selection.first.y = range.axis.p2c(range.from);
selection.second.y = axis.p2c(Math.max(range.from, range.to)); selection.second.y = range.axis.p2c(range.to);
} }
selection.show = true; selection.show = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment