Sorry for my english. I don't speak it well at all.
I'm designing a chart library for practicing with canvas. I send it a json with the data to draw and the config options. I'm inserting that json below:
function drawChart() {
let canvas = document.querySelector("#canvas")
var chart = new Chartest(canvas, {
chart: {
type: Chartest.Line,
style: {
background: "#fff",
barBackground: "#456"
},
title: "Unidades"
},
data: [{
val: 10600,
label: "Unidad 1"
}, {
val: 6000,
label: "Segunda Unidad"
}, {
val: 4000,
label: "Unidad numero tres"
}, {
val: 4005,
label: "u4"
}, {
val: 0,
label: "u5"
}, {
val: 50,
label: "u6"
}, {
val: 3400,
label: "u7"
}]
})
}
I draw a line from the current point to the predecessor point and so. Everything works fine except when I send a point with value 0. Then the line with the predecessor point doesn't draw. I'm logging the drawing point and everything seems to be correctly. Here you have the logged info and the final result where you can see, It's a missing line and I can't understand why:
Img: Logged info and final result
I'm inserting the part of my code when I draw the lines below:
for (let i = 0; i < this.config.data.length; i++) {
// line type
ctx.beginPath()
let scaledPointVal = this.config.data[i].val * scaledUnitVal
let gradient = ctx.createLinearGradient(0, absoluteGridTop - scaledPointVal, 0, absoluteGridTop)
gradient.addColorStop(0, 'rgba(10, 20, 200, .85)')
gradient.addColorStop(.7, 'rgba(10, 20, 200, .7)')
ctx.strokeStyle = gradient
ctx.lineWidth = 2
ctx.closePath()
let x = scaleSpaceWidth + spaceForEachOne * i
if (i != 0
&& !isNaN(parseFloat(this.config.data[i - 1].val)) && isFinite(this.config.data[i - 1].val)
&& !isNaN(parseFloat(this.config.data[i].val)) && isFinite(this.config.data[i].val)) {
// last and current point available
// draw current point
let pointYPos = headerHeight + chartContainerHeight - scaledPointVal
ctx.beginPath()
ctx.rect(x + spaceForEachOne / 2 - 2, pointYPos - 2, 4, 4)
ctx.fill()
/////////////////////////////////////////////////
// line from last point to current
/////////////////////////////////////////////////
ctx.closePath()
ctx.beginPath()
ctx.moveTo(this.fixPixelPos(x + spaceForEachOne / 2), this.fixPixelPos(pointYPos))
ctx.lineTo(this.fixPixelPos(x - spaceForEachOne / 2),
this.fixPixelPos(headerHeight + chartContainerHeight - this.config.data[i - 1].val * scaledUnitVal))
console.log(`from ${this.fixPixelPos(x + spaceForEachOne / 2)}, ${this.fixPixelPos(pointYPos)}. to ${this.fixPixelPos(x - spaceForEachOne / 2)}, ${this.fixPixelPos(headerHeight + chartContainerHeight - this.config.data[i - 1].val * scaledUnitVal)}`);
ctx.stroke()
ctx.closePath()
//////////////////////////////////////////////////
//////////////////////////////////////////////////
}
else if (!isNaN(parseFloat(this.config.data[i].val)) && isFinite(this.config.data[i].val)) {
// first point or last not valid or empty. just draw current point
let pointYPos = headerHeight + chartContainerHeight - scaledPointVal
ctx.beginPath()
ctx.rect(x + spaceForEachOne / 2 - 2, pointYPos - 2, 4,
4)
ctx.fill()
ctx.closePath()
}
ctx.beginPath()
ctx.font = "12.5px Barlow"
ctx.textAlign = "center"
let text = this.config.data[i].label
while (ctx.measureText(text).width > spaceForEachOne - 15)
text = text.slice(0, text.length - 1)
if (text.length < this.config.data[i].label.length)
text += "..."
ctx.fillText(text, x + spaceForEachOne / 2, headerHeight +
chartContainerHeight + 20)
}
Please help me.
Related
I have a custom doughnut ChartJS (with rounded edges), but cannot find the right way to:
make it work under version 2.6.0 (it works just fine under ChartJS 2.0.2, but not under 2.6.0)
add a static red circle under the green with same radius, but with half the lineWidth (like an axis on which the green circle plots) - like this
Here is the Plunker
Chart.defaults.RoundedDoughnut = Chart.helpers.clone(Chart.defaults.doughnut);
Chart.controllers.RoundedDoughnut = Chart.controllers.doughnut.extend({
draw: function (ease) {
var ctx = this.chart.chart.ctx;
var easingDecimal = ease || 1;
Chart.helpers.each(this.getDataset().metaData, function (arc, index) {
arc.transition(easingDecimal).draw();
var vm = arc._view;
var radius = (vm.outerRadius + vm.innerRadius) / 2;
var thickness = (vm.outerRadius - vm.innerRadius) / 2;
var angle = Math.PI - vm.endAngle - Math.PI / 2;
ctx.save();
ctx.fillStyle = vm.backgroundColor;
ctx.translate(vm.x, vm.y);
ctx.beginPath();
ctx.arc(radius * Math.sin(angle), radius * Math.cos(angle), thickness, 0, 2 * Math.PI);
ctx.arc(radius * Math.sin(Math.PI), radius * Math.cos(Math.PI), thickness, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();
});
},
});
var deliveredData = {
labels: [
"Value"
],
datasets: [
{
data: [85, 15],
backgroundColor: [
"#3ec556",
"rgba(0,0,0,0)"
],
borderWidth: [
0, 0
]
}]
};
var deliveredOpt = {
cutoutPercentage: 88,
animation: {
animationRotate: true,
duration: 3000
},
legend: {
display: false
},
tooltips: {
enabled: false
}
};
var chart = new Chart($('#myChart'), {
type: 'RoundedDoughnut',
data: deliveredData,
options: deliveredOpt
});
For question #1 the issue is with the this.getDataset().metaData inside the draw function. It is returning undefined and so the function under Chart.helpers.each doesn't execute
Try this.getMeta().data instead.
EDIT:
For question #2, you can refer to this other Stack Overflow question:
Charts.js: thin donut chart background
I have a grid with items inside of it with x and y co-orditantes. I am trying to write a function (with lodash) to determine where is the first empty spot where the top most left most spot is the first position.
I am trying to do this by iterating over each spot until I find the first empty spot. It is only a 2 column layout so I work through them in a pattern like so - x: 0, y:0 -> x:1, y:0 -> x:0, y:1 -> x:1, y:1 ... and then checking all the items along the way to see if there is not a match, so I then know if there is an opening. My attempt looks like so :
function fillEmptySpace(isFilled, startX, startY) {
if (!isFilled) {
_.forEach(items, function(item, i) {
if (!_.isMatch(item, {
'x': startX
}) && !_.isMatch(item, {
'y': startY
})
) {
console.log("empty spot at", startX, startY);
isFilled = true;
} else if (!_.isMatch(item, {
'x': startX + 1
}) && !_.isMatch(item, {
'y': startY
})) {
console.log("empty spot at", startX + 1, startY);
isFilled = true;
}
});
startY += 1;
fillEmptySpace(isFilled, startX, startY);
}
}
fillEmptySpace(false, 0, 0);
The data looks like so :
var items = [{
i: 'a',
x: 0,
y: 0,
w: 1,
h: 1,
maxW: 2
}, {
i: 'b',
x: 1,
y: 4,
w: 1,
h: 1,
maxW: 2
}, {
i: 'c',
x: 0,
y: 1,
w: 1,
h: 1,
maxW: 2
}, {
i: 'd',
x: 0,
y: 2,
w: 1,
h: 1,
maxW: 2
}];
And here is the fiddle I have been fooling around in : https://jsfiddle.net/alexjm/ugpy13xd/38/
I can't seem to get this logic quite right, I am not sure a this point where I am getting it wrong. Any input would be greatly appreciated!
Just as a note : with the provided data it should identify the first empty space as x:1, y:0, however right now it is saying empty spot at 0 0, which cannot be correct. Thanks!
When it comes to 2D arrays, the 1D index can be calculated with x + y * width. If we then sort the 1D indexes, we can create an O(nlogn) solution:
function findEmptySpace(grid, width) {
var index = _(grid)
.map(function(p) { return p.x + p.y * width })
.sortBy()
.findIndex(_.negate(_.eq));
if (index < 0) index = grid.length;
return {
x: index % width,
y: index / width >> 0 // ">> 0" has the same result as "Math.floor"
};
}
var items = [{x:0,y:0},{x:0,y:4},{x:0,y:1},{x:0,y:2}];
function findEmptySpace(grid, width) {
var index = _(grid)
.map(function(p) { return p.x + p.y * width; })
.sortBy()
.findIndex(_.negate(_.eq));
if (index < 0) index = grid.length;
return {
x: index % width,
y: index / width >> 0 // ">> 0" has the same result as "Math.floor"
};
}
document.getElementById('btn').onclick = function() {
var space = findEmptySpace(items, 2);
items.push(space);
console.log(space);
};
#btn { font-size: 14pt }
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<button id="btn">Fill the Empty Space</button>
If you pre-sort the array, the solution would be worst-case O(n).
May I suggest checking to see if the point exists vs checking to see if it doesn't. Iterate over each item in the list to see if it exists if it does set a flag, then increment positions through your grid. Keep in mind this will not account for coords less than your intial value of "startY". Consider the following code:
function findEmptySpace(startX, startY) {
var isFilled = false;
_.forEach(items, function(item, i) {
if (_.isMatch(item, { 'x': startX }) && _.isMatch(item, { 'y': startY }) {
// this spot is filled check next x
isFilled = true;
continue;
}
}
if (isFilled == true) {
// we need to recursively call our function but I don't know the value of x
(startX == 0) ? findEmptySpace(1, startY): findEmptySpace(0, startY + 1);
} else {
console.log("Congrats, we found a spot", startX, startY);
}
}
It looks like you're always going to find a match at 0,0 since your logic is finding if there is any item in the list that is not on 0,0, instead of if there is an item in the list on 0,0
What you really want to do is stop checking once you've found an item in the current x,y (and, additionally, check both x and y in your isMatch). You can use your existing routine and your existing isFilled check:
function fillEmptySpace(isFilled, startX, startY) {
_.forEach(items, function(item, i) {
if (!isFilled) {
if (!_.isMatch(item, {'x': startX, 'y': startY})) {
console.log("empty spot at", startX, startY);
isFilled = true;
} else if (!_.isMatch(item, {'x': startX + 1, 'y': startY})) {
console.log("empty spot at", startX + 1, startY);
isFilled = true;
}
}
});
if (!isFilled) {
startY += 1;
fillEmptySpace(isFilled, startX, startY);
}
}
Trying to develop an Agar clone, and I've got a lot of it, but I can't quite figure out how to decrement the player's speed as its mass increases. I've tried several different ways, but nothing works. How would I make the speed go down as the mass goes up? Here's my jsFiddle. This is where I set the speed of of the players:
var playerOneMass = 36;
var player1X = (canvas.width / 2) + 50;
var player = new Player({
x: player1X,
y: canvas.height / 2,
radius: playerOneMass,
speed: {
x: 5,
y: 5
},
name: "player 1",
dir: null
});
var playerTwoMass = 36;
var player2X = (canvas.width / 2) - 50;
var player2 = new Player({
x: player2X,
y: canvas.height / 2,
radius: playerTwoMass,
speed: {
x: 5,
y: 5
},
name: "player 2",
dir: null
});
Let us bring some math in to help us out a little bit. When you want something to grow smaller as another grows bigger, the best option that I have found is to use an inversely proportional relationship. This will allow a smooth smaller and smaller look for you.
new_speed = scalar * start_speed / current_mass
When coming up with the scalar, I have found it best to trial and error until it looks how you want it to.
Here is an example of the equation in action utilizing Two.js.
var two = new Two({width:320, height:180}).appendTo(document.getElementById("mytwo")),
rect = two.makeRectangle(100, 100, 10, 10),
circ = two.makeCircle(5, 100, 5),
mass = 10,
rspeed = Math.PI / 10,
mspeed = 14,
scalar = 10;
// Make it look pretty!
rect.fill = "rgb(100,255,100)";
circ.fill = "rgb(100,100,255)";
// Looping...
two.bind('update', function(fc) {
// Prevents from growing indefinitely
if(mass > 150) return;
mass += 1.5;
rect.scale += .1;
circ.scale += .1;
rect.rotation += scalar * rspeed / mass;
circ.translation.addSelf(new Two.Vector(
scalar * mspeed / mass, 0));
}).play();
<script type="text/javascript" src="http://cdn.jsdelivr.net/gh/jonobr1/two.js/build/two.min.js"></script>
<div id="mytwo"><div></div></div>
I am using Chart.js for drawing pie chart in my php page.I found tooltip as showing each slice values.
But I wish to display those values like below image.
I do not know how to do this with chart.js.
Please help me.
My Javascript code:
function drawPie(canvasId,data,legend){
var ctx = $("#pie-canvas-" + canvasId).get(0).getContext("2d");
var piedata = [];
$.each(data,function(i,val){
piedata.push({value:val.count,color:val.color,label:val.status});
});
var options =
{
tooltipTemplate: "<%= Math.round(circumference / 6.283 * 100) %>%",
}
var pie = new Chart(ctx).Pie(piedata,options);
if(legend)document.getElementById("legend").innerHTML = pie.generateLegend();
}
php code:
printf('<table><tr>');
echo '<td style="text-align: right;"><canvas id="pie-canvas-'
. $canvasId
. '" width="256" height="256" ></canvas></td><td style="text-align: left;width:360px;height:auto" id="legend" class="chart-legend"></td></tr></table>';
echo '<script type="text/javascript">drawPie('
. $canvasId
. ', '
. $data3
.', '
. $legend
. ');</script>';
For Chart.js 2.0 and up, the Chart object data has changed. For those who are using Chart.js 2.0+, below is an example of using HTML5 Canvas fillText() method to display data value inside of the pie slice. The code works for doughnut chart, too, with the only difference being type: 'pie' versus type: 'doughnut' when creating the chart.
Script:
Javascript
var data = {
datasets: [{
data: [
11,
16,
7,
3,
14
],
backgroundColor: [
"#FF6384",
"#4BC0C0",
"#FFCE56",
"#E7E9ED",
"#36A2EB"
],
label: 'My dataset' // for legend
}],
labels: [
"Red",
"Green",
"Yellow",
"Grey",
"Blue"
]
};
var pieOptions = {
events: false,
animation: {
duration: 500,
easing: "easeOutQuart",
onComplete: function () {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
total = dataset._meta[Object.keys(dataset._meta)[0]].total,
mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2,
start_angle = model.startAngle,
end_angle = model.endAngle,
mid_angle = start_angle + (end_angle - start_angle)/2;
var x = mid_radius * Math.cos(mid_angle);
var y = mid_radius * Math.sin(mid_angle);
ctx.fillStyle = '#fff';
if (i == 3){ // Darker text color for lighter background
ctx.fillStyle = '#444';
}
var percent = String(Math.round(dataset.data[i]/total*100)) + "%";
//Don't Display If Legend is hide or value is 0
if(dataset.data[i] != 0 && dataset._meta[0].data[i].hidden != true) {
ctx.fillText(dataset.data[i], model.x + x, model.y + y);
// Display percent in another line, line break doesn't work for fillText
ctx.fillText(percent, model.x + x, model.y + y + 15);
}
}
});
}
}
};
var pieChartCanvas = $("#pieChart");
var pieChart = new Chart(pieChartCanvas, {
type: 'pie', // or doughnut
data: data,
options: pieOptions
});
HTML
<canvas id="pieChart" width=200 height=200></canvas>
jsFiddle
I found an excellent Chart.js plugin that does exactly what you want:
https://github.com/emn178/Chart.PieceLabel.js
From what I know I don't believe that Chart.JS has any functionality to help for drawing text on a pie chart. But that doesn't mean you can't do it yourself in native JavaScript. I will give you an example on how to do that, below is the code for drawing text for each segment in the pie chart:
function drawSegmentValues()
{
for(var i=0; i<myPieChart.segments.length; i++)
{
// Default properties for text (size is scaled)
ctx.fillStyle="white";
var textSize = canvas.width/10;
ctx.font= textSize+"px Verdana";
// Get needed variables
var value = myPieChart.segments[i].value;
var startAngle = myPieChart.segments[i].startAngle;
var endAngle = myPieChart.segments[i].endAngle;
var middleAngle = startAngle + ((endAngle - startAngle)/2);
// Compute text location
var posX = (radius/2) * Math.cos(middleAngle) + midX;
var posY = (radius/2) * Math.sin(middleAngle) + midY;
// Text offside to middle of text
var w_offset = ctx.measureText(value).width/2;
var h_offset = textSize/4;
ctx.fillText(value, posX - w_offset, posY + h_offset);
}
}
A Pie Chart has an array of segments stored in PieChart.segments, we can look at the startAngle and endAngle of these segments to determine the angle in between where the text would be middleAngle. Then we would move in that direction by Radius/2 to be in the middle point of the chart in radians.
In the example above some other clean-up operations are done, due to the position of text drawn in fillText() being the top right corner, we need to get some offset values to correct for that. And finally textSize is determined based on the size of the chart itself, the larger the chart the larger the text.
Fiddle Example
With some slight modification you can change the discrete number values for a dataset into the percentile numbers in a graph. To do this get the total value of the items in your dataset, call this totalValue. Then on each segment you can find the percent by doing:
Math.round(myPieChart.segments[i].value/totalValue*100)+'%';
The section here myPieChart.segments[i].value/totalValue is what calculates the percent that the segment takes up in the chart. For example if the current segment had a value of 50 and the totalValue was 200. Then the percent that the segment took up would be: 50/200 => 0.25. The rest is to make this look nice. 0.25*100 => 25, then we add a % at the end. For whole number percent tiles I rounded to the nearest integer, although can can lead to problems with accuracy. If we need more accuracy you can use .toFixed(n) to save decimal places. For example we could do this to save a single decimal place when needed:
var value = myPieChart.segments[i].value/totalValue*100;
if(Math.round(value) !== value)
value = (myPieChart.segments[i].value/totalValue*100).toFixed(1);
value = value + '%';
Fiddle Example of percentile with decimals
Fiddle Example of percentile with integers
You can make use of PieceLabel plugin for Chart.js.
{ pieceLabel: { mode: 'percentage', precision: 2 } }
Demo |
Documentation
The plugin appears to have a new location (and name): Demo Docs.
#Hung Tran's answer works perfect. As an improvement, I would suggest not showing values that are 0. Say you have 5 elements and 2 of them are 0 and rest of them have values, the solution above will show 0 and 0%. It is better to filter that out with a not equal to 0 check!
var val = dataset.data[i];
var percent = String(Math.round(val/total*100)) + "%";
if(val != 0) {
ctx.fillText(dataset.data[i], model.x + x, model.y + y);
// Display percent in another line, line break doesn't work for fillText
ctx.fillText(percent, model.x + x, model.y + y + 15);
}
Updated code below:
var data = {
datasets: [{
data: [
11,
16,
7,
3,
14
],
backgroundColor: [
"#FF6384",
"#4BC0C0",
"#FFCE56",
"#E7E9ED",
"#36A2EB"
],
label: 'My dataset' // for legend
}],
labels: [
"Red",
"Green",
"Yellow",
"Grey",
"Blue"
]
};
var pieOptions = {
events: false,
animation: {
duration: 500,
easing: "easeOutQuart",
onComplete: function () {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
total = dataset._meta[Object.keys(dataset._meta)[0]].total,
mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2,
start_angle = model.startAngle,
end_angle = model.endAngle,
mid_angle = start_angle + (end_angle - start_angle)/2;
var x = mid_radius * Math.cos(mid_angle);
var y = mid_radius * Math.sin(mid_angle);
ctx.fillStyle = '#fff';
if (i == 3){ // Darker text color for lighter background
ctx.fillStyle = '#444';
}
var val = dataset.data[i];
var percent = String(Math.round(val/total*100)) + "%";
if(val != 0) {
ctx.fillText(dataset.data[i], model.x + x, model.y + y);
// Display percent in another line, line break doesn't work for fillText
ctx.fillText(percent, model.x + x, model.y + y + 15);
}
}
});
}
}
};
var pieChartCanvas = $("#pieChart");
var pieChart = new Chart(pieChartCanvas, {
type: 'pie', // or doughnut
data: data,
options: pieOptions
});
For Chart.js 3
I've modified "Hung Tran"'s Code.
animation: {
onProgress: function() {
// console.error('this', this);
const ctx = this.ctx;
// ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
let dataSum = 0;
if(this._sortedMetasets.length > 0 && this._sortedMetasets[0].data.length > 0) {
const dataset = this._sortedMetasets[0].data[0].$context.dataset;
dataSum = dataset.data.reduce((p, c) => p + c, 0);
}
if(dataSum <= 0) return;
this._sortedMetasets.forEach(meta => {
meta.data.forEach(metaData => {
const dataset = metaData.$context.dataset;
const datasetIndex = metaData.$context.dataIndex;
const value = dataset.data[datasetIndex];
const percent = (Math.round(value / dataSum * 1000) / 10) + '%';
const mid_radius = metaData.innerRadius + (metaData.outerRadius - metaData.innerRadius) * 0.7;
const start_angle = metaData.startAngle;
const end_angle = metaData.endAngle;
if(start_angle === end_angle) return; // hidden
const mid_angle = start_angle + (end_angle - start_angle) / 2;
const x = mid_radius * Math.cos(mid_angle);
const y = mid_radius * Math.sin(mid_angle);
ctx.fillStyle = '#fff';
ctx.fillText(percent, metaData.x + x, metaData.y + y + 15);
});
});
}
}
Give the option for pie chart
onAnimationProgress: drawSegmentValues
like:
var pOptions = {
onAnimationProgress: drawSegmentValues
};
var pieChart = new Chart(pieChartCanvas, {
type: 'pie', // or doughnut
data: data,
options: pOptions
});
Easiest way to do this with Chartjs. Just add below line in options:
pieceLabel: {
fontColor: '#000'
}
Best of luck
I currently have a pie chart successfully displaying on my reporting dashboard. However, a business request was made to retain a chart outline and display the 'noData' message in the center when all series are empty.
The business did not like the look of a floating label on the page when the chart was empty. Using an existing chart object, would it be possible to essentially fabricate a chart outline and display a noData message?
It is possible to add custom shape, e.g. circle, that will be showing in case there is no data. Using chart's events load and redraw you can update shape to fit in chart and be placed in center or remove when data is added to chart.
API reference for renderer.circle: http://api.highcharts.com/highcharts#Renderer.circle
Example: http://jsfiddle.net/v8n1159o/1/
chart: {
events: {
load: function () {
var chart = this;
if (!chart.hasData()) {
var r = Math.min(chart.plotWidth / 2, chart.plotHeight / 2),
y = chart.plotHeight / 2 + chart.plotTop,
x = chart.plotWidth / 2 + chart.plotLeft;
chart.pieOutline = chart.renderer.circle(x, y, r).attr({
fill: '#ddd',
stroke: 'black',
'stroke-width': 1
}).add();
}
},
redraw: function () {
var chart = this;
if (chart.pieOutline && chart.pieOutline.element) {
if (chart.hasData()) {
chart.pieOutline.destroy();
} else {
var r = Math.min(chart.plotWidth / 2, chart.plotHeight / 2),
y = chart.plotHeight / 2 + chart.plotTop,
x = chart.plotWidth / 2 + chart.plotLeft;
chart.pieOutline.attr({
cx: x,
cy: y,
r: r
});
}
} else if(!chart.hasData()) {
var r = Math.min(chart.plotWidth / 2, chart.plotHeight / 2),
y = chart.plotHeight / 2 + chart.plotTop,
x = chart.plotWidth / 2 + chart.plotLeft;
chart.pieOutline = chart.renderer.circle(x, y, r).attr({
fill: '#ddd',
stroke: 'black',
'stroke-width': 1
}).add();
}
}
},
...