first commit

This commit is contained in:
2026-02-06 20:21:10 +08:00
commit f9d7f980bb
3994 changed files with 1146030 additions and 0 deletions
+466
View File
@@ -0,0 +1,466 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { getLayoutRect } from '../../util/layout.js';
import { ListIterator } from '../../util/model.js';
import { eqNaN, isArray, retrieve2 } from 'zrender/lib/core/util.js';
import { WH, XY } from '../../util/graphic.js';
import Model from '../../model/Model.js';
import { mathMax, mathMin, parsePositionSizeOption } from '../../util/number.js';
import { createNaNRectLike, MatrixClampOption, MatrixCellLayoutInfoType, parseCoordRangeOption, resetXYLocatorRange, xyLocatorRangeToRectOneDim } from './matrixCoordHelper.js';
import { error } from '../../util/log.js';
import { injectCoordSysByOption, simpleCoordSysInjectionProvider } from '../../core/CoordinateSystem.js';
var Matrix = /** @class */function () {
function Matrix(matrixModel, ecModel, api) {
this.dimensions = Matrix.dimensions;
this.type = 'matrix';
this._model = matrixModel;
var models = this._dimModels = {
x: matrixModel.getDimensionModel('x'),
y: matrixModel.getDimensionModel('y')
};
this._dims = {
x: models.x.dim,
y: models.y.dim
};
this._resize(matrixModel, api);
}
/**
* @see fetchers in `model/referHelper.ts`,
* which is used to parse data in ordinal way.
* In most series only 'x' and 'y' is required,
* but some series, such as heatmap, can specify value.
*/
Matrix.getDimensionsInfo = function () {
return [{
name: 'x',
type: 'ordinal'
}, {
name: 'y',
type: 'ordinal'
}, {
name: 'value'
}];
};
Matrix.create = function (ecModel, api) {
var matrixList = [];
ecModel.eachComponent('matrix', function (matrixModel) {
var matrix = new Matrix(matrixModel, ecModel, api);
matrixList.push(matrix);
matrixModel.coordinateSystem = matrix;
});
// Inject coordinate system
// PENDING: optimize to not to travel all components?
// (collect relevant components in ecModel only when model update?)
ecModel.eachComponent(function (mainType, componentModel) {
injectCoordSysByOption({
targetModel: componentModel,
coordSysType: 'matrix',
coordSysProvider: simpleCoordSysInjectionProvider
});
});
return matrixList;
};
Matrix.prototype.getRect = function () {
return this._rect;
};
Matrix.prototype._resize = function (matrixModel, api) {
var dims = this._dims;
var dimModels = this._dimModels;
var rect = this._rect = getLayoutRect(matrixModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
});
layOutUnitsOnDimension(dimModels, dims, rect, 0);
layOutUnitsOnDimension(dimModels, dims, rect, 1);
layOutDimCellsRestInfoByUnit(0, dims);
layOutDimCellsRestInfoByUnit(1, dims);
layOutBodyCornerCellMerge(this._model.getBody(), dims);
layOutBodyCornerCellMerge(this._model.getCorner(), dims);
};
/**
* @implement
* - The input is allowed to be `[NaN/null/undefined, xxx]`/`[xxx, NaN/null/undefined]`;
* the return is `[NaN, xxxresult]`/`[xxxresult, NaN]` or clamped boundary value if
* `clamp` passed. This is for the usage that only get coord on single x or y.
* - Alwasy return an numeric array, but never be null/undefined.
* If it can not be located or invalid, return `[NaN, NaN]`.
*/
Matrix.prototype.dataToPoint = function (data, opt, out) {
out = out || [];
this.dataToLayout(data, opt, _dtpOutDataToLayout);
out[0] = _dtpOutDataToLayout.rect.x + _dtpOutDataToLayout.rect.width / 2;
out[1] = _dtpOutDataToLayout.rect.y + _dtpOutDataToLayout.rect.height / 2;
return out;
};
/**
* @implement
* - The input is allowed to be `[NaN/null/undefined, xxx]`/`[xxx, NaN/null/undefined]`;
* the return is `{x: NaN, width: NaN, y: xxxresulty, height: xxxresulth}`/
* `{y: NaN, height: NaN, x: xxxresultx, width: xxxresultw}` or clamped boundary value
* if `clamp` passed. This is for the usage that only get coord on single x or y.
* - The returned `out.rect` and `out.matrixXYLocatorRange` is always an object or an 2d-array,
* but never be null/undefined. If it cannot be located or invalid, `NaN` is in their
* corresponding number props.
* - Do not provide `out.contentRect`, because it's allowed to input non-leaf dimension x/y or
* a range of x/y, which determines a rect covering multiple cells (even not merged), in which
* case the padding and borderWidth can not be determined to make a contentRect. Therefore only
* return `out.rect` in any case for consistency. The caller is responsible for adding space to
* avoid covering cell borders, if necessary.
*/
Matrix.prototype.dataToLayout = function (data, opt, out) {
var dims = this._dims;
out = out || {};
var outRect = out.rect = out.rect || {};
outRect.x = outRect.y = outRect.width = outRect.height = NaN;
var outLocRange = out.matrixXYLocatorRange = resetXYLocatorRange(out.matrixXYLocatorRange);
if (!isArray(data)) {
if (process.env.NODE_ENV !== 'production') {
error('Input data must be an array in `convertToLayout`, `convertToPixel`');
}
return out;
}
parseCoordRangeOption(outLocRange, null, data, dims, retrieve2(opt && opt.clamp, MatrixClampOption.none));
if (!opt || !opt.ignoreMergeCells) {
if (!opt || opt.clamp !== MatrixClampOption.corner) {
this._model.getBody().expandRangeByCellMerge(outLocRange);
}
if (!opt || opt.clamp !== MatrixClampOption.body) {
this._model.getCorner().expandRangeByCellMerge(outLocRange);
}
}
xyLocatorRangeToRectOneDim(outRect, outLocRange, dims, 0);
xyLocatorRangeToRectOneDim(outRect, outLocRange, dims, 1);
return out;
};
/**
* The returned locator pair can be the input of `dataToPoint` or `dataToLayout`.
*
* If point[0] is out of the matrix rect,
* the out[0] is NaN;
* else if it is on the right of top-left corner of body,
* the out[0] is the oridinal number (>= 0).
* else
* out[0] is the locator for corner or header (<= 0).
*
* The same rule goes for point[1] and out[1].
*
* But point[0] and point[1] are calculated separately, i.e.,
* the reuslt can be `[1, NaN]` or `[NaN, 1]` if only one dimension is out of boundary.
*
* @implement
*/
Matrix.prototype.pointToData = function (point, opt, out) {
var dims = this._dims;
pointToDataOneDimPrepareCtx(_tmpCtxPointToData, 0, dims, point, opt && opt.clamp);
pointToDataOneDimPrepareCtx(_tmpCtxPointToData, 1, dims, point, opt && opt.clamp);
out = out || [];
out[0] = out[1] = NaN;
if (_tmpCtxPointToData.y === CtxPointToDataAreaType.inCorner && _tmpCtxPointToData.x === CtxPointToDataAreaType.inBody) {
pointToDataOnlyHeaderFillOut(_tmpCtxPointToData, out, 0, dims);
} else if (_tmpCtxPointToData.x === CtxPointToDataAreaType.inCorner && _tmpCtxPointToData.y === CtxPointToDataAreaType.inBody) {
pointToDataOnlyHeaderFillOut(_tmpCtxPointToData, out, 1, dims);
} else {
pointToDataBodyCornerFillOut(_tmpCtxPointToData, out, 0, dims);
pointToDataBodyCornerFillOut(_tmpCtxPointToData, out, 1, dims);
}
return out;
};
Matrix.prototype.convertToPixel = function (ecModel, finder, value, opt) {
var coordSys = getCoordSys(finder);
return coordSys === this ? coordSys.dataToPoint(value, opt) : undefined;
};
Matrix.prototype.convertToLayout = function (ecModel, finder, value, opt) {
var coordSys = getCoordSys(finder);
return coordSys === this ? coordSys.dataToLayout(value, opt) : undefined;
};
Matrix.prototype.convertFromPixel = function (ecModel, finder, pixel, opt) {
var coordSys = getCoordSys(finder);
return coordSys === this ? coordSys.pointToData(pixel, opt) : undefined;
};
Matrix.prototype.containPoint = function (point) {
return this._rect.contain(point[0], point[1]);
};
Matrix.dimensions = ['x', 'y', 'value'];
return Matrix;
}();
var _dtpOutDataToLayout = {
rect: createNaNRectLike()
};
var _ptdLevelIt = new ListIterator();
var _ptdDimCellIt = new ListIterator();
function layOutUnitsOnDimension(dimModels, dims, matrixRect, dimIdx) {
var otherDimIdx = 1 - dimIdx;
var thisDim = dims[XY[dimIdx]];
var otherDim = dims[XY[otherDimIdx]];
// Notice: If matrix.x/y.show is false, still lay out, to ensure the
// consistent return of `dataToLayout`.
var otherDimShow = otherDim.shouldShow();
// Reset
for (var it_1 = thisDim.resetCellIterator(); it_1.next();) {
it_1.item.wh = it_1.item.xy = NaN;
}
for (var it_2 = otherDim.resetLayoutIterator(null, dimIdx); it_2.next();) {
it_2.item.wh = it_2.item.xy = NaN;
}
// Set specified size from option.
var restSize = matrixRect[WH[dimIdx]];
var restCellsCount = thisDim.getLocatorCount(dimIdx) + otherDim.getLocatorCount(dimIdx);
var tmpLevelModel = new Model();
for (var it_3 = otherDim.resetLevelIterator(); it_3.next();) {
// Consider `matrix.x.levelSize` and `matrix.x.levels[i].levelSize`.
tmpLevelModel.option = it_3.item.option;
tmpLevelModel.parentModel = dimModels[XY[otherDimIdx]];
layOutSpecified(it_3.item, otherDimShow ? tmpLevelModel.get('levelSize') : 0);
}
var tmpCellModel = new Model();
for (var it_4 = thisDim.resetCellIterator(); it_4.next();) {
// Only leaf support size specification, to avoid unnecessary complexity.
if (it_4.item.type === MatrixCellLayoutInfoType.leaf) {
tmpCellModel.option = it_4.item.option;
tmpCellModel.parentModel = undefined;
layOutSpecified(it_4.item, tmpCellModel.get('size'));
}
}
function layOutSpecified(item, sizeOption) {
var size = parseSizeOption(sizeOption, dimIdx, matrixRect);
if (!eqNaN(size)) {
item.wh = confineSize(size, restSize);
restSize = confineSize(restSize - item.wh);
restCellsCount--;
}
}
// Set all sizes and positions to levels and leaf cells of which size is unspecified.
// Contents lay out based on matrix, rather than inverse; therefore do not support
// calculating size based on content, but allocate equally.
var computedCellWH = restCellsCount ? restSize / restCellsCount : 0;
// If all size specified, but some space remain (may also caused by matrix.x/y.show: false)
// do not align to the big most edge.
var notAlignToBigmost = !restCellsCount && restSize >= 1; // `1` for cumulative precision error.
var currXY = matrixRect[XY[dimIdx]];
var maxLocator = thisDim.getLocatorCount(dimIdx) - 1;
var it = new ListIterator();
// Lay out levels of the perpendicular dim.
for (otherDim.resetLayoutIterator(it, dimIdx); it.next();) {
layOutUnspecified(it.item);
}
for (thisDim.resetLayoutIterator(it, dimIdx); it.next();) {
layOutUnspecified(it.item);
}
function layOutUnspecified(item) {
if (eqNaN(item.wh)) {
item.wh = computedCellWH;
}
item.xy = currXY;
if (item.id[XY[dimIdx]] === maxLocator && !notAlignToBigmost) {
// Align to the rightmost border, consider cumulative precision error.
item.wh = matrixRect[XY[dimIdx]] + matrixRect[WH[dimIdx]] - item.xy;
}
currXY += item.wh;
}
}
function layOutDimCellsRestInfoByUnit(dimIdx, dims) {
// Finally save layout info based on the unit leaves and levels.
for (var it_5 = dims[XY[dimIdx]].resetCellIterator(); it_5.next();) {
var dimCell = it_5.item;
layOutRectOneDimBasedOnUnit(dimCell.rect, dimIdx, dimCell.id, dimCell.span, dims);
// Consider level varitation on tree leaves, should extend the size to touch matrix body
// to avoid weird appearance.
layOutRectOneDimBasedOnUnit(dimCell.rect, 1 - dimIdx, dimCell.id, dimCell.span, dims);
if (dimCell.type === MatrixCellLayoutInfoType.nonLeaf) {
// `xy` and `wh` need to be saved in non-leaf since it supports locating by non-leaf
// in `dataToPoint` or `dataToLayout`.
dimCell.xy = dimCell.rect[XY[dimIdx]];
dimCell.wh = dimCell.rect[WH[dimIdx]];
}
}
}
function layOutBodyCornerCellMerge(bodyOrCorner, dims) {
bodyOrCorner.travelExistingCells(function (cell) {
var computedSpan = cell.span;
if (computedSpan) {
var layoutRect = cell.spanRect;
var id = cell.id;
layOutRectOneDimBasedOnUnit(layoutRect, 0, id, computedSpan, dims);
layOutRectOneDimBasedOnUnit(layoutRect, 1, id, computedSpan, dims);
}
});
}
// Save to rect for rendering.
function layOutRectOneDimBasedOnUnit(outRect, dimIdx, id, span, dims) {
outRect[WH[dimIdx]] = 0;
var locator = id[XY[dimIdx]];
var dim = locator < 0 ? dims[XY[1 - dimIdx]] : dims[XY[dimIdx]];
var layoutUnit = dim.getUnitLayoutInfo(dimIdx, id[XY[dimIdx]]);
outRect[XY[dimIdx]] = layoutUnit.xy;
outRect[WH[dimIdx]] = layoutUnit.wh;
if (span[XY[dimIdx]] > 1) {
var layoutUnit2 = dim.getUnitLayoutInfo(dimIdx, id[XY[dimIdx]] + span[XY[dimIdx]] - 1);
// Be careful the cumulative error - cell must be aligned.
outRect[WH[dimIdx]] = layoutUnit2.xy + layoutUnit2.wh - layoutUnit.xy;
}
}
/**
* Return NaN if not defined or invalid.
*/
function parseSizeOption(sizeOption, dimIdx, matrixRect) {
var sizeNum = parsePositionSizeOption(sizeOption, matrixRect[WH[dimIdx]]);
return confineSize(sizeNum, matrixRect[WH[dimIdx]]);
}
function confineSize(sizeNum, sizeLimit) {
return Math.max(Math.min(sizeNum, retrieve2(sizeLimit, Infinity)), 0);
}
function getCoordSys(finder) {
var matrixModel = finder.matrixModel;
var seriesModel = finder.seriesModel;
var coordSys = matrixModel ? matrixModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem : null;
return coordSys;
}
var CtxPointToDataAreaType = {
inBody: 1,
inCorner: 2,
outside: 3
};
// For handy performance optimization in pointToData.
var _tmpCtxPointToData = {
x: null,
y: null,
point: []
};
function pointToDataOneDimPrepareCtx(ctx, dimIdx, dims, point, clamp) {
var thisDim = dims[XY[dimIdx]];
var otherDim = dims[XY[1 - dimIdx]];
// Notice: considered cases: `matrix.x/y.show: false`, `matrix.x/y.data` is empty.
// In this cases the `layout.xy` is on the edge and `layout.wh` is `0`; they still can be
// use to calculate clampping.
var bodyMaxUnit = thisDim.getUnitLayoutInfo(dimIdx, thisDim.getLocatorCount(dimIdx) - 1);
var body0Unit = thisDim.getUnitLayoutInfo(dimIdx, 0);
var cornerMinUnit = otherDim.getUnitLayoutInfo(dimIdx, -otherDim.getLocatorCount(dimIdx));
var cornerMinus1Unit = otherDim.shouldShow() ? otherDim.getUnitLayoutInfo(dimIdx, -1) : null;
var coord = ctx.point[dimIdx] = point[dimIdx]; // Transfer the oridinal coord.
if (!body0Unit && !cornerMinus1Unit) {
ctx[XY[dimIdx]] = CtxPointToDataAreaType.outside;
return;
}
if (clamp === MatrixClampOption.body) {
if (body0Unit) {
ctx[XY[dimIdx]] = CtxPointToDataAreaType.inBody;
coord = mathMin(bodyMaxUnit.xy + bodyMaxUnit.wh, mathMax(body0Unit.xy, coord));
ctx.point[dimIdx] = coord;
} else {
// If clamp to body, the result must not be in header.
ctx[XY[dimIdx]] = CtxPointToDataAreaType.outside;
}
return;
} else if (clamp === MatrixClampOption.corner) {
if (cornerMinus1Unit) {
ctx[XY[dimIdx]] = CtxPointToDataAreaType.inCorner;
coord = mathMin(cornerMinus1Unit.xy + cornerMinus1Unit.wh, mathMax(cornerMinUnit.xy, coord));
ctx.point[dimIdx] = coord;
} else {
// If clamp to corner, the result must not be in body.
ctx[XY[dimIdx]] = CtxPointToDataAreaType.outside;
}
return;
}
var pxLoc0 = body0Unit ? body0Unit.xy : cornerMinus1Unit ? cornerMinus1Unit.xy + cornerMinus1Unit.wh : NaN;
var pxMin = cornerMinUnit ? cornerMinUnit.xy : pxLoc0;
var pxMax = bodyMaxUnit ? bodyMaxUnit.xy + bodyMaxUnit.wh : pxLoc0;
if (coord < pxMin) {
if (!clamp) {
// Quick pass for later calc, since mouse event on any place will enter this method if use `pointToData`.
ctx[XY[dimIdx]] = CtxPointToDataAreaType.outside;
return;
}
coord = pxMin;
} else if (coord > pxMax) {
if (!clamp) {
ctx[XY[dimIdx]] = CtxPointToDataAreaType.outside;
return;
}
coord = pxMax;
}
ctx.point[dimIdx] = coord; // Save the updated coord.
ctx[XY[dimIdx]] = pxLoc0 <= coord && coord <= pxMax ? CtxPointToDataAreaType.inBody : pxMin <= coord && coord <= pxLoc0 ? CtxPointToDataAreaType.inCorner : CtxPointToDataAreaType.outside;
// Every props in ctx must be set in every branch of this method.
}
// Assume partialOut has been set to NaN outside.
// This method may fill out[0] and out[1] in one call.
function pointToDataOnlyHeaderFillOut(ctx, partialOut, dimIdx, dims) {
var otherDimIdx = 1 - dimIdx;
if (ctx[XY[dimIdx]] === CtxPointToDataAreaType.outside) {
return;
}
for (dims[XY[dimIdx]].resetCellIterator(_ptdDimCellIt); _ptdDimCellIt.next();) {
var cell = _ptdDimCellIt.item;
if (isCoordInRect(ctx.point[dimIdx], cell.rect, dimIdx) && isCoordInRect(ctx.point[otherDimIdx], cell.rect, otherDimIdx)) {
// non-leaves are also allowed to be located.
// If the point is in x or y dimension cell area, should check both x and y coord to
// determine a cell; in this way a non-leaf cell can be determined.
partialOut[dimIdx] = cell.ordinal;
partialOut[otherDimIdx] = cell.id[XY[otherDimIdx]];
return;
}
}
}
// Assume partialOut has been set to NaN outside.
// This method may fill out[0] and out[1] in one call.
function pointToDataBodyCornerFillOut(ctx, partialOut, dimIdx, dims) {
if (ctx[XY[dimIdx]] === CtxPointToDataAreaType.outside) {
return;
}
var dim = ctx[XY[dimIdx]] === CtxPointToDataAreaType.inCorner ? dims[XY[1 - dimIdx]] : dims[XY[dimIdx]];
for (dim.resetLayoutIterator(_ptdLevelIt, dimIdx); _ptdLevelIt.next();) {
if (isCoordInLayoutInfo(ctx.point[dimIdx], _ptdLevelIt.item)) {
partialOut[dimIdx] = _ptdLevelIt.item.id[XY[dimIdx]];
return;
}
}
}
function isCoordInLayoutInfo(coord, cell) {
return cell.xy <= coord && coord <= cell.xy + cell.wh;
}
function isCoordInRect(coord, rect, dimIdx) {
return rect[XY[dimIdx]] <= coord && coord <= rect[XY[dimIdx]] + rect[WH[dimIdx]];
}
export default Matrix;
+227
View File
@@ -0,0 +1,227 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createHashMap, each, extend, isArray, isObject } from 'zrender/lib/core/util.js';
import { error } from '../../util/log.js';
import Point from 'zrender/lib/core/Point.js';
import { resolveXYLocatorRangeByCellMerge, MatrixClampOption, parseCoordRangeOption, fillIdSpanFromLocatorRange, createNaNRectLike, isXYLocatorRangeInvalidOnDim, resetXYLocatorRange, cloneXYLocatorRange } from './matrixCoordHelper.js';
/**
* Lifetime: the same with `MatrixModel`, but different from `coord/Matrix`.
*/
var MatrixBodyCorner = /** @class */function () {
function MatrixBodyCorner(kind, bodyOrCornerModel, dims) {
this._model = bodyOrCornerModel;
this._dims = dims;
this._kind = kind;
this._cellMergeOwnerList = [];
}
/**
* Can not be called before series models initialization finished, since the ordinalMeta may
* use collect the values from `series.data` in series initialization.
*/
MatrixBodyCorner.prototype._ensureCellMap = function () {
var self = this;
var _cellMap = self._cellMap;
if (!_cellMap) {
_cellMap = self._cellMap = createHashMap();
fillCellMap();
}
return _cellMap;
function fillCellMap() {
var parsedList = [];
var cellOptionList = self._model.getShallow('data');
if (cellOptionList && !isArray(cellOptionList)) {
if (process.env.NODE_ENV !== 'production') {
error("matrix." + cellOptionList + ".data must be an array if specified.");
}
cellOptionList = null;
}
each(cellOptionList, function (option, idx) {
if (!isObject(option) || !isArray(option.coord)) {
if (process.env.NODE_ENV !== 'production') {
error("Illegal matrix." + self._kind + ".data[" + idx + "], must be a {coord: [...], ...}");
}
return;
}
var locatorRange = resetXYLocatorRange([]);
var reasonArr = null;
if (process.env.NODE_ENV !== 'production') {
reasonArr = [];
}
parseCoordRangeOption(locatorRange, reasonArr, option.coord, self._dims, option.coordClamp ? MatrixClampOption[self._kind] : MatrixClampOption.none);
if (isXYLocatorRangeInvalidOnDim(locatorRange, 0) || isXYLocatorRangeInvalidOnDim(locatorRange, 1)) {
if (process.env.NODE_ENV !== 'production') {
error("Can not determine cells by option matrix." + self._kind + ".data[" + idx + "]: " + ("" + reasonArr.join(' ')));
}
return;
}
var cellMergeOwner = option && option.mergeCells;
var parsed = {
id: new Point(),
span: new Point(),
locatorRange: locatorRange,
option: option,
cellMergeOwner: cellMergeOwner
};
fillIdSpanFromLocatorRange(parsed, locatorRange);
// The order of the `parsedList` determines the precedence of the styles, if there
// are overlaps between ranges specified in different items. Preserve the original
// order of `matrix.body/corner/data` to make it predictable for users.
parsedList.push(parsed);
});
// Resolve cell merging intersection - union to a larger rect.
var mergedMarkList = [];
for (var parsedIdx = 0; parsedIdx < parsedList.length; parsedIdx++) {
var parsed = parsedList[parsedIdx];
if (!parsed.cellMergeOwner) {
continue;
}
var locatorRange = parsed.locatorRange;
resolveXYLocatorRangeByCellMerge(locatorRange, mergedMarkList, parsedList, parsedIdx);
for (var idx = 0; idx < parsedIdx; idx++) {
if (mergedMarkList[idx]) {
parsedList[idx].cellMergeOwner = false;
}
}
if (locatorRange[0][0] !== parsed.id.x || locatorRange[1][0] !== parsed.id.y) {
// The top-left cell of the unioned locatorRange is not this cell any more.
parsed.cellMergeOwner = false;
// Reconcile: simply use the last style and value option if multiple styles involved
// in a merged area, since there might be no commonly used merge strategy.
var newOption = extend({}, parsed.option);
newOption.coord = null;
var newParsed = {
id: new Point(),
span: new Point(),
locatorRange: locatorRange,
option: newOption,
cellMergeOwner: true
};
fillIdSpanFromLocatorRange(newParsed, locatorRange);
parsedList.push(newParsed);
}
}
// Assign options to cells.
each(parsedList, function (parsed) {
var topLeftCell = ensureBodyOrCornerCell(parsed.id.x, parsed.id.y);
if (parsed.cellMergeOwner) {
topLeftCell.cellMergeOwner = true;
topLeftCell.span = parsed.span;
topLeftCell.locatorRange = parsed.locatorRange;
topLeftCell.spanRect = createNaNRectLike();
self._cellMergeOwnerList.push(topLeftCell);
}
if (!parsed.cellMergeOwner && !parsed.option) {
return;
}
for (var yidx = 0; yidx < parsed.span.y; yidx++) {
for (var xidx = 0; xidx < parsed.span.x; xidx++) {
var cell = ensureBodyOrCornerCell(parsed.id.x + xidx, parsed.id.y + yidx);
// If multiple style options are defined on a cell, the later ones takes precedence.
cell.option = parsed.option;
if (parsed.cellMergeOwner) {
cell.inSpanOf = topLeftCell;
}
}
}
});
} // End of fillCellMap
function ensureBodyOrCornerCell(x, y) {
var key = makeCellMapKey(x, y);
var cell = _cellMap.get(key);
if (!cell) {
cell = _cellMap.set(key, {
id: new Point(x, y),
option: null,
inSpanOf: null,
span: null,
spanRect: null,
locatorRange: null,
cellMergeOwner: false
});
}
return cell;
}
};
/**
* Body cells or corner cell are not commonly defined specifically, especially in a large
* table, thus his is a sparse data structure - bodys or corner cells exist only if there
* are options specified to it (in `matrix.body.data` or `matrix.corner.data`);
* otherwise, return `NullUndefined`.
*/
MatrixBodyCorner.prototype.getCell = function (xy) {
// Assert xy do not contain NaN
return this._ensureCellMap().get(makeCellMapKey(xy[0], xy[1]));
};
/**
* Only cell existing (has specific definition or props) will be travelled.
*/
MatrixBodyCorner.prototype.travelExistingCells = function (cb) {
this._ensureCellMap().each(cb);
};
/**
* @param locatorRange Must be the return of `parseCoordRangeOption`.
*/
MatrixBodyCorner.prototype.expandRangeByCellMerge = function (locatorRange) {
if (!isXYLocatorRangeInvalidOnDim(locatorRange, 0) && !isXYLocatorRangeInvalidOnDim(locatorRange, 1) && locatorRange[0][0] === locatorRange[0][1] && locatorRange[1][0] === locatorRange[1][1]) {
// If it locates to a single cell, use this quick path to avoid travelling.
// It is based on the fact that any cell is not contained by more than one cell merging rect.
_tmpERBCMLocator[0] = locatorRange[0][0];
_tmpERBCMLocator[1] = locatorRange[1][0];
var cell = this.getCell(_tmpERBCMLocator);
var inSpanOf = cell && cell.inSpanOf;
if (inSpanOf) {
cloneXYLocatorRange(locatorRange, inSpanOf.locatorRange);
return;
}
}
var list = this._cellMergeOwnerList;
resolveXYLocatorRangeByCellMerge(locatorRange, null, list, list.length);
};
return MatrixBodyCorner;
}();
export { MatrixBodyCorner };
var _tmpERBCMLocator = [];
function makeCellMapKey(x, y) {
return x + "|" + y;
}
+359
View File
@@ -0,0 +1,359 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createHashMap, defaults, each, eqNaN, isArray, isObject, isString } from 'zrender/lib/core/util.js';
import Point from 'zrender/lib/core/Point.js';
import OrdinalMeta from '../../data/OrdinalMeta.js';
import Ordinal from '../../scale/Ordinal.js';
import { WH, XY } from '../../util/graphic.js';
import { ListIterator } from '../../util/model.js';
import { createNaNRectLike, setDimXYValue, MatrixCellLayoutInfoType } from './matrixCoordHelper.js';
import { error } from '../../util/log.js';
import { mathMax } from '../../util/number.js';
/**
* Lifetime: the same with `MatrixModel`, but different from `coord/Matrix`.
*/
var MatrixDim = /** @class */function () {
function MatrixDim(dim, dimModel) {
// Under the current definition, every leave corresponds a unit cell,
// and leaves can serve as the locator of cells.
// Therefore make sure:
// - The first `_leavesCount` elements in `_cells` are leaves.
// - `_cells[leaf.id[XY[this.dimIdx]]]` is the leaf itself.
// - Leaves of each subtree are placed together, that is, the leaves of a dimCell are:
// `this._cells.slice(dimCell.firstLeafLocator, dimCell.span[XY[this.dimIdx]])`
this._cells = [];
// Can be visited by `_levels[cell.level]` or `_levels[cell.id[1 - dimIdx] + _levels.length]`.
// Items are never be null/undefined after initialized.
this._levels = [];
this.dim = dim;
this.dimIdx = dim === 'x' ? 0 : 1;
this._model = dimModel;
this._uniqueValueGen = createUniqueValueGenerator(dim);
var dimModelData = dimModel.get('data', true);
if (dimModelData != null && !isArray(dimModelData)) {
if (process.env.NODE_ENV !== 'production') {
error("Illegal echarts option - matrix." + this.dim + ".data must be an array if specified.");
}
dimModelData = [];
}
if (dimModelData) {
this._initByDimModelData(dimModelData);
} else {
this._initBySeriesData();
}
}
MatrixDim.prototype._initByDimModelData = function (dimModelData) {
var self = this;
var _cells = self._cells;
var _levels = self._levels;
var sameLocatorCellsLists = []; // Save for sorting.
var _cellCount = 0;
self._leavesCount = traverseInitCells(dimModelData, 0, 0);
postInitCells();
return;
function traverseInitCells(dimModelData, firstLeafLocator, level) {
var totalSpan = 0;
if (!dimModelData) {
return totalSpan;
}
each(dimModelData, function (option, optionIdx) {
var invalidOption = false;
var cellOption;
if (isString(option)) {
cellOption = {
value: option
};
} else if (isObject(option)) {
cellOption = option;
if (option.value != null && !isString(option.value)) {
invalidOption = true;
cellOption = {
value: null
};
}
} else {
cellOption = {
value: null
};
if (option != null) {
invalidOption = true;
}
}
if (invalidOption) {
if (process.env.NODE_ENV !== 'production') {
error("Illegal echarts option - matrix." + self.dim + ".data[" + optionIdx + "]" + ' must be `string | {value: string}`.');
}
}
var cell = {
type: MatrixCellLayoutInfoType.nonLeaf,
ordinal: NaN,
level: level,
firstLeafLocator: firstLeafLocator,
id: new Point(),
span: setDimXYValue(new Point(), self.dimIdx, 1, 1),
option: cellOption,
xy: NaN,
wh: NaN,
dim: self,
rect: createNaNRectLike()
};
_cellCount++;
(sameLocatorCellsLists[firstLeafLocator] || (sameLocatorCellsLists[firstLeafLocator] = [])).push(cell);
if (!_levels[level]) {
// Create a level only if at least one cell exists.
_levels[level] = {
type: MatrixCellLayoutInfoType.level,
xy: NaN,
wh: NaN,
option: null,
id: new Point(),
dim: self
};
}
var childrenSpan = traverseInitCells(cellOption.children, firstLeafLocator, level + 1);
var subSpan = Math.max(1, childrenSpan);
cell.span[XY[self.dimIdx]] = subSpan;
totalSpan += subSpan;
firstLeafLocator += subSpan;
});
return totalSpan;
}
function postInitCells() {
// Sort to make sure the leaves are at the beginning, so that
// they can be used as the locator of body cells.
var categories = [];
while (_cells.length < _cellCount) {
for (var locator = 0; locator < sameLocatorCellsLists.length; locator++) {
var cell = sameLocatorCellsLists[locator].pop();
if (cell) {
cell.ordinal = categories.length;
var val = cell.option.value;
categories.push(val);
_cells.push(cell);
self._uniqueValueGen.calcDupBase(val);
}
}
}
self._uniqueValueGen.ensureValueUnique(categories, _cells);
var ordinalMeta = self._ordinalMeta = new OrdinalMeta({
categories: categories,
needCollect: false,
deduplication: false
});
self._scale = new Ordinal({
ordinalMeta: ordinalMeta
});
for (var idx = 0; idx < self._leavesCount; idx++) {
var leaf = self._cells[idx];
leaf.type = MatrixCellLayoutInfoType.leaf;
// Handle the tree level variation: enlarge the span of the leaves to reach the body cells.
leaf.span[XY[1 - self.dimIdx]] = self._levels.length - leaf.level;
}
self._initCellsId();
self._initLevelIdOptions();
}
};
MatrixDim.prototype._initBySeriesData = function () {
var self = this;
self._leavesCount = 0;
self._levels = [{
type: MatrixCellLayoutInfoType.level,
xy: NaN,
wh: NaN,
option: null,
id: new Point(),
dim: self
}];
self._initLevelIdOptions();
var ordinalMeta = self._ordinalMeta = new OrdinalMeta({
needCollect: true,
deduplication: true,
onCollect: function (value, ordinalNumber) {
var cell = self._cells[ordinalNumber] = {
type: MatrixCellLayoutInfoType.leaf,
ordinal: ordinalNumber,
level: 0,
firstLeafLocator: ordinalNumber,
id: new Point(),
span: setDimXYValue(new Point(), self.dimIdx, 1, 1),
// Theoretically `value` is from `dataset` or `series.data`, so it may be any type.
// Do not restrict this case for user's convenience, and here simply convert it to
// string for display.
option: {
value: value + ''
},
xy: NaN,
wh: NaN,
dim: self,
rect: createNaNRectLike()
};
self._leavesCount++;
self._setCellId(cell);
}
});
self._scale = new Ordinal({
ordinalMeta: ordinalMeta
});
};
MatrixDim.prototype._setCellId = function (cell) {
var levelsLen = this._levels.length;
var dimIdx = this.dimIdx;
setDimXYValue(cell.id, dimIdx, cell.firstLeafLocator, cell.level - levelsLen);
};
MatrixDim.prototype._initCellsId = function () {
var levelsLen = this._levels.length;
var dimIdx = this.dimIdx;
each(this._cells, function (cell) {
setDimXYValue(cell.id, dimIdx, cell.firstLeafLocator, cell.level - levelsLen);
});
};
MatrixDim.prototype._initLevelIdOptions = function () {
var levelsLen = this._levels.length;
var dimIdx = this.dimIdx;
var levelOptionList = this._model.get('levels', true);
levelOptionList = isArray(levelOptionList) ? levelOptionList : [];
each(this._levels, function (levelCfg, level) {
setDimXYValue(levelCfg.id, dimIdx, 0, level - levelsLen);
levelCfg.option = levelOptionList[level];
});
};
MatrixDim.prototype.shouldShow = function () {
return !!this._model.getShallow('show', true);
};
/**
* Iterate leaves (they are layout units) if dimIdx === this.dimIdx.
* Iterate levels if dimIdx !== this.dimIdx.
*/
MatrixDim.prototype.resetLayoutIterator = function (it, dimIdx, startLocator, count) {
it = it || new ListIterator();
if (dimIdx === this.dimIdx) {
var len = this._leavesCount;
var startIdx = startLocator != null ? Math.max(0, startLocator) : 0;
count = count != null ? Math.min(count, len) : len;
it.reset(this._cells, startIdx, startIdx + count);
} else {
var len = this._levels.length;
// Corner locator is from `-this._levels.length` to `-1`.
var startIdx = startLocator != null ? Math.max(0, startLocator + len) : 0;
count = count != null ? Math.min(count, len) : len;
it.reset(this._levels, startIdx, startIdx + count);
}
return it;
};
MatrixDim.prototype.resetCellIterator = function (it) {
return (it || new ListIterator()).reset(this._cells, 0);
};
MatrixDim.prototype.resetLevelIterator = function (it) {
return (it || new ListIterator()).reset(this._levels, 0);
};
MatrixDim.prototype.getLayout = function (outRect, dimIdx, locator) {
var layout = this.getUnitLayoutInfo(dimIdx, locator);
outRect[XY[dimIdx]] = layout ? layout.xy : NaN;
outRect[WH[dimIdx]] = layout ? layout.wh : NaN;
};
/**
* Get leaf cell or get level info.
* Should be able to return null/undefined if not found on x or y, thus input `dimIdx` is needed.
*/
MatrixDim.prototype.getUnitLayoutInfo = function (dimIdx, locator) {
return dimIdx === this.dimIdx ? locator < this._leavesCount ? this._cells[locator] : undefined : this._levels[locator + this._levels.length];
};
/**
* Get dimension cell by data, including leaves and non-leaves.
*/
MatrixDim.prototype.getCell = function (value) {
var ordinal = this._scale.parse(value);
return eqNaN(ordinal) ? undefined : this._cells[ordinal];
};
/**
* Get leaf count or get level count.
*/
MatrixDim.prototype.getLocatorCount = function (dimIdx) {
return dimIdx === this.dimIdx ? this._leavesCount : this._levels.length;
};
MatrixDim.prototype.getOrdinalMeta = function () {
return this._ordinalMeta;
};
return MatrixDim;
}();
export { MatrixDim };
function createUniqueValueGenerator(dim) {
var dimUpper = dim.toUpperCase();
var defaultValReg = new RegExp("^" + dimUpper + "([0-9]+)$");
var dupBase = 0;
function calcDupBase(val) {
var matchResult;
if (val != null && (matchResult = val.match(defaultValReg))) {
dupBase = mathMax(dupBase, +matchResult[1] + 1);
}
}
function makeUniqueValue() {
return "" + dimUpper + dupBase++;
}
// Duplicated value is allowed, because the `matrix.x/y.data` can be a tree and it's reasonable
// that leaves in different subtrees has the same text. But only the first one is allowed to be
// queried by the text, and the other ones can only be queried by index.
// Additionally, `matrix.x/y.data: [null, null, ...]` is allowed.
function ensureValueUnique(categories, cells) {
// A simple way to deduplicate or handle illegal or not specified values to avoid unexpected behaviors.
// The tree structure should not be broken even if duplicated.
var cateMap = createHashMap();
for (var idx = 0; idx < categories.length; idx++) {
var value = categories[idx];
// value may be set to NullUndefined by users or if illegal.
if (value == null || cateMap.get(value) != null) {
// Still display the original option.value if duplicated, but loose the ability to query by text.
categories[idx] = value = makeUniqueValue();
cells[idx].option = defaults({
value: value
}, cells[idx].option);
}
cateMap.set(value, true);
}
}
return {
calcDupBase: calcDupBase,
ensureValueUnique: ensureValueUnique
};
}
+155
View File
@@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { __extends } from "tslib";
import ComponentModel from '../../model/Component.js';
import Model from '../../model/Model.js';
import { MatrixDim } from './MatrixDim.js';
import { MatrixBodyCorner } from './MatrixBodyCorner.js';
import tokens from '../../visual/tokens.js';
var defaultLabelOption = {
show: true,
color: tokens.color.secondary,
// overflow: 'truncate',
overflow: 'break',
lineOverflow: 'truncate',
padding: [2, 3, 2, 3],
// Prefer to use `padding`, rather than distance.
distance: 0
};
function makeDefaultCellItemStyleOption(isCorner) {
return {
color: 'none',
borderWidth: 1,
borderColor: isCorner ? 'none' : tokens.color.borderTint
};
}
;
var defaultDimOption = {
show: true,
label: defaultLabelOption,
itemStyle: makeDefaultCellItemStyleOption(false),
silent: undefined,
dividerLineStyle: {
width: 1,
color: tokens.color.border
}
};
var defaultBodyOption = {
label: defaultLabelOption,
itemStyle: makeDefaultCellItemStyleOption(false),
silent: undefined
};
var defaultCornerOption = {
label: defaultLabelOption,
itemStyle: makeDefaultCellItemStyleOption(true),
silent: undefined
};
var defaultMatrixOption = {
// As a most basic coord sys, `z` should be lower than
// other series and coord sys, such as, grid.
z: -50,
left: '10%',
top: '10%',
right: '10%',
bottom: '10%',
x: defaultDimOption,
y: defaultDimOption,
body: defaultBodyOption,
corner: defaultCornerOption,
backgroundStyle: {
color: 'none',
borderColor: tokens.color.axisLine,
borderWidth: 1
}
};
var MatrixModel = /** @class */function (_super) {
__extends(MatrixModel, _super);
function MatrixModel() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = MatrixModel.type;
return _this;
}
MatrixModel.prototype.optionUpdated = function () {
// Simply re-create all to follow model changes.
var dimModels = this._dimModels = {
// Do not use matrixModel as the parent model, for preventing from cascade-fetching options to it.
x: new MatrixDimensionModel(this.get('x', true) || {}),
y: new MatrixDimensionModel(this.get('y', true) || {})
};
dimModels.x.option.type = dimModels.y.option.type = 'category';
var xDim = dimModels.x.dim = new MatrixDim('x', dimModels.x);
var yDim = dimModels.y.dim = new MatrixDim('y', dimModels.y);
var dims = {
x: xDim,
y: yDim
};
this._body = new MatrixBodyCorner('body', new Model(this.getShallow('body')), dims);
this._corner = new MatrixBodyCorner('corner', new Model(this.getShallow('corner')), dims);
};
MatrixModel.prototype.getDimensionModel = function (dim) {
return this._dimModels[dim];
};
MatrixModel.prototype.getBody = function () {
return this._body;
};
MatrixModel.prototype.getCorner = function () {
return this._corner;
};
MatrixModel.type = 'matrix';
MatrixModel.layoutMode = 'box';
MatrixModel.defaultOption = defaultMatrixOption;
return MatrixModel;
}(ComponentModel);
var MatrixDimensionModel = /** @class */function (_super) {
__extends(MatrixDimensionModel, _super);
function MatrixDimensionModel() {
return _super !== null && _super.apply(this, arguments) || this;
}
MatrixDimensionModel.prototype.getOrdinalMeta = function () {
return this.dim.getOrdinalMeta();
};
return MatrixDimensionModel;
}(Model);
export { MatrixDimensionModel };
export default MatrixModel;
+295
View File
@@ -0,0 +1,295 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { eqNaN, isArray, isNumber } from 'zrender/lib/core/util.js';
import { WH, XY } from '../../util/graphic.js';
import { mathMax, mathMin } from '../../util/number.js';
export var MatrixCellLayoutInfoType = {
level: 1,
leaf: 2,
nonLeaf: 3
};
/**
* @public Public to users in `chart.convertFromPixel`.
*/
export var MatrixClampOption = {
// No clamp, be falsy, equals to null/undefined. It means if the input part is
// null/undefined/NaN/outOfBoundary, the result part is NaN, rather than clamp to
// the boundary of the matrix.
none: 0,
// Clamp, where null/undefined/NaN/outOfBoundary can be used to cover the entire row/column.
all: 1,
body: 2,
corner: 3
};
/**
* For the x direction,
* - find dimension cell from `xMatrixDim`,
* - If `xDimCell` or `yDimCell` is not a leaf, return the non-leaf cell itself.
* - otherwise find level from `yMatrixDim`.
* - otherwise return `NullUndefined`.
*
* For the y direction, it's the opposite.
*/
export function coordDataToAllCellLevelLayout(coordValue, dims, thisDimIdx // 0 | 1
) {
// Find in body.
var result = dims[XY[thisDimIdx]].getCell(coordValue);
// Find in corner or dimension area.
if (!result && isNumber(coordValue) && coordValue < 0) {
result = dims[XY[1 - thisDimIdx]].getUnitLayoutInfo(thisDimIdx, Math.round(coordValue));
}
return result;
}
export function resetXYLocatorRange(out) {
var rg = out || [];
rg[0] = rg[0] || [];
rg[1] = rg[1] || [];
rg[0][0] = rg[0][1] = rg[1][0] = rg[1][1] = NaN;
return rg;
}
/**
* If illegal or out of boundary, set NaN to `locOut`. See `isXYLocatorRangeInvalidOnDim`.
* x dimension and y dimension are calculated separately.
*/
export function parseCoordRangeOption(locOut,
// If illegal input or can not find any target, save reason to it.
// Do nothing if `NullUndefined`.
reasonOut, data, dims, clamp) {
// x and y are supported to be handled separately - if one dimension is invalid
// (may be users do not need that), the other one should also be calculated.
parseCoordRangeOptionOnOneDim(locOut[0], reasonOut, clamp, data, dims, 0);
parseCoordRangeOptionOnOneDim(locOut[1], reasonOut, clamp, data, dims, 1);
}
function parseCoordRangeOptionOnOneDim(locDimOut, reasonOut, clamp, data, dims, dimIdx) {
locDimOut[0] = Infinity;
locDimOut[1] = -Infinity;
var dataOnDim = data[dimIdx];
var coordValArr = isArray(dataOnDim) ? dataOnDim : [dataOnDim];
var len = coordValArr.length;
var hasClamp = !!clamp;
if (len >= 1) {
parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, 0);
if (len > 1) {
// Users may intuitively input the coords like `[[x1, x2, x3], ...]`;
// consider the range as `[x1, x3]` in this case.
parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, len - 1);
}
} else {
if (process.env.NODE_ENV !== 'production') {
if (reasonOut) {
reasonOut.push('Should be like [["x1", "x2"], ["y1", "y2"]], or ["x1", "y1"], rather than empty.');
}
}
locDimOut[0] = locDimOut[1] = NaN;
}
if (hasClamp) {
// null/undefined/NaN or illegal data represents the entire row/column;
// Cover the entire locator regardless of body or corner, and confine it later.
var locLowerBound = -dims[XY[1 - dimIdx]].getLocatorCount(dimIdx);
var locUpperBound = dims[XY[dimIdx]].getLocatorCount(dimIdx) - 1;
if (clamp === MatrixClampOption.body) {
locLowerBound = mathMax(0, locLowerBound);
} else if (clamp === MatrixClampOption.corner) {
locUpperBound = mathMin(-1, locUpperBound);
}
if (locUpperBound < locLowerBound) {
// Also considered that both x and y has no cell.
locLowerBound = locUpperBound = NaN;
}
if (eqNaN(locDimOut[0])) {
locDimOut[0] = locLowerBound;
}
if (eqNaN(locDimOut[1])) {
locDimOut[1] = locUpperBound;
}
locDimOut[0] = mathMax(mathMin(locDimOut[0], locUpperBound), locLowerBound);
locDimOut[1] = mathMax(mathMin(locDimOut[1], locUpperBound), locLowerBound);
}
}
// The return val must be finite or NaN.
function parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, partIdx) {
var layout = coordDataToAllCellLevelLayout(coordValArr[partIdx], dims, dimIdx);
if (!layout) {
if (process.env.NODE_ENV !== 'production') {
if (!hasClamp && reasonOut) {
reasonOut.push("Can not find cell by coord[" + dimIdx + "][" + partIdx + "].");
}
}
locDimOut[0] = locDimOut[1] = NaN;
return;
}
var locatorA = layout.id[XY[dimIdx]];
var locatorB = locatorA;
var dimCell = cellLayoutInfoToDimCell(layout);
if (dimCell) {
// Handle non-leaf
locatorB += dimCell.span[XY[dimIdx]] - 1;
}
locDimOut[0] = mathMin(locDimOut[0], locatorA, locatorB);
locDimOut[1] = mathMax(locDimOut[1], locatorA, locatorB);
}
/**
* @param locatorRange Must be the return of `parseCoordRangeOption`,
* where if not NaN, it must be a valid locator.
*/
export function isXYLocatorRangeInvalidOnDim(locatorRange, dimIdx) {
return eqNaN(locatorRange[dimIdx][0]) || eqNaN(locatorRange[dimIdx][1]);
}
// `locatorRange` will be expanded (modified) if an intersection is encountered.
export function resolveXYLocatorRangeByCellMerge(inOutLocatorRange,
// Item indices coorespond to mergeDefList (len: mergeDefListTravelLen).
// Indicating whether each item has be merged into the `locatorRange`
outMergedMarkList, mergeDefList, mergeDefListTravelLen) {
outMergedMarkList = outMergedMarkList || _tmpOutMergedMarkList;
for (var idx = 0; idx < mergeDefListTravelLen; idx++) {
outMergedMarkList[idx] = false;
}
// In most case, cell merging definition list length is smaller than the range extent,
// therefore, to detection intersection, travelling cell merging definition list is probably
// performant than traveling the four edges of the rect formed by the locator range.
while (true) {
var expanded = false;
for (var idx = 0; idx < mergeDefListTravelLen; idx++) {
var mergeDef = mergeDefList[idx];
if (!outMergedMarkList[idx] && mergeDef.cellMergeOwner && expandXYLocatorRangeIfIntersect(inOutLocatorRange, mergeDef.locatorRange)) {
outMergedMarkList[idx] = true;
expanded = true;
}
}
if (!expanded) {
break;
}
}
}
var _tmpOutMergedMarkList = [];
// Return whether intersect.
// `thisLocRange` will be expanded (modified) if an intersection is encountered.
function expandXYLocatorRangeIfIntersect(thisLocRange, otherLocRange) {
if (!locatorRangeIntersectOneDim(thisLocRange[0], otherLocRange[0]) || !locatorRangeIntersectOneDim(thisLocRange[1], otherLocRange[1])) {
return false;
}
thisLocRange[0][0] = mathMin(thisLocRange[0][0], otherLocRange[0][0]);
thisLocRange[0][1] = mathMax(thisLocRange[0][1], otherLocRange[0][1]);
thisLocRange[1][0] = mathMin(thisLocRange[1][0], otherLocRange[1][0]);
thisLocRange[1][1] = mathMax(thisLocRange[1][1], otherLocRange[1][1]);
return true;
}
// Notice: If containing NaN, not intersect.
function locatorRangeIntersectOneDim(locRange1OneDim, locRange2OneDim) {
return locRange1OneDim[1] >= locRange2OneDim[0] && locRange1OneDim[0] <= locRange2OneDim[1];
}
export function fillIdSpanFromLocatorRange(owner, locatorRange) {
owner.id.set(locatorRange[0][0], locatorRange[1][0]);
owner.span.set(locatorRange[0][1] - owner.id.x + 1, locatorRange[1][1] - owner.id.y + 1);
}
export function cloneXYLocatorRange(target, source) {
target[0][0] = source[0][0];
target[0][1] = source[0][1];
target[1][0] = source[1][0];
target[1][1] = source[1][1];
}
/**
* If illegal, the corresponding x/y/width/height is set to `NaN`.
* `x/width` or `y/height` is supported to be calculated separately,
* i.e., one side are NaN, the other side are normal.
* @param oneDimOut only write to `x/width` or `y/height`, depending on `dimIdx`.
*/
export function xyLocatorRangeToRectOneDim(oneDimOut, locRange, dims, dimIdx) {
var layoutMin = coordDataToAllCellLevelLayout(locRange[dimIdx][0], dims, dimIdx);
var layoutMax = coordDataToAllCellLevelLayout(locRange[dimIdx][1], dims, dimIdx);
oneDimOut[XY[dimIdx]] = oneDimOut[WH[dimIdx]] = NaN;
if (layoutMin && layoutMax) {
oneDimOut[XY[dimIdx]] = layoutMin.xy;
oneDimOut[WH[dimIdx]] = layoutMax.xy + layoutMax.wh - layoutMin.xy;
}
}
// No need currently, since `span` is not allowed to be defined directly by users.
// /**
// * If either span x or y is valid and > 1, return parsed span, otherwise return `NullUndefined`.
// */
// export function parseSpanOption(
// spanOptionHost: MatrixCellSpanOptionHost,
// dimCellPair: MatrixCellLayoutInfo[]
// ): Point | NullUndefined {
// const spanX = parseSpanOnDim(spanOptionHost.spanX, dimCellPair[0], 0);
// const spanY = parseSpanOnDim(spanOptionHost.spanY, dimCellPair[1], 1);
// if (!eqNaN(spanX) || !eqNaN(spanY)) {
// return new Point(spanX || 1, spanY || 1);
// }
// function parseSpanOnDim(spanOption: unknown, dimCell: MatrixCellLayoutInfo, dimIdx: number): number {
// if (!isNumber(spanOption)) {
// return NaN;
// }
// // Ensure positive integer (not NaN) to avoid dead loop.
// const span = mathMax(1, Math.round(spanOption || 1)) || 1;
// // Clamp, and consider may also be specified as `Infinity` to span the entire col/row.
// return mathMin(span, mathMax(1, dimCell.dim.getLocatorCount(dimIdx) - dimCell.id[XY[dimIdx]]));
// }
// }
/**
* @usage To get/set on dimension, use:
* `xyVal[XY[dim]] = val;` // set on this dimension.
* `xyVal[XY[1 - dim]] = val;` // set on the perpendicular dimension.
*/
export function setDimXYValue(out, dimIdx,
// 0 | 1
valueOnThisDim, valueOnOtherDim) {
out[XY[dimIdx]] = valueOnThisDim;
out[XY[1 - dimIdx]] = valueOnOtherDim;
return out;
}
/**
* Return NullUndefined if not dimension cell.
*/
function cellLayoutInfoToDimCell(cellLayoutInfo) {
return cellLayoutInfo && (cellLayoutInfo.type === MatrixCellLayoutInfoType.leaf || cellLayoutInfo.type === MatrixCellLayoutInfoType.nonLeaf) ? cellLayoutInfo : null;
}
export function createNaNRectLike() {
return {
x: NaN,
y: NaN,
width: NaN,
height: NaN
};
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export default function matrixPrepareCustom(coordSys) {
var rect = coordSys.getRect();
return {
coordSys: {
type: 'matrix',
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
},
api: {
coord: function (data, opt) {
return coordSys.dataToPoint(data, opt);
},
layout: function (data, opt) {
return coordSys.dataToLayout(data, opt);
}
}
};
}