]> git.proxmox.com Git - extjs.git/blob - extjs/packages/charts/src/chart/series/sprite/PieSlice.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / charts / src / chart / series / sprite / PieSlice.js
1 /**
2 * @class Ext.chart.series.sprite.PieSlice
3 *
4 * Pie slice sprite.
5 */
6 Ext.define('Ext.chart.series.sprite.PieSlice', {
7 extend: 'Ext.draw.sprite.Sector',
8 mixins: {
9 markerHolder: 'Ext.chart.MarkerHolder'
10 },
11 alias: 'sprite.pieslice',
12
13 inheritableStatics: {
14 def: {
15 processors: {
16 /**
17 * @cfg {Boolean} [doCallout=true]
18 * 'true' if the pie series uses label callouts.
19 */
20 doCallout: 'bool',
21
22 /**
23 * @cfg {String} [label='']
24 * Label associated with the Pie sprite.
25 */
26 label: 'string',
27
28 // @deprecated Use series.label.orientation config instead.
29 // @since 5.0.1
30 rotateLabels: 'bool',
31
32 /**
33 * @cfg {Number} [labelOverflowPadding=10]
34 * Padding around labels to determine overlap.
35 * Any negative number allows the labels to overlap.
36 */
37 labelOverflowPadding: 'number',
38
39 renderer: 'default'
40 },
41 defaults: {
42 doCallout: true,
43 rotateLabels: true,
44 label: '',
45 labelOverflowPadding: 10,
46 renderer: null
47 }
48 }
49 },
50
51 config: {
52 /**
53 * @private
54 * @cfg {Object} rendererData The object that is passed to the renderer.
55 *
56 * For instance when the PieSlice sprite is used in a Gauge chart, the object
57 * contains the 'store' and 'angleField' properties, and the 'value' as well
58 * for that one PieSlice that is used to draw the needle of the Gauge.
59 */
60 rendererData: null,
61 rendererIndex: 0,
62 series: null
63 },
64
65 setGradientBBox: function (ctx, rect) {
66 var me = this,
67 attr = me.attr,
68 hasGradients = (attr.fillStyle && attr.fillStyle.isGradient) ||
69 (attr.strokeStyle && attr.strokeStyle.isGradient);
70
71 if (hasGradients && !attr.constrainGradients) {
72 var midAngle = me.getMidAngle(),
73 margin = attr.margin,
74 cx = attr.centerX,
75 cy = attr.centerY,
76 r = attr.endRho,
77 matrix = attr.matrix,
78 scaleX = matrix.getScaleX(),
79 scaleY = matrix.getScaleY(),
80 w = scaleX * r,
81 h = scaleY * r,
82 bbox = {
83 width: w + w,
84 height: h + h
85 };
86 if (margin) {
87 cx += margin * Math.cos(midAngle);
88 cy += margin * Math.sin(midAngle);
89 }
90 bbox.x = matrix.x(cx, cy) - w;
91 bbox.y = matrix.y(cx, cy) - h;
92 ctx.setGradientBBox(bbox);
93 } else {
94 me.callParent([ctx, rect]);
95 }
96 },
97
98 render: function (surface, ctx, clip, rect) {
99 var me = this,
100 attr = me.attr,
101 itemCfg = {},
102 changes;
103
104 if (attr.renderer) {
105 itemCfg = {
106 type: 'sector',
107 text: attr.text,
108 centerX: attr.centerX,
109 centerY: attr.centerY,
110 margin: attr.margin,
111 startAngle: Math.min(attr.startAngle, attr.endAngle),
112 endAngle: Math.max(attr.startAngle, attr.endAngle),
113 startRho: Math.min(attr.startRho, attr.endRho),
114 endRho: Math.max(attr.startRho, attr.endRho)
115 };
116 changes = Ext.callback(attr.renderer, null,
117 [me, itemCfg, me.rendererData, me.rendererIndex], 0, me.getSeries());
118 me.setAttributes(changes);
119 me.useAttributes(ctx, clip);
120 }
121
122 // Draw the sector
123 me.callParent([surface, ctx, clip, rect]);
124
125 // Draw the labels
126 if (attr.label && me.getMarker('labels')) {
127 me.placeLabel();
128 }
129 },
130
131 placeLabel: function () {
132 var me = this,
133 attr = me.attr,
134 attributeId = attr.attributeId,
135 startAngle = Math.min(attr.startAngle, attr.endAngle),
136 endAngle = Math.max(attr.startAngle, attr.endAngle),
137 midAngle = (startAngle + endAngle) * 0.5,
138 margin = attr.margin,
139 centerX = attr.centerX,
140 centerY = attr.centerY,
141 sinMidAngle = Math.sin(midAngle),
142 cosMidAngle = Math.cos(midAngle),
143 startRho = Math.min(attr.startRho, attr.endRho) + margin,
144 endRho = Math.max(attr.startRho, attr.endRho) + margin,
145 midRho = (startRho + endRho) * 0.5,
146 surfaceMatrix = me.surfaceMatrix,
147 labelCfg = me.labelCfg || (me.labelCfg = {}),
148 label = me.getMarker('labels'),
149 labelTpl = label.getTemplate(),
150 calloutLine = labelTpl.getCalloutLine(),
151 calloutLineLength = calloutLine && calloutLine.length || 40,
152 labelBox, x, y, changes, params;
153
154 surfaceMatrix.appendMatrix(attr.matrix);
155
156 labelCfg.text = attr.label;
157
158 x = centerX + cosMidAngle * midRho;
159 y = centerY + sinMidAngle * midRho;
160 labelCfg.x = surfaceMatrix.x(x, y);
161 labelCfg.y = surfaceMatrix.y(x, y);
162
163 x = centerX + cosMidAngle * endRho;
164 y = centerY + sinMidAngle * endRho;
165 labelCfg.calloutStartX = surfaceMatrix.x(x, y);
166 labelCfg.calloutStartY = surfaceMatrix.y(x, y);
167
168 x = centerX + cosMidAngle * (endRho + calloutLineLength);
169 y = centerY + sinMidAngle * (endRho + calloutLineLength);
170 labelCfg.calloutPlaceX = surfaceMatrix.x(x, y);
171 labelCfg.calloutPlaceY = surfaceMatrix.y(x, y);
172
173 if (!attr.rotateLabels) {
174 labelCfg.rotationRads = 0;
175 //<debug>
176 Ext.log.warn("'series.style.rotateLabels' config is deprecated. " +
177 "Use 'series.label.orientation' config instead.");
178 //</debug>
179 } else {
180 switch (labelTpl.attr.orientation) {
181 case 'horizontal':
182 labelCfg.rotationRads = midAngle + Math.atan2(
183 surfaceMatrix.y(1, 0) - surfaceMatrix.y(0, 0),
184 surfaceMatrix.x(1, 0) - surfaceMatrix.x(0, 0)
185 ) + Math.PI/2;
186 break;
187 case 'vertical':
188 labelCfg.rotationRads = midAngle + Math.atan2(
189 surfaceMatrix.y(1, 0) - surfaceMatrix.y(0, 0),
190 surfaceMatrix.x(1, 0) - surfaceMatrix.x(0, 0)
191 );
192 break;
193 }
194 }
195 labelCfg.calloutColor = (calloutLine && calloutLine.color) || me.attr.fillStyle;
196 if (calloutLine) {
197 if (calloutLine.width) {
198 labelCfg.calloutWidth = calloutLine.width;
199 }
200 } else {
201 labelCfg.calloutHasLine = false;
202 }
203 labelCfg.globalAlpha = attr.globalAlpha * attr.fillOpacity;
204
205 // If a slice is empty, don't display the label.
206 // This behavior can be overridden by a renderer.
207 labelCfg.hidden = (attr.startAngle == attr.endAngle);
208
209 if (labelTpl.attr.renderer) {
210 params = [me.attr.label, label, labelCfg, me.rendererData, me.rendererIndex];
211 changes = Ext.callback(labelTpl.attr.renderer, null, params, 0, me.getSeries());
212 if (typeof changes === 'string') {
213 labelCfg.text = changes;
214 } else {
215 Ext.apply(labelCfg, changes);
216 }
217 }
218 me.putMarker('labels', labelCfg, attributeId);
219
220 labelBox = me.getMarkerBBox('labels', attributeId, true);
221 if (labelBox) {
222 if (attr.doCallout) {
223 if (labelTpl.attr.display === 'outside') {
224 me.putMarker('labels', {
225 callout: 1
226 }, attributeId);
227 } else if (labelTpl.attr.display === 'inside') {
228 me.putMarker('labels', {
229 callout: 0
230 }, attributeId);
231 } else {
232 me.putMarker('labels', {
233 callout: 1 - me.sliceContainsLabel(attr, labelBox)
234 }, attributeId);
235 }
236 } else {
237 me.putMarker('labels', {
238 globalAlpha: me.sliceContainsLabel(attr, labelBox)
239 }, attributeId);
240 }
241 }
242 },
243
244 sliceContainsLabel: function (attr, bbox) {
245 var padding = attr.labelOverflowPadding,
246 middle = (attr.endRho + attr.startRho) / 2,
247 outer = middle + (bbox.width + padding) / 2,
248 inner = middle - (bbox.width + padding) / 2,
249 sliceAngle, l1, l2, l3;
250
251 if (padding < 0) {
252 return 1;
253 }
254 if (bbox.width + padding * 2 > (attr.endRho - attr.startRho)) {
255 return 0;
256 }
257 l1 = Math.sqrt(attr.endRho * attr.endRho - outer * outer);
258 l2 = Math.sqrt(attr.endRho * attr.endRho - inner * inner);
259 sliceAngle = Math.abs(attr.endAngle - attr.startAngle);
260 l3 = (sliceAngle > Math.PI/2 ? inner : Math.abs(Math.tan(sliceAngle / 2)) * inner);
261 if (bbox.height + padding * 2 > Math.min(l1, l2, l3) * 2) {
262 return 0;
263 }
264 return 1;
265 }
266 });