1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-chart.axis.Axis'>/**
2 </span> * @class Ext.chart.axis.Axis
3 * @extends Ext.chart.axis.Abstract
5 * Defines axis for charts. The axis position, type, style can be configured.
6 * The axes are defined in an axes array of configuration objects where the type,
7 * field, grid and other configuration options can be set. To know more about how
8 * to create a Chart please check the Chart class documentation. Here's an example for the axes part:
9 * An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be:
15 * fields: ['data1', 'data2', 'data3'],
16 * title: 'Number of Hits',
30 * title: 'Month of the Year',
39 * In this case we use a `Numeric` axis for displaying the values of the Area series and a `Category` axis for displaying the names of
40 * the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart.
41 * Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the
42 * category axis the labels will be rotated so they can fit the space better.
44 Ext.define('Ext.chart.axis.Axis', {
46 /* Begin Definitions */
48 extend: 'Ext.chart.axis.Abstract',
50 alternateClassName: 'Ext.chart.Axis',
52 requires: ['Ext.draw.Draw'],
56 <span id='Ext-chart.axis.Axis-cfg-majorTickSteps'> /**
57 </span> * @cfg {Number} majorTickSteps
58 * If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.
61 <span id='Ext-chart.axis.Axis-cfg-minorTickSteps'> /**
62 </span> * @cfg {Number} minorTickSteps
63 * The number of small ticks between two major ticks. Default is zero.
66 <span id='Ext-chart.axis.Axis-cfg-dashSize'> /**
67 </span> * @cfg {Number} dashSize
68 * The size of the dash marker. Default's 3.
72 <span id='Ext-chart.axis.Axis-cfg-position'> /**
73 </span> * @cfg {String} position
74 * Where to set the axis. Available options are `left`, `bottom`, `right`, `top`. Default's `bottom`.
81 <span id='Ext-chart.axis.Axis-cfg-length'> /**
82 </span> * @cfg {Number} length
83 * Offset axis position. Default's 0.
87 <span id='Ext-chart.axis.Axis-cfg-width'> /**
88 </span> * @cfg {Number} width
89 * Offset axis width. Default's 0.
93 majorTickSteps: false,
96 applyData: Ext.emptyFn,
98 // @private creates a structure with start, end and step points.
99 calcEnds: function() {
104 store = me.chart.substore || me.chart.store,
105 series = me.chart.series.items,
108 min = isNaN(me.minimum) ? Infinity : me.minimum,
109 max = isNaN(me.maximum) ? -Infinity : me.maximum,
110 prevMin = me.prevMin,
111 prevMax = me.prevMax,
116 i, l, values, rec, out;
118 //if one series is stacked I have to aggregate the values
120 for (i = 0, l = series.length; !aggregate && i < l; i++) {
121 aggregate = aggregate || series[i].stacked;
122 excludes = series[i].__excludes || excludes;
124 store.each(function(record) {
126 if (!isFinite(min)) {
129 for (values = [0, 0], i = 0; i < ln; i++) {
133 rec = record.get(fields[i]);
134 values[+(rec > 0)] += math.abs(rec);
136 max = mmax(max, -values[0], values[1]);
137 min = mmin(min, -values[0], values[1]);
140 for (i = 0; i < ln; i++) {
144 value = record.get(fields[i]);
145 max = mmax(max, value);
146 min = mmin(min, value);
150 if (!isFinite(max)) {
151 max = me.prevMax || 0;
153 if (!isFinite(min)) {
154 min = me.prevMin || 0;
156 //normalize min max for snapEnds.
157 if (min != max && (max != (max >> 0))) {
158 max = (max >> 0) + 1;
160 out = Ext.draw.Draw.snapEnds(min, max, me.majorTickSteps !== false ? (me.majorTickSteps +1) : me.steps);
163 if (!isNaN(me.maximum)) {
164 //TODO(nico) users are responsible for their own minimum/maximum values set.
165 //Clipping should be added to remove lines in the chart which are below the axis.
168 if (!isNaN(me.minimum)) {
169 //TODO(nico) users are responsible for their own minimum/maximum values set.
170 //Clipping should be added to remove lines in the chart which are below the axis.
171 out.from = me.minimum;
174 //Adjust after adjusting minimum and maximum
175 out.step = (out.to - out.from) / (outto - outfrom) * out.step;
177 if (me.adjustMaximumByMajorUnit) {
180 if (me.adjustMinimumByMajorUnit) {
181 out.from -= out.step;
183 me.prevMin = min == max? 0 : min;
188 <span id='Ext-chart.axis.Axis-method-drawAxis'> /**
189 </span> * Renders the axis into the screen and updates it's position.
191 drawAxis: function (init) {
196 gutterX = me.chart.maxGutter[0],
197 gutterY = me.chart.maxGutter[1],
198 dashSize = me.dashSize,
199 subDashesX = me.minorTickSteps || 0,
200 subDashesY = me.minorTickSteps || 0,
202 position = me.position,
205 stepCalcs = me.applyData(),
206 step = stepCalcs.step,
207 steps = stepCalcs.steps,
208 from = stepCalcs.from,
219 //If no steps are specified
220 //then don't draw the axis. This generally happens
221 //when an empty store.
222 if (me.hidden || isNaN(step) || (from == to)) {
226 me.from = stepCalcs.from;
227 me.to = stepCalcs.to;
228 if (position == 'left' || position == 'right') {
229 currentX = Math.floor(x) + 0.5;
230 path = ["M", currentX, y, "l", 0, -length];
231 trueLength = length - (gutterY * 2);
234 currentY = Math.floor(y) + 0.5;
235 path = ["M", x, currentY, "l", length, 0];
236 trueLength = length - (gutterX * 2);
239 delta = trueLength / (steps || 1);
240 dashesX = Math.max(subDashesX +1, 0);
241 dashesY = Math.max(subDashesY +1, 0);
242 if (me.type == 'Numeric') {
244 me.labels = [stepCalcs.from];
246 if (position == 'right' || position == 'left') {
247 currentY = y - gutterY;
248 currentX = x - ((position == 'left') * dashSize * 2);
249 while (currentY >= y - gutterY - trueLength) {
250 path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashSize * 2 + 1, 0);
251 if (currentY != y - gutterY) {
252 for (i = 1; i < dashesY; i++) {
253 path.push("M", currentX + dashSize, Math.floor(currentY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);
256 inflections.push([ Math.floor(x), Math.floor(currentY) ]);
259 me.labels.push(me.labels[me.labels.length -1] + step);
265 if (Math.round(currentY + delta - (y - gutterY - trueLength))) {
266 path.push("M", currentX, Math.floor(y - length + gutterY) + 0.5, "l", dashSize * 2 + 1, 0);
267 for (i = 1; i < dashesY; i++) {
268 path.push("M", currentX + dashSize, Math.floor(y - length + gutterY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);
270 inflections.push([ Math.floor(x), Math.floor(currentY) ]);
272 me.labels.push(me.labels[me.labels.length -1] + step);
276 currentX = x + gutterX;
277 currentY = y - ((position == 'top') * dashSize * 2);
278 while (currentX <= x + gutterX + trueLength) {
279 path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);
280 if (currentX != x + gutterX) {
281 for (i = 1; i < dashesX; i++) {
282 path.push("M", Math.floor(currentX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);
285 inflections.push([ Math.floor(currentX), Math.floor(y) ]);
288 me.labels.push(me.labels[me.labels.length -1] + step);
294 if (Math.round(currentX - delta - (x + gutterX + trueLength))) {
295 path.push("M", Math.floor(x + length - gutterX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);
296 for (i = 1; i < dashesX; i++) {
297 path.push("M", Math.floor(x + length - gutterX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);
299 inflections.push([ Math.floor(currentX), Math.floor(y) ]);
301 me.labels.push(me.labels[me.labels.length -1] + step);
306 me.axis = me.chart.surface.add(Ext.apply({
311 me.axis.setAttributes({
314 me.inflections = inflections;
315 if (!init && me.grid) {
318 me.axisBBox = me.axis.getBBox();
322 <span id='Ext-chart.axis.Axis-method-drawGrid'> /**
323 </span> * Renders an horizontal and/or vertical grid into the Surface.
325 drawGrid: function() {
327 surface = me.chart.surface,
331 inflections = me.inflections,
332 ln = inflections.length - ((odd || even)? 0 : 1),
333 position = me.position,
334 gutter = me.chart.maxGutter,
335 width = me.width - 2,
339 path = [], styles, lineWidth, dlineWidth,
340 oddPath = [], evenPath = [];
342 if ((gutter[1] !== 0 && (position == 'left' || position == 'right')) ||
343 (gutter[0] !== 0 && (position == 'top' || position == 'bottom'))) {
347 for (; i < ln; i++) {
348 point = inflections[i];
349 prevPoint = inflections[i - 1];
351 path = (i % 2)? oddPath : evenPath;
352 styles = ((i % 2)? odd : even) || {};
353 lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
354 dlineWidth = 2 * lineWidth;
355 if (position == 'left') {
356 path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,
357 "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
358 "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
359 "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z");
361 else if (position == 'right') {
362 path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,
363 "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
364 "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
365 "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z");
367 else if (position == 'top') {
368 path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,
369 "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
370 "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
371 "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z");
374 path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,
375 "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
376 "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
377 "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z");
380 if (position == 'left') {
381 path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]);
383 else if (position == 'right') {
384 path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]);
386 else if (position == 'top') {
387 path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]);
390 path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]);
395 if (oddPath.length) {
396 if (!me.gridOdd && oddPath.length) {
397 me.gridOdd = surface.add({
402 me.gridOdd.setAttributes(Ext.apply({
405 }, odd || {}), true);
407 if (evenPath.length) {
409 me.gridEven = surface.add({
414 me.gridEven.setAttributes(Ext.apply({
417 }, even || {}), true);
423 me.gridLines = me.chart.surface.add({
426 "stroke-width": me.lineWidth || 1,
427 stroke: me.gridColor || '#ccc'
430 me.gridLines.setAttributes({
435 else if (me.gridLines) {
436 me.gridLines.hide(true);
442 getOrCreateLabel: function(i, text) {
444 labelGroup = me.labelGroup,
445 textLabel = labelGroup.getAt(i),
446 surface = me.chart.surface;
448 if (text != textLabel.attr.text) {
449 textLabel.setAttributes(Ext.apply({
452 textLabel._bbox = textLabel.getBBox();
456 textLabel = surface.add(Ext.apply({
463 surface.renderItem(textLabel);
464 textLabel._bbox = textLabel.getBBox();
466 //get untransformed bounding box
467 if (me.label.rotation) {
468 textLabel.setAttributes({
473 textLabel._ubbox = textLabel.getBBox();
474 textLabel.setAttributes(me.label, true);
476 textLabel._ubbox = textLabel._bbox;
481 rect2pointArray: function(sprite) {
482 var surface = this.chart.surface,
483 rect = surface.getBBox(sprite, true),
484 p1 = [rect.x, rect.y],
486 p2 = [rect.x + rect.width, rect.y],
488 p3 = [rect.x + rect.width, rect.y + rect.height],
490 p4 = [rect.x, rect.y + rect.height],
492 matrix = sprite.matrix;
493 //transform the points
494 p1[0] = matrix.x.apply(matrix, p1p);
495 p1[1] = matrix.y.apply(matrix, p1p);
497 p2[0] = matrix.x.apply(matrix, p2p);
498 p2[1] = matrix.y.apply(matrix, p2p);
500 p3[0] = matrix.x.apply(matrix, p3p);
501 p3[1] = matrix.y.apply(matrix, p3p);
503 p4[0] = matrix.x.apply(matrix, p4p);
504 p4[1] = matrix.y.apply(matrix, p4p);
505 return [p1, p2, p3, p4];
508 intersect: function(l1, l2) {
509 var r1 = this.rect2pointArray(l1),
510 r2 = this.rect2pointArray(l2);
511 return !!Ext.draw.Draw.intersect(r1, r2).length;
514 drawHorizontalLabels: function() {
516 labelConf = me.label,
519 axes = me.chart.axes,
520 position = me.position,
521 inflections = me.inflections,
522 ln = inflections.length,
524 labelGroup = me.labelGroup,
527 gutterY = me.chart.maxGutter[1],
528 ubbox, bbox, point, prevX, prevLabel,
530 textLabel, attr, textRight, text,
531 label, last, x, y, i, firstLabel;
534 //get a reference to the first text label dimensions
535 point = inflections[0];
536 firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
537 ratio = Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0)) >> 0;
539 for (i = 0; i < ln; i++) {
540 point = inflections[i];
541 text = me.label.renderer(labels[i]);
542 textLabel = me.getOrCreateLabel(i, text);
543 bbox = textLabel._bbox;
544 maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
545 x = floor(point[0] - (ratio? bbox.height : bbox.width) / 2);
546 if (me.chart.maxGutter[0] == 0) {
547 if (i == 0 && axes.findIndex('position', 'left') == -1) {
550 else if (i == last && axes.findIndex('position', 'right') == -1) {
551 x = point[0] - bbox.width;
554 if (position == 'top') {
555 y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
558 y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
561 textLabel.setAttributes({
567 // Skip label if there isn't available minimum space
568 if (i != 0 && (me.intersect(textLabel, prevLabel)
569 || me.intersect(textLabel, firstLabel))) {
570 textLabel.hide(true);
574 prevLabel = textLabel;
580 drawVerticalLabels: function() {
582 inflections = me.inflections,
583 position = me.position,
584 ln = inflections.length,
590 axes = me.chart.axes,
591 gutterY = me.chart.maxGutter[1],
592 ubbox, bbox, point, prevLabel,
594 textLabel, attr, textRight, text,
595 label, last, x, y, i;
598 for (i = 0; i < last; i++) {
599 point = inflections[i];
600 text = me.label.renderer(labels[i]);
601 textLabel = me.getOrCreateLabel(i, text);
602 bbox = textLabel._bbox;
604 maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
606 if (gutterY < bbox.height / 2) {
607 if (i == last - 1 && axes.findIndex('position', 'top') == -1) {
608 y = me.y - me.length + ceil(bbox.height / 2);
610 else if (i == 0 && axes.findIndex('position', 'bottom') == -1) {
611 y = me.y - floor(bbox.height / 2);
614 if (position == 'left') {
615 x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
618 x = point[0] + me.dashSize + me.label.padding + 2;
620 textLabel.setAttributes(Ext.apply({
625 // Skip label if there isn't available minimum space
626 if (i != 0 && me.intersect(textLabel, prevLabel)) {
627 textLabel.hide(true);
630 prevLabel = textLabel;
636 <span id='Ext-chart.axis.Axis-method-drawLabel'> /**
637 </span> * Renders the labels in the axes.
639 drawLabel: function() {
641 position = me.position,
642 labelGroup = me.labelGroup,
643 inflections = me.inflections,
648 if (position == 'left' || position == 'right') {
649 maxWidth = me.drawVerticalLabels();
651 maxHeight = me.drawHorizontalLabels();
655 ln = labelGroup.getCount();
656 i = inflections.length;
657 for (; i < ln; i++) {
658 labelGroup.getAt(i).hide(true);
662 Ext.apply(me.bbox, me.axisBBox);
663 me.bbox.height = maxHeight;
664 me.bbox.width = maxWidth;
665 if (Ext.isString(me.title)) {
666 me.drawTitle(maxWidth, maxHeight);
670 // @private creates the elipsis for the text.
671 elipsis: function(sprite, text, desiredWidth, minWidth, center) {
675 if (desiredWidth < minWidth) {
679 while (text.length > 4) {
680 text = text.substr(0, text.length - 4) + "...";
681 sprite.setAttributes({
684 bbox = sprite.getBBox();
685 if (bbox.width < desiredWidth) {
686 if (typeof center == 'number') {
687 sprite.setAttributes({
688 x: Math.floor(center - (bbox.width / 2))
697 <span id='Ext-chart.axis.Axis-method-setTitle'> /**
698 </span> * Updates the {@link #title} of this axis.
699 * @param {String} title
701 setTitle: function(title) {
706 // @private draws the title for the axis.
707 drawTitle: function(maxWidth, maxHeight) {
709 position = me.position,
710 surface = me.chart.surface,
711 displaySprite = me.displaySprite,
713 rotate = (position == 'left' || position == 'right'),
719 displaySprite.setAttributes({text: title}, true);
727 displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
728 surface.renderItem(displaySprite);
730 bbox = displaySprite.getBBox();
731 pad = me.dashSize + me.label.padding;
734 y -= ((me.length / 2) - (bbox.height / 2));
735 if (position == 'left') {
736 x -= (maxWidth + pad + (bbox.width / 2));
739 x += (maxWidth + pad + bbox.width - (bbox.width / 2));
741 me.bbox.width += bbox.width + 10;
744 x += (me.length / 2) - (bbox.width * 0.5);
745 if (position == 'top') {
746 y -= (maxHeight + pad + (bbox.height * 0.3));
749 y += (maxHeight + pad + (bbox.height * 0.8));
751 me.bbox.height += bbox.height + 10;
753 displaySprite.setAttributes({
760 });</pre></pre></body></html>