forked from ni-kismet/engineering-flot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjquery.flot.logaxis.js
281 lines (238 loc) · 9.05 KB
/
jquery.flot.logaxis.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/* Pretty handling of log axes.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Copyright (c) 2015 Ciprian Ceteras cipix2000@gmail.com.
Copyright (c) 2017 Raluca Portase
Licensed under the MIT license.
Set axis.mode to "log" to enable.
*/
/* global jQuery*/
/**
## jquery.flot.logaxis
This plugin is used to create logarithmic axis. This includes tick generation,
formatters and transformers to and from logarithmic representation.
### Methods and hooks
*/
(function ($) {
'use strict';
var options = {
xaxis: {}
};
/*tick generators and formatters*/
var PREFERRED_LOG_TICK_VALUES = computePreferedLogTickValues(Number.MAX_VALUE, 10),
EXTENDED_LOG_TICK_VALUES = computePreferedLogTickValues(Number.MAX_VALUE, 4);
function computePreferedLogTickValues(endLimit, rangeStep) {
var log10End = Math.floor(Math.log(endLimit) * Math.LOG10E) - 1,
log10Start = -log10End,
val, range, vals = [];
for (var power = log10Start; power <= log10End; power++) {
range = Math.pow(10, power);
for (var mult = 1; mult < 9; mult += rangeStep) {
val = range * mult;
vals.push(val);
}
}
return vals;
}
/**
- logTickGenerator(plot, axis, noTicks)
Generates logarithmic ticks, depending on axis range.
In case the number of ticks that can be generated is less than the expected noTicks/4,
a linear tick generation is used.
*/
var logTickGenerator = function (plot, axis, noTicks) {
var ticks = [],
minIdx = -1,
maxIdx = -1,
surface = plot.getCanvas(),
logTickValues = PREFERRED_LOG_TICK_VALUES,
min = clampAxis(axis, plot),
max = axis.max;
if (!noTicks) {
noTicks = 0.3 * Math.sqrt(axis.direction === "x" ? surface.width : surface.height);
}
PREFERRED_LOG_TICK_VALUES.some(function (val, i) {
if (val >= min) {
minIdx = i;
return true;
} else {
return false;
}
});
PREFERRED_LOG_TICK_VALUES.some(function (val, i) {
if (val >= max) {
maxIdx = i;
return true;
} else {
return false;
}
});
if (maxIdx === -1) {
maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1;
}
if (maxIdx - minIdx <= noTicks / 4 && logTickValues.length !== EXTENDED_LOG_TICK_VALUES.length) {
//try with multiple of 5 for tick values
logTickValues = EXTENDED_LOG_TICK_VALUES;
minIdx *= 2;
maxIdx *= 2;
}
var lastDisplayed = null,
inverseNoTicks = 1 / noTicks,
tickValue, pixelCoord, tick;
// Count the number of tick values would appear, if we can get at least
// nTicks / 4 accept them.
if (maxIdx - minIdx >= noTicks / 4) {
for (var idx = maxIdx; idx >= minIdx; idx--) {
tickValue = logTickValues[idx];
pixelCoord = (Math.log(tickValue) - Math.log(min)) / (Math.log(max) - Math.log(min));
tick = tickValue;
if (lastDisplayed === null) {
lastDisplayed = {
pixelCoord: pixelCoord,
idealPixelCoord: pixelCoord
};
} else {
if (Math.abs(pixelCoord - lastDisplayed.pixelCoord) >= inverseNoTicks) {
lastDisplayed = {
pixelCoord: pixelCoord,
idealPixelCoord: lastDisplayed.idealPixelCoord - inverseNoTicks
};
} else {
tick = null;
}
}
if (tick) {
ticks.push(tick);
}
}
// Since we went in backwards order.
ticks.reverse();
} else {
var tickSize = plot.computeTickSize(min, max, noTicks),
customAxis = {min: min, max: max, tickSize: tickSize};
ticks = $.plot.linearTickGenerator(customAxis);
}
return ticks;
};
var clampAxis = function (axis, plot) {
var min = axis.min,
max = axis.max;
if (min <= 0) {
//for empty graph if axis.min is not strictly positive make it 0.1
if (axis.datamin === null) {
min = axis.min = 0.1;
} else {
min = processAxisOffset(plot, axis);
}
if (max < min) {
axis.max = axis.datamax !== null ? axis.datamax : axis.options.max;
axis.options.offset.below = 0;
axis.options.offset.above = 0;
}
}
return min;
}
/**
- logTickFormatter(value, axis, precision)
This is the corresponding tickFormatter of the logaxis.
For a number greater that 10^6 or smaller than 10^(-3), this will be drawn
with e representation
*/
var logTickFormatter = function (value, axis, precision) {
var tenExponent = value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0;
if (precision) {
if ((tenExponent >= -4) && (tenExponent <= 7)) {
return $.plot.defaultTickFormatter(value, axis, precision);
} else {
return $.plot.expRepTickFormatter(value, axis, precision);
}
}
if ((tenExponent >= -4) && (tenExponent <= 7)) {
//if we have float numbers, return a limited length string(ex: 0.0009 is represented as 0.000900001)
var formattedValue = tenExponent < 0 ? value.toFixed(-tenExponent) : value.toFixed(tenExponent + 2);
if (formattedValue.indexOf('.') !== -1) {
var lastZero = formattedValue.lastIndexOf('0');
while (lastZero === formattedValue.length - 1) {
formattedValue = formattedValue.slice(0, -1);
lastZero = formattedValue.lastIndexOf('0');
}
//delete the dot if is last
if (formattedValue.indexOf('.') === formattedValue.length - 1) {
formattedValue = formattedValue.slice(0, -1);
}
}
return formattedValue;
} else {
return $.plot.expRepTickFormatter(value, axis);
}
};
/*logaxis caracteristic functions*/
var logTransform = function (v) {
if (v < PREFERRED_LOG_TICK_VALUES[0]) {
v = PREFERRED_LOG_TICK_VALUES[0];
}
return Math.log(v);
};
var logInverseTransform = function (v) {
return Math.exp(v);
};
/**
- setDataminRange(plot, axis)
It is used for clamping the starting point of a logarithmic axis.
This will set the axis datamin range to 0.1 or to the first datapoint greater then 0.
The function is usefull since the logarithmic representation can not show
values less than or equal to 0.
*/
function setDataminRange(plot, axis) {
if (axis.options.mode === 'log' && axis.datamin <= 0) {
if (axis.datamin === null) {
axis.datamin = 0.1;
} else {
axis.datamin = processAxisOffset(plot, axis);
}
}
}
function processAxisOffset(plot, axis) {
var series = plot.getData(),
range = series
.filter(function(series) {
return series.xaxis === axis || series.yaxis === axis;
})
.map(function(series) {
return plot.computeRangeForDataSeries(series, null, isValid);
}),
min = axis.direction === 'x' ? Math.min(0.1, range[0].xmin) : Math.min(0.1, range[0].ymin);
axis.min = min;
return min;
}
function isValid(a) {
return a > 0;
}
function init(plot) {
plot.hooks.processOptions.push(function (plot) {
$.each(plot.getAxes(), function (axisName, axis) {
var opts = axis.options;
if (opts.mode === 'log') {
axis.tickGenerator = function (axis) {
var noTicks = 11;
return logTickGenerator(plot, axis, noTicks);
};
if (typeof axis.options.tickFormatter !== 'function') {
axis.options.tickFormatter = logTickFormatter;
}
axis.options.transform = logTransform;
axis.options.inverseTransform = logInverseTransform;
axis.options.autoScaleMargin = 0;
plot.hooks.setRange.push(setDataminRange);
}
});
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'log',
version: '0.1'
});
$.plot.logTicksGenerator = logTickGenerator;
$.plot.logTickFormatter = logTickFormatter;
})(jQuery);