Commit bb64fec4 authored by MichaelZinsmaier's avatar MichaelZinsmaier

v0.5 api break

parents fb596bd5 3c5463a0
/* The MIT License
Copyright (c) 2011 by Michael Zinsmaier and nergal.dev
Copyright (c) 2012 by Thomas Ritou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -21,7 +22,7 @@
THE SOFTWARE.
*/
/*
/*
____________________________________________________
......@@ -35,8 +36,9 @@
=> 1) with large data sets you may get trouble
=> 2) if you want to display the points too, you have to plot them as 2nd data series over the lines
This is version 0.3 of curvedLines so it will probably not work in every case. However
This is version 0.5 of curvedLines so it will probably not work in every case. However
the basic form of use descirbed next works (:
Feel free to further improve the code
____________________________________________________
......@@ -48,44 +50,46 @@
var options = { series: { curvedLines: { active: true }}};
$.plot($("#placeholder"), [{data = d1, curvedLines: { show: true}}], options);
$.plot($("#placeholder"), [{data = d1, lines: { show: true}, curvedLines: {apply: true}}], options);
_____________________________________________________
options:
_____________________________________________________
fill: bool true => lines get filled
fillColor: null or the color that should be used for filling
active: bool true => plugin can be used
show: bool true => series will be drawn as curved line
apply: bool true => series will be drawn as curved line
fit: bool true => forces the max,mins of the curve to be on the datapoints
lineWidth: int width of the line
curvePointFactor int defines how many "virtual" points are used per "real" data point to
emulate the curvedLines
fitPointDist: int defines the x axis distance of the additional two points that are used
to enforce the min max condition. (you will get curvePointFactor * 3 * |datapoints|
"virtual" points if fit is true)
+ line options (since v0.5 curved lines use flots line implementation for drawing
=> line options like fill, show ... are supported out of the box)
*/
/*
/*
* v0.1 initial commit
* v0.15 negative values should work now (outcommented a negative -> 0 hook hope it does no harm)
* v0.2 added fill option (thanks to monemihir) and multi axis support (thanks to soewono effendi)
* v0.3 improved saddle handling and added basic handling of Dates
*
* v0.4 rewritten fill option (thomas ritou) mostly from original flot code (now fill between points rather than to graph bottom), corrected fill Opacity bug
* v0.5 rewritten instead of implementing a own draw function CurvedLines is now based on the processDatapoints flot hook (credits go to thomas ritou).
* This change breakes existing code however CurvedLines are now just many tiny straight lines to flot and therefore all flot lines options (like gradient fill,
* shadow) are now supported out of the box
*/
(function($) {
(function($) {
var options = {
series : {
curvedLines : {
active : false,
show : false,
apply: false,
fit : false,
fill : false,
fillColor : null,
lineWidth : 2,
curvePointFactor : 20,
fitPointDist : 0.0001
}
......@@ -96,239 +100,163 @@
plot.hooks.processOptions.push(processOptions);
//if the plugin is active register draw method
//if the plugin is active register processDatapoints method
function processOptions(plot, options) {
if (options.series.curvedLines.active) {
plot.hooks.draw.push(draw);
plot.hooks.processDatapoints.push(processDatapoints);
}
}
//select the data sets that should be drawn with curved lines and draws them
function draw(plot, ctx) {
var series;
var sdata = plot.getData();
var offset = plot.getPlotOffset();
for (var i = 0; i < sdata.length; i++) {
series = sdata[i];
if (series.curvedLines.show && series.curvedLines.lineWidth > 0) {
axisx = series.xaxis;
axisy = series.yaxis;
ctx.save();
ctx.translate(offset.left, offset.top);
ctx.lineJoin = "round";
ctx.strokeStyle = series.color;
if (series.curvedLines.fill) {
var fill = series.curvedLines.fill;
var fillColor = series.curvedLines.fillColor == null ? series.color : series.curvedLines.fillColor;
var c = $.color.parse(fillColor);
c.a = typeof fill == "number" ? fill : 0.4;
c.normalize();
ctx.fillStyle = c.toString();
}
ctx.lineWidth = series.curvedLines.lineWidth;
var points, dataX, dataY, data;
//only if the plugin is active
function processDatapoints(plot, series, datapoints) {
if (series.curvedLines.apply == true) {
if (series.lines.fill) {
//convenience check for x or y values if they are Dates if so apply .getTime()
//only check on first value mixing numeric and Date fields in one input array is not allowed
if (series.data[0][0] instanceof Date || series.data[0][1] instanceof Date) {
data = series.data.map(getTimeFromDate);
} else {
//default case
data = series.data;
}
var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1)
,pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2); //flot makes sur for us that we've got a second y point if fill is true !
var points = calculateCurvePoints(data, series.curvedLines);
plotLine(ctx, points, axisx, axisy, series.curvedLines.fill);
ctx.restore();
}
}
}
//helper method that convertes Dates to a numeric representation
function getTimeFromDate(timeElement) {
var xVal = timeElement[0];
var yVal = timeElement[1];
var ret = new Array;
if (timeElement[0] instanceof Date) {
ret[0] = xVal.getTime();
//Merge top and bottom curve
datapoints.pointsize = 3;
datapoints.points = [];
var j = 0;
var k = 0;
var i = 0;
var ps = 2;
while (i < pointsTop.length || j < pointsBottom.length) {
if (pointsTop[i] == pointsBottom[j]) {
datapoints.points[k] = pointsTop[i];
datapoints.points[k + 1] = pointsTop[i + 1];
datapoints.points[k + 2] = pointsBottom[j + 1];
j += ps;
i += ps;
} else if (pointsTop[i] < pointsBottom[j]) {
datapoints.points[k] = pointsTop[i];
datapoints.points[k + 1] = pointsTop[i + 1];
datapoints.points[k + 2] = k > 0 ? datapoints.points[k-1] : null;
i += ps;
} else {
ret[0] = xVal;
datapoints.points[k] = pointsBottom[j];
datapoints.points[k + 1] = k > 1 ? datapoints.points[k-2] : null;
datapoints.points[k + 2] = pointsBottom[j + 1];
j += ps;
}
if (timeElement[1] instanceof Date) {
ret[1] = yVal.getTime();
} else {
ret[1] = yVal;
k += 3;
}
return ret;
if (series.lines.lineWidth > 0) {//Let's draw line in separate series
var newSerie = $.extend({}, series);
newSerie.lines = $.extend({}, series.lines);
newSerie.lines.fill = undefined;
newSerie.label = undefined;
newSerie.datapoints = {};
//Redefine datapoints to top only (else it can have null values which will open the cruve !)
newSerie.datapoints.points = pointsTop;
newSerie.datapoints.pointsize = 2;
newSerie.curvedLines.apply = false;
//Don't redo curve point calculation as datapoint is copied to this new serie
//We find our series to add the line just after the fill (so other series you wanted above this one will still be)
var allSeries = plot.getData();
for ( i = 0; i < allSeries.length; i++) {
if (allSeries[i] == series) {
plot.getData().splice(i + 1, 0, newSerie);
break;
}
//nearly the same as in the core library
//only ps is adjusted to 2
function plotLine(ctx, points, axisx, axisy, fill) {
var ps = 2;
var prevx = null;
var prevy = null;
var firsty = 0;
ctx.beginPath();
for (var i = ps; i < points.length; i += ps) {
var x1 = points[i - ps], y1 = points[i - ps + 1];
var x2 = points[i], y2 = points[i + 1];
if (x1 == null || x2 == null)
continue;
// clip with ymin
if (y1 <= y2 && y1 < axisy.min) {
if (y2 < axisy.min)
continue;
// line segment is outside
// compute new intersection point
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
y1 = axisy.min;
} else if (y2 <= y1 && y2 < axisy.min) {
if (y1 < axisy.min)
continue;
x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
y2 = axisy.min;
}
// clip with ymax
if (y1 >= y2 && y1 > axisy.max) {
if (y2 > axisy.max)
continue;
x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
y1 = axisy.max;
} else if (y2 >= y1 && y2 > axisy.max) {
if (y1 > axisy.max)
continue;
x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
y2 = axisy.max;
series.lines.lineWidth = 0;
}
// clip with xmin
if (x1 <= x2 && x1 < axisx.min) {
if (x2 < axisx.min)
continue;
y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
x1 = axisx.min;
} else if (x2 <= x1 && x2 < axisx.min) {
if (x1 < axisx.min)
continue;
y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
x2 = axisx.min;
} else if (series.lines.lineWidth > 0) {
datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1);
datapoints.pointsize = 2;
}
// clip with xmax
if (x1 >= x2 && x1 > axisx.max) {
if (x2 > axisx.max)
continue;
y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
x1 = axisx.max;
} else if (x2 >= x1 && x2 > axisx.max) {
if (x1 > axisx.max)
continue;
y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
x2 = axisx.max;
}
if (x1 != prevx || y1 != prevy)
ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
if (prevx == null) {
firsty = y2;
}
prevx = x2;
prevy = y2;
ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
}
if (fill) {
ctx.lineTo(axisx.p2c(axisx.max), axisy.p2c(axisy.min));
ctx.lineTo(axisx.p2c(axisx.min), axisy.p2c(axisy.min));
ctx.lineTo(axisx.p2c(axisx.min), axisy.p2c(firsty));
ctx.fill();
}
ctx.stroke();
}
//no real idea whats going on here code mainly from https://code.google.com/p/flot/issues/detail?id=226
//if fit option is selected additional datapoints get inserted before the curve calculations in nergal.dev s code.
function calculateCurvePoints(data, curvedLinesOptions) {
function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) {
var points = datapoints.points, ps = datapoints.pointsize;
var num = curvedLinesOptions.curvePointFactor * (points.length / ps);
var num = curvedLinesOptions.curvePointFactor * data.length;
var xdata = new Array;
var ydata = new Array;
var X = 0;
var Y = 1;
var Y = yPos;
var curX = -1;
var curY = -1;
var j = 0;
if (curvedLinesOptions.fit) {
//insert a point before and after the "real" data point to force the line
//to have a max,min at the data point however only if it is a lowest or highest point of the
//curve => avoid saddles
var neigh = curvedLinesOptions.fitPointDist;
var j = 0;
var fpDist = curvedLinesOptions.fitPointDist;
for (var i = 0; i < data.length; i++) {
for (var i = 0; i < points.length; i += ps) {
var front = new Array;
var back = new Array;
curX = i;
curY = i + yPos;
//smooth front
front[X] = data[i][X] - 0.1;
if (i > 0) {
front[Y] = data[i-1][Y] * neigh + data[i][Y] * (1 - neigh);
} else {
front[Y] = data[i][Y];
}
//add point to front
front[X] = points[curX] - fpDist;
front[Y] = points[curY];
//add point to back
back[X] = points[curX] + fpDist;
back[Y] = points[curY];
//smooth back
back[X] = data[i][X] + 0.1;
if ((i + 1) < data.length) {
back[Y] = data[i+1][Y] * neigh + data[i][Y] * (1 - neigh);
} else {
back[Y] = data[i][Y];
//get points (front and back) Y value for saddle test
var frontPointY = points[curY];
var backPointY = points[curY];
if (i >= ps) {
frontPointY = points[curY - ps];
}
if ((i + ps) < points.length) {
backPointY = points[curY + ps];
}
//test for a saddle
if ((front[Y] <= data[i][Y] && back[Y] <= data[i][Y]) || //max or partial horizontal
(front[Y] >= data[i][Y] && back[Y] >= data[i][Y])) { //min or partial horizontal
if ((frontPointY <= points[curY] && backPointY <= points[curY]) || //max or partial horizontal
(frontPointY >= points[curY] && backPointY >= points[curY])) {//min or partial horizontal
//add curve points
xdata[j] = front[X];
ydata[j] = front[Y];
j++;
xdata[j] = data[i][0];
ydata[j] = data[i][1];
xdata[j] = points[curX];
ydata[j] = points[curY];
j++;
xdata[j] = back[X];
ydata[j] = back[Y];
j++;
} else { //saddle
} else {//saddle
//use original point only
xdata[j] = data[i][0];
ydata[j] = data[i][1];
xdata[j] = points[curX];
ydata[j] = points[curY];
j++;
}
}
} else {
//just use the datapoints
for (var i = 0; i < data.length; i++) {
xdata[i] = data[i][0];
ydata[i] = data[i][1];
for (var i = 0; i < points.length; i += ps) {
curX = i;
curY = i + yPos;
xdata[j] = points[curX];
ydata[j] = points[curY];
j++;
}
}
......@@ -394,9 +322,7 @@
var b = (xnew[j] - xdata[min]) / h;
ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6;
// if (ynew[j] < 0.01){
// ynew[j] = 0;
// }
result.push(xnew[j]);
result.push(ynew[j]);
}
......@@ -406,12 +332,12 @@
}//end init
$.plot.plugins.push({
init : init,
options : options,
name : 'curvedLines',
version : '0.3'
version : '0.5'
});
})(jQuery);
})(jQuery);
<div id="flotOrig"></div>
<script id="source" language="javascript" type="text/javascript">
$(function () {
//<div id="flotOrig" style="width: 800;height: 400;"></div>
var d1 = [[20,20], [42,60], [54, 20], [80,80]];
var options = { series: {
......@@ -14,6 +14,5 @@ $(function () {
yaxis: { min:10, max: 90}
};
$.plot($("#flotOrig"), [{data: d1, curvedLines: { show: true, lineWidth: 3}}, {data: d1, points: { show: true }}], options);
$.plot($("#flotOrig"), [{data: d1, lines: { show: true, lineWidth: 3}, curvedLines: {apply:true}}, {data: d1, points: { show: true }}], options);
});
\ No newline at end of file
</script>
\ No newline at end of file
<div id="fillAndMultiAxis"></div>
<script id="source" language="javascript" type="text/javascript">
$(function () {
//<div id="fillAndMultiAxis" style="width: 800;height: 400;"></div>
var d1 = [[20,20], [42,60], [54, 20], [80,80]];
var d2 = [[20,700], [80,300]];
......@@ -17,8 +16,7 @@
$.plot($("#fillAndMultiAxis"),
[
{data: d1, curvedLines: { show: true, fill: true, fillColor: "#C3C3C3", lineWidth: 3}}, {data: d1, points: { show: true }},
{data: d2, curvedLines: { show: true, lineWidth: 3}, yaxis:2}, {data: d2, points: { show: true }, yaxis:2}
{data: d1, lines: { show: true, fill: true, fillColor: "#C3C3C3", lineWidth: 3}, curvedLines: {apply:true}}, {data: d1, points: { show: true }},
{data: d2, lines: { show: true, lineWidth: 3}, curvedLines: {apply:true}, yaxis:2}, {data: d2, points: { show: true }, yaxis:2}
], options);
});
\ No newline at end of file
</script>
\ No newline at end of file
<div id="flotFit"></div>
<script id="source" language="javascript" type="text/javascript">
$(function () {
//<div id="flotFit" style="width: 800;height: 400;"></div>
var d1 = [[20,20], [42,60], [54, 30], [80,80]];
var options = { series: {
......@@ -13,6 +13,5 @@ $(function () {
yaxis: { min:10, max: 90}
};
$.plot($("#flotFit"), [{data: d1, curvedLines: { show: true, fit: true, fitPointDist: 0.000001, lineWidth: 3}}, {data: d1, points: { show: true }}], options);
$.plot($("#flotFit"), [{data: d1, lines: { show: true, lineWidth: 3}, curvedLines: {apply:true, fit: true, fitPointDist: 0.000001}}, {data: d1, points: { show: true }}], options);
});
\ No newline at end of file
</script>
\ No newline at end of file
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