300 lines
8.9 KiB
Plaintext
300 lines
8.9 KiB
Plaintext
/**
|
|
* Parallel Coordinates
|
|
* <https://en.wikipedia.org/wiki/Parallel_coordinates>
|
|
*/
|
|
define(function(require) {
|
|
|
|
var layout = require('../../util/layout');
|
|
var axisHelper = require('../../coord/axisHelper');
|
|
var zrUtil = require('zrender/core/util');
|
|
var ParallelAxis = require('./ParallelAxis');
|
|
var matrix = require('zrender/core/matrix');
|
|
var vector = require('zrender/core/vector');
|
|
|
|
var each = zrUtil.each;
|
|
|
|
var PI = Math.PI;
|
|
|
|
function Parallel(parallelModel, ecModel, api) {
|
|
|
|
/**
|
|
* key: dimension
|
|
* @type {Object.<string, module:echarts/coord/parallel/Axis>}
|
|
* @private
|
|
*/
|
|
this._axesMap = {};
|
|
|
|
/**
|
|
* key: dimension
|
|
* value: {position: [], rotation, }
|
|
* @type {Object.<string, Object>}
|
|
* @private
|
|
*/
|
|
this._axesLayout = {};
|
|
|
|
/**
|
|
* Always follow axis order.
|
|
* @type {Array.<string>}
|
|
* @readOnly
|
|
*/
|
|
this.dimensions = parallelModel.dimensions;
|
|
|
|
/**
|
|
* @type {module:zrender/core/BoundingRect}
|
|
*/
|
|
this._rect;
|
|
|
|
/**
|
|
* @type {module:echarts/coord/parallel/ParallelModel}
|
|
*/
|
|
this._model = parallelModel;
|
|
|
|
this._init(parallelModel, ecModel, api);
|
|
}
|
|
|
|
Parallel.prototype = {
|
|
|
|
type: 'parallel',
|
|
|
|
constructor: Parallel,
|
|
|
|
/**
|
|
* Initialize cartesian coordinate systems
|
|
* @private
|
|
*/
|
|
_init: function (parallelModel, ecModel, api) {
|
|
|
|
var dimensions = parallelModel.dimensions;
|
|
var parallelAxisIndex = parallelModel.parallelAxisIndex;
|
|
|
|
each(dimensions, function (dim, idx) {
|
|
|
|
var axisIndex = parallelAxisIndex[idx];
|
|
var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
|
|
|
|
var axis = this._axesMap[dim] = new ParallelAxis(
|
|
dim,
|
|
axisHelper.createScaleByModel(axisModel),
|
|
[0, 0],
|
|
axisModel.get('type'),
|
|
axisIndex
|
|
);
|
|
|
|
var isCategory = axis.type === 'category';
|
|
axis.onBand = isCategory && axisModel.get('boundaryGap');
|
|
axis.inverse = axisModel.get('inverse');
|
|
|
|
// Inject axis into axisModel
|
|
axisModel.axis = axis;
|
|
|
|
// Inject axisModel into axis
|
|
axis.model = axisModel;
|
|
}, this);
|
|
},
|
|
|
|
/**
|
|
* Update axis scale after data processed
|
|
* @param {module:echarts/model/Global} ecModel
|
|
* @param {module:echarts/ExtensionAPI} api
|
|
*/
|
|
update: function (ecModel, api) {
|
|
this._updateAxesFromSeries(this._model, ecModel);
|
|
},
|
|
|
|
/**
|
|
* Update properties from series
|
|
* @private
|
|
*/
|
|
_updateAxesFromSeries: function (parallelModel, ecModel) {
|
|
ecModel.eachSeries(function (seriesModel) {
|
|
|
|
if (!parallelModel.contains(seriesModel, ecModel)) {
|
|
return;
|
|
}
|
|
|
|
var data = seriesModel.getData();
|
|
|
|
each(this.dimensions, function (dim) {
|
|
var axis = this._axesMap[dim];
|
|
axis.scale.unionExtent(data.getDataExtent(dim));
|
|
axisHelper.niceScaleExtent(axis, axis.model);
|
|
}, this);
|
|
}, this);
|
|
},
|
|
|
|
/**
|
|
* Resize the parallel coordinate system.
|
|
* @param {module:echarts/coord/parallel/ParallelModel} parallelModel
|
|
* @param {module:echarts/ExtensionAPI} api
|
|
*/
|
|
resize: function (parallelModel, api) {
|
|
this._rect = layout.getLayoutRect(
|
|
parallelModel.getBoxLayoutParams(),
|
|
{
|
|
width: api.getWidth(),
|
|
height: api.getHeight()
|
|
}
|
|
);
|
|
|
|
this._layoutAxes(parallelModel);
|
|
},
|
|
|
|
/**
|
|
* @return {module:zrender/core/BoundingRect}
|
|
*/
|
|
getRect: function () {
|
|
return this._rect;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_layoutAxes: function (parallelModel) {
|
|
var rect = this._rect;
|
|
var layout = parallelModel.get('layout');
|
|
var axes = this._axesMap;
|
|
var dimensions = this.dimensions;
|
|
|
|
var size = [rect.width, rect.height];
|
|
var sizeIdx = layout === 'horizontal' ? 0 : 1;
|
|
var layoutLength = size[sizeIdx];
|
|
var axisLength = size[1 - sizeIdx];
|
|
var axisExtent = [0, axisLength];
|
|
|
|
each(axes, function (axis) {
|
|
var idx = axis.inverse ? 1 : 0;
|
|
axis.setExtent(axisExtent[idx], axisExtent[1 - idx]);
|
|
});
|
|
|
|
each(dimensions, function (dim, idx) {
|
|
var pos = layoutLength * idx / (dimensions.length - 1);
|
|
|
|
var positionTable = {
|
|
horizontal: {
|
|
x: pos,
|
|
y: axisLength
|
|
},
|
|
vertical: {
|
|
x: 0,
|
|
y: pos
|
|
}
|
|
};
|
|
var rotationTable = {
|
|
horizontal: PI / 2,
|
|
vertical: 0
|
|
};
|
|
|
|
var position = [
|
|
positionTable[layout].x + rect.x,
|
|
positionTable[layout].y + rect.y
|
|
];
|
|
|
|
var rotation = rotationTable[layout];
|
|
var transform = matrix.create();
|
|
matrix.rotate(transform, transform, rotation);
|
|
matrix.translate(transform, transform, position);
|
|
|
|
// TODO
|
|
// tick等排布信息。
|
|
|
|
// TODO
|
|
// 根据axis order 更新 dimensions顺序。
|
|
|
|
this._axesLayout[dim] = {
|
|
position: position,
|
|
rotation: rotation,
|
|
transform: transform,
|
|
tickDirection: 1,
|
|
labelDirection: 1
|
|
};
|
|
}, this);
|
|
},
|
|
|
|
/**
|
|
* Get axis by dim.
|
|
* @param {string} dim
|
|
* @return {module:echarts/coord/parallel/ParallelAxis} [description]
|
|
*/
|
|
getAxis: function (dim) {
|
|
return this._axesMap[dim];
|
|
},
|
|
|
|
/**
|
|
* Convert a dim value of a single item of series data to Point.
|
|
* @param {*} value
|
|
* @param {string} dim
|
|
* @return {Array}
|
|
*/
|
|
dataToPoint: function (value, dim) {
|
|
return this.axisCoordToPoint(
|
|
this._axesMap[dim].dataToCoord(value),
|
|
dim
|
|
);
|
|
},
|
|
|
|
/**
|
|
* @param {module:echarts/data/List} data
|
|
* @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal'
|
|
* {number} dataIndex
|
|
* @param {Object} context
|
|
*/
|
|
eachActiveState: function (data, callback, context) {
|
|
var dimensions = this.dimensions;
|
|
var axesMap = this._axesMap;
|
|
var hasActiveSet = false;
|
|
|
|
for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
|
|
if (axesMap[dimensions[j]].model.getActiveState() !== 'normal') {
|
|
hasActiveSet = true;
|
|
}
|
|
}
|
|
|
|
for (var i = 0, len = data.count(); i < len; i++) {
|
|
var values = data.getValues(dimensions, i);
|
|
var activeState;
|
|
|
|
if (!hasActiveSet) {
|
|
activeState = 'normal';
|
|
}
|
|
else {
|
|
activeState = 'active';
|
|
for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
|
|
var dimName = dimensions[j];
|
|
var state = axesMap[dimName].model.getActiveState(values[j], j);
|
|
|
|
if (state === 'inactive') {
|
|
activeState = 'inactive';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
callback.call(context, activeState, i);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Convert coords of each axis to Point.
|
|
* Return point. For example: [10, 20]
|
|
* @param {Array.<number>} coords
|
|
* @param {string} dim
|
|
* @return {Array.<number>}
|
|
*/
|
|
axisCoordToPoint: function (coord, dim) {
|
|
var axisLayout = this._axesLayout[dim];
|
|
var point = [coord, 0];
|
|
vector.applyTransform(point, point, axisLayout.transform);
|
|
return point;
|
|
},
|
|
|
|
/**
|
|
* Get axis layout.
|
|
*/
|
|
getAxisLayout: function (dim) {
|
|
return zrUtil.clone(this._axesLayout[dim]);
|
|
}
|
|
|
|
};
|
|
|
|
return Parallel;
|
|
}); |