211 lines
6.7 KiB
JavaScript
211 lines
6.7 KiB
JavaScript
/** Pie **/
|
|
/**
|
|
* Formats the pies labels.
|
|
* @param {Object} slice - Slice object
|
|
* @return {String} Formatted pie label string
|
|
*/
|
|
(function () {
|
|
|
|
var
|
|
_ = Flotr._;
|
|
|
|
Flotr.defaultPieLabelFormatter = function (total, value) {
|
|
return (100 * value / total).toFixed(2)+'%';
|
|
};
|
|
|
|
Flotr.addType('pie', {
|
|
options: {
|
|
show: false, // => setting to true will show bars, false will hide
|
|
lineWidth: 1, // => in pixels
|
|
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
|
fillColor: null, // => fill color
|
|
fillOpacity: 0.6, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
|
explode: 6, // => the number of pixels the splices will be far from the center
|
|
sizeRatio: 0.6, // => the size ratio of the pie relative to the plot
|
|
startAngle: Math.PI/4, // => the first slice start angle
|
|
labelFormatter: Flotr.defaultPieLabelFormatter,
|
|
pie3D: false, // => whether to draw the pie in 3 dimenstions or not (ineffective)
|
|
pie3DviewAngle: (Math.PI/2 * 0.8),
|
|
pie3DspliceThickness: 20
|
|
},
|
|
|
|
draw : function (options) {
|
|
|
|
// TODO 3D charts what?
|
|
|
|
var
|
|
data = options.data,
|
|
context = options.context,
|
|
canvas = context.canvas,
|
|
lineWidth = options.lineWidth,
|
|
shadowSize = options.shadowSize,
|
|
sizeRatio = options.sizeRatio,
|
|
height = options.height,
|
|
width = options.width,
|
|
explode = options.explode,
|
|
color = options.color,
|
|
fill = options.fill,
|
|
fillStyle = options.fillStyle,
|
|
radius = Math.min(canvas.width, canvas.height) * sizeRatio / 2,
|
|
value = data[0][1],
|
|
html = [],
|
|
vScale = 1,//Math.cos(series.pie.viewAngle);
|
|
measure = Math.PI * 2 * value / this.total,
|
|
startAngle = this.startAngle || (2 * Math.PI * options.startAngle), // TODO: this initial startAngle is already in radians (fixing will be test-unstable)
|
|
endAngle = startAngle + measure,
|
|
bisection = startAngle + measure / 2,
|
|
label = options.labelFormatter(this.total, value),
|
|
//plotTickness = Math.sin(series.pie.viewAngle)*series.pie.spliceThickness / vScale;
|
|
explodeCoeff = explode + radius + 4,
|
|
distX = Math.cos(bisection) * explodeCoeff,
|
|
distY = Math.sin(bisection) * explodeCoeff,
|
|
textAlign = distX < 0 ? 'right' : 'left',
|
|
textBaseline = distY > 0 ? 'top' : 'bottom',
|
|
style,
|
|
x, y,
|
|
distX, distY;
|
|
|
|
context.save();
|
|
context.translate(width / 2, height / 2);
|
|
context.scale(1, vScale);
|
|
|
|
x = Math.cos(bisection) * explode;
|
|
y = Math.sin(bisection) * explode;
|
|
|
|
// Shadows
|
|
if (shadowSize > 0) {
|
|
this.plotSlice(x + shadowSize, y + shadowSize, radius, startAngle, endAngle, context);
|
|
if (fill) {
|
|
context.fillStyle = 'rgba(0,0,0,0.1)';
|
|
context.fill();
|
|
}
|
|
}
|
|
|
|
this.plotSlice(x, y, radius, startAngle, endAngle, context);
|
|
if (fill) {
|
|
context.fillStyle = fillStyle;
|
|
context.fill();
|
|
}
|
|
context.lineWidth = lineWidth;
|
|
context.strokeStyle = color;
|
|
context.stroke();
|
|
|
|
style = {
|
|
size : options.fontSize * 1.2,
|
|
color : options.fontColor,
|
|
weight : 1.5
|
|
};
|
|
|
|
if (label) {
|
|
if (options.htmlText || !options.textEnabled) {
|
|
divStyle = 'position:absolute;' + textBaseline + ':' + (height / 2 + (textBaseline === 'top' ? distY : -distY)) + 'px;';
|
|
divStyle += textAlign + ':' + (width / 2 + (textAlign === 'right' ? -distX : distX)) + 'px;';
|
|
html.push('<div style="', divStyle, '" class="flotr-grid-label">', label, '</div>');
|
|
}
|
|
else {
|
|
style.textAlign = textAlign;
|
|
style.textBaseline = textBaseline;
|
|
Flotr.drawText(context, label, distX, distY, style);
|
|
}
|
|
}
|
|
|
|
if (options.htmlText || !options.textEnabled) {
|
|
var div = Flotr.DOM.node('<div style="color:' + options.fontColor + '" class="flotr-labels"></div>');
|
|
Flotr.DOM.insert(div, html.join(''));
|
|
Flotr.DOM.insert(options.element, div);
|
|
}
|
|
|
|
context.restore();
|
|
|
|
// New start angle
|
|
this.startAngle = endAngle;
|
|
this.slices = this.slices || [];
|
|
this.slices.push({
|
|
radius : Math.min(canvas.width, canvas.height) * sizeRatio / 2,
|
|
x : x,
|
|
y : y,
|
|
explode : explode,
|
|
start : startAngle,
|
|
end : endAngle
|
|
});
|
|
},
|
|
plotSlice : function (x, y, radius, startAngle, endAngle, context) {
|
|
context.beginPath();
|
|
context.moveTo(x, y);
|
|
context.arc(x, y, radius, startAngle, endAngle, false);
|
|
context.lineTo(x, y);
|
|
context.closePath();
|
|
},
|
|
hit : function (options) {
|
|
|
|
var
|
|
data = options.data[0],
|
|
args = options.args,
|
|
index = options.index,
|
|
mouse = args[0],
|
|
n = args[1],
|
|
slice = this.slices[index],
|
|
x = mouse.relX - options.width / 2,
|
|
y = mouse.relY - options.height / 2,
|
|
r = Math.sqrt(x * x + y * y),
|
|
theta = Math.atan(y / x),
|
|
circle = Math.PI * 2,
|
|
explode = slice.explode || options.explode,
|
|
start = slice.start % circle,
|
|
end = slice.end % circle;
|
|
|
|
if (x < 0) {
|
|
theta += Math.PI;
|
|
} else if (x > 0 && y < 0) {
|
|
theta += circle;
|
|
}
|
|
|
|
if (r < slice.radius + explode && r > explode) {
|
|
if ((start > end && (theta < end || theta > start)) ||
|
|
(theta > start && theta < end)) {
|
|
|
|
// TODO Decouple this from hit plugin (chart shouldn't know what n means)
|
|
n.x = data[0];
|
|
n.y = data[1];
|
|
n.sAngle = start;
|
|
n.eAngle = end;
|
|
n.index = 0;
|
|
n.seriesIndex = index;
|
|
n.fraction = data[1] / this.total;
|
|
}
|
|
}
|
|
},
|
|
drawHit: function (options) {
|
|
var
|
|
context = options.context,
|
|
slice = this.slices[options.args.seriesIndex];
|
|
|
|
context.save();
|
|
context.translate(options.width / 2, options.height / 2);
|
|
this.plotSlice(slice.x, slice.y, slice.radius, slice.start, slice.end, context);
|
|
context.stroke();
|
|
context.restore();
|
|
},
|
|
clearHit : function (options) {
|
|
var
|
|
context = options.context,
|
|
slice = this.slices[options.args.seriesIndex],
|
|
padding = 2 * options.lineWidth,
|
|
radius = slice.radius + padding;
|
|
|
|
context.save();
|
|
context.translate(options.width / 2, options.height / 2);
|
|
context.clearRect(
|
|
slice.x - radius,
|
|
slice.y - radius,
|
|
2 * radius + padding,
|
|
2 * radius + padding
|
|
);
|
|
context.restore();
|
|
},
|
|
extendYRange : function (axis, data) {
|
|
this.total = (this.total || 0) + data[0][1];
|
|
}
|
|
});
|
|
})();
|