]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.chart.CartesianChart\r | |
3 | * @extends Ext.chart.AbstractChart\r | |
4 | * @xtype cartesian\r | |
5 | *\r | |
6 | * Represents a chart that uses cartesian coordinates.\r | |
7 | * A cartesian chart has two directions, X direction and Y direction.\r | |
8 | * The series and axes are coordinated along these directions.\r | |
9 | * By default the x direction is horizontal and y direction is vertical,\r | |
10 | * You can swap the direction by setting the {@link #flipXY} config to `true`.\r | |
11 | *\r | |
12 | * Cartesian series often treats x direction an y direction differently.\r | |
13 | * In most cases, data on x direction are assumed to be monotonically increasing.\r | |
14 | * Based on this property, cartesian series can be trimmed and summarized properly\r | |
15 | * to gain a better performance.\r | |
16 | *\r | |
17 | */\r | |
18 | \r | |
19 | Ext.define('Ext.chart.CartesianChart', {\r | |
20 | extend: 'Ext.chart.AbstractChart',\r | |
21 | alternateClassName: 'Ext.chart.Chart',\r | |
22 | requires: [\r | |
23 | 'Ext.chart.grid.HorizontalGrid',\r | |
24 | 'Ext.chart.grid.VerticalGrid'\r | |
25 | ],\r | |
26 | xtype: [ 'cartesian', 'chart' ],\r | |
27 | isCartesian: true,\r | |
28 | \r | |
29 | config: {\r | |
30 | /**\r | |
31 | * @cfg {Boolean} flipXY Flip the direction of X and Y axis.\r | |
32 | * If flipXY is `true`, the X axes will be vertical and Y axes will be horizontal.\r | |
33 | * Note that {@link Ext.chart.axis.Axis#position positions} of chart axes have\r | |
34 | * to be updated accordingly: axes positioned to the `top` and `bottom` should\r | |
35 | * be positioned to the `left` or `right` and vice versa.\r | |
36 | */\r | |
37 | flipXY: false,\r | |
38 | /*\r | |
39 | \r | |
40 | While it may seem tedious to change the position config of all axes every time\r | |
41 | when the value of the flipXY config is changed, it's hard to predict the\r | |
42 | expectaction of the user here, as illustrated below.\r | |
43 | \r | |
44 | The 'num' and 'cat' here stand for the numeric and the category axis, respectively.\r | |
45 | And the right column shows the expected (subjective) result of setting the flipXY\r | |
46 | config of the chart to 'true'.\r | |
47 | \r | |
48 | As one can see, there's no single rule (e.g. position swapping, clockwise 90° chart\r | |
49 | rotation) that will produce a universally accepted result.\r | |
50 | So we are letting the user decide, instead of doing it for them.\r | |
51 | \r | |
52 | ---------------------------------------------\r | |
53 | | flipXY: false | flipXY: true |\r | |
54 | ---------------------------------------------\r | |
55 | | ^ | ^ |\r | |
56 | | | * | | * * * |\r | |
57 | | num1 | * * | cat | * * |\r | |
58 | | | * * * | | * |\r | |
59 | | --------> | --------> |\r | |
60 | | cat | num1 |\r | |
61 | ---------------------------------------------\r | |
62 | | | num1 |\r | |
63 | | ^ ^ | ^-------> |\r | |
64 | | | * | | | * * * |\r | |
65 | | num1 | * * | num2 | cat | * * |\r | |
66 | | | * * * | | | * |\r | |
67 | | --------> | --------> |\r | |
68 | | cat | num2 |\r | |
69 | ---------------------------------------------\r | |
70 | \r | |
71 | */\r | |
72 | \r | |
73 | innerRect: [0, 0, 1, 1],\r | |
74 | \r | |
75 | /**\r | |
76 | * @cfg {Object} innerPadding The amount of inner padding in pixels.\r | |
77 | * Inner padding is the padding from the innermost axes to the series.\r | |
78 | */\r | |
79 | innerPadding: {\r | |
80 | top: 0,\r | |
81 | left: 0,\r | |
82 | right: 0,\r | |
83 | bottom: 0\r | |
84 | }\r | |
85 | },\r | |
86 | \r | |
87 | applyInnerPadding: function (padding, oldPadding) {\r | |
88 | if (!Ext.isObject(padding)) {\r | |
89 | return Ext.util.Format.parseBox(padding);\r | |
90 | } else if (!oldPadding) {\r | |
91 | return padding;\r | |
92 | } else {\r | |
93 | return Ext.apply(oldPadding, padding);\r | |
94 | }\r | |
95 | },\r | |
96 | \r | |
97 | getDirectionForAxis: function (position) {\r | |
98 | var flipXY = this.getFlipXY();\r | |
99 | if (position === 'left' || position === 'right') {\r | |
100 | if (flipXY) {\r | |
101 | return 'X';\r | |
102 | } else {\r | |
103 | return 'Y';\r | |
104 | }\r | |
105 | } else {\r | |
106 | if (flipXY) {\r | |
107 | return 'Y';\r | |
108 | } else {\r | |
109 | return 'X';\r | |
110 | }\r | |
111 | }\r | |
112 | },\r | |
113 | \r | |
114 | /**\r | |
115 | * Layout the axes and series.\r | |
116 | */\r | |
117 | performLayout: function () {\r | |
118 | var me = this;\r | |
119 | \r | |
120 | me.animationSuspendCount++;\r | |
121 | if (me.callParent() === false) {\r | |
122 | --me.animationSuspendCount;\r | |
123 | return;\r | |
124 | }\r | |
125 | me.suspendThicknessChanged();\r | |
126 | \r | |
127 | var chartRect = me.getSurface('chart').getRect(),\r | |
128 | width = chartRect[2],\r | |
129 | height = chartRect[3],\r | |
130 | axes = me.getAxes(), axis,\r | |
131 | seriesList = me.getSeries(), series,\r | |
132 | axisSurface, thickness,\r | |
133 | insetPadding = me.getInsetPadding(),\r | |
134 | innerPadding = me.getInnerPadding(),\r | |
135 | surface, gridSurface,\r | |
136 | shrinkBox = Ext.apply({}, insetPadding),\r | |
137 | mainRect, innerWidth, innerHeight,\r | |
138 | elements, floating, floatingValue, matrix, i, ln,\r | |
139 | isRtl = me.getInherited().rtl,\r | |
140 | flipXY = me.getFlipXY();\r | |
141 | \r | |
142 | if (width <= 0 || height <= 0) {\r | |
143 | return;\r | |
144 | }\r | |
145 | \r | |
146 | for (i = 0; i < axes.length; i++) {\r | |
147 | axis = axes[i];\r | |
148 | axisSurface = axis.getSurface();\r | |
149 | floating = axis.getFloating();\r | |
150 | floatingValue = floating ? floating.value : null;\r | |
151 | thickness = axis.getThickness();\r | |
152 | switch (axis.getPosition()) {\r | |
153 | case 'top':\r | |
154 | axisSurface.setRect([0, shrinkBox.top + 1, width, thickness]);\r | |
155 | break;\r | |
156 | case 'bottom':\r | |
157 | axisSurface.setRect([0, height - (shrinkBox.bottom + thickness), width, thickness]);\r | |
158 | break;\r | |
159 | case 'left':\r | |
160 | axisSurface.setRect([shrinkBox.left, 0, thickness, height]);\r | |
161 | break;\r | |
162 | case 'right':\r | |
163 | axisSurface.setRect([width - (shrinkBox.right + thickness), 0, thickness, height]);\r | |
164 | break;\r | |
165 | }\r | |
166 | if (floatingValue === null) {\r | |
167 | shrinkBox[axis.getPosition()] += thickness;\r | |
168 | }\r | |
169 | }\r | |
170 | \r | |
171 | width -= shrinkBox.left + shrinkBox.right;\r | |
172 | height -= shrinkBox.top + shrinkBox.bottom;\r | |
173 | \r | |
174 | mainRect = [shrinkBox.left, shrinkBox.top, width, height];\r | |
175 | \r | |
176 | shrinkBox.left += innerPadding.left;\r | |
177 | shrinkBox.top += innerPadding.top;\r | |
178 | shrinkBox.right += innerPadding.right;\r | |
179 | shrinkBox.bottom += innerPadding.bottom;\r | |
180 | \r | |
181 | innerWidth = width - innerPadding.left - innerPadding.right;\r | |
182 | innerHeight = height - innerPadding.top - innerPadding.bottom;\r | |
183 | \r | |
184 | me.setInnerRect([shrinkBox.left, shrinkBox.top, innerWidth, innerHeight]);\r | |
185 | \r | |
186 | if (innerWidth <= 0 || innerHeight <= 0) {\r | |
187 | return;\r | |
188 | }\r | |
189 | \r | |
190 | me.setMainRect(mainRect);\r | |
191 | me.getSurface().setRect(mainRect);\r | |
192 | \r | |
193 | for (i = 0, ln = me.surfaceMap.grid && me.surfaceMap.grid.length; i < ln; i++) {\r | |
194 | gridSurface = me.surfaceMap.grid[i];\r | |
195 | gridSurface.setRect(mainRect);\r | |
196 | gridSurface.matrix.set(1, 0, 0, 1, innerPadding.left, innerPadding.top);\r | |
197 | gridSurface.matrix.inverse(gridSurface.inverseMatrix);\r | |
198 | }\r | |
199 | \r | |
200 | for (i = 0; i < axes.length; i++) {\r | |
201 | axis = axes[i];\r | |
202 | axisSurface = axis.getSurface();\r | |
203 | matrix = axisSurface.matrix;\r | |
204 | elements = matrix.elements;\r | |
205 | switch (axis.getPosition()) {\r | |
206 | case 'top':\r | |
207 | case 'bottom':\r | |
208 | elements[4] = shrinkBox.left;\r | |
209 | axis.setLength(innerWidth);\r | |
210 | break;\r | |
211 | case 'left':\r | |
212 | case 'right':\r | |
213 | elements[5] = shrinkBox.top;\r | |
214 | axis.setLength(innerHeight);\r | |
215 | break;\r | |
216 | }\r | |
217 | axis.updateTitleSprite();\r | |
218 | matrix.inverse(axisSurface.inverseMatrix);\r | |
219 | }\r | |
220 | \r | |
221 | for (i = 0, ln = seriesList.length; i < ln; i++) {\r | |
222 | series = seriesList[i];\r | |
223 | surface = series.getSurface();\r | |
224 | surface.setRect(mainRect);\r | |
225 | if (flipXY) {\r | |
226 | if (isRtl) {\r | |
227 | surface.matrix.set(0, -1, -1, 0,\r | |
228 | innerPadding.left + innerWidth,\r | |
229 | innerPadding.top + innerHeight);\r | |
230 | } else {\r | |
231 | surface.matrix.set(0, -1, 1, 0,\r | |
232 | innerPadding.left,\r | |
233 | innerPadding.top + innerHeight);\r | |
234 | }\r | |
235 | } else {\r | |
236 | surface.matrix.set(1, 0, 0, -1,\r | |
237 | innerPadding.left,\r | |
238 | innerPadding.top + innerHeight);\r | |
239 | }\r | |
240 | surface.matrix.inverse(surface.inverseMatrix);\r | |
241 | series.getOverlaySurface().setRect(mainRect);\r | |
242 | }\r | |
243 | me.redraw();\r | |
244 | \r | |
245 | me.animationSuspendCount--;\r | |
246 | me.resumeThicknessChanged();\r | |
247 | },\r | |
248 | \r | |
249 | refloatAxes: function () {\r | |
250 | var me = this,\r | |
251 | axes = me.getAxes(),\r | |
252 | axesCount = (axes && axes.length) || 0,\r | |
253 | axis, axisSurface, axisRect,\r | |
254 | floating, value, alongAxis, matrix,\r | |
255 | size = me.getChartSize(),\r | |
256 | inset = me.getInsetPadding(),\r | |
257 | inner = me.getInnerPadding(),\r | |
258 | width = size.width - inset.left - inset.right,\r | |
259 | height = size.height - inset.top - inset.bottom,\r | |
260 | isHorizontal, i;\r | |
261 | \r | |
262 | for (i = 0; i < axesCount; i++) {\r | |
263 | axis = axes[i];\r | |
264 | floating = axis.getFloating();\r | |
265 | value = floating ? floating.value : null;\r | |
266 | if (value === null) {\r | |
267 | delete axis.floatingAtCoord;\r | |
268 | continue;\r | |
269 | }\r | |
270 | axisSurface = axis.getSurface();\r | |
271 | axisRect = axisSurface.getRect();\r | |
272 | if (!axisRect) {\r | |
273 | continue;\r | |
274 | }\r | |
275 | axisRect = axisRect.slice();\r | |
276 | alongAxis = me.getAxis(floating.alongAxis);\r | |
277 | if (alongAxis) {\r | |
278 | isHorizontal = alongAxis.getAlignment() === 'horizontal';\r | |
279 | if (Ext.isString(value)) {\r | |
280 | value = alongAxis.getCoordFor(value);\r | |
281 | }\r | |
282 | alongAxis.floatingAxes[axis.getId()] = value;\r | |
283 | matrix = alongAxis.getSprites()[0].attr.matrix;\r | |
284 | if (isHorizontal) {\r | |
285 | value = value * matrix.getXX() + matrix.getDX();\r | |
286 | axis.floatingAtCoord = value + inner.left + inner.right;\r | |
287 | } else {\r | |
288 | value = value * matrix.getYY() + matrix.getDY();\r | |
289 | axis.floatingAtCoord = value + inner.top + inner.bottom;\r | |
290 | }\r | |
291 | } else {\r | |
292 | isHorizontal = axis.getAlignment() === 'horizontal';\r | |
293 | if (isHorizontal) {\r | |
294 | axis.floatingAtCoord = value + inner.top + inner.bottom;\r | |
295 | } else {\r | |
296 | axis.floatingAtCoord = value + inner.left + inner.right;\r | |
297 | }\r | |
298 | value = axisSurface.roundPixel(0.01 * value * (isHorizontal ? height : width));\r | |
299 | }\r | |
300 | switch (axis.getPosition()) {\r | |
301 | case 'top':\r | |
302 | axisRect[1] = inset.top + inner.top + value - axisRect[3] + 1;\r | |
303 | break;\r | |
304 | case 'bottom':\r | |
305 | axisRect[1] = inset.top + inner.top + (alongAxis ? value : height - value);\r | |
306 | break;\r | |
307 | case 'left':\r | |
308 | axisRect[0] = inset.left + inner.left + value - axisRect[2];\r | |
309 | break;\r | |
310 | case 'right':\r | |
311 | axisRect[0] = inset.left + inner.left + (alongAxis ? value : width - value) - 1;\r | |
312 | break;\r | |
313 | }\r | |
314 | axisSurface.setRect(axisRect);\r | |
315 | }\r | |
316 | },\r | |
317 | \r | |
318 | redraw: function () {\r | |
319 | var me = this,\r | |
320 | seriesList = me.getSeries(),\r | |
321 | axes = me.getAxes(),\r | |
322 | rect = me.getMainRect(),\r | |
323 | innerWidth, innerHeight,\r | |
324 | innerPadding = me.getInnerPadding(),\r | |
325 | sprites, xRange, yRange, isSide, attr, i, j, ln,\r | |
326 | axis, axisX, axisY, range, visibleRange,\r | |
327 | flipXY = me.getFlipXY(),\r | |
328 | zBase = 1000,\r | |
329 | zIndex, markersZIndex,\r | |
330 | series, sprite, markers;\r | |
331 | \r | |
332 | if (!rect) {\r | |
333 | return;\r | |
334 | }\r | |
335 | \r | |
336 | innerWidth = rect[2] - innerPadding.left - innerPadding.right;\r | |
337 | innerHeight = rect[3] - innerPadding.top - innerPadding.bottom;\r | |
338 | \r | |
339 | for (i = 0; i < seriesList.length; i++) {\r | |
340 | series = seriesList[i];\r | |
341 | if ((axisX = series.getXAxis())) {\r | |
342 | visibleRange = axisX.getVisibleRange();\r | |
343 | xRange = axisX.getRange();\r | |
344 | xRange = [\r | |
345 | xRange[0] + (xRange[1] - xRange[0]) * visibleRange[0],\r | |
346 | xRange[0] + (xRange[1] - xRange[0]) * visibleRange[1]\r | |
347 | ];\r | |
348 | } else {\r | |
349 | xRange = series.getXRange();\r | |
350 | }\r | |
351 | \r | |
352 | if ((axisY = series.getYAxis())) {\r | |
353 | visibleRange = axisY.getVisibleRange();\r | |
354 | yRange = axisY.getRange();\r | |
355 | yRange = [\r | |
356 | yRange[0] + (yRange[1] - yRange[0]) * visibleRange[0],\r | |
357 | yRange[0] + (yRange[1] - yRange[0]) * visibleRange[1]\r | |
358 | ];\r | |
359 | } else {\r | |
360 | yRange = series.getYRange();\r | |
361 | }\r | |
362 | \r | |
363 | attr = {\r | |
364 | visibleMinX: xRange[0],\r | |
365 | visibleMaxX: xRange[1],\r | |
366 | visibleMinY: yRange[0],\r | |
367 | visibleMaxY: yRange[1],\r | |
368 | innerWidth: innerWidth,\r | |
369 | innerHeight: innerHeight,\r | |
370 | flipXY: flipXY\r | |
371 | };\r | |
372 | \r | |
373 | sprites = series.getSprites();\r | |
374 | for (j = 0, ln = sprites.length; j < ln; j++) {\r | |
375 | \r | |
376 | // All the series now share the same surface, so we must assign\r | |
377 | // the sprites a zIndex that depends on the index of their series.\r | |
378 | sprite = sprites[j];\r | |
379 | zIndex = sprite.attr.zIndex;\r | |
380 | if (zIndex < zBase) {\r | |
381 | // Set the sprite's zIndex\r | |
382 | zIndex += (i + 1) * 100 + zBase;\r | |
383 | sprite.attr.zIndex = zIndex;\r | |
384 | // If the sprite is a MarkerHolder, set zIndex of the bound markers as well.\r | |
385 | // Do this for the 'items' markers only, as those are the only ones\r | |
386 | // that go into the 'series' surface. 'labels' and 'markers' markers\r | |
387 | // go into the 'overlay' surface instead.\r | |
388 | markers = sprite.getMarker('items');\r | |
389 | if (markers) {\r | |
390 | markersZIndex = markers.attr.zIndex;\r | |
391 | if (markersZIndex === Number.MAX_VALUE) {\r | |
392 | markers.attr.zIndex = zIndex;\r | |
393 | } else if (markersZIndex < zBase) {\r | |
394 | markers.attr.zIndex = zIndex + markersZIndex;\r | |
395 | }\r | |
396 | }\r | |
397 | }\r | |
398 | \r | |
399 | sprite.setAttributes(attr, true);\r | |
400 | }\r | |
401 | }\r | |
402 | \r | |
403 | for (i = 0; i < axes.length; i++) {\r | |
404 | axis = axes[i];\r | |
405 | isSide = axis.isSide();\r | |
406 | sprites = axis.getSprites();\r | |
407 | range = axis.getRange();\r | |
408 | visibleRange = axis.getVisibleRange();\r | |
409 | attr = {\r | |
410 | dataMin: range[0],\r | |
411 | dataMax: range[1],\r | |
412 | visibleMin: visibleRange[0],\r | |
413 | visibleMax: visibleRange[1]\r | |
414 | };\r | |
415 | if (isSide) {\r | |
416 | attr.length = innerHeight;\r | |
417 | attr.startGap = innerPadding.bottom;\r | |
418 | attr.endGap = innerPadding.top;\r | |
419 | } else {\r | |
420 | attr.length = innerWidth;\r | |
421 | attr.startGap = innerPadding.left;\r | |
422 | attr.endGap = innerPadding.right;\r | |
423 | }\r | |
424 | for (j = 0, ln = sprites.length; j < ln; j++) {\r | |
425 | sprites[j].setAttributes(attr, true);\r | |
426 | }\r | |
427 | }\r | |
428 | me.renderFrame();\r | |
429 | me.callParent(arguments);\r | |
430 | },\r | |
431 | \r | |
432 | renderFrame: function () {\r | |
433 | this.refloatAxes();\r | |
434 | this.callParent();\r | |
435 | }\r | |
436 | });\r |