You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
663 lines
24 KiB
663 lines
24 KiB
2 years ago
|
/**
|
||
|
## jquery.flot.drawSeries.js
|
||
|
|
||
|
This plugin is used by flot for drawing lines, plots, bars or area.
|
||
|
|
||
|
### Public methods
|
||
|
*/
|
||
|
|
||
|
(function($) {
|
||
|
"use strict";
|
||
|
|
||
|
function DrawSeries() {
|
||
|
function plotLine(datapoints, xoffset, yoffset, axisx, axisy, ctx, steps) {
|
||
|
var points = datapoints.points,
|
||
|
ps = datapoints.pointsize,
|
||
|
prevx = null,
|
||
|
prevy = null;
|
||
|
var x1 = 0.0,
|
||
|
y1 = 0.0,
|
||
|
x2 = 0.0,
|
||
|
y2 = 0.0,
|
||
|
mx = null,
|
||
|
my = null,
|
||
|
i = 0;
|
||
|
|
||
|
ctx.beginPath();
|
||
|
for (i = ps; i < points.length; i += ps) {
|
||
|
x1 = points[i - ps];
|
||
|
y1 = points[i - ps + 1];
|
||
|
x2 = points[i];
|
||
|
y2 = points[i + 1];
|
||
|
|
||
|
if (x1 === null || x2 === null) {
|
||
|
mx = null;
|
||
|
my = null;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (isNaN(x1) || isNaN(x2) || isNaN(y1) || isNaN(y2)) {
|
||
|
prevx = null;
|
||
|
prevy = null;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (steps) {
|
||
|
if (mx !== null && my !== null) {
|
||
|
// if middle point exists, transfer p2 -> p1 and p1 -> mp
|
||
|
x2 = x1;
|
||
|
y2 = y1;
|
||
|
x1 = mx;
|
||
|
y1 = my;
|
||
|
|
||
|
// 'remove' middle point
|
||
|
mx = null;
|
||
|
my = null;
|
||
|
|
||
|
// subtract pointsize from i to have current point p1 handled again
|
||
|
i -= ps;
|
||
|
} else if (y1 !== y2 && x1 !== x2) {
|
||
|
// create a middle point
|
||
|
y2 = y1;
|
||
|
mx = x2;
|
||
|
my = y1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clip with ymin
|
||
|
if (y1 <= y2 && y1 < axisy.min) {
|
||
|
if (y2 < axisy.min) {
|
||
|
// line segment is outside
|
||
|
continue;
|
||
|
}
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// 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.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
|
||
|
}
|
||
|
|
||
|
prevx = x2;
|
||
|
prevy = y2;
|
||
|
ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
|
||
|
}
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
|
||
|
function plotLineArea(datapoints, axisx, axisy, fillTowards, ctx, steps) {
|
||
|
var points = datapoints.points,
|
||
|
ps = datapoints.pointsize,
|
||
|
bottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min,
|
||
|
i = 0,
|
||
|
ypos = 1,
|
||
|
areaOpen = false,
|
||
|
segmentStart = 0,
|
||
|
segmentEnd = 0,
|
||
|
mx = null,
|
||
|
my = null;
|
||
|
|
||
|
// we process each segment in two turns, first forward
|
||
|
// direction to sketch out top, then once we hit the
|
||
|
// end we go backwards to sketch the bottom
|
||
|
while (true) {
|
||
|
if (ps > 0 && i > points.length + ps) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
i += ps; // ps is negative if going backwards
|
||
|
|
||
|
var x1 = points[i - ps],
|
||
|
y1 = points[i - ps + ypos],
|
||
|
x2 = points[i],
|
||
|
y2 = points[i + ypos];
|
||
|
|
||
|
if (ps === -2) {
|
||
|
/* going backwards and no value for the bottom provided in the series*/
|
||
|
y1 = y2 = bottom;
|
||
|
}
|
||
|
|
||
|
if (areaOpen) {
|
||
|
if (ps > 0 && x1 != null && x2 == null) {
|
||
|
// at turning point
|
||
|
segmentEnd = i;
|
||
|
ps = -ps;
|
||
|
ypos = 2;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (ps < 0 && i === segmentStart + ps) {
|
||
|
// done with the reverse sweep
|
||
|
ctx.fill();
|
||
|
areaOpen = false;
|
||
|
ps = -ps;
|
||
|
ypos = 1;
|
||
|
i = segmentStart = segmentEnd + ps;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (x1 == null || x2 == null) {
|
||
|
mx = null;
|
||
|
my = null;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (steps) {
|
||
|
if (mx !== null && my !== null) {
|
||
|
// if middle point exists, transfer p2 -> p1 and p1 -> mp
|
||
|
x2 = x1;
|
||
|
y2 = y1;
|
||
|
x1 = mx;
|
||
|
y1 = my;
|
||
|
|
||
|
// 'remove' middle point
|
||
|
mx = null;
|
||
|
my = null;
|
||
|
|
||
|
// subtract pointsize from i to have current point p1 handled again
|
||
|
i -= ps;
|
||
|
} else if (y1 !== y2 && x1 !== x2) {
|
||
|
// create a middle point
|
||
|
y2 = y1;
|
||
|
mx = x2;
|
||
|
my = y1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clip x values
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// 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 (!areaOpen) {
|
||
|
// open area
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
|
||
|
areaOpen = true;
|
||
|
}
|
||
|
|
||
|
// now first check the case where both is outside
|
||
|
if (y1 >= axisy.max && y2 >= axisy.max) {
|
||
|
ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
|
||
|
ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
|
||
|
continue;
|
||
|
} else if (y1 <= axisy.min && y2 <= axisy.min) {
|
||
|
ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
|
||
|
ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// else it's a bit more complicated, there might
|
||
|
// be a flat maxed out rectangle first, then a
|
||
|
// triangular cutout or reverse; to find these
|
||
|
// keep track of the current x values
|
||
|
var x1old = x1,
|
||
|
x2old = x2;
|
||
|
|
||
|
// clip the y values, without shortcutting, we
|
||
|
// go through all cases in turn
|
||
|
|
||
|
// clip with ymin
|
||
|
if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
|
||
|
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||
|
y1 = axisy.min;
|
||
|
} else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
|
||
|
x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||
|
y2 = axisy.min;
|
||
|
}
|
||
|
|
||
|
// clip with ymax
|
||
|
if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
|
||
|
x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||
|
y1 = axisy.max;
|
||
|
} else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
|
||
|
x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||
|
y2 = axisy.max;
|
||
|
}
|
||
|
|
||
|
// if the x value was changed we got a rectangle
|
||
|
// to fill
|
||
|
if (x1 !== x1old) {
|
||
|
ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
|
||
|
// it goes to (x1, y1), but we fill that below
|
||
|
}
|
||
|
|
||
|
// fill triangular section, this sometimes result
|
||
|
// in redundant points if (x1, y1) hasn't changed
|
||
|
// from previous line to, but we just ignore that
|
||
|
ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
|
||
|
ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
|
||
|
|
||
|
// fill the other rectangle if it's there
|
||
|
if (x2 !== x2old) {
|
||
|
ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
|
||
|
ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
- drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
|
||
|
|
||
|
This function is used for drawing lines or area fill. In case the series has line decimation function
|
||
|
attached, before starting to draw, as an optimization the points will first be decimated.
|
||
|
|
||
|
The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
|
||
|
plotHeight are the corresponding parameters of flot used to determine the drawing surface.
|
||
|
The function getColorOrGradient is used to compute the fill style of lines and area.
|
||
|
*/
|
||
|
function drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
|
||
|
ctx.save();
|
||
|
ctx.translate(plotOffset.left, plotOffset.top);
|
||
|
ctx.lineJoin = "round";
|
||
|
|
||
|
if (series.lines.dashes && ctx.setLineDash) {
|
||
|
ctx.setLineDash(series.lines.dashes);
|
||
|
}
|
||
|
|
||
|
var datapoints = {
|
||
|
format: series.datapoints.format,
|
||
|
points: series.datapoints.points,
|
||
|
pointsize: series.datapoints.pointsize
|
||
|
};
|
||
|
|
||
|
if (series.decimate) {
|
||
|
datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight);
|
||
|
}
|
||
|
|
||
|
var lw = series.lines.lineWidth;
|
||
|
|
||
|
ctx.lineWidth = lw;
|
||
|
ctx.strokeStyle = series.color;
|
||
|
var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight, getColorOrGradient);
|
||
|
if (fillStyle) {
|
||
|
ctx.fillStyle = fillStyle;
|
||
|
plotLineArea(datapoints, series.xaxis, series.yaxis, series.lines.fillTowards || 0, ctx, series.lines.steps);
|
||
|
}
|
||
|
|
||
|
if (lw > 0) {
|
||
|
plotLine(datapoints, 0, 0, series.xaxis, series.yaxis, ctx, series.lines.steps);
|
||
|
}
|
||
|
|
||
|
ctx.restore();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
- drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
|
||
|
|
||
|
This function is used for drawing points using a given symbol. In case the series has points decimation
|
||
|
function attached, before starting to draw, as an optimization the points will first be decimated.
|
||
|
|
||
|
The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
|
||
|
plotHeight are the corresponding parameters of flot used to determine the drawing surface.
|
||
|
The function drawSymbol is used to compute and draw the symbol chosen for the points.
|
||
|
*/
|
||
|
function drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
|
||
|
function drawCircle(ctx, x, y, radius, shadow, fill) {
|
||
|
ctx.moveTo(x + radius, y);
|
||
|
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
|
||
|
}
|
||
|
drawCircle.fill = true;
|
||
|
function plotPoints(datapoints, radius, fill, offset, shadow, axisx, axisy, drawSymbolFn) {
|
||
|
var points = datapoints.points,
|
||
|
ps = datapoints.pointsize;
|
||
|
|
||
|
ctx.beginPath();
|
||
|
for (var i = 0; i < points.length; i += ps) {
|
||
|
var x = points[i],
|
||
|
y = points[i + 1];
|
||
|
if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
x = axisx.p2c(x);
|
||
|
y = axisy.p2c(y) + offset;
|
||
|
|
||
|
drawSymbolFn(ctx, x, y, radius, shadow, fill);
|
||
|
}
|
||
|
if (drawSymbolFn.fill && !shadow) {
|
||
|
ctx.fill();
|
||
|
}
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
|
||
|
ctx.save();
|
||
|
ctx.translate(plotOffset.left, plotOffset.top);
|
||
|
|
||
|
var datapoints = {
|
||
|
format: series.datapoints.format,
|
||
|
points: series.datapoints.points,
|
||
|
pointsize: series.datapoints.pointsize
|
||
|
};
|
||
|
|
||
|
if (series.decimatePoints) {
|
||
|
datapoints.points = series.decimatePoints(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight);
|
||
|
}
|
||
|
|
||
|
var lw = series.points.lineWidth,
|
||
|
radius = series.points.radius,
|
||
|
symbol = series.points.symbol,
|
||
|
drawSymbolFn;
|
||
|
|
||
|
if (symbol === 'circle') {
|
||
|
drawSymbolFn = drawCircle;
|
||
|
} else if (typeof symbol === 'string' && drawSymbol && drawSymbol[symbol]) {
|
||
|
drawSymbolFn = drawSymbol[symbol];
|
||
|
} else if (typeof drawSymbol === 'function') {
|
||
|
drawSymbolFn = drawSymbol;
|
||
|
}
|
||
|
|
||
|
// If the user sets the line width to 0, we change it to a very
|
||
|
// small value. A line width of 0 seems to force the default of 1.
|
||
|
|
||
|
if (lw === 0) {
|
||
|
lw = 0.0001;
|
||
|
}
|
||
|
|
||
|
ctx.lineWidth = lw;
|
||
|
ctx.fillStyle = getFillStyle(series.points, series.color, null, null, getColorOrGradient);
|
||
|
ctx.strokeStyle = series.color;
|
||
|
plotPoints(datapoints, radius,
|
||
|
true, 0, false,
|
||
|
series.xaxis, series.yaxis, drawSymbolFn);
|
||
|
ctx.restore();
|
||
|
}
|
||
|
|
||
|
function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
|
||
|
var left = x + barLeft,
|
||
|
right = x + barRight,
|
||
|
bottom = b, top = y,
|
||
|
drawLeft, drawRight, drawTop, drawBottom = false,
|
||
|
tmp;
|
||
|
|
||
|
drawLeft = drawRight = drawTop = true;
|
||
|
|
||
|
// in horizontal mode, we start the bar from the left
|
||
|
// instead of from the bottom so it appears to be
|
||
|
// horizontal rather than vertical
|
||
|
if (horizontal) {
|
||
|
drawBottom = drawRight = drawTop = true;
|
||
|
drawLeft = false;
|
||
|
left = b;
|
||
|
right = x;
|
||
|
top = y + barLeft;
|
||
|
bottom = y + barRight;
|
||
|
|
||
|
// account for negative bars
|
||
|
if (right < left) {
|
||
|
tmp = right;
|
||
|
right = left;
|
||
|
left = tmp;
|
||
|
drawLeft = true;
|
||
|
drawRight = false;
|
||
|
}
|
||
|
} else {
|
||
|
drawLeft = drawRight = drawTop = true;
|
||
|
drawBottom = false;
|
||
|
left = x + barLeft;
|
||
|
right = x + barRight;
|
||
|
bottom = b;
|
||
|
top = y;
|
||
|
|
||
|
// account for negative bars
|
||
|
if (top < bottom) {
|
||
|
tmp = top;
|
||
|
top = bottom;
|
||
|
bottom = tmp;
|
||
|
drawBottom = true;
|
||
|
drawTop = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clip
|
||
|
if (right < axisx.min || left > axisx.max ||
|
||
|
top < axisy.min || bottom > axisy.max) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (left < axisx.min) {
|
||
|
left = axisx.min;
|
||
|
drawLeft = false;
|
||
|
}
|
||
|
|
||
|
if (right > axisx.max) {
|
||
|
right = axisx.max;
|
||
|
drawRight = false;
|
||
|
}
|
||
|
|
||
|
if (bottom < axisy.min) {
|
||
|
bottom = axisy.min;
|
||
|
drawBottom = false;
|
||
|
}
|
||
|
|
||
|
if (top > axisy.max) {
|
||
|
top = axisy.max;
|
||
|
drawTop = false;
|
||
|
}
|
||
|
|
||
|
left = axisx.p2c(left);
|
||
|
bottom = axisy.p2c(bottom);
|
||
|
right = axisx.p2c(right);
|
||
|
top = axisy.p2c(top);
|
||
|
|
||
|
// fill the bar
|
||
|
if (fillStyleCallback) {
|
||
|
c.fillStyle = fillStyleCallback(bottom, top);
|
||
|
c.fillRect(left, top, right - left, bottom - top)
|
||
|
}
|
||
|
|
||
|
// draw outline
|
||
|
if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
|
||
|
c.beginPath();
|
||
|
|
||
|
// FIXME: inline moveTo is buggy with excanvas
|
||
|
c.moveTo(left, bottom);
|
||
|
if (drawLeft) {
|
||
|
c.lineTo(left, top);
|
||
|
} else {
|
||
|
c.moveTo(left, top);
|
||
|
}
|
||
|
|
||
|
if (drawTop) {
|
||
|
c.lineTo(right, top);
|
||
|
} else {
|
||
|
c.moveTo(right, top);
|
||
|
}
|
||
|
|
||
|
if (drawRight) {
|
||
|
c.lineTo(right, bottom);
|
||
|
} else {
|
||
|
c.moveTo(right, bottom);
|
||
|
}
|
||
|
|
||
|
if (drawBottom) {
|
||
|
c.lineTo(left, bottom);
|
||
|
} else {
|
||
|
c.moveTo(left, bottom);
|
||
|
}
|
||
|
|
||
|
c.stroke();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
- drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
|
||
|
|
||
|
This function is used for drawing series represented as bars. In case the series has decimation
|
||
|
function attached, before starting to draw, as an optimization the points will first be decimated.
|
||
|
|
||
|
The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
|
||
|
plotHeight are the corresponding parameters of flot used to determine the drawing surface.
|
||
|
The function getColorOrGradient is used to compute the fill style of bars.
|
||
|
*/
|
||
|
function drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
|
||
|
function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
|
||
|
var points = datapoints.points,
|
||
|
ps = datapoints.pointsize,
|
||
|
fillTowards = series.bars.fillTowards || 0,
|
||
|
defaultBottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min;
|
||
|
|
||
|
for (var i = 0; i < points.length; i += ps) {
|
||
|
if (points[i] == null) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Use third point as bottom if pointsize is 3
|
||
|
var bottom = ps === 3 ? points[i + 2] : defaultBottom;
|
||
|
drawBar(points[i], points[i + 1], bottom, barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ctx.save();
|
||
|
ctx.translate(plotOffset.left, plotOffset.top);
|
||
|
|
||
|
var datapoints = {
|
||
|
format: series.datapoints.format,
|
||
|
points: series.datapoints.points,
|
||
|
pointsize: series.datapoints.pointsize
|
||
|
};
|
||
|
|
||
|
if (series.decimate) {
|
||
|
datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth);
|
||
|
}
|
||
|
|
||
|
ctx.lineWidth = series.bars.lineWidth;
|
||
|
ctx.strokeStyle = series.color;
|
||
|
|
||
|
var barLeft;
|
||
|
var barWidth = series.bars.barWidth[0] || series.bars.barWidth;
|
||
|
switch (series.bars.align) {
|
||
|
case "left":
|
||
|
barLeft = 0;
|
||
|
break;
|
||
|
case "right":
|
||
|
barLeft = -barWidth;
|
||
|
break;
|
||
|
default:
|
||
|
barLeft = -barWidth / 2;
|
||
|
}
|
||
|
|
||
|
var fillStyleCallback = series.bars.fill ? function(bottom, top) {
|
||
|
return getFillStyle(series.bars, series.color, bottom, top, getColorOrGradient);
|
||
|
} : null;
|
||
|
|
||
|
plotBars(datapoints, barLeft, barLeft + barWidth, fillStyleCallback, series.xaxis, series.yaxis);
|
||
|
ctx.restore();
|
||
|
}
|
||
|
|
||
|
function getFillStyle(filloptions, seriesColor, bottom, top, getColorOrGradient) {
|
||
|
var fill = filloptions.fill;
|
||
|
if (!fill) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (filloptions.fillColor) {
|
||
|
return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
|
||
|
}
|
||
|
|
||
|
var c = $.color.parse(seriesColor);
|
||
|
c.a = typeof fill === "number" ? fill : 0.4;
|
||
|
c.normalize();
|
||
|
return c.toString();
|
||
|
}
|
||
|
|
||
|
this.drawSeriesLines = drawSeriesLines;
|
||
|
this.drawSeriesPoints = drawSeriesPoints;
|
||
|
this.drawSeriesBars = drawSeriesBars;
|
||
|
this.drawBar = drawBar;
|
||
|
};
|
||
|
|
||
|
$.plot.drawSeries = new DrawSeries();
|
||
|
})(jQuery);
|