<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
- <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
- <script type="text/javascript" src="../prettify/prettify.js"></script>
+ <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<pre class="prettyprint lang-js"><span id='Ext-chart-series-Pie'>/**
</span> * @class Ext.chart.series.Pie
* @extends Ext.chart.series.Series
- *
- * Creates a Pie Chart. A Pie Chart is a useful visualization technique to display quantitative information for different
+ *
+ * Creates a Pie Chart. A Pie Chart is a useful visualization technique to display quantitative information for different
* categories that also have a meaning as a whole.
- * As with all other series, the Pie Series must be appended in the *series* Chart array configuration. See the Chart
+ * As with all other series, the Pie Series must be appended in the *series* Chart array configuration. See the Chart
* documentation for more information. A typical configuration object for the pie series could be:
- *
- * {@img Ext.chart.series.Pie/Ext.chart.series.Pie.png Ext.chart.series.Pie chart series}
*
+ * @example
* var store = Ext.create('Ext.data.JsonStore', {
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
* data: [
- * {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
- * {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
- * {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
- * {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
- * {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
+ * { 'name': 'metric one', 'data1': 10, 'data2': 12, 'data3': 14, 'data4': 8, 'data5': 13 },
+ * { 'name': 'metric two', 'data1': 7, 'data2': 8, 'data3': 16, 'data4': 10, 'data5': 3 },
+ * { 'name': 'metric three', 'data1': 5, 'data2': 2, 'data3': 14, 'data4': 12, 'data5': 7 },
+ * { 'name': 'metric four', 'data1': 2, 'data2': 14, 'data3': 6, 'data4': 1, 'data5': 23 },
+ * { 'name': 'metric five', 'data1': 27, 'data2': 38, 'data3': 36, 'data4': 13, 'data5': 33 }
* ]
* });
- *
+ *
* Ext.create('Ext.chart.Chart', {
* renderTo: Ext.getBody(),
* width: 500,
- * height: 300,
+ * height: 350,
* animate: true,
* store: store,
* theme: 'Base:gradients',
* field: 'data1',
* showInLegend: true,
* tips: {
- * trackMouse: true,
- * width: 140,
- * height: 28,
- * renderer: function(storeItem, item) {
- * //calculate and display percentage on hover
- * var total = 0;
- * store.each(function(rec) {
- * total += rec.get('data1');
- * });
- * this.setTitle(storeItem.get('name') + ': ' + Math.round(storeItem.get('data1') / total * 100) + '%');
- * }
+ * trackMouse: true,
+ * width: 140,
+ * height: 28,
+ * renderer: function(storeItem, item) {
+ * // calculate and display percentage on hover
+ * var total = 0;
+ * store.each(function(rec) {
+ * total += rec.get('data1');
+ * });
+ * this.setTitle(storeItem.get('name') + ': ' + Math.round(storeItem.get('data1') / total * 100) + '%');
+ * }
* },
* highlight: {
- * segment: {
- * margin: 20
- * }
+ * segment: {
+ * margin: 20
+ * }
* },
* label: {
* field: 'name',
* contrast: true,
* font: '18px Arial'
* }
- * }]
+ * }]
* });
- *
- * In this configuration we set `pie` as the type for the series, set an object with specific style properties for highlighting options
- * (triggered when hovering elements). We also set true to `showInLegend` so all the pie slices can be represented by a legend item.
- * We set `data1` as the value of the field to determine the angle span for each pie slice. We also set a label configuration object
- * where we set the field name of the store field to be renderer as text for the label. The labels will also be displayed rotated.
- * We set `contrast` to `true` to flip the color of the label if it is to similar to the background color. Finally, we set the font family
- * and size through the `font` parameter.
- *
+ *
+ * In this configuration we set `pie` as the type for the series, set an object with specific style properties for highlighting options
+ * (triggered when hovering elements). We also set true to `showInLegend` so all the pie slices can be represented by a legend item.
+ *
+ * We set `data1` as the value of the field to determine the angle span for each pie slice. We also set a label configuration object
+ * where we set the field name of the store field to be renderer as text for the label. The labels will also be displayed rotated.
+ *
+ * We set `contrast` to `true` to flip the color of the label if it is to similar to the background color. Finally, we set the font family
+ * and size through the `font` parameter.
+ *
* @xtype pie
*/
Ext.define('Ext.chart.series.Pie', {
/* End Definitions */
type: "pie",
-
+
alias: 'series.pie',
rad: Math.PI / 180,
highlightDuration: 150,
<span id='Ext-chart-series-Pie-cfg-angleField'> /**
-</span> * @cfg {String} angleField
+</span> * @cfg {String} angleField (required)
* The store record field name to be used for the pie angles.
* The values bound to this field name must be positive real numbers.
- * This parameter is required.
*/
angleField: false,
</span> * @cfg {String} lengthField
* The store record field name to be used for the pie slice lengths.
* The values bound to this field name must be positive real numbers.
- * This parameter is optional.
*/
lengthField: false,
<span id='Ext-chart-series-Pie-cfg-donut'> /**
-</span> * @cfg {Boolean|Number} donut
+</span> * @cfg {Boolean/Number} donut
* Whether to set the pie chart as donut chart.
* Default's false. Can be set to a particular percentage to set the radius
* of the donut chart.
</span> * @cfg {Array} colorSet
* An array of color values which will be used, in order, as the pie slice fill colors.
*/
-
+
<span id='Ext-chart-series-Pie-cfg-style'> /**
</span> * @cfg {Object} style
* An object containing styles for overriding series styles from Theming.
*/
style: {},
-
+
constructor: function(config) {
this.callParent(arguments);
var me = this,
}
}
});
- Ext.apply(me, config, {
+ Ext.apply(me, config, {
shadowAttributes: [{
"stroke-width": 6,
"stroke-opacity": 1,
surface.customAttributes.segment = function(opt) {
return me.getSegment(opt);
};
+ me.__excludes = me.__excludes || [];
},
-
+
//@private updates some onbefore render parameters.
initialize: function() {
var me = this,
- store = me.chart.substore || me.chart.store;
+ store = me.chart.getChartStore();
//Add yFields to be used in Legend.js
me.yField = [];
if (me.label.field) {
rad = me.rad,
cos = Math.cos,
sin = Math.sin,
- abs = Math.abs,
x = me.centerX,
y = me.centerY,
x1 = 0, x2 = 0, x3 = 0, x4 = 0,
y1 = 0, y2 = 0, y3 = 0, y4 = 0,
+ x5 = 0, y5 = 0, x6 = 0, y6 = 0,
delta = 1e-2,
- r = opt.endRho - opt.startRho,
startAngle = opt.startAngle,
endAngle = opt.endAngle,
midAngle = (startAngle + endAngle) / 2 * rad,
margin = opt.margin || 0,
- flag = abs(endAngle - startAngle) > 180,
a1 = Math.min(startAngle, endAngle) * rad,
a2 = Math.max(startAngle, endAngle) * rad,
- singleSlice = false;
+ c1 = cos(a1), s1 = sin(a1),
+ c2 = cos(a2), s2 = sin(a2),
+ cm = cos(midAngle), sm = sin(midAngle),
+ flag = 0, hsqr2 = 0.7071067811865476; // sqrt(0.5)
- x += margin * cos(midAngle);
- y += margin * sin(midAngle);
-
- x1 = x + opt.startRho * cos(a1);
- y1 = y + opt.startRho * sin(a1);
+ if (a2 - a1 < delta) {
+ return {path: ""};
+ }
- x2 = x + opt.endRho * cos(a1);
- y2 = y + opt.endRho * sin(a1);
+ if (margin !== 0) {
+ x += margin * cm;
+ y += margin * sm;
+ }
- x3 = x + opt.startRho * cos(a2);
- y3 = y + opt.startRho * sin(a2);
+ x2 = x + opt.endRho * c1;
+ y2 = y + opt.endRho * s1;
- x4 = x + opt.endRho * cos(a2);
- y4 = y + opt.endRho * sin(a2);
+ x4 = x + opt.endRho * c2;
+ y4 = y + opt.endRho * s2;
- if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
- singleSlice = true;
+ if (Math.abs(x2 - x4) + Math.abs(y2 - y4) < delta) {
+ cm = hsqr2;
+ sm = -hsqr2;
+ flag = 1;
}
- //Solves mysterious clipping bug with IE
- if (singleSlice) {
+
+ x6 = x + opt.endRho * cm;
+ y6 = y + opt.endRho * sm;
+
+ // TODO(bei): It seems that the canvas engine cannot render half circle command correctly on IE.
+ // Better fix the VML engine for half circles.
+
+ if (opt.startRho !== 0) {
+ x1 = x + opt.startRho * c1;
+ y1 = y + opt.startRho * s1;
+
+ x3 = x + opt.startRho * c2;
+ y3 = y + opt.startRho * s2;
+
+ x5 = x + opt.startRho * cm;
+ y5 = y + opt.startRho * sm;
+
return {
path: [
- ["M", x1, y1],
- ["L", x2, y2],
- ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
- ["Z"]]
+ ["M", x2, y2],
+ ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
+ ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
+ ["L", x3, y3],
+ ["A", opt.startRho, opt.startRho, 0, flag, 0, x5, y5], ["L", x5, y5],
+ ["A", opt.startRho, opt.startRho, 0, 0, 0, x1, y1], ["L", x1, y1],
+ ["Z"]
+ ]
};
} else {
return {
path: [
- ["M", x1, y1],
- ["L", x2, y2],
- ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
- ["L", x3, y3],
- ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
- ["Z"]]
+ ["M", x, y],
+ ["L", x2, y2],
+ ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
+ ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
+ ["L", x, y],
+ ["Z"]
+ ]
};
}
},
startAngle = slice.startAngle,
endAngle = slice.endAngle,
donut = +me.donut,
- a1 = Math.min(startAngle, endAngle) * rad,
- a2 = Math.max(startAngle, endAngle) * rad,
- midAngle = -(a1 + (a2 - a1) / 2),
- xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
- ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
+ midAngle = -(startAngle + endAngle) * rad / 2,
+ r = (item.endRho + item.startRho) / 2,
+ xm = x + r * Math.cos(midAngle),
+ ym = y - r * Math.sin(midAngle);
item.middle = {
x: xm,
*/
drawSeries: function() {
var me = this,
- store = me.chart.substore || me.chart.store,
+ store = me.chart.getChartStore(),
group = me.group,
animate = me.chart.animate,
field = me.angleField || me.field || me.xField,
colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
gutterX = chart.maxGutter[0],
gutterY = chart.maxGutter[1],
+ abs = Math.abs,
rendererAttributes,
shadowGroup,
shadowAttr,
path,
p,
spriteOptions, bbox;
-
+
Ext.apply(seriesStyle, me.style || {});
me.setBBox();
colorArrayStyle = me.colorSet;
colorArrayLength = colorArrayStyle.length;
}
-
+
//if not store or store is empty then there's nothing to draw
if (!store || !store.getCount()) {
return;
}
-
+
me.unHighlightItem();
me.cleanHighlights();
}
}, this);
+ totalField = totalField || 1;
store.each(function(record, i) {
if (this.__excludes && this.__excludes[i]) {
- //hidden series
- return;
- }
- value = record.get(field);
- middleAngle = angle - 360 * value / totalField / 2;
- // TODO - Put up an empty circle
- if (isNaN(middleAngle)) {
- middleAngle = 360;
- value = 1;
- totalField = 1;
+ value = 0;
+ } else {
+ value = record.get(field);
+ if (first == 0) {
+ first = 1;
+ }
}
+
// First slice
- if (!i || first == 0) {
- angle = 360 - middleAngle;
- me.firstAngle = angle;
- middleAngle = angle - 360 * value / totalField / 2;
+ if (first == 1) {
+ first = 2;
+ me.firstAngle = angle = 360 * value / totalField / 2;
+ for (j = 0; j < i; j++) {
+ slices[j].startAngle = slices[j].endAngle = me.firstAngle;
+ }
}
+
endAngle = angle - 360 * value / totalField;
slice = {
series: me,
slice.rho = me.radius;
}
slices[i] = slice;
- if((slice.startAngle % 360) == (slice.endAngle % 360)) {
- slice.startAngle -= 0.0001;
- }
angle = endAngle;
- first++;
}, me);
-
//do all shadows first.
if (enableShadows) {
for (i = 0, ln = slices.length; i < ln; i++) {
- if (this.__excludes && this.__excludes[i]) {
- //hidden series
- continue;
- }
slice = slices[i];
slice.shadowAttrs = [];
for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) {
rho: slice.rho,
startRho: rhoAcum + (deltaRho * donut / 100),
endRho: rhoAcum + deltaRho
- }
+ },
+ hidden: !slice.value && (slice.startAngle % 360) == (slice.endAngle % 360)
};
//create shadows
for (shindex = 0, shadows = []; shindex < lnsh; shindex++) {
to: shadowAttr
});
} else {
- shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply(shadowAttr, {
- hidden: false
- }), i, store);
+ shadowAttr = me.renderer(shadow, store.getAt(i), shadowAttr, i, store);
shadow.setAttributes(shadowAttr, true);
}
shadows.push(shadow);
}
//do pie slices after.
for (i = 0, ln = slices.length; i < ln; i++) {
- if (this.__excludes && this.__excludes[i]) {
- //hidden series
- continue;
- }
slice = slices[i];
for (j = 0, rhoAcum = 0; j < layers; j++) {
sprite = group.getAt(i * layers + j);
rho: slice.rho,
startRho: rhoAcum + (deltaRho * donut / 100),
endRho: rhoAcum + deltaRho
- }
+ },
+ hidden: (!slice.value && (slice.startAngle % 360) == (slice.endAngle % 360))
}, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
item = Ext.apply({},
rendererAttributes.segment, {
rhoAcum += deltaRho;
}
}
-
+
// Hide unused bars
ln = group.getCount();
for (i = 0; i < ln; i++) {
centerY = me.centerY,
middle = item.middle,
endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {});
-
+
return me.chart.surface.add(Ext.apply({
'type': 'text',
'text-anchor': 'middle',
theta = Math.atan2(y, x || 1),
dg = theta * 180 / Math.PI,
prevDg;
-
+ if (this.__excludes && this.__excludes[i]) {
+ opt.hidden = true;
+ }
function fixAngle(a) {
- if (a < 0) a += 360;
+ if (a < 0) {
+ a += 360;
+ }
return a % 360;
}
}
//ensure the object has zero translation
opt.translate = {
- x: 0, y: 0
+ x: 0, y: 0
};
if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
me.onAnimate(label, {
startAngle = item.startAngle,
endAngle = item.endAngle,
rho = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(y - cy, x - cx) / me.rad + 360;
-
+ angle = Math.atan2(y - cy, x - cx) / me.rad;
+
// normalize to the same range of angles created by drawSeries
if (angle > me.firstAngle) {
angle -= 360;
return (angle <= startAngle && angle > endAngle
&& rho >= item.startRho && rho <= item.endRho);
},
-
+
// @private hides all elements in the series.
hideAll: function() {
var i, l, shadow, shadows, sh, lsh, sprite;
this.drawSeries();
}
},
-
+
// @private shows all elements in the series.
showAll: function() {
if (!isNaN(this._index)) {
var me = this,
rad = me.rad;
item = item || this.items[this._index];
-
+
//TODO(nico): sometimes in IE itemmouseover is triggered
//twice without triggering itemmouseout in between. This
//fixes the highlighting bug. Eventually, events should be
//changed to trigger one itemmouseout between two itemmouseovers.
this.unHighlightItem();
-
+
if (!item || item.sprite && item.sprite._animating) {
return;
}
if (Math.abs(y) < 1e-10) {
y = 0;
}
-
+
if (animate) {
label.stopAnimation();
label.animate({
},
<span id='Ext-chart-series-Pie-method-unHighlightItem'> /**
-</span> * un-highlights the specified item. If no item is provided it will un-highlight the entire series.
+</span> * Un-highlights the specified item. If no item is provided it will un-highlight the entire series.
* @param item {Object} Info about the item; same format as returned by #getItemForPoint
*/
unHighlightItem: function() {
}
me.callParent(arguments);
},
-
+
<span id='Ext-chart-series-Pie-method-getLegendColor'> /**
</span> * Returns the color of the series (to be displayed as color for the series legend item).
* @param item {Object} Info about the item; same format as returned by #getItemForPoint