提交 802d78a5 编写于 作者: S sushuang

refactor

上级 ca2c5974
......@@ -23,7 +23,6 @@ CoordinateSystemManager.prototype = {
update: function (ecModel, api) {
zrUtil.each(this._coordinateSystems, function (coordSys) {
// FIXME MUST have
coordSys.update && coordSys.update(ecModel, api);
});
},
......
......@@ -14,7 +14,7 @@ import {updateCenterAndZoom} from './roamHelper';
echarts.registerAction({
type: 'geoRoam',
event: 'geoRoam',
update: 'updateView'
update: 'updateLayout'
}, function (payload, ecModel) {
var componentType = payload.componentType || 'series';
......
import * as echarts from '../echarts';
import * as zrUtil from 'zrender/src/core/util';
import './effectScatter/EffectScatterSeries';
import './effectScatter/EffectScatterView';
......@@ -7,9 +6,5 @@ import './effectScatter/EffectScatterView';
import visualSymbol from '../visual/symbol';
import layoutPoints from '../layout/points';
echarts.registerVisual(zrUtil.curry(
visualSymbol, 'effectScatter', 'circle', null
));
echarts.registerLayout(zrUtil.curry(
layoutPoints, 'effectScatter'
));
\ No newline at end of file
echarts.registerVisual(visualSymbol('effectScatter', 'circle'));
echarts.registerLayout(layoutPoints('effectScatter'));
\ No newline at end of file
......@@ -5,69 +5,79 @@ import * as quadraticContain from 'zrender/src/contain/quadratic';
export default graphic.extendShape({
shape: {
polyline: false,
curveness: 0,
segs: []
},
buildPath: function (path, shape) {
var segs = shape.segs;
var isPolyline = shape.polyline;
var curveness = shape.curveness;
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if (isPolyline) {
path.moveTo(seg[0][0], seg[0][1]);
for (var j = 1; j < seg.length; j++) {
path.lineTo(seg[j][0], seg[j][1]);
if (shape.polyline) {
for (var i = 0; i < segs.length;) {
var count = segs[i++];
if (count > 0) {
path.moveTo(segs[i++], segs[i++]);
for (var k = 1; k < count; k++) {
path.lineTo(segs[i++], segs[i++]);
}
}
}
else {
path.moveTo(seg[0][0], seg[0][1]);
if (seg.length > 2) {
path.quadraticCurveTo(seg[2][0], seg[2][1], seg[1][0], seg[1][1]);
}
else {
for (var i = 0; i < segs.length;) {
var x0 = segs[i++];
var y0 = segs[i++];
var x1 = segs[i++];
var y1 = segs[i++];
path.moveTo(x0, y0);
if (curveness > 0) {
var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;
var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;
path.quadraticCurveTo(x2, y2, x1, y1);
}
else {
path.lineTo(seg[1][0], seg[1][1]);
path.lineTo(x1, y1);
}
}
}
},
findDataIndex: function (x, y) {
var shape = this.shape;
var segs = shape.segs;
var isPolyline = shape.polyline;
var lineWidth = Math.max(this.style.lineWidth, 1);
// var shape = this.shape;
// var segs = shape.segs;
// var isPolyline = shape.polyline;
// var lineWidth = Math.max(this.style.lineWidth, 1);
// Not consider transform
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if (isPolyline) {
for (var j = 1; j < seg.length; j++) {
if (lineContain.containStroke(
seg[j - 1][0], seg[j - 1][1], seg[j][0], seg[j][1], lineWidth, x, y
)) {
return i;
}
}
}
else {
if (seg.length > 2) {
if (quadraticContain.containStroke(
seg[0][0], seg[0][1], seg[2][0], seg[2][1], seg[1][0], seg[1][1], lineWidth, x, y
)) {
return i;
}
}
else {
if (lineContain.containStroke(
seg[0][0], seg[0][1], seg[1][0], seg[1][1], lineWidth, x, y
)) {
return i;
}
}
}
}
// for (var i = 0; i < segs.length; i++) {
// var seg = segs[i];
// if (isPolyline) {
// for (var j = 1; j < seg.length; j++) {
// if (lineContain.containStroke(
// seg[j - 1][0], seg[j - 1][1], seg[j][0], seg[j][1], lineWidth, x, y
// )) {
// return i;
// }
// }
// }
// else {
// if (seg.length > 2) {
// if (quadraticContain.containStroke(
// seg[0][0], seg[0][1], seg[2][0], seg[2][1], seg[1][0], seg[1][1], lineWidth, x, y
// )) {
// return i;
// }
// }
// else {
// if (lineContain.containStroke(
// seg[0][0], seg[0][1], seg[1][0], seg[1][1], lineWidth, x, y
// )) {
// return i;
// }
// }
// }
// }
return -1;
}
......
......@@ -2,6 +2,7 @@
import * as graphic from '../../util/graphic';
import LargeLineShape from './LargeLine';
import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
function LargeLineDraw() {
this.group = new graphic.Group();
......@@ -19,35 +20,13 @@ largeLineProto.updateData = function (data) {
this.group.removeAll();
var lineEl = this._lineEl;
var seriesModel = data.hostModel;
lineEl.setShape({
segs: data.mapArray(data.getItemLayout),
polyline: seriesModel.get('polyline')
segs: data.mapArray(data.getItemLayout)
});
lineEl.useStyle(
seriesModel.getModel('lineStyle.normal').getLineStyle()
);
var visualColor = data.getVisual('color');
if (visualColor) {
lineEl.setStyle('stroke', visualColor);
}
lineEl.setStyle('fill');
// Enable tooltip
// PENDING May have performance issue when path is extremely large
lineEl.seriesIndex = seriesModel.seriesIndex;
lineEl.on('mousemove', function (e) {
lineEl.dataIndex = null;
var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY);
if (dataIndex > 0) {
// Provide dataIndex for tooltip
lineEl.dataIndex = dataIndex;
}
});
this._setCommon(data, seriesModel);
// Add back
this.group.add(lineEl);
......@@ -60,8 +39,75 @@ largeLineProto.updateLayout = function (seriesModel) {
});
};
/**
* @override
*/
largeLineProto.incrementalPrepare = function (data) {
this._setCommon(data, data.hostModel);
this._clearIncremental();
!this._incremental && this.group.add(
this._incremental = new IncrementalDisplayable()
);
};
/**
* @override
*/
largeLineProto.incrementalProgress = function (taskParams, data) {
this._lineEl.setShape({
segs: data.getLayout('linesPoints')
});
this._incremental.addDisplayable(this._lineEl, true);
};
/**
* @override
*/
largeLineProto.remove = function () {
this._clearIncremental();
this._incremental = null;
this.group.removeAll();
};
largeLineProto._setCommon = function (data, seriesModel, isIncremental) {
var lineEl = this._lineEl;
lineEl.setShape({
polyline: seriesModel.get('polyline'),
curveness: seriesModel.get('lineStyle.normal.curveness')
});
lineEl.useStyle(
seriesModel.getModel('lineStyle.normal').getLineStyle()
);
var visualColor = data.getVisual('color');
if (visualColor) {
lineEl.setStyle('stroke', visualColor);
}
lineEl.setStyle('fill');
if (!isIncremental) {
// Enable tooltip
// PENDING May have performance issue when path is extremely large
lineEl.seriesIndex = seriesModel.seriesIndex;
lineEl.on('mousemove', function (e) {
lineEl.dataIndex = null;
var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY);
if (dataIndex > 0) {
// Provide dataIndex for tooltip
lineEl.dataIndex = dataIndex;
}
});
}
};
largeLineProto._clearIncremental = function () {
var incremental = this._incremental;
if (incremental) {
incremental.clearDisplaybles();
}
};
export default LargeLineDraw;
\ No newline at end of file
......@@ -4,11 +4,7 @@
import * as graphic from '../../util/graphic';
import LineGroup from './Line';
import Polyline from './Polyline';
import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
import LargeLineShape from './LargeLine';
import {curry} from 'zrender/src/core/util';
/**
* @alias module:echarts/component/marker/LineDraw
......@@ -17,14 +13,8 @@ import {curry} from 'zrender/src/core/util';
function LineDraw(ctor) {
this._ctor = ctor || LineGroup;
// ??? The third mode: largeLineDraw?
// Create when needed.
this._incremental;
this._largeLine;
this._largeLineAdded;
this.group = new graphic.Group();
}
......@@ -42,121 +32,41 @@ lineDrawProto.updateData = function (lineData) {
var seriesScope = makeSeriesScope(lineData);
// Check and change mode.
var streamRendering = seriesScope.streamRendering;
var incremental = lineDraw._incremental;
if (incremental ^ streamRendering) {
lineDraw.remove();
if (streamRendering) {
if (!incremental) {
incremental = lineDraw._incremental = new IncrementalDisplayable();
incremental.afterBrush = function () {
clearLargeLine(lineDraw);
lineDraw._largeLineAdded = false;
};
}
group.add(incremental);
}
}
// ??? Process that switch from stream to non-stream.
if (streamRendering) {
clearIncremental(lineDraw);
if (seriesScope.isLargeMode) {
if (!lineDraw._largeLine) {
lineDraw._largeLine = new LargeLineShape();
}
// ??? set style should not be here, but in task?
setLargeLineCommon(lineDraw, lineData, seriesScope);
}
}
else {
lineData.diff(oldLineData)
.add(function (idx) {
doAdd(lineDraw, lineData, idx, seriesScope);
})
.update(function (newIdx, oldIdx) {
doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope);
})
.remove(function (idx) {
group.remove(oldLineData.getItemGraphicEl(idx));
})
.execute();
}
// ??? set task start index.
createRenderTask(lineDraw, lineData, seriesScope);
};
// ??? remove?
// lineDrawProto.updateLayout = function () {
// var lineData = this._lineData;
// lineData.eachItemGraphicEl(function (el, idx) {
// el.updateLayout(lineData, idx);
// }, this);
// };
lineDrawProto.updateView = function () {
var lineData = this._lineData;
var lineDraw = this;
var seriesScope = makeSeriesScope(lineData);
if (seriesScope.streamRendering) {
clearIncremental(lineDraw);
}
else {
lineData.each(function (item, idx) {
doUpdate(lineDraw, lineData, lineData, idx, idx, seriesScope);
});
}
// ??? set task start index.
createRenderTask(lineDraw, lineData, seriesScope);
lineData.diff(oldLineData)
.add(function (idx) {
doAdd(lineDraw, lineData, idx, seriesScope);
})
.update(function (newIdx, oldIdx) {
doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope);
})
.remove(function (idx) {
group.remove(oldLineData.getItemGraphicEl(idx));
})
.execute();
};
function doAdd(lineDraw, lineData, idx, seriesScope) {
var itemLayout = lineData.getItemLayout(idx);
var el;
if (!lineNeedsDraw(itemLayout)) {
return;
}
if (seriesScope.streamRendering) {
if (seriesScope.isLargeMode) {
// ??? remove when switch mode?
var largeLine = lineDraw._largeLine;
largeLine.shape.segs.push(itemLayout);
if (!lineDraw._largeLineAdded) {
lineDraw._incremental.addDisplayable(largeLine, true);
lineDraw._largeLineAdded = true;
}
lineDraw._incremental.dirty();
}
else {
el = createItemEl(lineDraw, lineData, idx, seriesScope);
lineDraw._incremental.addDisplayable(el, true);
}
lineData.$releaseItemMemory(idx); // ???
}
else {
el = createItemEl(lineDraw, lineData, idx, seriesScope);
lineData.setItemGraphicEl(idx, el);
lineDraw.group.add(el);
}
var el = new lineDraw._ctor(lineData, idx, seriesScope);
lineData.setItemGraphicEl(idx, el);
lineDraw.group.add(el);
}
function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) {
var itemEl = oldLineData.getItemGraphicEl(oldIdx);
if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {
lineDraw.group.remove(itemEl);
return;
}
if (!itemEl) {
itemEl = createItemEl(lineDraw, newLineData, newIdx, seriesScope);
itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope);
}
else {
itemEl.updateData(newLineData, newIdx, seriesScope);
......@@ -167,92 +77,58 @@ function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScop
lineDraw.group.add(itemEl);
}
function createRenderTask(lineDraw, lineData, seriesScope) {
var hostModel = lineData.hostModel;
hostModel.pipeTask && hostModel.pipeTask(lineData.createEachTask(function (idx) {
doAdd(lineDraw, lineData, idx, seriesScope);
}), 'render');
}
lineDrawProto.updateLayout = function () {
var lineData = this._lineData;
lineData.eachItemGraphicEl(function (el, idx) {
el.updateLayout(lineData, idx);
}, this);
};
// ??? Modify Polyline, Line.js to support IncrementalDisplable?
function createItemEl(lineDraw, newLineData, newIdx, seriesScope) {
if (seriesScope.streamRendering) {
var el = new Polyline(newLineData, newIdx, seriesScope);
return el.childAt(0);
}
else {
return new lineDraw._ctor(newLineData, newIdx, seriesScope);
lineDrawProto.incrementalPrepare = function (lineData) {
this._seriesScope = makeSeriesScope(this);
this._lineData = lineData;
this._clearIncremental();
!this._incremental && this.group.add(
this._incremental = new IncrementalDisplayable()
);
};
lineDrawProto.incrementalProgress = function (taskParams, lineData) {
for (var idx = taskParams.start; idx < taskParams.end; idx++) {
var itemLayout = lineData.getItemLayout(idx);
if (lineNeedsDraw(itemLayout)) {
var el = new this._ctor(lineData, idx, this._seriesScope);
this._incremental.addDisplayable(el, true);
this.$releaseItemMemory(idx);
}
}
}
};
function makeSeriesScope(lineData) {
var hostModel = lineData.hostModel;
var streamSetting = hostModel.getStreamSetting();
return {
lineStyle: hostModel.getModel('lineStyle.normal').getLineStyle(),
hoverLineStyle: hostModel.getModel('lineStyle.emphasis').getLineStyle(),
labelModel: hostModel.getModel('label.normal'),
hoverLabelModel: hostModel.getModel('label.emphasis'),
streamRendering: streamSetting && streamSetting.threshold < lineData.count(),
isLargeMode: hostModel.get('large')
};
}
lineDrawProto.remove = function () {
clearIncremental(this);
this._clearIncremental();
this._incremental = null;
this.group.removeAll();
};
function setLargeLineCommon(lineDraw, lineData, seriesScope) {
var seriesModel = lineData.hostModel;
var largeLine = lineDraw._largeLine;
largeLine.setShape({
segs: [],
polyline: seriesModel.get('polyline')
});
largeLine.useStyle(
seriesModel.getModel('lineStyle.normal').getLineStyle()
);
// ???! do it in echarts.js ?
var blendMode = seriesModel.get('blendMode') || null;
largeLine.style.blend = blendMode;
var visualColor = lineData.getVisual('color');
if (visualColor) {
largeLine.setStyle('stroke', visualColor);
}
largeLine.setStyle('fill');
// Enable tooltip
// PENDING May have performance issue when path is extremely large
// largeLine.seriesIndex = seriesScope.seriesIndex;
// lineEl.on('mousemove', function (e) {
// lineEl.dataIndex = null;
// var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY);
// if (dataIndex > 0) {
// // Provide dataIndex for tooltip
// lineEl.dataIndex = dataIndex;
// }
// });
}
function clearLargeLine(lineDraw) {
// Do not set dirty.
lineDraw._largeLine && (lineDraw._largeLine.shape.segs.length = 0);
}
function clearIncremental(lineDraw) {
var incremental = lineDraw._incremental;
lineDrawProto._clearIncremental = function () {
var incremental = this._incremental;
if (incremental) {
incremental.clearDisplaybles();
lineDraw._largeLineAdded = false;
}
clearLargeLine(lineDraw);
}
};
function isPointNaN(pt) {
return isNaN(pt[0]) || isNaN(pt[1]);
......
......@@ -10,12 +10,8 @@ import dataSample from '../processor/dataSample';
// In case developer forget to include grid component
import '../component/gridSimple';
echarts.registerVisual(zrUtil.curry(
visualSymbol, 'line', 'circle', 'line'
));
echarts.registerLayout(zrUtil.curry(
layoutPoints, 'line'
));
echarts.registerVisual(visualSymbol('line', 'circle', 'line'));
echarts.registerLayout(layoutPoints('line'));
// Down sample after filter
echarts.registerProcessor(echarts.PRIORITY.PROCESSOR.STATISTIC, zrUtil.curry(
......
......@@ -5,6 +5,11 @@ import * as zrUtil from 'zrender/src/core/util';
import {encodeHTML} from '../../util/format';
import CoordinateSystem from '../../CoordinateSystem';
var globalObj = typeof window === 'undefined' ? global : window;
var Uint32Array = globalObj.Uint32Array || Array;
var Float64Array = globalObj.Float64Array || Array;
// Convert [ [{coord: []}, {coord: []}] ]
// to [ { coords: [[]] } ]
function preprocessOption(seriesOpt) {
......@@ -45,16 +50,106 @@ var LinesSeries = SeriesModel.extend({
init: function (option) {
// Not using preprocessor because mergeOption may not have series.type
preprocessOption(option);
this._processFlatCoordsArray(option);
LinesSeries.superApply(this, 'init', arguments);
},
mergeOption: function (option) {
preprocessOption(option);
this._processFlatCoordsArray(option);
LinesSeries.superApply(this, 'mergeOption', arguments);
},
_getCoordsFromItemModel: function (idx) {
var itemModel = this.getData().getItemModel(idx);
var coords = (itemModel.option instanceof Array)
? itemModel.option : itemModel.getShallow('coords');
if (__DEV__) {
if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {
throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');
}
}
return coords;
},
getLineCoordsCount: function (idx) {
if (this._flatCoordsOffset) {
return this._flatCoordsOffset[idx * 2 + 1];
}
else {
return this._getCoordsFromItemModel(idx).length;
}
},
getLineCoords: function (idx, out) {
if (this._flatCoordsOffset) {
var offset = this._flatCoordsOffset[idx * 2];
var len = this._flatCoordsOffset[idx * 2 + 1];
for (var i = 0; i < len; i++) {
out[i] = out[i] || [];
out[i][0] = this._flatCoords[offset + i * 2];
out[i][1] = this._flatCoords[offset + i * 2 + 1];
}
return len;
}
else {
var coords = this._getCoordsFromItemModel(idx);
for (var i = 0; i < coords.length; i++) {
out[i] = out[i] || [];
out[i][0] = coords[i][0];
out[i][1] = coords[i][1];
}
return coords.length;
}
},
_processFlatCoordsArray: function (option) {
var data = option.data;
// Stored as a typed array. In format
// Points Count(2) | x | y | x | y | Points Count(3) | x | y | x | y | x | y |
if (typeof data[0] === 'number') {
var len = data.length;
// Store offset and len of each segment
var coordsOffsetAndLenStorage = new Uint32Array(len);
var coordsStorage = new Float64Array(len);
var coordsCursor = 0;
var offsetCursor = 0;
var dataCount = 0;
for (var i = 0; i < len;) {
dataCount++;
var count = data[i++];
// Offset
coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor;
// Len
coordsOffsetAndLenStorage[offsetCursor++] = count;
for (var k = 0; k < count; k++) {
var x = data[i++];
var y = data[i++];
coordsStorage[coordsCursor++] = x;
coordsStorage[coordsCursor++] = y;
if (i > len) {
if (__DEV__) {
throw new Error('Invalid data format.');
}
}
}
}
this._flatCoordsOffset = coordsOffsetAndLenStorage;
this._flatCoords = coordsStorage;
// Fill with zero values.
option.data = new Float32Array(dataCount);
}
else {
this._flatCoordsOffset = this._flatCoords = null;
}
},
getInitialData: function (option, ecModel) {
if (__DEV__) {
var CoordSys = CoordinateSystem.get(option.coordinateSystem);
......@@ -65,6 +160,7 @@ var LinesSeries = SeriesModel.extend({
var lineData = new List(['value'], this);
lineData.hasItemOption = false;
lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) {
// dataItem is simply coords
if (dataItem instanceof Array) {
......@@ -156,4 +252,4 @@ var LinesSeries = SeriesModel.extend({
}
});
export default LinesSeries;
export default LinesSeries;
\ No newline at end of file
......@@ -13,36 +13,14 @@ export default echarts.extendChartView({
init: function () {},
cannotStreamRender: function (seriesModel) {
return this._showEffect(seriesModel);
},
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var lineDraw = this._lineDraw;
var hasEffect = seriesModel.get('effect.show');
var isPolyline = seriesModel.get('polyline');
// ??? largeLineDraw merge?
var isLarge = seriesModel.get('large') && data.count() >= seriesModel.get('largeThreshold');
isLarge = false;
if (__DEV__) {
if (hasEffect && isLarge) {
console.warn('Large lines not support effect');
}
}
if (hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLarge !== this._isLarge) {
if (lineDraw) {
lineDraw.remove();
}
lineDraw = this._lineDraw = isLarge
? new LargeLineDraw()
: new LineDraw(
isPolyline
? (hasEffect ? EffectPolyline : Polyline)
: (hasEffect ? EffectLine : Line)
);
this._hasEffet = hasEffect;
this._isPolyline = isPolyline;
this._isLarge = isLarge;
}
var lineDraw = this._updateLineDraw(data, seriesModel);
var zlevel = seriesModel.get('zlevel');
var trailLength = seriesModel.get('effect.trailLength');
......@@ -61,7 +39,7 @@ export default echarts.extendChartView({
motionBlur: false
});
}
if (hasEffect && trailLength) {
if (this._showEffect(seriesModel) && trailLength) {
if (__DEV__) {
var notInIndividual = false;
ecModel.eachSeries(function (otherSeriesModel) {
......@@ -80,24 +58,67 @@ export default echarts.extendChartView({
}
}
this.group.add(lineDraw.group);
lineDraw.updateData(data);
this._lastZlevel = zlevel;
},
updateLayout: function (seriesModel, ecModel, api) {
// ??? do not support updateLayout in stream
this._lineDraw.updateLayout(seriesModel);
this._lineDraw.updateLayout();
this._clearLayer(api);
},
updateView: function (seriesModel, ecModel, api) {
this._lineDraw.updateView(seriesModel);
incrementalPrepare: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var lineDraw = this._updateLineDraw(data, seriesModel);
lineDraw.incrementalPrepare(data);
this._clearLayer(api);
},
incrementalProgress: function (taskParams, seriesModel, ecModel) {
this._lineDraw.incrementalProgress(taskParams, seriesModel.getData());
},
_updateLineDraw: function (data, seriesModel) {
var lineDraw = this._lineDraw;
var hasEffect = this._showEffect(seriesModel);
var isPolyline = !!seriesModel.get('polyline');
var isLarge = !!seriesModel.get('large')
&& data.count() >= seriesModel.get('largeThreshold');
if (__DEV__) {
if (hasEffect && isLarge) {
console.warn('Large lines not support effect');
}
}
if (hasEffect !== this._hasEffet
|| isPolyline !== this._isPolyline
|| isLarge !== this._isLarge
) {
if (lineDraw) {
lineDraw.remove();
}
lineDraw = this._lineDraw = isLarge
? new LargeLineDraw()
: new LineDraw(
isPolyline
? (hasEffect ? EffectPolyline : Polyline)
: (hasEffect ? EffectLine : Line)
);
this._hasEffet = hasEffect;
this._isPolyline = isPolyline;
this._isLarge = isLarge;
}
this.group.add(lineDraw.group);
return lineDraw;
},
_showEffect: function (seriesModel) {
return !!seriesModel.get('effect.show');
},
_clearLayer: function (api) {
// Not use motion when dragging or zooming
var zr = api.getZr();
......@@ -119,4 +140,3 @@ export default echarts.extendChartView({
dispose: function () {}
});
import {__DEV__} from '../../config';
export default function (ecModel) {
ecModel.eachSeriesByType('lines', function (seriesModel) {
export default {
seriesType: 'lines',
reset: function (data, seriesModel, ecModel) {
var coordSys = seriesModel.coordinateSystem;
var lineData = seriesModel.getData();
var isPolyline = seriesModel.get('polyline');
var isLarge = seriesModel.get('large');
// FIXME Use data dimensions ?
var task = lineData.createEachTask(function (idx) {
var itemModel = lineData.getItemModel(idx);
var coords = (itemModel.option instanceof Array) ?
itemModel.option : itemModel.get('coords');
if (__DEV__) {
if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {
throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');
function progress(params, lineData) {
var lineCoords = [];
if (isLarge) {
var points;
var segCount = params.end - params.start;
if (isPolyline) {
var totalCoordsCount = 0;
for (var i = params.start; i < params.end; i++) {
totalCoordsCount += seriesModel.getLineCoordsCount(i);
}
points = new Float32Array(segCount + totalCoordsCount * 2);
}
else {
points = new Float32Array(segCount * 2);
}
}
var pts = [];
if (seriesModel.get('polyline')) {
for (var i = 0; i < coords.length; i++) {
pts.push(coordSys.dataToPoint(coords[i]));
var offset = 0;
var pt = [];
for (var i = params.start; i < params.end; i++) {
var len = seriesModel.getLineCoords(i, lineCoords);
if (isPolyline) {
points[offset++] = len;
}
for (var k = 0; k < len; k++) {
coordSys.dataToPoint(lineCoords[k], pt);
points[offset++] = pt[0];
points[offset++] = pt[1];
}
}
lineData.setLayout('linesPoints', points);
}
else {
pts[0] = coordSys.dataToPoint(coords[0]);
pts[1] = coordSys.dataToPoint(coords[1]);
for (var i = params.start; i < params.end; i++) {
var itemModel = lineData.getItemModel(i);
var len = seriesModel.getLineCoords(i, lineCoords);
var pts = [];
if (isPolyline) {
for (var i = 0; i < len; i++) {
pts.push(coordSys.dataToPoint(lineCoords[i]));
}
}
else {
pts[0] = coordSys.dataToPoint(lineCoords[0]);
pts[1] = coordSys.dataToPoint(lineCoords[1]);
var curveness = itemModel.get('lineStyle.normal.curveness');
if (+curveness) {
pts[2] = [
(pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness,
(pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness
];
var curveness = itemModel.get('lineStyle.normal.curveness');
if (+curveness) {
pts[2] = [
(pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness,
(pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness
];
}
}
lineData.setItemLayout(i, pts);
}
}
lineData.setItemLayout(idx, pts);
});
}
seriesModel.pipeTask(task, 'visual');
});
}
\ No newline at end of file
return {progress: progress};
}
};
\ No newline at end of file
......@@ -6,21 +6,21 @@ function normalize(a) {
return a;
}
export default function (ecModel) {
ecModel.eachSeriesByType('lines', function (seriesModel) {
var data = seriesModel.getData();
var opacityQuery = 'lineStyle.normal.opacity'.split('.');
export default {
seriesType: 'lines',
reset: function (data, seriesModel, ecModel, api) {
var symbolType = normalize(seriesModel.get('symbol'));
var symbolSize = normalize(seriesModel.get('symbolSize'));
var opacityQuery = 'lineStyle.normal.opacity'.split('.');
data.setVisual('fromSymbol', symbolType && symbolType[0]);
data.setVisual('toSymbol', symbolType && symbolType[1]);
data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);
data.setVisual('toSymbolSize', symbolSize && symbolSize[1]);
data.setVisual('opacity', seriesModel.get(opacityQuery));
var task = data.createEachTask(function (idx) {
function dataEach(data, idx) {
var itemModel = data.getItemModel(idx);
var symbolType = normalize(itemModel.getShallow('symbol', true));
var symbolSize = normalize(itemModel.getShallow('symbolSize', true));
......@@ -32,8 +32,8 @@ export default function (ecModel) {
symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]);
data.setItemVisual(idx, 'opacity', opacity);
});
}
seriesModel.pipeTask(task, 'visual');
});
}
\ No newline at end of file
return {dataEach: dataEach};
}
};
......@@ -13,8 +13,8 @@ import layoutPoints from '../layout/points';
// In case developer forget to include grid component
import '../component/gridSimple';
echarts.registerVisual(zrUtil.curry(visualSymbol, 'scatter', 'circle', null));
echarts.registerLayout(zrUtil.curry(layoutPoints, 'scatter'));
echarts.registerVisual(visualSymbol('scatter', 'circle'));
echarts.registerLayout(layoutPoints('scatter'));
// echarts.registerVisual(zrUtil.curry(visualSymbol, 'streamScatter', 'circle', null));
// echarts.registerLayout(zrUtil.curry(layoutPoints, 'streamScatter'));
......@@ -39,12 +39,13 @@ echarts.registerProcessor(function (ecModel, api) {
endValue: valueRange[1]
}, true);
});
});
function resetSingleAxis(dimNames, axisIndex, dataZoomModel) {
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel);
}
function resetSingleAxis(dimNames, axisIndex, dataZoomModel) {
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel, api);
}
function filterSingleAxis(dimNames, axisIndex, dataZoomModel) {
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel, api);
}
});
function filterSingleAxis(dimNames, axisIndex, dataZoomModel) {
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel);
}
......@@ -231,11 +231,12 @@ View.prototype = {
* @param {Array.<number>} data
* @return {Array.<number>}
*/
dataToPoint: function (data) {
dataToPoint: function (data, out) {
var transform = this.transform;
out = out || [];
return transform
? v2ApplyTransform([], data, transform)
: [data[0], data[1]];
? v2ApplyTransform(out, data, transform)
: vector.copy(out, data);
},
/**
......
......@@ -488,7 +488,7 @@ gridProto._updateScale = function (ecModel, gridModel) {
if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
|| !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
) {
) {
return;
}
......
......@@ -195,13 +195,13 @@ Geo.prototype = {
* @param {string|Array.<number>} data
* @return {Array.<number>}
*/
dataToPoint: function (data) {
dataToPoint: function (data, out) {
if (typeof data === 'string') {
// Map area name to geoCoord
data = this.getGeoCoord(data);
}
if (data) {
return View.prototype.dataToPoint.call(this, data);
return View.prototype.dataToPoint.call(this, data, out);
}
},
......
......@@ -8,7 +8,6 @@ import * as zrUtil from 'zrender/src/core/util';
import Model from '../model/Model';
import DataDiffer from './DataDiffer';
import * as modelUtil from '../util/model';
import {createTask} from '../stream/task';
var isObject = zrUtil.isObject;
......@@ -26,6 +25,8 @@ var dataCtors = {
'time': Array
};
var CtorUint32Array = typeof globalObj.Uint32Array === UNDEFINED ? Array : globalObj.Uint32Array;
function cloneChunk(originalChunk) {
var Ctor = originalChunk.constructor;
// Only shallow clone is enough when Array.
......@@ -50,14 +51,20 @@ function transferProperties(a, b) {
* If normal array used, mutable chunk size is supported.
* If typed array used, chunk size must be fixed.
*/
function DefaultDataProvider(dataArray) {
function DefaultDataProvider(dataArray, dimSize) {
var methods;
// Typed array.
if (dataArray && !zrUtil.isArray(dataArray)) {
this._chunkSize = dataArray.length;
this._array = [dataArray];
if (dataArray && typeof dataArray[0] === 'number') {
if (__DEV__) {
if (dimSize == null) {
throw new Error('Typed array data must specify dimension size');
}
}
this._dimSize = dimSize;
this._array = dataArray;
methods = typedArrayProviderMethods;
this.pure = true;
}
// Normal array.
else {
......@@ -80,12 +87,15 @@ var normalProviderMethods = {
};
var typedArrayProviderMethods = {
count: function () {
return this._array.length * this._chunkSize;
return this._array.length / this._dimSize;
},
getItem: function (idx) {
var chunkSize = this._chunkSize;
var chunk = this._array[Math.floor(idx / chunkSize)];
return chunk ? chunk[idx % chunkSize] : null;
var item = [];
var offset = this._dimSize * idx;
for (var i = 0; i < this._dimSize; i++) {
item[i] = this._array[offset + i];
}
return item;
}
};
......@@ -98,7 +108,7 @@ DefaultDataProvider.prototype.addData = function (newData) {
if (__DEV__) {
// If using typed array, should always use typed array with the same size.
var isArrayData = zrUtil.isArray(newData);
var isArrayData = zrUtil.isArrayLike(newData);
zrUtil.assert(
(isArrayData && chunkSize == null)
|| (!isArrayData && chunkSize === newData.length),
......@@ -316,6 +326,14 @@ listProto.getDimension = function (dim) {
return dim;
};
/**
* @param {Array.<string|number>} dims
* @return {Array.<string>}
*/
listProto.parseDimensions = function (dims) {
return zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
};
/**
* Get type and stackable info of particular dimension
* @param {string|number} dim
......@@ -335,9 +353,9 @@ listProto.getDimensionInfo = function (dim) {
listProto.initData = function (data, nameList, dimValueGetter) {
data = data || [];
var isDataArray = zrUtil.isArray(data);
var isDataArray = zrUtil.isArrayLike(data);
if (isDataArray) {
data = new DefaultDataProvider(data);
data = new DefaultDataProvider(data, this.dimensions.length);
}
if (__DEV__) {
if (!isDataArray && (typeof data.getItem != 'function' || typeof data.count != 'function')) {
......@@ -349,9 +367,9 @@ listProto.initData = function (data, nameList, dimValueGetter) {
// Clear
this._storage = {};
this.indices = [];
this._chunkOffsets = [];
this._chunkIndices = [];
this.indices = new CtorUint32Array(data.count());
this._chunkOffsets = new CtorUint32Array(data.count());
this._chunkIndices = new CtorUint32Array(data.count());
var dimensionInfoMap = this._dimensionInfos;
......@@ -390,51 +408,53 @@ listProto.initData = function (data, nameList, dimValueGetter) {
);
};
doInit(this, 0, size, true);
this.initFromRawData(0, size, true);
};
listProto.getProvider = function () {
return this._rawData;
};
listProto.getInitTask = function () {
if (!this._initTask) {
this._initTask = createTask({
input: this._rawData,
progress: zrUtil.bind(initProgress, this)
});
listProto.initFromRawData = function (start, end, checkNameRepeat) {
// Optimize.
if (start >= end) {
return;
}
return this._initTask;
};
function initProgress(params, notify) {
notify(doInit(this, params.dueIndex, params.dueEnd));
}
function doInit(list, dueIndex, dueEnd, isInit) {
// Optimize.
if (dueIndex >= dueEnd) {
return dueIndex;
if (end > this.indices.length) {
// Expand indices
var oldIndices = this.indices;
var oldChunkIndices = this._chunkIndices;
var oldChunkOffsets = this._chunkOffsets;
this.indices = new CtorUint32Array(end);
this._chunkIndices = new CtorUint32Array(end);
this._chunkOffsets = new CtorUint32Array(end);
// Copy value to new array
for (var i = 0; i < oldIndices.length; i++) {
this.indices[i] = oldIndices[i];
this._chunkIndices[i] = oldChunkIndices[i];
this._chunkOffsets[i] = oldChunkOffsets[i];
}
}
var data = list._rawData;
var storage = list._storage;
var indices = list.indices;
var chunkOffsets = list._chunkOffsets;
var chunkIndices = list._chunkIndices;
var dimensions = list.dimensions;
var dimensionInfoMap = list._dimensionInfos;
var nameList = list._nameList;
var idList = list._idList;
var chunkSize = list._chunkSize;
var nameRepeatCount = list._nameRepeatCount = {};
var data = this._rawData;
var storage = this._storage;
var indices = this.indices;
var chunkOffsets = this._chunkOffsets;
var chunkIndices = this._chunkIndices;
var dimensions = this.dimensions;
var dimensionInfoMap = this._dimensionInfos;
var nameList = this._nameList;
var idList = this._idList;
var chunkSize = this._chunkSize;
var nameRepeatCount = this._nameRepeatCount = {};
var nameDimIdx;
// Reset cached extent
list._extent = {};
this._extent = {};
// Create more chunk storage.
var newChunkCount = Math.ceil(dueEnd / chunkSize);
var newChunkCount = Math.ceil(end / chunkSize);
for (var i = 0; i < dimensions.length; i++) {
var dim = dimensions[i];
......@@ -446,17 +466,17 @@ function doInit(list, dueIndex, dueEnd, isInit) {
storage[dim] = [];
}
var DataCtor = dataCtors[dimInfo.type];
for (var j = list._chunkCount; j < newChunkCount; j++) {
for (var j = this._chunkCount; j < newChunkCount; j++) {
storage[dim][j] = new DataCtor(chunkSize);
}
}
list._chunkCount = newChunkCount;
this._chunkCount = newChunkCount;
var chunkIndex = newChunkCount - 1;
for (; dueIndex < dueEnd; dueIndex++) {
for (var idx = start; idx < end; idx++) {
// NOTICE: Try not to write things into dataItem
var dataItem = data.getItem(dueIndex);
var dataItem = data.getItem(i);
// Each data item is value
// [1, 2]
// 2
......@@ -464,33 +484,33 @@ function doInit(list, dueIndex, dueEnd, isInit) {
// only gives the 'y' value. 'x' value is the indices of cateogry
// Use a tempValue to normalize the value to be a (x, y) value
var chunkOffset = dueIndex % chunkSize;
var chunkOffset = idx % chunkSize;
// Store the data by dimensions
for (var k = 0; k < dimensions.length; k++) {
var dim = dimensions[k];
var dimStorage = storage[dim][chunkIndex];
// PENDING NULL is empty or zero
dimStorage[chunkOffset] = list._dimValueGetter(dataItem, dim, dueIndex, k);
dimStorage[chunkOffset] = this._dimValueGetter(dataItem, dim, idx, k);
}
indices.push(chunkIndex * chunkSize + chunkOffset);
chunkOffsets.push(chunkOffset);
chunkIndices.push(chunkIndex);
indices[idx] = chunkIndex * chunkSize + chunkOffset;
chunkOffsets[idx] = chunkOffset;
chunkIndices[chunkIndex] = chunkIndex;
// Use the name in option and create id
if (!nameList[dueIndex] && dataItem) {
if (!nameList[idx] && dataItem) {
if (dataItem.name != null) {
nameList[dueIndex] = dataItem.name;
nameList[idx] = dataItem.name;
}
else if (nameDimIdx != null) {
nameList[dueIndex] = storage[dimensions[nameDimIdx]][chunkIndex][chunkOffset];
nameList[idx] = storage[dimensions[nameDimIdx]][chunkIndex][chunkOffset];
}
}
// ??? do not fill idList and do not checked by nameRepeatCount when init.
if (isInit) {
var name = nameList[dueIndex];
if (checkNameRepeat) {
var name = nameList[idx];
// Try using the id in option
var id = dataItem && dataItem.id;
......@@ -503,12 +523,10 @@ function doInit(list, dueIndex, dueEnd, isInit) {
}
nameRepeatCount[name]++;
}
id != null && (idList[dueIndex] = id);
id != null && (idList[idx] = id);
}
}
return dueIndex;
}
};
/**
* @return {number}
......@@ -831,10 +849,6 @@ function normalizeDimensions(dimensions) {
* list.each(function (idx) {})
*/
listProto.each = function (dims, cb, stack, context) {
// ???
// merge to createTaskForEach?
if (typeof dims === 'function') {
context = stack;
stack = cb;
......@@ -874,84 +888,6 @@ listProto.each = function (dims, cb, stack, context) {
}
};
/**
* Data iteration
*
*
* @param {string|Array.<string>}
* @param {Function} cb
* @param {boolean} [stack=false]
* @param {*} [context=this]
* @return {Object} task
*
* @example
* var task = list.createEachTask('x', function (val) {
* // val: 1212
* });
*
* var task = list.createEachTask(['x', 'y'], function (vals) {
* // vals: [1212, 3434]
* });
*
* task.progress(100); // Work by chunk size.
* task.unfinished();
*/
listProto.createEachTask = function (dims, cb, stack) {
if (typeof dims === 'function') {
stack = cb;
cb = dims;
dims = [];
}
var list = this;
dims = zrUtil.map(normalizeDimensions(dims), list.getDimension, list);
return createTask({
input: list,
progress: function (params, notify) {
var dimSize = dims.length;
var dueIndex = params.dueIndex;
for (; dueIndex < params.dueEnd; dueIndex++) {
// Simple optimization
switch (dimSize) {
case 0:
cb(
dueIndex
);
break;
case 1:
cb(
list.get(dims[0], dueIndex, stack),
dueIndex
);
break;
case 2:
cb(
list.get(dims[0], dueIndex, stack),
list.get(dims[1], dueIndex, stack),
dueIndex
);
break;
default:
var k = 0;
var value = [];
for (; k < dimSize; k++) {
value[k] = list.get(dims[k], dueIndex, stack);
}
value[k] = dueIndex;
cb(value);
}
}
notify(dueIndex);
}
});
};
// ??? remove
/**
* Data filter
* @param {string|Array.<string>}
......@@ -1396,11 +1332,10 @@ listProto.cloneShallow = function () {
transferProperties(list, this);
// Clone will not change the data extent and indices
list.indices = this.indices.slice();
list._chunkIndices = this._chunkIndices.slice();
list._chunkOffsets = this._chunkOffsets.slice();
list.indices = new CtorUint32Array(this.indices);
list._chunkIndices = new CtorUint32Array(this._chunkIndices);
list._chunkOffsets = new CtorUint32Array(this._chunkOffsets);
if (this._extent) {
list._extent = zrUtil.extend({}, this._extent);
......@@ -1411,25 +1346,6 @@ listProto.cloneShallow = function () {
return list;
};
// ??? duplicate with cloneShallow?
listProto.createCloneShallowTask = function () {
var input = this;
var output = this.cloneShallow();
return createTask({
input: input,
output: output,
progress: function (params, notify) {
var dueIndex = params.dueIndex;
for (; dueIndex < params.dueEnd; dueIndex++) {
output.indices[dueIndex] = input.indices[dueIndex];
output._chunkIndices[dueIndex] = input._chunkIndices[dueIndex];
output._chunkOffsets[dueIndex] = input._chunkOffsets[dueIndex];
}
notify(dueIndex);
}
});
};
// ??? temporarily
listProto.$releaseItemMemory = function (idx) {
if (this._itemLayouts) {
......
此差异已折叠。
import {map} from 'zrender/src/core/util';
export default function (seriesType, ecModel) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
seriesModel.pipeTask(
createTask(seriesType, seriesModel),
'visual'
);
});
}
export default function (seriesType) {
return {
seriesType: seriesType,
reset: function (data, seriesModel, ecModel, api) {
var coordSys = seriesModel.coordinateSystem;
if (!coordSys) {
return;
}
var dims = map(coordSys.dimensions, function (dim) {
return data.getDimension(seriesModel.coordDimToDataDim(dim)[0]);
});
function createTask(seriesType, seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (!coordSys) {
return;
}
var data = seriesModel.getData();
var dims = [];
for (var i = 0; i < coordSys.dimensions.length; i++) {
dims.push(seriesModel.coordDimToDataDim(coordSys.dimensions[i])[0]);
}
var task;
// ??? no task support ?
if (dims.length === 1) {
task = data.createEachTask(dims[0], function (x, idx) {
// Also {Array.<number>}, not undefined to avoid if...else... statement
data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
});
}
else if (dims.length === 2) {
task = data.createEachTask(dims, function (x, y, idx) {
// Also {Array.<number>}, not undefined to avoid if...else... statement
data.setItemLayout(
idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
);
});
}
return task;
var dataEach = dims.length === 1
? function (data, idx) {
var x = data.get(dims[0], idx);
// Also {Array.<number>}, not undefined to avoid if...else... statement
data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
}
: dims.length === 2
? function (data, idx) {
var x = data.get(dims[0], idx);
var y = data.get(dims[1], idx);
// Also {Array.<number>}, not undefined to avoid if...else... statement
data.setItemLayout(
idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
);
}
: null;
return {dataEach: dataEach};
}
};
}
// function createTask(seriesType, seriesModel) {
// var coordSys = seriesModel.coordinateSystem;
// if (!coordSys) {
// return;
// }
// var data = seriesModel.getData();
// var dims = [];
// for (var i = 0; i < coordSys.dimensions.length; i++) {
// dims.push(seriesModel.coordDimToDataDim(coordSys.dimensions[i])[0]);
// }
// var task;
// // ??? no task support ?
// if (dims.length === 1) {
// task = data.createEachTask(dims[0], function (x, idx) {
// // Also {Array.<number>}, not undefined to avoid if...else... statement
// data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
// });
// }
// else if (dims.length === 2) {
// task = data.createEachTask(dims, function (x, y, idx) {
// // Also {Array.<number>}, not undefined to avoid if...else... statement
// data.setItemLayout(
// idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
// );
// });
// }
// return task;
// }
......@@ -7,11 +7,13 @@
import * as zrUtil from 'zrender/src/core/util';
import Model from './Model';
import * as componentUtil from '../util/component';
import * as clazzUtil from '../util/clazz';
import {enableClassManagement, parseClassType} from '../util/clazz';
import {makeInner} from '../util/model';
import * as layout from '../util/layout';
import boxLayoutMixin from './mixin/boxLayout';
var arrayPush = Array.prototype.push;
var inner = makeInner();
/**
* @alias module:echarts/model/Component
......@@ -125,7 +127,8 @@ var ComponentModel = Model.extend({
optionUpdated: function (newCptOption, isInit) {},
getDefaultOption: function () {
if (!clazzUtil.hasOwn(this, '__defaultOption')) {
var fields = inner(this);
if (!fields.defaultOption) {
var optList = [];
var Class = this.constructor;
while (Class) {
......@@ -138,9 +141,9 @@ var ComponentModel = Model.extend({
for (var i = optList.length - 1; i >= 0; i--) {
defaultOption = zrUtil.merge(defaultOption, optList[i], true);
}
clazzUtil.set(this, '__defaultOption', defaultOption);
fields.defaultOption = defaultOption;
}
return clazzUtil.get(this, '__defaultOption');
return fields.defaultOption;
},
getReferringComponents: function (mainType) {
......@@ -170,7 +173,7 @@ var ComponentModel = Model.extend({
// );
// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
clazzUtil.enableClassManagement(
enableClassManagement(
ComponentModel, {registerWhenExtend: true}
);
componentUtil.enableSubTypeDefaulter(ComponentModel);
......@@ -185,7 +188,7 @@ function getDependencies(componentType) {
});
// Ensure main type
return zrUtil.map(deps, function (type) {
return clazzUtil.parseClassType(type).main;
return parseClassType(type).main;
});
}
......
......@@ -19,19 +19,16 @@
*/
import {__DEV__} from '../config';
import * as zrUtil from 'zrender/src/core/util';
import {
each, filter, map, isArray, indexOf, isObject, isString,
createHashMap, assert, clone, merge, extend, mixin
} from 'zrender/src/core/util';
import * as modelUtil from '../util/model';
import Model from './Model';
import ComponentModel from './Component';
import globalDefault from './globalDefault';
import colorPaletteMinin from './mixin/colorPalette';
var each = zrUtil.each;
var filter = zrUtil.filter;
var map = zrUtil.map;
var isArray = zrUtil.isArray;
var indexOf = zrUtil.indexOf;
var isObject = zrUtil.isObject;
// import {createTask} from '../stream/task';
var OPTION_INNER_KEY = '\0_ec_inner';
......@@ -61,10 +58,12 @@ var GlobalModel = Model.extend({
* @type {module:echarts/model/OptionManager}
*/
this._optionManager = optionManager;
// this.settingTask = createTask();
},
setOption: function (option, optionPreprocessorFuncs) {
zrUtil.assert(
assert(
!(OPTION_INNER_KEY in option),
'please use chart.getOption()'
);
......@@ -126,7 +125,7 @@ var GlobalModel = Model.extend({
var option = this.option;
var componentsMap = this._componentsMap;
var newCptTypes = [];
var scheduler = this.scheduler;
// var globalSettingTask = this.settingTask;
// 如果不存在对应的 component model 则直接 merge
each(newOption, function (componentOption, mainType) {
......@@ -135,9 +134,10 @@ var GlobalModel = Model.extend({
}
if (!ComponentModel.hasClass(mainType)) {
// globalSettingTask.dirty();
option[mainType] = option[mainType] == null
? zrUtil.clone(componentOption)
: zrUtil.merge(option[mainType], componentOption, true);
? clone(componentOption)
: merge(option[mainType], componentOption, true);
}
else {
newCptTypes.push(mainType);
......@@ -149,7 +149,9 @@ var GlobalModel = Model.extend({
newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
);
this._seriesIndices = this._seriesIndices || [];
this._seriesIndicesMap = createHashMap(
this._seriesIndices = this._seriesIndices || []
);
function visitComponent(mainType, dependencies) {
......@@ -181,7 +183,7 @@ var GlobalModel = Model.extend({
var componentModel = resultItem.exist;
var newCptOption = resultItem.option;
zrUtil.assert(
assert(
isObject(newCptOption) || componentModel,
'Empty component definition'
);
......@@ -200,12 +202,13 @@ var GlobalModel = Model.extend({
if (componentModel && componentModel instanceof ComponentModelClass) {
componentModel.name = resultItem.keyInfo.name;
// componentModel.settingTask && componentModel.settingTask.dirty();
componentModel.mergeOption(newCptOption, this);
componentModel.optionUpdated(newCptOption, false);
}
else {
// PENDING Global as parent ?
var extraOpt = zrUtil.extend(
var extraOpt = extend(
{
dependentModels: dependentModels,
componentIndex: index
......@@ -215,11 +218,14 @@ var GlobalModel = Model.extend({
componentModel = new ComponentModelClass(
newCptOption, this, this, extraOpt
);
extend(componentModel, extraOpt);
componentModel.init(newCptOption, this, this, extraOpt);
if (mainType === 'series') {
this.scheduler.initPipeline(componentModel);
// componentModel.settingTask.pipe(componentModel.dataInitTask);
componentModel.dataInitTask.pipe(componentModel.dataRestoreTask);
}
zrUtil.extend(componentModel, extraOpt);
componentModel.init(newCptOption, this, this, extraOpt);
// Call optionUpdated after init.
// newCptOption has been used as componentModel.option
// and may be merged with theme and default, so pass null
......@@ -234,11 +240,7 @@ var GlobalModel = Model.extend({
// Backup series for filtering.
if (mainType === 'series') {
var seriesModels = componentsMap.get('series');
this._seriesIndices = createSeriesIndices(seriesModels);
// Reset pipeline, should be after `restoreData`.
scheduler.clearUnusedPipelines(seriesModels);
scheduler.flushTemps('start', seriesModels);
createSeriesIndices(this, componentsMap.get('series'));
}
}
},
......@@ -249,7 +251,7 @@ var GlobalModel = Model.extend({
* @return {Object}
*/
getOption: function () {
var option = zrUtil.clone(this.option);
var option = clone(this.option);
each(option, function (opts, mainType) {
if (ComponentModel.hasClass(mainType)) {
......@@ -448,7 +450,7 @@ var GlobalModel = Model.extend({
});
});
}
else if (zrUtil.isString(mainType)) {
else if (isString(mainType)) {
each(componentsMap.get(mainType), cb, context);
}
else if (isObject(mainType)) {
......@@ -553,7 +555,7 @@ var GlobalModel = Model.extend({
*/
isSeriesFiltered: function (seriesModel) {
assertSeriesInitialized(this);
return zrUtil.indexOf(this._seriesIndices, seriesModel.componentIndex) < 0;
return !this._seriesIndicesMap.get(seriesModel.componentIndex);
},
/**
......@@ -572,13 +574,13 @@ var GlobalModel = Model.extend({
var filteredSeries = filter(
this._componentsMap.get('series'), cb, context
);
this._seriesIndices = createSeriesIndices(filteredSeries);
createSeriesIndices(this, filteredSeries);
},
restoreData: function () {
var componentsMap = this._componentsMap;
this._seriesIndices = createSeriesIndices(componentsMap.get('series'));
createSeriesIndices(this, componentsMap.get('series'));
var componentTypes = [];
componentsMap.each(function (components, componentType) {
......@@ -602,13 +604,13 @@ var GlobalModel = Model.extend({
* @inner
*/
function mergeTheme(option, theme) {
zrUtil.each(theme, function (themeItem, name) {
each(theme, function (themeItem, name) {
// 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
if (!ComponentModel.hasClass(name)) {
if (typeof themeItem === 'object') {
option[name] = !option[name]
? zrUtil.clone(themeItem)
: zrUtil.merge(option[name], themeItem, false);
? clone(themeItem)
: merge(option[name], themeItem, false);
}
else {
if (option[name] == null) {
......@@ -633,7 +635,7 @@ function initBase(baseOption) {
* @type {Object.<string, Array.<module:echarts/model/Model>>}
* @private
*/
this._componentsMap = zrUtil.createHashMap({series: []});
this._componentsMap = createHashMap({series: []});
/**
* Mapping between filtered series list and raw series list.
......@@ -641,12 +643,14 @@ function initBase(baseOption) {
* @type {Array.<nubmer>}
* @private
*/
this._seriesIndices = null;
this._seriesIndices;
this._seriesIndicesMap;
mergeTheme(baseOption, this._theme.option);
// TODO Needs clone when merging to the unexisted property
zrUtil.merge(baseOption, globalDefault, false);
merge(baseOption, globalDefault, false);
this.mergeOption(baseOption);
}
......@@ -657,7 +661,7 @@ function initBase(baseOption) {
* @return {Object} key: {string} type, value: {Array.<Object>} models
*/
function getComponentsByTypes(componentsMap, types) {
if (!zrUtil.isArray(types)) {
if (!isArray(types)) {
types = types ? [types] : [];
}
......@@ -687,10 +691,12 @@ function determineSubType(mainType, newCptOption, existComponent) {
/**
* @inner
*/
function createSeriesIndices(seriesModels) {
return map(seriesModels, function (series) {
return series.componentIndex;
}) || [];
function createSeriesIndices(ecModel, seriesModels) {
ecModel._seriesIndicesMap = createHashMap(
ecModel._seriesIndices = map(seriesModels, function (series) {
return series.componentIndex;
}) || []
);
}
/**
......@@ -719,6 +725,6 @@ function assertSeriesInitialized(ecModel) {
}
}
zrUtil.mixin(GlobalModel, colorPaletteMinin);
mixin(GlobalModel, colorPaletteMinin);
export default GlobalModel;
......@@ -4,7 +4,8 @@
import * as zrUtil from 'zrender/src/core/util';
import env from 'zrender/src/core/env';
import * as clazzUtil from '../util/clazz';
import {makeInner} from '../util/model';
import {enableClassExtend} from '../util/clazz';
import lineStyleMixin from './mixin/lineStyle';
import areaStyleMixin from './mixin/areaStyle';
......@@ -12,6 +13,7 @@ import textStyleMixin from './mixin/textStyle';
import itemStyleMixin from './mixin/itemStyle';
var mixin = zrUtil.mixin;
var inner = makeInner();
/**
* @alias module:echarts/model/Model
......@@ -135,7 +137,7 @@ Model.prototype = {
},
setReadOnly: function (properties) {
clazzUtil.setReadOnly(this, properties);
// clazzUtil.setReadOnly(this, properties);
},
// If path is null/undefined, return null/undefined.
......@@ -152,7 +154,7 @@ Model.prototype = {
* return {module:echarts/model/Model}
*/
customizeGetParent: function (getParentMethod) {
clazzUtil.set(this, 'getParent', getParentMethod);
inner(this).getParent = getParentMethod;
},
isAnimationEnabled: function () {
......@@ -165,6 +167,7 @@ Model.prototype = {
}
}
}
};
function doGet(obj, pathArr, parentModel) {
......@@ -187,12 +190,12 @@ function doGet(obj, pathArr, parentModel) {
// `path` can be null/undefined
function getParent(model, path) {
var getParentMethod = clazzUtil.get(model, 'getParent');
var getParentMethod = inner(model).getParent;
return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
}
// Enable Model.extend.
clazzUtil.enableClassExtend(Model);
enableClassExtend(Model);
mixin(Model, lineStyleMixin);
mixin(Model, areaStyleMixin);
......
......@@ -7,7 +7,6 @@ import {
addCommas,
getTooltipMarker
} from '../util/format';
import {set, get} from '../util/clazz';
import * as modelUtil from '../util/model';
import ComponentModel from './Component';
import {isNumeric} from '../util/number';
......@@ -16,6 +15,9 @@ import {
getLayoutParams,
mergeLayoutParam
} from '../util/layout';
import {createTask} from '../stream/task';
var inner = modelUtil.makeInner();
var SeriesModel = ComponentModel.extend({
......@@ -70,9 +72,22 @@ var SeriesModel = ComponentModel.extend({
*/
this.seriesIndex = this.componentIndex;
// this.settingTask = createTask();
this.dataInitTask = createTask({
reset: dataInitTaskReset,
count: dataInitTaskCount,
progress: dataInitTaskProgress
}, {model: this});
this.dataRestoreTask = createTask({
reset: dataRestoreTaskReset
}, {model: this});
this.mergeDefaultAndTheme(option, ecModel);
var data = this.getInitialData(option, ecModel);
this.dataInitTask.dirty();
if (__DEV__) {
zrUtil.assert(data, 'getInitialData returned invalid data.');
}
......@@ -81,7 +96,7 @@ var SeriesModel = ComponentModel.extend({
* @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
* @private
*/
set(this, 'dataBeforeProcessed', data);
inner(this).dataBeforeProcessed = data;
// If we reverse the order (make data firstly, and then make
// dataBeforeProcessed by cloneShallow), cloneShallow will
......@@ -128,6 +143,8 @@ var SeriesModel = ComponentModel.extend({
},
mergeOption: function (newSeriesOption, ecModel) {
// this.settingTask.dirty();
newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true);
this.fillDataTextStyle(newSeriesOption.data);
......@@ -137,12 +154,11 @@ var SeriesModel = ComponentModel.extend({
}
var data = this.getInitialData(newSeriesOption, ecModel);
if (data) {
// ??? progress data?
set(this, 'dataBeforeProcessed', data);
// ??? should not restoreData here? but called by echart?
// this.restoreData();
}
this.dataInitTask.dirty();
// ??? progress data?
inner(this).dataBeforeProcessed = data;
// ??? should not restoreData here? but called by echart?
// this.restoreData();
},
fillDataTextStyle: function (data) {
......@@ -170,7 +186,7 @@ var SeriesModel = ComponentModel.extend({
* @return {module:echarts/data/List}
*/
getData: function (dataType) {
var data = get(this, 'data');
var data = inner(this).data;
return dataType == null ? data : data.getLinkedData(dataType);
},
......@@ -178,7 +194,7 @@ var SeriesModel = ComponentModel.extend({
* @param {module:echarts/data/List} data
*/
setData: function (data) {
set(this, 'data', data);
inner(this).data = data;
},
/**
......@@ -186,7 +202,7 @@ var SeriesModel = ComponentModel.extend({
* @return {module:echarts/data/List}
*/
getRawData: function () {
return get(this, 'dataBeforeProcessed');
return inner(this).dataBeforeProcessed;
},
/**
......@@ -271,7 +287,7 @@ var SeriesModel = ComponentModel.extend({
return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');
}
var data = get(this, 'data');
var data = inner(this).data;
var value = this.getRawValue(dataIndex);
var formattedValue = zrUtil.isArray(value)
......@@ -322,16 +338,7 @@ var SeriesModel = ComponentModel.extend({
},
restoreData: function () {
var dataBeforeProcessed = get(this, 'dataBeforeProcessed');
var dataCloneTask = dataBeforeProcessed.createCloneShallowTask();
set(this, 'data', dataCloneTask.output);
// var provisionTask = dataBeforeProcessed.getProvider().provisionTask;
var dataInitTask = dataBeforeProcessed.getInitTask();
// this.pipeTask(provisionTask, 'dataProvide');
this.pipeTask(dataInitTask, 'dataInit');
this.pipeTask(dataCloneTask, 'dataClone', 'updateBase');
this.dataRestoreTask.dirty();
},
getColorFromPalette: function (name, scope) {
......@@ -381,4 +388,22 @@ var SeriesModel = ComponentModel.extend({
zrUtil.mixin(SeriesModel, modelUtil.dataFormatMixin);
zrUtil.mixin(SeriesModel, colorPaletteMixin);
function dataInitTaskCount(context) {
return context.model.getRawData().count();
}
function dataInitTaskReset(context) {
return {finished: true};
}
function dataInitTaskProgress(taskParams, context) {
context.model.getRawData().initFromRawData(taskParams.dueIndex, taskParams.dueEnd);
}
function dataRestoreTaskReset(context) {
var model = context.model;
model.setData(model.getRawData().cloneShallow());
return {noProgress: true};
}
export default SeriesModel;
\ No newline at end of file
import {set, get} from '../../util/clazz';
import {makeInner} from '../../util/model';
var inner = makeInner();
export default {
clearColorPalette: function () {
set(this, 'colorIdx', 0);
set(this, 'colorNameMap', {});
inner(this).colorIdx = 0;
inner(this).colorNameMap = {};
},
getColorFromPalette: function (name, scope) {
scope = scope || this;
var colorIdx = get(scope, 'colorIdx') || 0;
var colorNameMap = get(scope, 'colorNameMap') || set(scope, 'colorNameMap', {});
var scopeFields = inner(scope);
var colorIdx = scopeFields.colorIdx || 0;
var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {};
// Use `hasOwnProperty` to avoid conflict with Object.prototype.
if (colorNameMap.hasOwnProperty(name)) {
return colorNameMap[name];
......@@ -23,8 +26,8 @@ export default {
if (name) {
colorNameMap[name] = color;
}
set(scope, 'colorIdx', (colorIdx + 1) % colorPalette.length);
scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length;
return color;
}
};
\ No newline at end of file
};
......@@ -2,233 +2,260 @@
* @module echarts/stream/Scheduler
*/
import {__DEV__} from '../config';
import {assert, each, createHashMap} from 'zrender/src/core/util';
import {normalizeToArray, makeInner} from '../util/model';
var inner = makeInner();
/**
* @const
*/
var STAGE = {
dataInit: 1,
dataClone: 2,
processor: 3,
visual: 4,
render: 5
};
import {each, createHashMap} from 'zrender/src/core/util';
import {createTask} from './task';
import arrayEqual from '../util/array/equal';
/**
* @constructor
*/
function Scheduler(ecInstance) {
this._pipelineMap = createHashMap();
function Scheduler(ecInstance, api) {
// this._pipelineMap = createHashMap();
this.ecInstance = ecInstance;
var stageMap = this._stageMap = createHashMap();
each(STAGE, function (value, name) {
stageMap.set(name, []);
});
// this._stageUnfinished = [];
this.api = api;
this.unfinished;
/**
* @private
* @type {
* [handlerUID: string]: {
* seriesTaskMap?: {
* [seriesUID: string]: Task
* },
* overallTask?: Task
* }
* }
*/
this._stageTaskMap = createHashMap();
}
var proto = Scheduler.prototype;
proto.progressStage = function (stage) {
// if (!this._stageUnfinished[STAGE[stage]]) {
// return;
proto.getStep = function () {
return this.ecInstance.getModel().get('streamStep') || 700;
};
proto.prepareStageTasks = function (stageHandlers, pipelineTails, useClearVisual) {
each(stageHandlers, function (stageHandler) {
prepareStageHandler(
stageHandler,
this._stageTaskMap,
pipelineTails,
this.ecInstance.getModel(),
this.api
);
}, this);
};
function prepareStageHandler(stageHandler, stageTaskMap, pipelineTails, ecModel, api) {
var handlerUID = stageHandler.uid;
var stageHandlerRecord = stageTaskMap.get(handlerUID) || stageTaskMap.set(handlerUID, []);
if ((stageHandler.seriesType || stageHandler.allSeries) && stageHandler.reset) {
createSeriesStageTask(stageHandler, stageHandlerRecord, pipelineTails, ecModel, api);
}
// else if (stageHandler.execute && stageHandler.getTargetSeries) {
// // ???!
// // Just listen to axis setting.
// stageHandlerRecord.upstreamShouldFinished = true;
// var originalModelUIDs = stageHandlerRecord.modelUIDs;
// var modelUIDs = stageHandlerRecord.modelUIDs = stageHandler.getTargetModelUIDs();
// if (!arrayEqual(originalModelUIDs, modelUIDs)) {
// var originalOverallTask = stageHandlerRecord.overallTask;
// originalOverallTask && originalOverallTask.dispose();
// var overallTask = stageHandlerRecord.overallTask = createTask({
// progress: stageHandler.legacyFunc
// });
// var modelUIDMap = createHashMap(modelUIDs);
// pipelineTails.each(function (tailTask, seriesUID) {
// if (modelUIDMap.get(seriesUID)) {
// tailTask.pipe(overallTask);
// }
// });
// }
// }
else if (stageHandler.legacyFunc) {
createLegacyStageTask(stageHandler, stageHandlerRecord, pipelineTails, ecModel, api);
}
}
// ???! Fragile way.
// function detectSeriesType(stageHandler, ecModel, api) {
// var type;
// var origin = ecModel.eachSeriesByType;
// ecModel.eachSeriesByType = function (seriesType, cb) {
// type = seriesType;
// };
// try {
// stageHandler.legacyFunc(ecModel, api);
// }
// catch (e) {
// }
// ecModel.eachSeriesByType = origin;
// return type;
// }
// ???! make upateVisual updateView updateLayout the same?
// visualType: 'visual' or 'layout'
proto.performStageTasks = function (stageHandlers, ecModel, payload, visualType, setDirty) {
var unfinished;
var tasks = this.getTasksByStage(stage);
// ??? temporarily
var step = this.ecInstance.getModel().get('streamStep') || 700;
each(tasks, function (task) {
task.progress({
step: step
});
unfinished |= task.unfinished();
});
var step = this.getStep();
this.unfinished |= unfinished;
};
/**
* @param {Object} host Should has uid.
*/
proto.initPipeline = function (host) {
var pipelineId = host.uid;
if (__DEV__) {
assert(!this._pipelineMap.get(pipelineId) && !host.pipeTask);
}
var pipeline = {tasks: [], temps: []};
this._pipelineMap.set(pipelineId, pipeline);
each(stageHandlers, function (stageHandler, idx) {
if (visualType && visualType !== stageHandler.visualType) {
return;
}
// Inject method pipeTask
host.pipeTask = hostPipeTask;
inner(host).temps = pipeline.temps;
};
var stageHandlerRecord = this._stageTaskMap.get(stageHandler.uid);
var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
var overallTask = stageHandlerRecord.overallTask;
var contextOnReset = {payload: payload};
function hostPipeTask(task, stage, tags) {
task && inner(this).temps.push({task: task, stage: stage, tags: tags});
}
if (overallTask) {
setDirty && overallTask.dirty();
unfinished |= overallTask.perform({step: step}, contextOnReset);
}
else if (seriesTaskMap) {
stageHandler.allSeries
? ecModel.eachRawSeries(eachSeries)
: ecModel.eachRawSeriesByType(stageHandler.seriesType, eachSeries);
}
/**
* @param {Array.<Object>} allHosts Should has uid.
*/
proto.clearUnusedPipelines = function (allHosts) {
var pipelineMap = this._pipelineMap;
var idMap = createHashMap();
var stageMap = this._stageMap;
each(allHosts, function (host) {
idMap.set(host.uid, 1);
});
pipelineMap.each(function (pipeline, id) {
if (!idMap.get(id)) {
clearPipeline(stageMap, pipeline, -1);
pipelineMap.set(id, null);
function eachSeries(seriesModel) {
var task = seriesTaskMap.get(seriesModel.uid);
setDirty && task.dirty();
unfinished |= task.perform({
step: step,
skip: !stageHandler.processRawSeries && ecModel.isSeriesFiltered(seriesModel)
}, contextOnReset);
}
});
};
}, this);
proto.clearTemps = function () {
this._pipelineMap.each(clearPipelineTemp);
this.unfinished |= unfinished;
};
function clearPipelineTemp(pipeline) {
pipeline.temps.length = 0;
}
proto.performSeriesTasks = function (ecModel) {
var unfinished;
// ??? temporarily
var step = this.getStep();
/**
* @param {string} tag
* @param {Array.<Object>} [hosts]
*/
proto.flushTemps = function (tag, hosts) {
var pipelineMap = this._pipelineMap;
var stageMap = this._stageMap;
var self = this;
hosts
? each(hosts, function (host) {
flushPipelineTemps(pipelineMap.get(host.uid));
})
: pipelineMap.each(flushPipelineTemps);
function flushPipelineTemps(pipeline) {
clearPipelineDownstreams(stageMap, pipeline, tag);
var temps = pipeline.temps;
setTags(temps);
each(temps, function (tmp) {
self.unfinished = true;
pipeTask(stageMap, pipeline.tasks, tmp.task, tmp.stage, tmp.tags);
}, this);
clearPipelineTemp(pipeline);
}
};
ecModel.eachRawSeries(function (seriesModel) {
unfinished |= seriesModel.dataInitTask.perform({step: step});
unfinished |= seriesModel.dataRestoreTask.perform({step: step});
});
// TODO when needed: customize tag
function setTags(temps) {
var hasUpdateViewBase;
for (var i = temps.length - 1; i >= 0; i--) {
var item = temps[i];
var stage = item.stage;
var innerTask = inner(item.task);
this.unfinished |= unfinished;
};
if (stage === 'render') {
innerTask.updateLayoutBase = innerTask.updateVisualBase = true;
}
else if (stage === 'dataClone') {
innerTask.updateBase = true;
}
// Find the last dataClone or processor task.
if (!hasUpdateViewBase && (stage === 'dataClone' || stage === 'processor')) {
innerTask.updateViewBase = true;
hasUpdateViewBase = true;
function createSeriesStageTask(stageHandler, stageHandlerRecord, pipelineTails, ecModel, api) {
var seriesTaskMap = stageHandlerRecord.seriesTaskMap || (stageHandlerRecord.seriesTaskMap = createHashMap());
var pipelineIdMap = createHashMap();
stageHandler.allSeries
? ecModel.eachRawSeries(create)
: ecModel.eachRawSeriesByType(stageHandler.seriesType, create);
function create(seriesModel) {
var pipelineId = seriesModel.uid;
pipelineIdMap.set(pipelineId, 1);
// Init tasks for each seriesModel only once.
if (!seriesTaskMap.get(pipelineId)) {
var task = createTask({
reset: seriesTaskReset,
progress: seriesTaskProgress,
count: seriesTaskCount
}, {
model: seriesModel,
ecModel: ecModel,
api: api,
useClearVisual: stageHandler.isVisual && !stageHandler.isLayout
});
task.__handlerReset = stageHandler.reset;
seriesTaskMap.set(pipelineId, task);
pipe(pipelineTails, pipelineId, task);
}
}
}
/**
* Only clear streams start from the tagged tasks.
* This is for cases like `updateView`, `updateVisual` and `updateLayout`,
* who are partial streams, depending on the render task of the full stream,
* and should be cleared if another partial stream is started.
*/
function clearPipelineDownstreams(stageMap, pipeline, tag) {
var tasks = pipeline.tasks;
var baseIndex;
if (tag === 'start') {
baseIndex = -1;
}
else {
for (var i = 0; i < tasks.length; i++) {
if (inner(tasks[i])[tag]) {
baseIndex = i;
break;
}
// Clear unused series tasks.
seriesTaskMap.each(function (task, pipelineId) {
if (!pipelineIdMap.get(pipelineId)) {
task.dispose();
seriesTaskMap.removeKey(pipelineId);
}
}
clearPipeline(stageMap, pipeline, baseIndex);
});
}
function pipeTask(stageMap, pipelineTasks, task, stage) {
if (__DEV__) {
// In case typo.
stage && assert(STAGE[stage] != null);
each(pipelineTasks, function (taskInPipeline) {
assert(taskInPipeline != task);
});
}
pipelineTasks.length && pipelineTasks[pipelineTasks.length - 1].pipe(task);
pipelineTasks.push(task);
// ??? Should keep the original register order of tasks
// within single stage. Especially visual and layout.
stage && stageMap.get(stage).push(task);
function createLegacyStageTask(stageHandler, stageHandlerRecord, pipelineTails, ecModel, api) {
var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask
|| createTask(
{reset: legacyTaskReset},
{ecModel: ecModel, api: api, legacyFunc: stageHandler.legacyFunc}
);
// ???!
var stubs = overallTask.agentStubs = [];
ecModel.eachRawSeries(function (seriesModel) {
var stub = createTask({reset: emptyTaskReset});
stub.agent = overallTask;
stubs.push(stub);
pipe(pipelineTails, seriesModel.uid, stub);
});
}
proto.getTasksByStage = function (stage) {
return this._stageMap.get(stage).slice();
};
function emptyTaskReset() {
return {noProgress: true};
}
// taskBaseIndex can be -1;
function clearPipeline(stageMap, pipeline, taskBaseIndex) {
var tasks = pipeline.tasks;
if (!tasks.length) {
return;
}
function legacyTaskReset(context) {
context.legacyFunc(context.ecModel, context.api, context.payload);
return {noProgress: true};
}
if (taskBaseIndex === -1) {
var taskBase = tasks[0];
taskBase.clearDownstreams();
taskBase.reset();
}
else {
tasks[taskBaseIndex].clearDownstreams();
function seriesTaskReset(context) {
var data = context.model.getData();
if (context.useClearVisual) {
data.clearAllVisual();
}
for (var i = taskBaseIndex + 1; i < tasks.length; i++) {
inner(tasks[i]).cleared = 1;
var resetDefine = this.__handlerReset(
data, context.model, context.ecModel, context.api
);
if (!resetDefine) {
return {noProgress: true};
}
tasks.length = taskBaseIndex + 1;
this.__dataEach = resetDefine.dataEach;
this.__progress = resetDefine.progress;
}
stageMap.each(function (stageTasks) {
for (var i = stageTasks.length - 1; i >= 0; i--) {
if (inner(stageTasks[i]).cleared) {
stageTasks.splice(i, 1);
function seriesTaskProgress(params, context) {
var data = context.model.getData();
if (data) {
if (this.__dataEach) {
for (var i = params.start; i < params.end; i++) {
this.__dataEach(data, i);
}
}
});
else if (this.__progress) {
this.__progress(params, data);
}
}
}
function seriesTaskCount(context) {
return context.model.getData().count();
}
i = 10;
function pipe(pipelineTails, pipelineId, task) {
var tail = pipelineTails.get(pipelineId);
tail.pipe(task);
pipelineTails.set(pipelineId, task);
}
export default Scheduler;
import {assert, each, bind, indexOf, isArray} from 'zrender/src/core/util';
import {assert, extend, each} from 'zrender/src/core/util';
import {makeInner} from '../util/model';
import { __DEV__ } from '../config';
var inner = makeInner();
/**
* @param {Object} define
* @param {Function} define.progress
* @param {Object|Array} define.input {count: Function} or a pure array.
* @return See the return of `createTask`.
*/
export function createTask(define) {
return new Task(define);
export function createTask(define, context) {
return new Task(define, context);
}
/**
......@@ -21,144 +20,134 @@ export function isTask(obj) {
return obj instanceof Task;
}
// ----------------
// Task
// ----------------
/**
* @constructor
* @param {Object} define
* @param {Function} define.progress Custom progress
* @param {Function} define.reset Custom reset
* @param {Function} [define.input] {count: Function}
* @param {Function} [define.output] {count: Function}
* @param {Function} [define.count] count is used to determin data task.
* @param {Object} [context]
*/
function Task(define) {
function Task(define, context) {
var fields = inner(this);
define = define || {};
fields.downstreams = [];
fields.upstreams = [];
var input = fields.input = this.input = define.input;
// Just for programing convenience.
this.output = define.output || input;
this._progress = define.progress;
this._reset = define.reset;
this._count = define.count;
this._progressCustom = define.progress;
this._resetCustom = define.reset;
this._progressNotify = bind(progressNotify, this);
this._count = isArray(input) ? arrayCount : listCount;
fields.dirty = true;
this.reset();
this.context = context || {};
}
var taskProto = Task.prototype;
/**
* @param {Object} [params]
*/
taskProto.reset = function (params) {
var fields = inner(this);
fields.started = false;
fields.dueEnd = fields.upstreams.length ? 0 : null;
fields.dueIndex = fields.outputDueEnd = 0;
this._resetCustom && this._resetCustom(params);
taskProto.perform = function (opt, contextOnReset) {
this.plan(contextOnReset);
this.progress(opt);
return this.unfinished();
};
each(fields.downstreams, function (downTask) {
downTask.reset(params);
});
taskProto.dirty = function () {
inner(this).dirty = true;
};
/**
* @param {Array|Object} input
*/
taskProto.changeInput = function (input) {
taskProto.plan = function (contextOnReset) {
var fields = inner(this);
// ???
assert(!fields.upstreams.length);
var finishedAfterReset;
if (fields.dirty) {
fields.dirty = false;
finishedAfterReset = reset(this, contextOnReset);
}
// This should always be performed so it can be passed to downstream.
var upTask = fields.upstream;
if (upTask) {
var progressInfo = upTask.getProgressInfo();
if (__DEV__) {
assert(progressInfo.outputDueEnd != null);
}
fields.dueEnd = Math.max(progressInfo.outputDueEnd, fields.dueEnd);
}
// If noProgress, pass index from upstream to downstream each time plan called.
if (finishedAfterReset || fields.noProgress) {
fields.dueIndex = fields.outputDueEnd = fields.dueEnd;
}
fields.input = this.input = input;
fields.dueIndex = 0;
// Keep outputDueEnd, should not rollback.
fields.dueEnd = null;
// FIXME
each(this.agentStubs, function (agentStub) {
agentStub.plan(contextOnReset);
});
};
/**
* @param {Object} [params]
* @param {number} [params.step] Specified step.
* If not specified, progress to current dueEnd.
*/
taskProto.progress = function (params) {
params = params || {};
var fields = inner(this);
fields.started = true;
function reset(taskIns, contextOnReset) {
extend(taskIns.context, contextOnReset);
this._progressCustom({
dueEnd: Math.min(
params.step != null ? fields.dueIndex + params.step : Infinity,
fields.dueEnd != null ? fields.dueEnd : Infinity,
this._count()
),
dueIndex: fields.dueIndex
}, this._progressNotify);
};
taskProto.getProgressInfo = function () {
var fields = inner(this);
return {
dueIndex: fields.dueIndex,
dueEnd: fields.dueEnd
};
};
function progressNotify(dueIndex, outputDueEnd) {
var fields = inner(this);
var fields = inner(taskIns);
fields.dueIndex = fields.outputDueEnd = 0;
fields.dueEnd = taskIns._count ? taskIns._count(taskIns.context) : 0;
assert(dueIndex != null);
var result = taskIns._reset && taskIns._reset(taskIns.context) || {};
fields.dueIndex = dueIndex;
fields.noProgress = result.noProgress;
// If no `outputDueEnd`, assume that output data and
// input data is the same, so use `dueIndex` as `outputDueEnd`.
outputDueEnd = outputDueEnd != null ? outputDueEnd : dueIndex;
fields.downstream && fields.downstream.dirty();
// FIXME
taskIns.agent && taskIns.agent.dirty();
// ??? Can not rollback.
assert(outputDueEnd >= fields.outputDueEnd);
fields.outputDueEnd = outputDueEnd;
each(fields.downstreams, function (downTask) {
downTask.plan();
});
return result.finished;
}
/**
* Receive notify. ??? Only on notify? check pipe.
* @param {Object} opt
* @param {number} [opt.step] Specified step.
* @param {number} [opt.skip] Skip customer perform call.
*/
taskProto.plan = function () {
taskProto.progress = function (opt) {
var fields = inner(this);
var upDueEnd;
each(fields.upstreams, function (upTask) {
var dueEnd = upTask.getOutputDueEnd();
upDueEnd = upDueEnd != null
// Current no scenario that upstreams
// outputs data are not the same.
? Math.min(upDueEnd, dueEnd)
: dueEnd;
});
if (fields.noProgress) {
return;
}
var start = fields.dueIndex;
var end = Math.min(
opt.step != null ? start + opt.step : Infinity,
fields.dueEnd,
this._count ? this._count(this.context) : Infinity
);
var outputDueEnd;
!opt.skip && start < end && (
outputDueEnd = this._progress({start: start, end: end}, this.context)
);
assert(upDueEnd >= fields.dueEnd);
fields.dueEnd = upDueEnd;
fields.dueIndex = end;
// If no `outputDueEnd`, assume that output data and
// input data is the same, so use `dueIndex` as `outputDueEnd`.
if (outputDueEnd == null) {
outputDueEnd = end;
}
if (__DEV__) {
// ??? Can not rollback.
assert(outputDueEnd >= fields.outputDueEnd);
}
fields.outputDueEnd = outputDueEnd;
};
/**
* @return {number}
*/
taskProto.getOutputDueEnd = function () {
return inner(this).outputDueEnd;
taskProto.getProgressInfo = function () {
var fields = inner(this);
return {
dueIndex: fields.dueIndex,
dueEnd: fields.dueEnd,
outputDueEnd: fields.outputDueEnd
};
};
/**
......@@ -166,10 +155,7 @@ taskProto.getOutputDueEnd = function () {
*/
taskProto.unfinished = function () {
var fields = inner(this);
return fields.dueIndex < (
fields.dueEnd != null ? fields.dueEnd : this._count()
);
return !fields.noProgress && fields.dueIndex < fields.dueEnd;
};
/**
......@@ -177,60 +163,31 @@ taskProto.unfinished = function () {
* @return {Object} The downstream task.
*/
taskProto.pipe = function (downTask) {
// ???
assert(!inner(downTask).disposed);
var fields = inner(this);
var downTaskUpstreams = inner(downTask).upstreams;
if (indexOf(downTaskUpstreams, this) >= 0) {
return;
if (__DEV__) {
assert(downTask && !inner(downTask).disposed);
}
downTask.reset();
fields.downstreams.push(downTask);
downTaskUpstreams.push(this);
downTask.plan();
var fields = inner(this);
return downTask;
};
// If already downstream, do not dirty downTask.
if (fields.downstream !== downTask) {
fields.downstream = downTask;
inner(downTask).upstream = this;
/**
* Remove all downstreams.
*/
taskProto.clearDownstreams = function () {
var downstreams = inner(this).downstreams;
each(downstreams, function (downTask) {
// ??? Current forbiden reuse task to avoid troubles
// (piped by multiple task but difficult to unpipe).
downTask.dispose();
// var downTaskUpstream = inner(downTask).upstreams;
// downTaskUpstream.splice(indexOf(downTaskUpstream, this), 1);
// // Stop the down task, but do not leave it from all its upstreams,
// // because it may keep working with its other upstreams.
// downTask.reset();
}, this);
downstreams.length = 0;
downTask.dirty();
}
};
taskProto.dispose = function () {
var fields = inner(this);
each(fields.upstreams, function (upTask) {
// var downTaskUpstream = inner(downTask).upstreams;
// downTaskUpstream.splice(indexOf(downTaskUpstream, this), 1);
var upTaskDownstream = inner(upTask).downstreams;
upTaskDownstream.splice(indexOf(upTaskDownstream, this), 1);
}, this);
this.reset();
fields.disposed = true;
};
function listCount() {
return inner(this).input.count();
}
if (fields.disposed) {
return;
}
function arrayCount() {
return inner(this).input.length;
}
fields.upstream && (inner(fields.upstream).downstream = null);
fields.downstream && (inner(fields.downstream).upstream = null);
fields.dirty = false;
fields.disposed = true;
};
......@@ -3,34 +3,6 @@ import * as zrUtil from 'zrender/src/core/util';
var TYPE_DELIMITER = '.';
var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
var MEMBER_PRIFIX = '\0ec_\0';
/**
* Hide private class member.
* The same behavior as `host[name] = value;` (can be right-value)
* @public
*/
export function set(host, name, value) {
return (host[MEMBER_PRIFIX + name] = value);
}
/**
* Hide private class member.
* The same behavior as `host[name];`
* @public
*/
export function get(host, name) {
return host[MEMBER_PRIFIX + name];
}
/**
* For hidden private class member.
* The same behavior as `host.hasOwnProperty(name);`
* @public
*/
export function hasOwn(host, name) {
return host.hasOwnProperty(MEMBER_PRIFIX + name);
}
/**
* Notice, parseClassType('') should returns {main: '', sub: ''}
......
import * as zrUtil from 'zrender/src/core/util';
import * as formatUtil from './format';
import * as nubmerUtil from './number';
import Model from '../model/Model';
var each = zrUtil.each;
var isObject = zrUtil.isObject;
......@@ -119,28 +118,28 @@ export function converDataValue(value, dimInfo) {
? NaN : +value; // If string (like '-'), using '+' parse to NaN
}
/**
* Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
* @param {module:echarts/data/List} data
* @param {Object} opt
* @param {string} [opt.seriesIndex]
* @param {Object} [opt.name]
* @param {Object} [opt.mainType]
* @param {Object} [opt.subType]
*/
export function createDataFormatModel(data, opt) {
var model = new Model();
zrUtil.mixin(model, dataFormatMixin);
model.seriesIndex = opt.seriesIndex;
model.name = opt.name || '';
model.mainType = opt.mainType;
model.subType = opt.subType;
model.getData = function () {
return data;
};
return model;
}
// /**
// * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
// * @param {module:echarts/data/List} data
// * @param {Object} opt
// * @param {string} [opt.seriesIndex]
// * @param {Object} [opt.name]
// * @param {Object} [opt.mainType]
// * @param {Object} [opt.subType]
// */
// export function createDataFormatModel(data, opt) {
// var model = new Model();
// zrUtil.mixin(model, dataFormatMixin);
// model.seriesIndex = opt.seriesIndex;
// model.name = opt.name || '';
// model.mainType = opt.mainType;
// model.subType = opt.subType;
// model.getData = function () {
// return data;
// };
// return model;
// }
// PENDING A little ugly
export var dataFormatMixin = {
......
import * as zrUtil from 'zrender/src/core/util';
import {bind, each} from 'zrender/src/core/util';
import Group from 'zrender/src/container/Group';
import * as componentUtil from '../util/component';
import * as clazzUtil from '../util/clazz';
import * as modelUtil from '../util/model';
import {createTask} from '../stream/task';
var inner = modelUtil.makeInner();
function Chart() {
......@@ -17,6 +20,11 @@ function Chart() {
* @readOnly
*/
this.uid = componentUtil.getUID('viewChart');
this.renderTask = createTask({
reset: bind(this.renderTaskReset, this),
progress: bind(this.renderTaskProgress, this)
}, {view: this});
}
Chart.prototype = {
......@@ -75,7 +83,67 @@ Chart.prototype = {
* @param {module:echarts/model/Global} ecModel
* @param {module:echarts/ExtensionAPI} api
*/
dispose: function () {}
dispose: function () {},
/**
* @protected
*/
renderTaskReset: function (context) {
if (!this.incrementalPrepare) {
return {noProgress: true};
}
var seriesModel = context.model;
var ecModel = context.ecModel;
var api = context.api;
var shouldStreamRender = this.shouldStreamRender(seriesModel);
var payload = context.payload;
var updateMethod = payload && inner(payload).updateMethod || 'render';
if (inner(this).streamRendering ^ shouldStreamRender) {
this.remove(ecModel, api);
}
(inner(this).streamRendering = shouldStreamRender)
? this.incrementalPrepare(seriesModel, ecModel, api)
: this[updateMethod](seriesModel, ecModel, api, payload);
},
/**
* @protected
*/
renderTaskProgress: function (params, context) {
var seriesModel = context.model;
var ecModel = context.ecModel;
var api = context.api;
if (this.incrementalAdd) {
for (var i = params.start; i < params.end; i++) {
this.incrementalAdd(seriesModel, ecModel, api, i);
}
}
else {
this.incrementalProgress(params, seriesModel, ecModel, api);
}
},
/**
* @protected
*/
shouldStreamRender: function (seriesModel) {
var data = seriesModel.getData();
var streamSetting = seriesModel.getStreamSetting();
return streamSetting
&& this.incrementalPrepare
&& streamSetting.threshold < data.count()
&& !this.cannotStreamRender(seriesModel);
},
/**
* Convinient for override in extended class.
* @protected
*/
cannotStreamRender: function () {}
/**
* The view contains the given point.
......@@ -120,7 +188,7 @@ function toggleHighlight(data, payload, state) {
var dataIndex = modelUtil.queryDataIndex(data, payload);
if (dataIndex != null) {
zrUtil.each(modelUtil.normalizeToArray(dataIndex), function (dataIdx) {
each(modelUtil.normalizeToArray(dataIndex), function (dataIdx) {
elSetState(data.getItemGraphicEl(dataIdx), state);
});
}
......@@ -137,4 +205,8 @@ clazzUtil.enableClassExtend(Chart, ['dispose']);
// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
clazzUtil.enableClassManagement(Chart, {registerWhenExtend: true});
Chart.markUpdateMethod = function (payload, methodName) {
inner(payload).updateMethod = methodName;
};
export default Chart;
\ No newline at end of file
import Gradient from 'zrender/src/graphic/Gradient';
export default function (ecModel) {
function encodeColor(seriesModel) {
export default {
allSeries: true,
processRawSeries: true,
reset: function (data, seriesModel, ecModel) {
var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.');
var data = seriesModel.getData();
var color = seriesModel.get(colorAccessPath) // Set in itemStyle
|| seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color
......@@ -21,16 +22,15 @@ export default function (ecModel) {
}
// itemStyle in each data item
var task = data.createEachTask(function (idx) {
var dataEach = function (idx) {
var itemModel = data.getItemModel(idx);
var color = itemModel.get(colorAccessPath, true);
if (color != null) {
data.setItemVisual(idx, 'color', color);
}
});
};
seriesModel.pipeTask(task, 'visual');
return {dataEach: dataEach};
}
}
ecModel.eachRawSeries(encodeColor);
}
\ No newline at end of file
};
export default function (seriesType, defaultSymbolType, legendSymbol, ecModel) {
export default function (seriesType, defaultSymbolType, legendSymbol) {
// Encoding visual for all series include which is filtered for legend drawing
ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
seriesModel.pipeTask(
createTask(seriesModel, defaultSymbolType, legendSymbol, ecModel),
'visual'
);
});
}
return {
seriesType: seriesType,
processRawSeries: true,
reset: function (data, seriesModel, ecModel, api) {
var data = seriesModel.getData();
function createTask(seriesModel, defaultSymbolType, legendSymbol, ecModel) {
var data = seriesModel.getData();
var symbolType = seriesModel.get('symbol') || defaultSymbolType;
var symbolSize = seriesModel.get('symbolSize');
data.setVisual({
legendSymbol: legendSymbol || symbolType,
symbol: symbolType,
symbolSize: symbolSize
});
// Only visible series has each data be visual encoded
if (ecModel.isSeriesFiltered(seriesModel)) {
return;
}
return data.createEachTask(function (idx) {
if (typeof symbolSize === 'function') {
var rawValue = seriesModel.getRawValue(idx);
// FIXME
var params = seriesModel.getDataParams(idx);
data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
}
var symbolType = seriesModel.get('symbol') || defaultSymbolType;
var symbolSize = seriesModel.get('symbolSize');
var itemModel = data.getItemModel(idx);
var itemSymbolType = itemModel.getShallow('symbol', true);
var itemSymbolSize = itemModel.getShallow('symbolSize', true);
// If has item symbol
if (itemSymbolType != null) {
data.setItemVisual(idx, 'symbol', itemSymbolType);
}
if (itemSymbolSize != null) {
// PENDING Transform symbolSize ?
data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
data.setVisual({
legendSymbol: legendSymbol || symbolType,
symbol: symbolType,
symbolSize: symbolSize
});
// Only visible series has each data be visual encoded
if (ecModel.isSeriesFiltered(seriesModel)) {
return;
}
function dataEach(data, idx) {
if (typeof symbolSize === 'function') {
var rawValue = seriesModel.getRawValue(idx);
// FIXME
var params = seriesModel.getDataParams(idx);
data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
}
var itemModel = data.getItemModel(idx);
var itemSymbolType = itemModel.getShallow('symbol', true);
var itemSymbolSize = itemModel.getShallow('symbolSize', true);
// If has item symbol
if (itemSymbolType != null) {
data.setItemVisual(idx, 'symbol', itemSymbolType);
}
if (itemSymbolSize != null) {
// PENDING Transform symbolSize ?
data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
}
}
return {dataEach: dataEach};
}
});
};
}
// export default function (seriesType, defaultSymbolType, legendSymbol, ecModel) {
// // Encoding visual for all series include which is filtered for legend drawing
// ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
// seriesModel.pipeTask(
// createTask(seriesModel, defaultSymbolType, legendSymbol, ecModel),
// 'visual'
// );
// });
// }
// function createTask(seriesModel, defaultSymbolType, legendSymbol, ecModel) {
// var data = seriesModel.getData();
// var symbolType = seriesModel.get('symbol') || defaultSymbolType;
// var symbolSize = seriesModel.get('symbolSize');
// data.setVisual({
// legendSymbol: legendSymbol || symbolType,
// symbol: symbolType,
// symbolSize: symbolSize
// });
// // Only visible series has each data be visual encoded
// if (ecModel.isSeriesFiltered(seriesModel)) {
// return;
// }
// return data.createEachTask(function (idx) {
// if (typeof symbolSize === 'function') {
// var rawValue = seriesModel.getRawValue(idx);
// // FIXME
// var params = seriesModel.getDataParams(idx);
// data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
// }
// var itemModel = data.getItemModel(idx);
// var itemSymbolType = itemModel.getShallow('symbol', true);
// var itemSymbolSize = itemModel.getShallow('symbolSize', true);
// // If has item symbol
// if (itemSymbolType != null) {
// data.setItemVisual(idx, 'symbol', itemSymbolType);
// }
// if (itemSymbolSize != null) {
// // PENDING Transform symbolSize ?
// data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
// }
// });
// }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册