| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- var _config = require("../../config");
- var __DEV__ = _config.__DEV__;
- var zrUtil = require("zrender/lib/core/util");
- var BoundingRect = require("zrender/lib/core/BoundingRect");
- var _layout = require("../../util/layout");
- var getLayoutRect = _layout.getLayoutRect;
- var axisHelper = require("../../coord/axisHelper");
- var Cartesian2D = require("./Cartesian2D");
- var Axis2D = require("./Axis2D");
- var CoordinateSystem = require("../../CoordinateSystem");
- require("./GridModel");
- /**
- * Grid is a region which contains at most 4 cartesian systems
- *
- * TODO Default cartesian
- */
- // Depends on GridModel, AxisModel, which performs preprocess.
- var each = zrUtil.each;
- var ifAxisCrossZero = axisHelper.ifAxisCrossZero;
- var niceScaleExtent = axisHelper.niceScaleExtent;
- /**
- * Check if the axis is used in the specified grid
- * @inner
- */
- function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
- return axisModel.getCoordSysModel() === gridModel;
- }
- function rotateTextRect(textRect, rotate) {
- var rotateRadians = rotate * Math.PI / 180;
- var boundingBox = textRect.plain();
- var beforeWidth = boundingBox.width;
- var beforeHeight = boundingBox.height;
- var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
- var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
- var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
- return rotatedRect;
- }
- function getLabelUnionRect(axis) {
- var axisModel = axis.model;
- var labels = axisModel.getFormattedLabels();
- var axisLabelModel = axisModel.getModel('axisLabel');
- var rect;
- var step = 1;
- var labelCount = labels.length;
- if (labelCount > 40) {
- // Simple optimization for large amount of labels
- step = Math.ceil(labelCount / 40);
- }
- for (var i = 0; i < labelCount; i += step) {
- if (!axis.isLabelIgnored(i)) {
- var unrotatedSingleRect = axisLabelModel.getTextRect(labels[i]);
- var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
- rect ? rect.union(singleRect) : rect = singleRect;
- }
- }
- return rect;
- }
- function Grid(gridModel, ecModel, api) {
- /**
- * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
- * @private
- */
- this._coordsMap = {};
- /**
- * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
- * @private
- */
- this._coordsList = [];
- /**
- * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
- * @private
- */
- this._axesMap = {};
- /**
- * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
- * @private
- */
- this._axesList = [];
- this._initCartesian(gridModel, ecModel, api);
- this.model = gridModel;
- }
- var gridProto = Grid.prototype;
- gridProto.type = 'grid';
- gridProto.axisPointerEnabled = true;
- gridProto.getRect = function () {
- return this._rect;
- };
- gridProto.update = function (ecModel, api) {
- var axesMap = this._axesMap;
- this._updateScale(ecModel, this.model);
- each(axesMap.x, function (xAxis) {
- niceScaleExtent(xAxis.scale, xAxis.model);
- });
- each(axesMap.y, function (yAxis) {
- niceScaleExtent(yAxis.scale, yAxis.model);
- });
- each(axesMap.x, function (xAxis) {
- fixAxisOnZero(axesMap, 'y', xAxis);
- });
- each(axesMap.y, function (yAxis) {
- fixAxisOnZero(axesMap, 'x', yAxis);
- }); // Resize again if containLabel is enabled
- // FIXME It may cause getting wrong grid size in data processing stage
- this.resize(this.model, api);
- };
- function fixAxisOnZero(axesMap, otherAxisDim, axis) {
- // onZero can not be enabled in these two situations:
- // 1. When any other axis is a category axis.
- // 2. When no axis is cross 0 point.
- var axes = axesMap[otherAxisDim];
- if (!axis.onZero) {
- return;
- }
- var onZeroAxisIndex = axis.onZeroAxisIndex; // If target axis is specified.
- if (onZeroAxisIndex != null) {
- var otherAxis = axes[onZeroAxisIndex];
- if (otherAxis && canNotOnZeroToAxis(otherAxis)) {
- axis.onZero = false;
- }
- return;
- }
- for (var idx in axes) {
- if (axes.hasOwnProperty(idx)) {
- var otherAxis = axes[idx];
- if (otherAxis && !canNotOnZeroToAxis(otherAxis)) {
- onZeroAxisIndex = +idx;
- break;
- }
- }
- }
- if (onZeroAxisIndex == null) {
- axis.onZero = false;
- }
- axis.onZeroAxisIndex = onZeroAxisIndex;
- }
- function canNotOnZeroToAxis(axis) {
- return axis.type === 'category' || axis.type === 'time' || !ifAxisCrossZero(axis);
- }
- /**
- * Resize the grid
- * @param {module:echarts/coord/cartesian/GridModel} gridModel
- * @param {module:echarts/ExtensionAPI} api
- */
- gridProto.resize = function (gridModel, api, ignoreContainLabel) {
- var gridRect = getLayoutRect(gridModel.getBoxLayoutParams(), {
- width: api.getWidth(),
- height: api.getHeight()
- });
- this._rect = gridRect;
- var axesList = this._axesList;
- adjustAxes(); // Minus label size
- if (!ignoreContainLabel && gridModel.get('containLabel')) {
- each(axesList, function (axis) {
- if (!axis.model.get('axisLabel.inside')) {
- var labelUnionRect = getLabelUnionRect(axis);
- if (labelUnionRect) {
- var dim = axis.isHorizontal() ? 'height' : 'width';
- var margin = axis.model.get('axisLabel.margin');
- gridRect[dim] -= labelUnionRect[dim] + margin;
- if (axis.position === 'top') {
- gridRect.y += labelUnionRect.height + margin;
- } else if (axis.position === 'left') {
- gridRect.x += labelUnionRect.width + margin;
- }
- }
- }
- });
- adjustAxes();
- }
- function adjustAxes() {
- each(axesList, function (axis) {
- var isHorizontal = axis.isHorizontal();
- var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
- var idx = axis.inverse ? 1 : 0;
- axis.setExtent(extent[idx], extent[1 - idx]);
- updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y);
- });
- }
- };
- /**
- * @param {string} axisType
- * @param {number} [axisIndex]
- */
- gridProto.getAxis = function (axisType, axisIndex) {
- var axesMapOnDim = this._axesMap[axisType];
- if (axesMapOnDim != null) {
- if (axisIndex == null) {
- // Find first axis
- for (var name in axesMapOnDim) {
- if (axesMapOnDim.hasOwnProperty(name)) {
- return axesMapOnDim[name];
- }
- }
- }
- return axesMapOnDim[axisIndex];
- }
- };
- /**
- * @return {Array.<module:echarts/coord/Axis>}
- */
- gridProto.getAxes = function () {
- return this._axesList.slice();
- };
- /**
- * Usage:
- * grid.getCartesian(xAxisIndex, yAxisIndex);
- * grid.getCartesian(xAxisIndex);
- * grid.getCartesian(null, yAxisIndex);
- * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
- *
- * @param {number|Object} [xAxisIndex]
- * @param {number} [yAxisIndex]
- */
- gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
- if (xAxisIndex != null && yAxisIndex != null) {
- var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
- return this._coordsMap[key];
- }
- if (zrUtil.isObject(xAxisIndex)) {
- yAxisIndex = xAxisIndex.yAxisIndex;
- xAxisIndex = xAxisIndex.xAxisIndex;
- } // When only xAxisIndex or yAxisIndex given, find its first cartesian.
- for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
- if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {
- return coordList[i];
- }
- }
- };
- gridProto.getCartesians = function () {
- return this._coordsList.slice();
- };
- /**
- * @implements
- * see {module:echarts/CoodinateSystem}
- */
- gridProto.convertToPixel = function (ecModel, finder, value) {
- var target = this._findConvertTarget(ecModel, finder);
- return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;
- };
- /**
- * @implements
- * see {module:echarts/CoodinateSystem}
- */
- gridProto.convertFromPixel = function (ecModel, finder, value) {
- var target = this._findConvertTarget(ecModel, finder);
- return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;
- };
- /**
- * @inner
- */
- gridProto._findConvertTarget = function (ecModel, finder) {
- var seriesModel = finder.seriesModel;
- var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis')[0];
- var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis')[0];
- var gridModel = finder.gridModel;
- var coordsList = this._coordsList;
- var cartesian;
- var axis;
- if (seriesModel) {
- cartesian = seriesModel.coordinateSystem;
- zrUtil.indexOf(coordsList, cartesian) < 0 && (cartesian = null);
- } else if (xAxisModel && yAxisModel) {
- cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
- } else if (xAxisModel) {
- axis = this.getAxis('x', xAxisModel.componentIndex);
- } else if (yAxisModel) {
- axis = this.getAxis('y', yAxisModel.componentIndex);
- } // Lowest priority.
- else if (gridModel) {
- var grid = gridModel.coordinateSystem;
- if (grid === this) {
- cartesian = this._coordsList[0];
- }
- }
- return {
- cartesian: cartesian,
- axis: axis
- };
- };
- /**
- * @implements
- * see {module:echarts/CoodinateSystem}
- */
- gridProto.containPoint = function (point) {
- var coord = this._coordsList[0];
- if (coord) {
- return coord.containPoint(point);
- }
- };
- /**
- * Initialize cartesian coordinate systems
- * @private
- */
- gridProto._initCartesian = function (gridModel, ecModel, api) {
- var axisPositionUsed = {
- left: false,
- right: false,
- top: false,
- bottom: false
- };
- var axesMap = {
- x: {},
- y: {}
- };
- var axesCount = {
- x: 0,
- y: 0
- }; /// Create axis
- ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
- ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
- if (!axesCount.x || !axesCount.y) {
- // Roll back when there no either x or y axis
- this._axesMap = {};
- this._axesList = [];
- return;
- }
- this._axesMap = axesMap; /// Create cartesian2d
- each(axesMap.x, function (xAxis, xAxisIndex) {
- each(axesMap.y, function (yAxis, yAxisIndex) {
- var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
- var cartesian = new Cartesian2D(key);
- cartesian.grid = this;
- cartesian.model = gridModel;
- this._coordsMap[key] = cartesian;
- this._coordsList.push(cartesian);
- cartesian.addAxis(xAxis);
- cartesian.addAxis(yAxis);
- }, this);
- }, this);
- function createAxisCreator(axisType) {
- return function (axisModel, idx) {
- if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
- return;
- }
- var axisPosition = axisModel.get('position');
- if (axisType === 'x') {
- // Fix position
- if (axisPosition !== 'top' && axisPosition !== 'bottom') {
- // Default bottom of X
- axisPosition = 'bottom';
- if (axisPositionUsed[axisPosition]) {
- axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
- }
- }
- } else {
- // Fix position
- if (axisPosition !== 'left' && axisPosition !== 'right') {
- // Default left of Y
- axisPosition = 'left';
- if (axisPositionUsed[axisPosition]) {
- axisPosition = axisPosition === 'left' ? 'right' : 'left';
- }
- }
- }
- axisPositionUsed[axisPosition] = true;
- var axis = new Axis2D(axisType, axisHelper.createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);
- var isCategory = axis.type === 'category';
- axis.onBand = isCategory && axisModel.get('boundaryGap');
- axis.inverse = axisModel.get('inverse');
- axis.onZero = axisModel.get('axisLine.onZero');
- axis.onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex'); // Inject axis into axisModel
- axisModel.axis = axis; // Inject axisModel into axis
- axis.model = axisModel; // Inject grid info axis
- axis.grid = this; // Index of axis, can be used as key
- axis.index = idx;
- this._axesList.push(axis);
- axesMap[axisType][idx] = axis;
- axesCount[axisType]++;
- };
- }
- };
- /**
- * Update cartesian properties from series
- * @param {module:echarts/model/Option} option
- * @private
- */
- gridProto._updateScale = function (ecModel, gridModel) {
- // Reset scale
- zrUtil.each(this._axesList, function (axis) {
- axis.scale.setExtent(Infinity, -Infinity);
- });
- ecModel.eachSeries(function (seriesModel) {
- if (isCartesian2D(seriesModel)) {
- var axesModels = findAxesModels(seriesModel, ecModel);
- var xAxisModel = axesModels[0];
- var yAxisModel = axesModels[1];
- if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)) {
- return;
- }
- var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
- var data = seriesModel.getData();
- var xAxis = cartesian.getAxis('x');
- var yAxis = cartesian.getAxis('y');
- if (data.type === 'list') {
- unionExtent(data, xAxis, seriesModel);
- unionExtent(data, yAxis, seriesModel);
- }
- }
- }, this);
- function unionExtent(data, axis, seriesModel) {
- each(seriesModel.coordDimToDataDim(axis.dim), function (dim) {
- axis.scale.unionExtentFromData(data, dim);
- });
- }
- };
- /**
- * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
- * @return {Object} {baseAxes: [], otherAxes: []}
- */
- gridProto.getTooltipAxes = function (dim) {
- var baseAxes = [];
- var otherAxes = [];
- each(this.getCartesians(), function (cartesian) {
- var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
- var otherAxis = cartesian.getOtherAxis(baseAxis);
- zrUtil.indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
- zrUtil.indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
- });
- return {
- baseAxes: baseAxes,
- otherAxes: otherAxes
- };
- };
- /**
- * @inner
- */
- function updateAxisTransfrom(axis, coordBase) {
- var axisExtent = axis.getExtent();
- var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform
- axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {
- return coord + coordBase;
- } : function (coord) {
- return axisExtentSum - coord + coordBase;
- };
- axis.toLocalCoord = axis.dim === 'x' ? function (coord) {
- return coord - coordBase;
- } : function (coord) {
- return axisExtentSum - coord + coordBase;
- };
- }
- var axesTypes = ['xAxis', 'yAxis'];
- /**
- * @inner
- */
- function findAxesModels(seriesModel, ecModel) {
- return zrUtil.map(axesTypes, function (axisType) {
- var axisModel = seriesModel.getReferringComponents(axisType)[0];
- return axisModel;
- });
- }
- /**
- * @inner
- */
- function isCartesian2D(seriesModel) {
- return seriesModel.get('coordinateSystem') === 'cartesian2d';
- }
- Grid.create = function (ecModel, api) {
- var grids = [];
- ecModel.eachComponent('grid', function (gridModel, idx) {
- var grid = new Grid(gridModel, ecModel, api);
- grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize
- // should be performed in create stage.
- grid.resize(gridModel, api, true);
- gridModel.coordinateSystem = grid;
- grids.push(grid);
- }); // Inject the coordinateSystems into seriesModel
- ecModel.eachSeries(function (seriesModel) {
- if (!isCartesian2D(seriesModel)) {
- return;
- }
- var axesModels = findAxesModels(seriesModel, ecModel);
- var xAxisModel = axesModels[0];
- var yAxisModel = axesModels[1];
- var gridModel = xAxisModel.getCoordSysModel();
- var grid = gridModel.coordinateSystem;
- seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
- });
- return grids;
- }; // For deciding which dimensions to use when creating list data
- Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
- CoordinateSystem.register('cartesian2d', Grid);
- var _default = Grid;
- module.exports = _default;
|