/** * @class Ext.chart.axis.layout.Discrete * @extends Ext.chart.axis.layout.Layout * * Simple processor for data that cannot be interpolated. */Ext.define('Ext.chart.axis.layout.Discrete', { extend: 'Ext.chart.axis.layout.Layout', alias: 'axisLayout.discrete', isDiscrete: true, processData: function() { var me = this, axis = me.getAxis(), seriesList = axis.boundSeries, direction = axis.getDirection(), i, ln, series; me.labels = []; me.labelMap = {}; for (i = 0, ln = seriesList.length; i < ln; i++) { series = seriesList[i]; if (series['get' + direction + 'Axis']() === axis) { series['coordinate' + direction](); } } // About the labels on Category axes (aka. axes with a Discrete layout)... // // When the data set from the store changes, series.processData() is called, which does // its thing at the series level and then calls series.updateLabelData() to update // the labels in the sprites that belong to the series. At the same time, // series.processData() calls axis.processData(), which also does its thing but at the axis // level, and also needs to update the labels for the sprite(s) that belong to the axis. // This is not that simple, however. So how are the axis labels rendered? // First, axis.sprite.Axis.render() calls renderLabels() which obtains the majorTicks // from the axis.layout and iterate() through them. The majorTicks are an object returned // by snapEnds() below which provides a getLabel() function that returns the label // from the axis.layoutContext.data array. So now the question is: how are the labels // transferred from the axis.layout to the axis.layoutContext? // The easy response is: it's in calculateLayout() below. The issue is to call // calculateLayout() because it takes in an axis.layoutContext that can only be created // in axis.sprite.Axis.layoutUpdater(), which is a private "updater" function that is // called by all the sprite's "triggers". Of course, we don't want to call layoutUpdater() // directly from here, so instead we update the sprite's data attribute, which sets // the trigger which calls layoutUpdater() which calls calculateLayout() etc... // Note that the sprite's data attribute could be set to any value and it would still result // in the trigger we need. For consistency, however, it is set to the labels. axis.getSprites()[0].setAttributes({ data: me.labels }); me.fireEvent('datachange', me.labels); }, /** * @method calculateLayout * @inheritdoc */ calculateLayout: function(context) { context.data = this.labels; this.callParent([context]); }, /** * @method calculateMajorTicks * @inheritdoc */ calculateMajorTicks: function(context) { var me = this, attr = context.attr, data = context.data, range = attr.max - attr.min, viewMin = attr.min + range * attr.visibleMin, viewMax = attr.min + range * attr.visibleMax, out; out = me.snapEnds(context, Math.max(0, attr.min), Math.min(attr.max, data.length - 1), 1); if (out) { me.trimByRange(context, out, viewMin, viewMax); context.majorTicks = out; } }, /** * @method snapEnds * @inheritdoc */ snapEnds: function(context, min, max, estStepSize) { var data = context.data, steps; estStepSize = Math.ceil(estStepSize); steps = Math.floor((max - min) / estStepSize); return { min: min, max: max, from: min, to: steps * estStepSize + min, step: estStepSize, steps: steps, unit: 1, getLabel: function(currentStep) { return data[this.from + this.step * currentStep]; }, get: function(currentStep) { return this.from + this.step * currentStep; } }; }, /** * @method trimByRange * @inheritdoc */ trimByRange: function(context, out, trimMin, trimMax) { var unit = out.unit, beginIdx = Math.ceil((trimMin - out.from) / unit) * unit, endIdx = Math.floor((trimMax - out.from) / unit) * unit, begin = Math.max(0, Math.ceil(beginIdx / out.step)), end = Math.min(out.steps, Math.floor(endIdx / out.step)); if (end < out.steps) { out.to = end; } if (out.max > trimMax) { out.max = out.to; } if (out.from < trimMin && out.step > 0) { out.from = out.from + begin * out.step * unit; while (out.from < trimMin) { begin++; out.from += out.step * unit; } } if (out.min < trimMin) { out.min = out.from; } out.steps = end - begin; }, getCoordFor: function(value, field, idx, items) { this.labels.push(value); return this.labels.length - 1; }});