From 2ff880fff4e12245cf6477ec812b735a259024a0 Mon Sep 17 00:00:00 2001 From: lang Date: Mon, 7 Sep 2015 22:12:05 +0800 Subject: [PATCH] Series stack. List improvement --- ecn.todo | 1 - src/chart/bar/barLayoutPolar.js | 0 src/coord/cartesian/Cartesian2D.js | 4 +- src/coord/cartesian/Grid.js | 2 +- src/coord/polar/Polar.js | 2 +- src/coord/polar/polarCreator.js | 7 +- src/data/List.js | 185 +++++++++++++++++++++-------- src/echarts.js | 22 ++++ src/layout/barGrid.js | 2 +- test/bar.html | 3 + test/line.html | 5 +- 11 files changed, 173 insertions(+), 60 deletions(-) delete mode 100644 ecn.todo delete mode 100644 src/chart/bar/barLayoutPolar.js diff --git a/ecn.todo b/ecn.todo deleted file mode 100644 index 81b651deb..000000000 --- a/ecn.todo +++ /dev/null @@ -1 +0,0 @@ -Default Option \ No newline at end of file diff --git a/src/chart/bar/barLayoutPolar.js b/src/chart/bar/barLayoutPolar.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/coord/cartesian/Cartesian2D.js b/src/coord/cartesian/Cartesian2D.js index 21e4939d3..d6cda63d5 100644 --- a/src/coord/cartesian/Cartesian2D.js +++ b/src/coord/cartesian/Cartesian2D.js @@ -29,8 +29,8 @@ define(function(require) { return data.map(function (dataItem) { var coord = []; - coord[xIndex] = xAxis.dataToCoord(dataItem.getX()); - coord[1 - xIndex] = yAxis.dataToCoord(dataItem.getY()); + coord[xIndex] = xAxis.dataToCoord(dataItem.getX(true)); + coord[1 - xIndex] = yAxis.dataToCoord(dataItem.getY(true)); return coord; }, this); }, diff --git a/src/coord/cartesian/Grid.js b/src/coord/cartesian/Grid.js index 37d9ee9f7..336d3fa17 100644 --- a/src/coord/cartesian/Grid.js +++ b/src/coord/cartesian/Grid.js @@ -309,7 +309,7 @@ define(function(require, factory) { if (value != null) { axisData[categoryAxis.dim === 'y' ? 'x' : 'y'].push(value); } - }); + }, true); } } } diff --git a/src/coord/polar/Polar.js b/src/coord/polar/Polar.js index 5b9efbba5..5bb0220ee 100644 --- a/src/coord/polar/Polar.js +++ b/src/coord/polar/Polar.js @@ -74,7 +74,7 @@ define(function(require) { */ dataToCoords: function (data) { return data.map(function (dataItem) { - return this.dataToCoord([dataItem.getRadius(), dataItem.getAngle()]); + return this.dataToCoord([dataItem.getRadius(true), dataItem.getAngle(true)]); }, this); }, diff --git a/src/coord/polar/polarCreator.js b/src/coord/polar/polarCreator.js index f9641ad02..f765a9c7d 100644 --- a/src/coord/polar/polarCreator.js +++ b/src/coord/polar/polarCreator.js @@ -108,11 +108,14 @@ define(function (require) { var isAngleCategory = angleAxis.type === 'category'; var data = seriesModel.getData(); + var valueMapper = function (a) { + return a; + } if (! isRadiusCategory) { - radiusAxis.scale.setExtentFromData(data.map(function (dataItem) { return dataItem.getRadius(); }), true); + radiusAxis.scale.setExtentFromData(data.mapRadius(valueMapper, true), true); } if (! isAngleCategory) { - angleAxis.scale.setExtentFromData(data.map(function (dataItem) { return dataItem.getAngle(); }), true); + angleAxis.scale.setExtentFromData(data.mapAngle(valueMapper, true), true); } } }); diff --git a/src/data/List.js b/src/data/List.js index 745df08f3..e921281cb 100644 --- a/src/data/List.js +++ b/src/data/List.js @@ -7,29 +7,24 @@ define(function(require) { var Model = require('../model/Model'); var DataDiffer = require('./DataDiffer'); - function createArrayIterWithDepth(maxDepth, properties, cb, context, iterType) { - // Simple optimization to avoid read the undefined value in properties array - var nestedProperties = properties.length > 0; - return function eachAxis(array, depth) { - if (depth === maxDepth) { - return zrUtil[iterType](array, cb, context); - } - else if (array) { - var property = properties[depth]; - for (var i = 0; i < array.length; i++) { - var item = array[i]; - // Access property of each item - if (nestedProperties && property && item) { - item = item[property]; - } - array[i] = eachAxis(item, depth); - } + var POSSIBLE_DIMENSIONS = ['x', 'y', 'z', 'value', 'radius', 'angle']; + + /** + * Check if two entries has same xIndex, yIndex, zIndex, valueIndex, etc. + * @param {module:echarts/data/List~Entry} entry1 + * @param {module:echarts/data/List~Entry} entry2 + * @inner + */ + function isEntrySameShape(entry1, entry2) { + for (var i = 0; i < POSSIBLE_DIMENSIONS.length; i++) { + var key = POSSIBLE_DIMENSIONS[i] + 'Index'; + if (entry1[key] !== entry2[key]) { + return false; } - }; + } + return true; } - var dimensions = ['x', 'y', 'z', 'value', 'radius', 'angle']; - /** * @name echarts/data/List~Entry * @extends {module:echarts/model/Model} @@ -80,6 +75,11 @@ define(function(require) { */ valueIndex: 1, + /** + * @type {module:echarts/data/List~Entry} + */ + stackedOn: null, + init: function (option, parentModel, rawDataIndex, independentVar, dependentVar) { /** @@ -143,25 +143,19 @@ define(function(require) { this.rawDataIndex = rawDataIndex; }, - /** - * @return {number} - */ - getStackedValue: function () { - - }, - setDataIndex: function (index) { if (this.dataIndexIndex != null) { this._value[this.dataIndexIndex] = index; } }, - clone: function () { - var entry = new Entry(this.option, this.parentModel, this.rawDataIndex); + clone: function (dataIndex) { + var entry = new Entry(this.option, this.parentModel, dataIndex); entry.name = this.name; + entry.stackedOn = this.stackedOn; - for (var i = 0; i < dimensions.length; i++) { - var key = dimensions[i] + 'Index'; + for (var i = 0; i < POSSIBLE_DIMENSIONS.length; i++) { + var key = POSSIBLE_DIMENSIONS[i] + 'Index'; if (this.hasOwnProperty(key)) { entry[key] = this[key]; } @@ -170,23 +164,62 @@ define(function(require) { } }); - zrUtil.each(dimensions, function (dim) { + zrUtil.each(POSSIBLE_DIMENSIONS, function (dim) { var capitalized = dim[0].toUpperCase() + dim.substr(1); var indexKey = dim + 'Index'; - Entry.prototype['get' + capitalized] = function () { + var getterName = 'get' + capitalized; + Entry.prototype[getterName] = function (stack) { var index = this[indexKey]; if (index >= 0) { - return this._value[index]; + var val = this._value[index]; + var stackedOn = this.stackedOn; + + // Normalize empty value + if (val === '-' || val == null) { + val = null; + } + if (val != null + // Has stack + && stack && stackedOn + // Is getValue + && index === this.valueIndex + // Has same dimensions shape on stack + // PENDING If check the two stacking entries have same shape + && isEntrySameShape(this, stackedOn) + ) { + var stackValue = stackedOn[getterName](stack); + if ( + // Positive stack + val > 0 && stackValue > 0 + // Negative stack + || (val < 0 && stackValue < 0) + ) { + val += stackValue; + } + } + return val; } }; }); - function List() { + function List(dimensions, value) { /** * @readOnly * @type {Array} */ this.elements = []; + + /** + * @readOnly + * @type {Array.} + */ + this.dimensions = dimensions || ['x'] + + /** + * @readOnly + * @type {string} + */ + this.value = value || 'y'; } List.prototype = { @@ -195,17 +228,47 @@ define(function(require) { type: 'list', + /** + * @type {module:echarts/data/List~Entry} + */ + at: function (idx) { + return this.elements[idx]; + }, + + /** + * Create and add a new entry + * @param {Object} option + * @param {module:echarts/model/Series} seriesModel + * @return {module:echarts/data/List~Entry} + */ + add: function (option, seriesModel) { + var elements = this.elements; + var entry = new Entry(option, seriesModel, elements.length, this.dimensions, this.value); + elements.push(entry); + return entry; + }, + + /** + * Get elements count + * @return {number} + */ count: function () { return this.elements.length; }, + /** + * Iterate each element + * @param {Function} cb + * @param {*} context + */ each: function (cb, context) { zrUtil.each(this.elements, cb, context || this); }, /** - * Data mapping, returned array is flatten - * PENDING + * Map elemements to a new created array + * @param {Function} cb + * @param {*} context */ map: function (cb, context) { var ret = []; @@ -216,6 +279,11 @@ define(function(require) { return ret; }, + /** + * Filter elements in place + * @param {Function} cb + * @param {*} context + */ filterSelf: function (cb, context) { this.elements = zrUtil.filter(this.elements, cb, context || this); this.each(this._setEntryDataIndex); @@ -229,7 +297,6 @@ define(function(require) { * @return {module:echarts/data/List~Entry} */ getByName: function (name) { - // TODO deep hierarchy var elements = this.elements; for (var i = 0; i < elements.length; i++) { if (elements[i].name === name) { @@ -250,31 +317,47 @@ define(function(require) { return el; }, + /** + * Get the diff result with the old list data + * @param {module:echarts/data/List} oldList + * @return {module:echarts/data/DataDiffer} + * @example + * data.diff(this._data) + * .add(function (item) { // Add a new shape}) + * .update(function (newItem, oldItem) { // Update the shape}) + * .remove(function (item) { // Remove unused shape}) + * .execute() + */ diff: function (oldList) { return new DataDiffer(oldList ? oldList.elements : [], this.elements); }, + /** + * Clone a new list and all its' entries + * @return {module:echarts/data/List} + */ clone: function () { - var list = new List(); - var elements = this.elements; - for (var i = 0; i < elements.length; i++) { - list.elements.push(elements[i].clone()); - } + var list = new List(this.dimensions, this.value); + list.elements = zrUtil.map(this.elements, function (el, i) { + return el.clone(i); + }); return list; } }; - zrUtil.each(['X', 'Y', 'Z', 'Value'], function (name) { - List.prototype['each' + name] = function (cb, context) { + zrUtil.each(POSSIBLE_DIMENSIONS, function (dim) { + var capitalized = dim[0].toUpperCase() + dim.substr(1); + + List.prototype['each' + capitalized] = function (cb, stack, context) { this.each(function (item, idx) { - cb && cb.call(context || this, item['get' + name](idx)); + cb && cb.call(context || this, item['get' + capitalized](stack)); }, context); }; - List.prototype['map' + name] = function (cb, context) { + List.prototype['map' + capitalized] = function (cb, stack, context) { var ret = []; this.each(function (item) { - ret.push(cb && cb.call(context || this, item['get' + name]())); + ret.push(cb && cb.call(context || this, item['get' + capitalized](stack))); }, context); return ret; }; @@ -340,11 +423,11 @@ define(function(require) { } } - var list = new List(); + var list = new List(independentVar, dependentVar); // Normalize data - list.elements = zrUtil.map(data, function (dataItem, index) { - var entry = new Entry(dataItem, seriesModel, index, independentVar, dependentVar); + zrUtil.each(data, function (dataItem, index) { + var entry = list.add(dataItem, seriesModel); // FIXME if (! dataItem.name) { entry.name = index; diff --git a/src/echarts.js b/src/echarts.js index 8fdde9ad2..5171304c9 100644 --- a/src/echarts.js +++ b/src/echarts.js @@ -167,6 +167,8 @@ define(function (require) { this._processData(ecModel); + this._stackSeriesData(ecModel); + this._coordinateSystem.update(ecModel, this._extensionAPI); this._doVisualCoding(ecModel); @@ -311,6 +313,26 @@ define(function (require) { }); }, + /** + * @private + */ + _stackSeriesData: function (ecModel) { + var stackedDataMap = {}; + ecModel.eachSeries(function (series) { + var stack = series.get('stack'); + var data = series.getData(); + if (stack && data.type === 'list' && data.dimensions.length === 1) { + var previousStack = stackedDataMap[stack]; + if (previousStack) { + data.each(function (dataItem, idx) { + dataItem.stackedOn = previousStack.at(idx); + }); + } + stackedDataMap[stack] = data; + } + }); + }, + /** * Layout before each chart render there series after visual coding and data processing * diff --git a/src/layout/barGrid.js b/src/layout/barGrid.js index 5c1f42df1..000823a88 100644 --- a/src/layout/barGrid.js +++ b/src/layout/barGrid.js @@ -146,7 +146,7 @@ define(function(require) { lastStackCoords[stackId] = lastStackCoords[stackId] || []; data.each(function (dataItem, dataIndex) { - var value = dataItem.getValue(); + var value = dataItem.getValue(true); // 空数据 if (value == null) { return; diff --git a/test/bar.html b/test/bar.html index 5c62e1b3b..2bec038ee 100644 --- a/test/bar.html +++ b/test/bar.html @@ -66,14 +66,17 @@ series: [{ name: 'bar', type: 'bar', + stack: 'one', data: data1 }, { name: 'bar2', type: 'bar', + stack: 'one', data: data2 }, { name: 'bar3', type: 'bar', + stack: 'two', data: data3 }] }); diff --git a/test/line.html b/test/line.html index a9c54e455..f032fd72a 100644 --- a/test/line.html +++ b/test/line.html @@ -32,7 +32,7 @@ for (var i = 0; i < 20; i++) { xAxisData.push('类目' + i); - data1.push(Math.random() * 5); + data1.push(Math.random()); data2.push(Math.random()); data3.push(Math.random()); } @@ -53,14 +53,17 @@ series: [{ name: 'line', type: 'line', + stack: 'all', data: data1 }, { name: 'line2', type: 'line', + stack: 'all', data: data2 }, { name: 'line3', type: 'line', + stack: 'all', data: data3 }] }); -- GitLab