100 monotonicFit :
false,
103 legacyOverride : undefined
108 function init(plot) {
110 plot.hooks.processOptions.push(processOptions);
113 function processOptions(plot, options) {
114 if (options.series.curvedLines.active) {
115 plot.hooks.processDatapoints.unshift(processDatapoints);
120 function processDatapoints(plot, series, datapoints) {
121 var nrPoints = datapoints.points.length / datapoints.pointsize;
126 var invalidLegacyOptions = hasInvalidParameters(series.curvedLines);
128 if (!invalidLegacyOptions && series.curvedLines.apply ==
true && series.originSeries === undefined && nrPoints > (1 + EPSILON)) {
129 if (series.lines.fill) {
131 var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1);
132 var pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2);
136 datapoints.pointsize = 3;
137 datapoints.points = [];
142 while (i < pointsTop.length || j < pointsBottom.length) {
143 if (pointsTop[i] == pointsBottom[j]) {
144 datapoints.points[k] = pointsTop[i];
145 datapoints.points[k + 1] = pointsTop[i + 1];
146 datapoints.points[k + 2] = pointsBottom[j + 1];
150 }
else if (pointsTop[i] < pointsBottom[j]) {
151 datapoints.points[k] = pointsTop[i];
152 datapoints.points[k + 1] = pointsTop[i + 1];
153 datapoints.points[k + 2] = k > 0 ? datapoints.points[k - 1] : null;
156 datapoints.points[k] = pointsBottom[j];
157 datapoints.points[k + 1] = k > 1 ? datapoints.points[k - 2] : null;
158 datapoints.points[k + 2] = pointsBottom[j + 1];
163 }
else if (series.lines.lineWidth > 0) {
164 datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1);
165 datapoints.pointsize = 2;
170 function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) {
171 if ( typeof curvedLinesOptions.legacyOverride !=
'undefined' && curvedLinesOptions.legacyOverride !=
false) {
172 var defaultOptions = {
174 curvePointFactor : 20,
175 fitPointDist : undefined
177 var legacyOptions = jQuery.extend(defaultOptions, curvedLinesOptions.legacyOverride);
178 return calculateLegacyCurvePoints(datapoints, legacyOptions, yPos);
181 return calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos);
184 function calculateSplineCurvePoints(datapoints, curvedLinesOptions, yPos) {
185 var points = datapoints.points;
186 var ps = datapoints.pointsize;
189 var splines = createHermiteSplines(datapoints, curvedLinesOptions, yPos);
196 for (var i = 0; i < points.length - ps; i += ps) {
200 var xStart = points[curX];
201 var xEnd = points[curX + ps];
202 var xStep = (xEnd - xStart) / Number(curvedLinesOptions.nrSplinePoints);
205 result.push(points[curX]);
206 result.push(points[curY]);
209 for (var x = (xStart += xStep); x < xEnd; x += xStep) {
211 result.push(splines[j](x));
218 result.push(points[points.length - ps]);
219 result.push(points[points.length - ps + yPos]);
232 function createHermiteSplines(datapoints, curvedLinesOptions, yPos) {
233 var points = datapoints.points;
234 var ps = datapoints.pointsize;
237 var segmentLengths = [];
238 var segmentSlopes = [];
240 for (var i = 0; i < points.length - ps; i += ps) {
243 var dx = points[curX + ps] - points[curX];
244 var dy = points[curY + ps] - points[curY];
246 segmentLengths.push(dx);
247 segmentSlopes.push(dy / dx);
252 var gradients = [segmentSlopes[0]];
253 if (curvedLinesOptions.monotonicFit) {
255 for (var i = 1; i < segmentLengths.length; i++) {
256 var slope = segmentSlopes[i];
257 var prev_slope = segmentSlopes[i - 1];
258 if (slope * prev_slope <= 0) {
261 var length = segmentLengths[i];
262 var prev_length = segmentLengths[i - 1];
263 var common = length + prev_length;
265 gradients.push(3 * common / ((common + length) / prev_slope + (common + prev_length) / slope));
271 for (var i = ps; i < points.length - ps; i += ps) {
274 gradients.push(Number(curvedLinesOptions.tension) * (points[curY + ps] - points[curY - ps]) / (points[curX + ps] - points[curX - ps]));
277 gradients.push(segmentSlopes[segmentSlopes.length - 1]);
282 for (i = 0; i < segmentLengths.length; i++) {
283 var m_k = gradients[i];
284 var m_k_plus = gradients[i + 1];
285 var slope = segmentSlopes[i];
286 var invLength = 1 / segmentLengths[i];
287 var common = m_k + m_k_plus - slope - slope;
289 coefs1.push(common * invLength * invLength);
290 coefs2.push((slope - common - m_k) * invLength);
295 for (var i = 0; i < segmentLengths.length; i ++) {
296 var spline =
function (x_k, coef1, coef2, coef3, coef4) {
298 return function (x) {
300 var diffSq = diff * diff;
301 return coef1 * diff * diffSq + coef2 * diffSq + coef3 * diff + coef4;
305 ret.push(spline(points[i * ps], coefs1[i], coefs2[i], gradients[i], points[i * ps + yPos]));
313 function calculateLegacyCurvePoints(datapoints, curvedLinesOptions, yPos) {
315 var points = datapoints.points;
316 var ps = datapoints.pointsize;
317 var num = Number(curvedLinesOptions.curvePointFactor) * (points.length / ps);
319 var xdata =
new Array;
320 var ydata =
new Array;
326 if (curvedLinesOptions.fit) {
331 if ( typeof curvedLinesOptions.fitPointDist ==
'undefined') {
333 var minX = points[0];
334 var maxX = points[points.length - ps];
335 fpDist = (maxX - minX) / (500 * 100);
339 fpDist = Number(curvedLinesOptions.fitPointDist);
342 for (var i = 0; i < points.length; i += ps) {
350 frontX = points[curX] - fpDist;
351 backX = points[curX] + fpDist;
354 while (frontX == points[curX] || backX == points[curX]) {
356 frontX = points[curX] - (fpDist * factor);
357 backX = points[curX] + (fpDist * factor);
363 ydata[j] = points[curY];
366 xdata[j] = points[curX];
367 ydata[j] = points[curY];
371 ydata[j] = points[curY];
376 for (var i = 0; i < points.length; i += ps) {
380 xdata[j] = points[curX];
381 ydata[j] = points[curY];
386 var n = xdata.length;
388 var y2 =
new Array();
389 var delta =
new Array();
394 for (var i = 1; i < n - 1; ++i) {
395 var d = (xdata[i + 1] - xdata[i - 1]);
401 var s = (xdata[i] - xdata[i - 1]) / d;
402 var p = s * y2[i - 1] + 2;
404 delta[i] = (ydata[i + 1] - ydata[i]) / (xdata[i + 1] - xdata[i]) - (ydata[i] - ydata[i - 1]) / (xdata[i] - xdata[i - 1]);
405 delta[i] = (6 * delta[i] / (xdata[i + 1] - xdata[i - 1]) - s * delta[i - 1]) / p;
408 for (var j = n - 2; j >= 0; --j) {
409 y2[j] = y2[j] * y2[j + 1] + delta[j];
413 var step = (xdata[n - 1] - xdata[0]) / (num - 1);
415 var xnew =
new Array;
416 var ynew =
new Array;
417 var result =
new Array;
422 result.push(xnew[0]);
423 result.push(ynew[0]);
425 for ( j = 1; j < num; ++j) {
427 xnew[j] = xnew[0] + j * step;
432 while (max - min > 1) {
433 var k = Math.round((max + min) / 2);
434 if (xdata[k] > xnew[j]) {
442 var h = (xdata[max] - xdata[min]);
449 var a = (xdata[max] - xnew[j]) / h;
450 var b = (xnew[j] - xdata[min]) / h;
452 ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6;
454 result.push(xnew[j]);
455 result.push(ynew[j]);
461 function hasInvalidParameters(curvedLinesOptions) {
462 if (typeof curvedLinesOptions.fit !=
'undefined' ||
463 typeof curvedLinesOptions.curvePointFactor !=
'undefined' ||
464 typeof curvedLinesOptions.fitPointDist !=
'undefined') {
465 throw new Error(
"CurvedLines detected illegal parameters. The CurvedLines API changed with version 1.0.0 please check the options object.");
475 $.plot.plugins.push({
478 name :
'curvedLines',