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

Some cleanup in tick generation code, added support for aligning axes

with alignTicksWithAxis


git-svn-id: https://flot.googlecode.com/svn/trunk@258 1e0a6537-2640-0410-bfb7-f154510ff394
parent 947c1663
...@@ -195,6 +195,8 @@ Customizing the axes ...@@ -195,6 +195,8 @@ Customizing the axes
labelHeight: null or number labelHeight: null or number
tickLength: null or number tickLength: null or number
alignTicksWithAxis: null or number
} }
All axes have the same kind of options. The following describes how to All axes have the same kind of options. The following describes how to
...@@ -332,7 +334,15 @@ innermost axes will have ticks that extend all across the plot, while ...@@ -332,7 +334,15 @@ innermost axes will have ticks that extend all across the plot, while
any extra axes use small ticks. A value of null means use the default, any extra axes use small ticks. A value of null means use the default,
while a number means small ticks of that length - set it to 0 to hide while a number means small ticks of that length - set it to 0 to hide
the lines completely. the lines completely.
If you set "alignTicksWithAxis" to the number of another axis, e.g.
alignTicksWithAxis: 1, Flot will ensure that the autogenerated ticks
of this axis are aligned with the ticks of the other axis. This may
improve the looks, e.g. if you have one y axis to the left and one to
the right, because the grid lines will then match the ticks in both
ends. The trade-off is that the forced ticks won't necessarily be at
natural places.
Multiple axes Multiple axes
============= =============
......
...@@ -60,6 +60,10 @@ Changes: ...@@ -60,6 +60,10 @@ Changes:
Services, www.flightdataservices.com). Services, www.flightdataservices.com).
- Tick color is now auto-generated as the base color with some - Tick color is now auto-generated as the base color with some
transparency (unless you override it). transparency (unless you override it).
- Support for aligning ticks in the axes with "alignTicksWithAxis" to
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).
- New hooks: drawSeries - New hooks: drawSeries
......
...@@ -39,7 +39,12 @@ $(function () { ...@@ -39,7 +39,12 @@ $(function () {
{ {
xaxes: [ { mode: 'time' } ], xaxes: [ { mode: 'time' } ],
yaxes: [ { min: 0 }, yaxes: [ { min: 0 },
{ position: position, tickFormatter: euroFormatter } ], {
// align if we are to the right
alignTicksWithAxis: position == "right" ? 1 : null,
position: position,
tickFormatter: euroFormatter
} ],
legend: { position: 'sw' } legend: { position: 'sw' }
}); });
} }
......
...@@ -62,9 +62,10 @@ ...@@ -62,9 +62,10 @@
autoscaleMargin: null, // margin in % to add if auto-setting min/max autoscaleMargin: null, // margin in % to add if auto-setting min/max
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
tickFormatter: null, // fn: number -> string tickFormatter: null, // fn: number -> string
tickLength: null, // size in pixels of ticks, or "full" for whole line
labelWidth: null, // size of tick labels in pixels labelWidth: null, // size of tick labels in pixels
labelHeight: null, labelHeight: null,
tickLength: null, // size in pixels of ticks, or "full" for whole line
alignTicksWithAxis: null, // axis number or null for no sync
// mode specific options // mode specific options
tickDecimals: null, // no. of decimals, null means auto tickDecimals: null, // no. of decimals, null means auto
...@@ -881,8 +882,9 @@ ...@@ -881,8 +882,9 @@
var sameDirection = $.grep(all, function (a) { var sameDirection = $.grep(all, function (a) {
return a && (a.labelHeight || a.labelWidth); return a && (a.labelHeight || a.labelWidth);
}); });
if ($.inArray(axis, sameDirection) != 0 && tickLength == "full") var innermost = $.inArray(axis, sameDirection) == 0;
if (!innermost && tickLength == "full")
tickLength = 5; tickLength = 5;
if (!isNaN(+tickLength)) if (!isNaN(+tickLength))
...@@ -918,7 +920,7 @@ ...@@ -918,7 +920,7 @@
axis.position = pos; axis.position = pos;
axis.tickLength = tickLength; axis.tickLength = tickLength;
axis.box.padding = padding; axis.box.padding = padding;
axis.innermost = $.inArray(axis, samePosition) == 0; axis.innermost = innermost;
} }
function fixupAxisBox(axis) { function fixupAxisBox(axis) {
...@@ -947,11 +949,12 @@ ...@@ -947,11 +949,12 @@
for (k = 0; k < axes.length; ++k) { for (k = 0; k < axes.length; ++k) {
setupTickGeneration(axes[k]); setupTickGeneration(axes[k]);
setTicks(axes[k]); setTicks(axes[k]);
snapRangeToTicks(axes[k], axes[k].ticks);
} }
// find labelWidth/Height, do this on all, not just // find labelWidth/Height, do this on all, not just
// used as we might need to reserve space for unused // used as we might need to reserve space for unused
// to if their labelWidth/Height is set // too if their labelWidth/Height is set
for (j = 0; j < xaxes.length; ++j) for (j = 0; j < xaxes.length; ++j)
measureTickLabels(xaxes[j]); measureTickLabels(xaxes[j]);
for (j = 0; j < yaxes.length; ++j) for (j = 0; j < yaxes.length; ++j)
...@@ -1042,7 +1045,7 @@ ...@@ -1042,7 +1045,7 @@
noTicks = 0.3 * Math.sqrt(canvasWidth); noTicks = 0.3 * Math.sqrt(canvasWidth);
else else
noTicks = 0.3 * Math.sqrt(canvasHeight); noTicks = 0.3 * Math.sqrt(canvasHeight);
var delta = (axis.max - axis.min) / noTicks, var delta = (axis.max - axis.min) / noTicks,
size, generator, unit, formatter, i, magn, norm; size, generator, unit, formatter, i, magn, norm;
...@@ -1107,10 +1110,7 @@ ...@@ -1107,10 +1110,7 @@
size *= magn; size *= magn;
} }
if (opts.tickSize) { axis.tickSize = opts.tickSize || [size, unit];
size = opts.tickSize[0];
unit = opts.tickSize[1];
}
generator = function(axis) { generator = function(axis) {
var ticks = [], var ticks = [],
...@@ -1148,7 +1148,7 @@ ...@@ -1148,7 +1148,7 @@
do { do {
prev = v; prev = v;
v = d.getTime(); v = d.getTime();
ticks.push({ v: v, label: axis.tickFormatter(v, axis) }); ticks.push(v);
if (unit == "month") { if (unit == "month") {
if (tickSize < 1) { if (tickSize < 1) {
// a bit complicated - we'll divide the month // a bit complicated - we'll divide the month
...@@ -1238,10 +1238,8 @@ ...@@ -1238,10 +1238,8 @@
if (opts.minTickSize != null && size < opts.minTickSize) if (opts.minTickSize != null && size < opts.minTickSize)
size = opts.minTickSize; size = opts.minTickSize;
if (opts.tickSize != null) axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
size = opts.tickSize; axis.tickSize = opts.tickSize || size;
axis.tickDecimals = Math.max(0, (maxDec != null) ? maxDec : dec);
generator = function (axis) { generator = function (axis) {
var ticks = []; var ticks = [];
...@@ -1252,7 +1250,7 @@ ...@@ -1252,7 +1250,7 @@
do { do {
prev = v; prev = v;
v = start + i * axis.tickSize; v = start + i * axis.tickSize;
ticks.push({ v: v, label: axis.tickFormatter(v, axis) }); ticks.push(v);
++i; ++i;
} while (v < axis.max && v != prev); } while (v < axis.max && v != prev);
return ticks; return ticks;
...@@ -1263,7 +1261,44 @@ ...@@ -1263,7 +1261,44 @@
}; };
} }
axis.tickSize = unit ? [size, unit] : size; if (opts.alignTicksWithAxis != null) {
var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
if (otherAxis && otherAxis.used && otherAxis != axis) {
// consider snapping min/max to outermost nice ticks
var niceTicks = generator(axis);
if (niceTicks.length > 0) {
if (opts.min == null)
axis.min = Math.min(axis.min, niceTicks[0]);
if (opts.max == null && niceTicks.length > 1)
axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
}
generator = function (axis) {
// copy ticks, scaled to this axis
var ticks = [], v, i;
for (i = 0; i < otherAxis.ticks.length; ++i) {
v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
v = axis.min + v * (axis.max - axis.min);
ticks.push(v);
}
return ticks;
};
// we might need an extra decimal since forced
// ticks don't necessarily fit naturally
if (axis.mode != "time" && opts.tickDecimals == null) {
var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1),
ts = generator(axis);
// only proceed if the tick interval rounded
// with an extra decimal doesn't give us a
// zero at end
if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
axis.tickDecimals = extraDec;
}
}
}
axis.tickGenerator = generator; axis.tickGenerator = generator;
if ($.isFunction(opts.tickFormatter)) if ($.isFunction(opts.tickFormatter))
axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
...@@ -1272,47 +1307,44 @@ ...@@ -1272,47 +1307,44 @@
} }
function setTicks(axis) { function setTicks(axis) {
var opts = axis.options;
axis.ticks = []; axis.ticks = [];
if (opts.ticks == null) var oticks = axis.options.ticks, ticks = null;
axis.ticks = axis.tickGenerator(axis); if (oticks == null || (typeof oticks == "number" && oticks > 0))
else if (typeof opts.ticks == "number") { ticks = axis.tickGenerator(axis);
if (opts.ticks > 0) else if (oticks) {
axis.ticks = axis.tickGenerator(axis); if ($.isFunction(oticks))
}
else if (opts.ticks) {
var ticks = opts.ticks;
if ($.isFunction(ticks))
// generate the ticks // generate the ticks
ticks = ticks({ min: axis.min, max: axis.max }); ticks = oticks({ min: axis.min, max: axis.max });
else
// clean up the user-supplied ticks, copy them over ticks = oticks;
var i, v; }
for (i = 0; i < ticks.length; ++i) {
var label = null; // clean up/labelify the supplied ticks, copy them over
var t = ticks[i]; var i, v;
if (typeof t == "object") { for (i = 0; i < ticks.length; ++i) {
v = t[0]; var label = null;
if (t.length > 1) var t = ticks[i];
label = t[1]; if (typeof t == "object") {
} v = t[0];
else if (t.length > 1)
v = t; label = t[1];
if (label == null)
label = axis.tickFormatter(v, axis);
axis.ticks[i] = { v: v, label: label };
} }
else
v = t;
if (label == null)
label = axis.tickFormatter(v, axis);
axis.ticks[i] = { v: v, label: label };
} }
}
if (opts.autoscaleMargin != null && axis.ticks.length > 0) { function snapRangeToTicks(axis, ticks) {
if (axis.options.autoscaleMargin != null && ticks.length > 0) {
// snap to ticks // snap to ticks
if (opts.min == null) if (axis.options.min == null)
axis.min = Math.min(axis.min, axis.ticks[0].v); axis.min = Math.min(axis.min, ticks[0].v);
if (opts.max == null && axis.ticks.length > 1) if (axis.options.max == null && ticks.length > 1)
axis.max = Math.max(axis.max, axis.ticks[axis.ticks.length - 1].v); axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
} }
} }
......
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