12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>The source code</title>
- <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>
- <script type="text/javascript">
- function highlight() {
- document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
- }
- </script>
- </head>
- <body onload="prettyPrint(); highlight();">
- <pre class="prettyprint lang-js"><span id='Ext-chart-series-Pie'>/**
- </span> * @class Ext.chart.series.Pie
- *
- * 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
- * documentation for more information. A typical configuration object for the pie series could be:
- *
- * @example
- * var store = Ext.create('Ext.data.JsonStore', {
- * fields: ['name', 'data'],
- * data: [
- * { 'name': 'metric one', 'data': 10 },
- * { 'name': 'metric two', 'data': 7 },
- * { 'name': 'metric three', 'data': 5 },
- * { 'name': 'metric four', 'data': 2 },
- * { 'name': 'metric five', 'data': 27 }
- * ]
- * });
- *
- * Ext.create('Ext.chart.Chart', {
- * renderTo: Ext.getBody(),
- * width: 500,
- * height: 350,
- * animate: true,
- * store: store,
- * theme: 'Base:gradients',
- * series: [{
- * type: 'pie',
- * angleField: 'data',
- * 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('data');
- * });
- * this.setTitle(storeItem.get('name') + ': ' + Math.round(storeItem.get('data') / total * 100) + '%');
- * }
- * },
- * highlight: {
- * segment: {
- * margin: 20
- * }
- * },
- * label: {
- * field: 'name',
- * display: 'rotate',
- * 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 `data` 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', {
- /* Begin Definitions */
- alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'],
- extend: 'Ext.chart.series.Series',
- /* End Definitions */
- type: "pie",
- alias: 'series.pie',
- accuracy: 100000,
- rad: Math.PI * 2 / 100000,
- <span id='Ext-chart-series-Pie-cfg-highlightDuration'> /**
- </span> * @cfg {Number} highlightDuration
- * The duration for the pie slice highlight effect.
- */
- highlightDuration: 150,
- <span id='Ext-chart-series-Pie-cfg-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.
- */
- angleField: false,
- <span id='Ext-chart-series-Pie-cfg-field'> /**
- </span> * @cfg {String} field
- * Alias for {@link #angleField}.
- */
- <span id='Ext-chart-series-Pie-cfg-xField'> /**
- </span> * @cfg {String} xField
- * Alias for {@link #angleField}.
- */
- <span id='Ext-chart-series-Pie-cfg-lengthField'> /**
- </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.
- */
- lengthField: false,
- <span id='Ext-chart-series-Pie-cfg-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.
- */
- donut: false,
- <span id='Ext-chart-series-Pie-cfg-showInLegend'> /**
- </span> * @cfg {Boolean} showInLegend
- * Whether to add the pie chart elements as legend items. Default's false.
- */
- showInLegend: false,
- <span id='Ext-chart-series-Pie-cfg-colorSet'> /**
- </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,
- chart = me.chart,
- surface = chart.surface,
- store = chart.store,
- shadow = chart.shadow, i, l, cfg;
- config.highlightCfg = Ext.merge({
- segment: {
- margin: 20
- }
- }, config.highlightCfg);
- Ext.apply(me, config, {
- shadowAttributes: [{
- "stroke-width": 6,
- "stroke-opacity": 1,
- stroke: 'rgb(200, 200, 200)',
- translate: {
- x: 1.2,
- y: 2
- }
- },
- {
- "stroke-width": 4,
- "stroke-opacity": 1,
- stroke: 'rgb(150, 150, 150)',
- translate: {
- x: 0.9,
- y: 1.5
- }
- },
- {
- "stroke-width": 2,
- "stroke-opacity": 1,
- stroke: 'rgb(100, 100, 100)',
- translate: {
- x: 0.6,
- y: 1
- }
- }]
- });
- me.group = surface.getGroup(me.seriesId);
- if (shadow) {
- for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
- me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
- }
- }
- surface.customAttributes.segment = function(opt) {
- //Browsers will complain if we create a path
- //element that has no path commands. So ensure a dummy
- //path command for an empty path.
- var ans = me.getSegment(opt);
- if (!ans.path || ans.path.length === 0) {
- ans.path = ['M', 0, 0];
- }
- return ans;
- };
- me.__excludes = me.__excludes || [];
- },
- // @private updates some onbefore render parameters.
- initialize: function() {
- var me = this,
- store = me.chart.getChartStore(),
- data = store.data.items,
- i, ln, rec;
- //Add yFields to be used in Legend.js
- me.yField = [];
- if (me.label.field) {
- for (i = 0, ln = data.length; i < ln; i++) {
- rec = data[i];
- me.yField.push(rec.get(me.label.field));
- }
- }
- },
- // @private returns an object with properties for a PieSlice.
- getSegment: function(opt) {
- var me = this,
- rad = me.rad,
- cos = Math.cos,
- sin = Math.sin,
- 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,
- startAngle = opt.startAngle,
- endAngle = opt.endAngle,
- midAngle = (startAngle + endAngle) / 2 * rad,
- margin = opt.margin || 0,
- a1 = Math.min(startAngle, endAngle) * rad,
- a2 = Math.max(startAngle, endAngle) * rad,
- 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)
- if (a2 - a1 < delta) {
- return {path: ""};
- }
- if (margin !== 0) {
- x += margin * cm;
- y += margin * sm;
- }
- x2 = x + opt.endRho * c1;
- y2 = y + opt.endRho * s1;
- x4 = x + opt.endRho * c2;
- y4 = y + opt.endRho * s2;
- x6 = x + opt.endRho * cm;
- y6 = y + opt.endRho * sm;
- 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", 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", 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"]
- ]
- };
- }
- },
- // @private utility function to calculate the middle point of a pie slice.
- calcMiddle: function(item) {
- var me = this,
- rad = me.rad,
- slice = item.slice,
- x = me.centerX,
- y = me.centerY,
- startAngle = slice.startAngle,
- endAngle = slice.endAngle,
- donut = +me.donut,
- 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,
- y: ym
- };
- },
- <span id='Ext-chart-series-Pie-method-drawSeries'> /**
- </span> * Draws the series for the current chart.
- */
- drawSeries: function() {
- var me = this,
- store = me.chart.getChartStore(),
- data = store.data.items,
- record,
- group = me.group,
- animate = me.chart.animate,
- field = me.angleField || me.field || me.xField,
- lenField = [].concat(me.lengthField),
- totalLenField = 0,
- chart = me.chart,
- surface = chart.surface,
- chartBBox = chart.chartBBox,
- enableShadows = chart.shadow,
- shadowGroups = me.shadowGroups,
- shadowAttributes = me.shadowAttributes,
- lnsh = shadowGroups.length,
- layers = lenField.length,
- rhoAcum = 0,
- donut = +me.donut,
- layerTotals = [],
- items = [],
- totalField = 0,
- maxLenField = 0,
- angle = 0,
- seriesStyle = me.seriesStyle,
- colorArrayStyle = me.colorArrayStyle,
- colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
- rendererAttributes,
- shadowAttr,
- shadows,
- shadow,
- shindex,
- centerX,
- centerY,
- deltaRho,
- first = 0,
- slice,
- slices,
- sprite,
- value,
- item,
- lenValue,
- ln,
- i,
- j,
- endAngle,
- path,
- p,
- spriteOptions, bbox;
- Ext.apply(seriesStyle, me.style || {});
- me.setBBox();
- bbox = me.bbox;
- //override theme colors
- if (me.colorSet) {
- colorArrayStyle = me.colorSet;
- colorArrayLength = colorArrayStyle.length;
- }
- //if not store or store is empty then there's nothing to draw
- if (!store || !store.getCount() || me.seriesIsHidden) {
- me.hide();
- me.items = [];
- return;
- }
- me.unHighlightItem();
- me.cleanHighlights();
- centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
- centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
- me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
- me.slices = slices = [];
- me.items = items = [];
- for (i = 0, ln = data.length; i < ln; i++) {
- record = data[i];
- if (this.__excludes && this.__excludes[i]) {
- //hidden series
- continue;
- }
- totalField += +record.get(field);
- if (lenField[0]) {
- for (j = 0, totalLenField = 0; j < layers; j++) {
- totalLenField += +record.get(lenField[j]);
- }
- layerTotals[i] = totalLenField;
- maxLenField = Math.max(maxLenField, totalLenField);
- }
- }
- totalField = totalField || 1;
- for (i = 0, ln = data.length; i < ln; i++) {
- record = data[i];
- if (this.__excludes && this.__excludes[i]) {
- value = 0;
- } else {
- value = record.get(field);
- if (first == 0) {
- first = 1;
- }
- }
- // First slice
- if (first == 1) {
- first = 2;
- me.firstAngle = angle = me.accuracy * value / totalField / 2;
- for (j = 0; j < i; j++) {
- slices[j].startAngle = slices[j].endAngle = me.firstAngle;
- }
- }
- endAngle = angle - me.accuracy * value / totalField;
- slice = {
- series: me,
- value: value,
- startAngle: angle,
- endAngle: endAngle,
- storeItem: record
- };
- if (lenField[0]) {
- lenValue = +layerTotals[i];
- //removing the floor will break Opera 11.6*
- slice.rho = Math.floor(me.radius / maxLenField * lenValue);
- } else {
- slice.rho = me.radius;
- }
- slices[i] = slice;
- // Do not remove this closure for the sake of https://sencha.jira.com/browse/EXTJSIV-5836
- (function () {
- angle = endAngle;
- })();
- }
- //do all shadows first.
- if (enableShadows) {
- for (i = 0, ln = slices.length; i < ln; i++) {
- slice = slices[i];
- slice.shadowAttrs = [];
- for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) {
- sprite = group.getAt(i * layers + j);
- deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
- //set pie slice properties
- rendererAttributes = {
- segment: {
- startAngle: slice.startAngle,
- endAngle: slice.endAngle,
- margin: 0,
- rho: slice.rho,
- startRho: rhoAcum + (deltaRho * donut / 100),
- endRho: rhoAcum + deltaRho
- },
- hidden: !slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy)
- };
- //create shadows
- for (shindex = 0, shadows = []; shindex < lnsh; shindex++) {
- shadowAttr = shadowAttributes[shindex];
- shadow = shadowGroups[shindex].getAt(i);
- if (!shadow) {
- shadow = chart.surface.add(Ext.apply({}, {
- type: 'path',
- group: shadowGroups[shindex],
- strokeLinejoin: "round"
- }, rendererAttributes, shadowAttr));
- }
- shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply({}, rendererAttributes, shadowAttr), i, store);
- if (animate) {
- me.onAnimate(shadow, {
- to: shadowAttr
- });
- } else {
- shadow.setAttributes(shadowAttr, true);
- }
- shadows.push(shadow);
- }
- slice.shadowAttrs[j] = shadows;
- }
- }
- }
- //do pie slices after.
- for (i = 0, ln = slices.length; i < ln; i++) {
- slice = slices[i];
- for (j = 0, rhoAcum = 0; j < layers; j++) {
- sprite = group.getAt(i * layers + j);
- deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
- //set pie slice properties
- rendererAttributes = Ext.apply({
- segment: {
- startAngle: slice.startAngle,
- endAngle: slice.endAngle,
- margin: 0,
- rho: slice.rho,
- startRho: rhoAcum + (deltaRho * donut / 100),
- endRho: rhoAcum + deltaRho
- },
- hidden: (!slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy))
- }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
- item = Ext.apply({},
- rendererAttributes.segment, {
- slice: slice,
- series: me,
- storeItem: slice.storeItem,
- index: i
- });
- me.calcMiddle(item);
- if (enableShadows) {
- item.shadows = slice.shadowAttrs[j];
- }
- items[i] = item;
- // Create a new sprite if needed (no height)
- if (!sprite) {
- spriteOptions = Ext.apply({
- type: "path",
- group: group,
- middle: item.middle
- }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
- sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
- }
- slice.sprite = slice.sprite || [];
- item.sprite = sprite;
- slice.sprite.push(sprite);
- slice.point = [item.middle.x, item.middle.y];
- if (animate) {
- rendererAttributes = me.renderer(sprite, store.getAt(i), rendererAttributes, i, store);
- sprite._to = rendererAttributes;
- sprite._animating = true;
- me.onAnimate(sprite, {
- to: rendererAttributes,
- listeners: {
- afteranimate: {
- fn: function() {
- this._animating = false;
- },
- scope: sprite
- }
- }
- });
- } else {
- rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(rendererAttributes, {
- hidden: false
- }), i, store);
- sprite.setAttributes(rendererAttributes, true);
- }
- rhoAcum += deltaRho;
- }
- }
- // Hide unused bars
- ln = group.getCount();
- for (i = 0; i < ln; i++) {
- if (!slices[(i / layers) >> 0] && group.getAt(i)) {
- group.getAt(i).hide(true);
- }
- }
- if (enableShadows) {
- lnsh = shadowGroups.length;
- for (shindex = 0; shindex < ln; shindex++) {
- if (!slices[(shindex / layers) >> 0]) {
- for (j = 0; j < lnsh; j++) {
- if (shadowGroups[j].getAt(shindex)) {
- shadowGroups[j].getAt(shindex).hide(true);
- }
- }
- }
- }
- }
- me.renderLabels();
- me.renderCallouts();
- },
- // @private callback for when creating a label sprite.
- onCreateLabel: function(storeItem, item, i, display) {
- var me = this,
- group = me.labelsGroup,
- config = me.label,
- centerX = me.centerX,
- centerY = me.centerY,
- middle = item.middle,
- endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {});
- return me.chart.surface.add(Ext.apply({
- 'type': 'text',
- 'text-anchor': 'middle',
- 'group': group,
- 'x': middle.x,
- 'y': middle.y
- }, endLabelStyle));
- },
- // @private callback for when placing a label sprite.
- onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
- var me = this,
- chart = me.chart,
- resizing = chart.resizing,
- config = me.label,
- format = config.renderer,
- field = [].concat(config.field),
- centerX = me.centerX,
- centerY = me.centerY,
- middle = item.middle,
- opt = {
- x: middle.x,
- y: middle.y
- },
- x = middle.x - centerX,
- y = middle.y - centerY,
- from = {},
- rho = 1,
- theta = Math.atan2(y, x || 1),
- dg = theta * 180 / Math.PI,
- prevDg;
- opt.hidden = false;
- if (this.__excludes && this.__excludes[i]) {
- opt.hidden = true;
- }
- function fixAngle(a) {
- if (a < 0) {
- a += 360;
- }
- return a % 360;
- }
- label.setAttributes({
- text: format(storeItem.get(field[index]))
- }, true);
- switch (display) {
- case 'outside':
- rho = Math.sqrt(x * x + y * y) * 2;
- //update positions
- opt.x = rho * Math.cos(theta) + centerX;
- opt.y = rho * Math.sin(theta) + centerY;
- break;
- case 'rotate':
- dg = fixAngle(dg);
- dg = (dg > 90 && dg < 270) ? dg + 180: dg;
- prevDg = label.attr.rotation.degrees;
- if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
- if (dg > prevDg) {
- dg -= 360;
- } else {
- dg += 360;
- }
- dg = dg % 360;
- } else {
- dg = fixAngle(dg);
- }
- //update rotation angle
- opt.rotate = {
- degrees: dg,
- x: opt.x,
- y: opt.y
- };
- break;
- default:
- break;
- }
- //ensure the object has zero translation
- opt.translate = {
- x: 0, y: 0
- };
- if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
- me.onAnimate(label, {
- to: opt
- });
- } else {
- label.setAttributes(opt, true);
- }
- label._from = from;
- },
- // @private callback for when placing a callout sprite.
- onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
- var me = this,
- chart = me.chart,
- centerX = me.centerX,
- centerY = me.centerY,
- middle = item.middle,
- opt = {
- x: middle.x,
- y: middle.y
- },
- x = middle.x - centerX,
- y = middle.y - centerY,
- rho = 1,
- rhoCenter,
- theta = Math.atan2(y, x || 1),
- bbox = callout.label.getBBox(),
- offsetFromViz = 20,
- offsetToSide = 10,
- offsetBox = 10,
- p;
- //should be able to config this.
- rho = item.endRho + offsetFromViz;
- rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3;
- //update positions
- opt.x = rho * Math.cos(theta) + centerX;
- opt.y = rho * Math.sin(theta) + centerY;
- x = rhoCenter * Math.cos(theta);
- y = rhoCenter * Math.sin(theta);
- if (chart.animate) {
- //set the line from the middle of the pie to the box.
- me.onAnimate(callout.lines, {
- to: {
- path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
- }
- });
- //set box position
- me.onAnimate(callout.box, {
- to: {
- x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
- y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
- width: bbox.width + 2 * offsetBox,
- height: bbox.height + 2 * offsetBox
- }
- });
- //set text position
- me.onAnimate(callout.label, {
- to: {
- x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
- y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
- }
- });
- } else {
- //set the line from the middle of the pie to the box.
- callout.lines.setAttributes({
- path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
- },
- true);
- //set box position
- callout.box.setAttributes({
- x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
- y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
- width: bbox.width + 2 * offsetBox,
- height: bbox.height + 2 * offsetBox
- },
- true);
- //set text position
- callout.label.setAttributes({
- x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
- y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
- },
- true);
- }
- for (p in callout) {
- callout[p].show(true);
- }
- },
- // @private handles sprite animation for the series.
- onAnimate: function(sprite, attr) {
- sprite.show();
- return this.callParent(arguments);
- },
- isItemInPoint: function(x, y, item, i) {
- var me = this,
- cx = me.centerX,
- cy = me.centerY,
- abs = Math.abs,
- dx = abs(x - cx),
- dy = abs(y - cy),
- startAngle = item.startAngle,
- endAngle = item.endAngle,
- rho = Math.sqrt(dx * dx + dy * dy),
- angle = Math.atan2(y - cy, x - cx) / me.rad;
- // normalize to the same range of angles created by drawSeries
- if (angle > me.firstAngle) {
- angle -= me.accuracy;
- }
- return (angle <= startAngle && angle > endAngle
- && rho >= item.startRho && rho <= item.endRho);
- },
- // @private hides all elements in the series.
- hideAll: function(index) {
- var i, l, shadow, shadows, sh, lsh, sprite;
- index = (isNaN(this._index) ? index : this._index) || 0;
- this.__excludes = this.__excludes || [];
- this.__excludes[index] = true;
- sprite = this.slices[index].sprite;
- for (sh = 0, lsh = sprite.length; sh < lsh; sh++) {
- sprite[sh].setAttributes({
- hidden: true
- }, true);
- }
- if (this.slices[index].shadowAttrs) {
- for (i = 0, shadows = this.slices[index].shadowAttrs, l = shadows.length; i < l; i++) {
- shadow = shadows[i];
- for (sh = 0, lsh = shadow.length; sh < lsh; sh++) {
- shadow[sh].setAttributes({
- hidden: true
- }, true);
- }
- }
- }
- this.drawSeries();
- },
- // @private shows all elements in the series.
- showAll: function(index) {
- index = (isNaN(this._index) ? index : this._index) || 0;
- this.__excludes[index] = false;
- this.drawSeries();
- },
- <span id='Ext-chart-series-Pie-method-highlightItem'> /**
- </span> * Highlight the specified item. If no item is provided the whole series will be highlighted.
- * @param item {Object} Info about the item; same format as returned by #getItemForPoint
- */
- highlightItem: function(item) {
- var me = this,
- rad = me.rad,
- highlightSegment,
- animate,
- attrs,
- i,
- shadows,
- shadow,
- ln,
- to,
- itemHighlightSegment,
- prop,
- group,
- display,
- label,
- middle,
- r,
- x,
- y;
- 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 || me.animating || (item.sprite && item.sprite._animating)) {
- return;
- }
- me.callParent([item]);
- if (!me.highlight) {
- return;
- }
- if ('segment' in me.highlightCfg) {
- highlightSegment = me.highlightCfg.segment;
- animate = me.chart.animate;
- //animate labels
- if (me.labelsGroup) {
- group = me.labelsGroup;
- display = me.label.display;
- label = group.getAt(item.index);
- middle = (item.startAngle + item.endAngle) / 2 * rad;
- r = highlightSegment.margin || 0;
- x = r * Math.cos(middle);
- y = r * Math.sin(middle);
- //TODO(nico): rounding to 1e-10
- //gives the right translation. Translation
- //was buggy for very small numbers. In this
- //case we're not looking to translate to very small
- //numbers but not to translate at all.
- if (Math.abs(x) < 1e-10) {
- x = 0;
- }
- if (Math.abs(y) < 1e-10) {
- y = 0;
- }
- if (animate) {
- label.stopAnimation();
- label.animate({
- to: {
- translate: {
- x: x,
- y: y
- }
- },
- duration: me.highlightDuration
- });
- }
- else {
- label.setAttributes({
- translate: {
- x: x,
- y: y
- }
- }, true);
- }
- }
- //animate shadows
- if (me.chart.shadow && item.shadows) {
- i = 0;
- shadows = item.shadows;
- ln = shadows.length;
- for (; i < ln; i++) {
- shadow = shadows[i];
- to = {};
- itemHighlightSegment = item.sprite._from.segment;
- for (prop in itemHighlightSegment) {
- if (! (prop in highlightSegment)) {
- to[prop] = itemHighlightSegment[prop];
- }
- }
- attrs = {
- segment: Ext.applyIf(to, me.highlightCfg.segment)
- };
- if (animate) {
- shadow.stopAnimation();
- shadow.animate({
- to: attrs,
- duration: me.highlightDuration
- });
- }
- else {
- shadow.setAttributes(attrs, true);
- }
- }
- }
- }
- },
- <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.
- * @param item {Object} Info about the item; same format as returned by #getItemForPoint
- */
- unHighlightItem: function() {
- var me = this,
- items,
- animate,
- shadowsEnabled,
- group,
- len,
- i,
- j,
- display,
- shadowLen,
- p,
- to,
- ihs,
- hs,
- sprite,
- shadows,
- shadow,
- item,
- label,
- attrs;
- if (!me.highlight) {
- return;
- }
- if (('segment' in me.highlightCfg) && me.items) {
- items = me.items;
- animate = me.chart.animate;
- shadowsEnabled = !!me.chart.shadow;
- group = me.labelsGroup;
- len = items.length;
- i = 0;
- j = 0;
- display = me.label.display;
- for (; i < len; i++) {
- item = items[i];
- if (!item) {
- continue;
- }
- sprite = item.sprite;
- if (sprite && sprite._highlighted) {
- //animate labels
- if (group) {
- label = group.getAt(item.index);
- attrs = Ext.apply({
- translate: {
- x: 0,
- y: 0
- }
- },
- display == 'rotate' ? {
- rotate: {
- x: label.attr.x,
- y: label.attr.y,
- degrees: label.attr.rotation.degrees
- }
- }: {});
- if (animate) {
- label.stopAnimation();
- label.animate({
- to: attrs,
- duration: me.highlightDuration
- });
- }
- else {
- label.setAttributes(attrs, true);
- }
- }
- if (shadowsEnabled) {
- shadows = item.shadows;
- shadowLen = shadows.length;
- for (; j < shadowLen; j++) {
- to = {};
- ihs = item.sprite._to.segment;
- hs = item.sprite._from.segment;
- Ext.apply(to, hs);
- for (p in ihs) {
- if (! (p in hs)) {
- to[p] = ihs[p];
- }
- }
- shadow = shadows[j];
- if (animate) {
- shadow.stopAnimation();
- shadow.animate({
- to: {
- segment: to
- },
- duration: me.highlightDuration
- });
- }
- else {
- shadow.setAttributes({ segment: to }, true);
- }
- }
- }
- }
- }
- }
- 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
- */
- getLegendColor: function(index) {
- var me = this;
- return (me.colorSet && me.colorSet[index % me.colorSet.length]) || me.colorArrayStyle[index % me.colorArrayStyle.length];
- }
- });
- </pre>
- </body>
- </html>
|