| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- /*
- * 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 { each, defaults, hasOwn, assert } from 'zrender/lib/core/util.js';
- import { mathAbs, mathMax, mathMin, parsePercent } from '../util/number.js';
- import { isDimensionStacked } from '../data/helper/dataStackHelper.js';
- import createRenderPlanner from '../chart/helper/createRenderPlanner.js';
- import Axis2D from '../coord/cartesian/Axis2D.js';
- import { createFloat32Array } from '../util/vendor.js';
- import { makeCallOnlyOnce } from '../util/model.js';
- import { isOrdinalScale } from '../scale/helper.js';
- import { isCartesian2DInjectedAsDataCoordSys } from '../coord/cartesian/cartesianAxisHelper.js';
- import { registerAxisContainShapeHandler } from '../coord/scaleRawExtentInfo.js';
- import { eachAxisOnKey, eachSeriesOnAxisOnKey } from '../coord/axisStatistics.js';
- import { calcBandWidth } from '../coord/axisBand.js';
- import { getStartValue, requireAxisStatisticsForBaseBar } from './barCommon.js';
- import { COORD_SYS_TYPE_CARTESIAN_2D } from '../coord/cartesian/GridModel.js';
- import { createBandWidthBasedAxisContainShapeHandler, makeAxisStatKey2 } from '../chart/helper/axisSnippets.js';
- var callOnlyOnce = makeCallOnlyOnce();
- var STACK_PREFIX = '__ec_stack_';
- function getSeriesStackId(seriesModel) {
- return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;
- }
- /**
- * Return null/undefined if not 'category' axis.
- *
- * PENDING: The layout on non-'category' axis relies on `bandWidth`, which is calculated
- * based on the `linearPositiveMinGap` of series data. This strategy is somewhat heuristic
- * and will not be public to custom series until required in future. Additionally, more ec
- * options may be introduced for that, because it requires `requireAxisStatistics` to be
- * called on custom series that requires this feature.
- */
- export function computeBarLayoutForCustomSeries(opt) {
- if (!isOrdinalScale(opt.axis.scale)) {
- return;
- }
- var bandWidthResult = calcBandWidth(opt.axis);
- var params = [];
- for (var i = 0; i < opt.count || 0; i++) {
- params.push(defaults({
- stackId: STACK_PREFIX + i
- }, opt));
- }
- var widthAndOffsets = calcBarWidthAndOffset({
- bandWidthResult: bandWidthResult,
- seriesInfo: params
- });
- var result = [];
- for (var i = 0; i < opt.count; i++) {
- var item = widthAndOffsets[STACK_PREFIX + i];
- item.offsetCenter = item.offset + item.width / 2;
- result.push(item);
- }
- return result;
- }
- /**
- * NOTICE: This layout is based on axis pixel extent and scale extent.
- * It may be used on estimation, where axis pixel extent and scale extent
- * are approximately set. But the result should not be cached since the
- * axis pixel extent and scale extent may be changed finally.
- */
- function makeColumnLayoutOnAxisReal(baseAxis, seriesType) {
- var seriesInfoListOnAxis = createLayoutInfoListOnAxis(baseAxis, seriesType);
- seriesInfoListOnAxis.columnMap = calcBarWidthAndOffset(seriesInfoListOnAxis);
- return seriesInfoListOnAxis;
- }
- function createLayoutInfoListOnAxis(baseAxis, seriesType) {
- var axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D);
- var seriesInfoOnAxis = [];
- var bandWidthResult = calcBandWidth(baseAxis, {
- fromStat: {
- key: axisStatKey
- },
- min: 1
- });
- eachSeriesOnAxisOnKey(baseAxis, axisStatKey, function (seriesModel) {
- seriesInfoOnAxis.push({
- barWidth: parsePercent(seriesModel.get('barWidth'), bandWidthResult.w),
- barMaxWidth: parsePercent(seriesModel.get('barMaxWidth'), bandWidthResult.w),
- barMinWidth: parsePercent(
- // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,
- // the auto-calculated bar width might be less than 0.5 / 1.
- seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidthResult.w),
- barGap: seriesModel.get('barGap'),
- barCategoryGap: seriesModel.get('barCategoryGap'),
- defaultBarGap: seriesModel.get('defaultBarGap'),
- stackId: getSeriesStackId(seriesModel)
- });
- });
- return {
- bandWidthResult: bandWidthResult,
- seriesInfo: seriesInfoOnAxis
- };
- }
- /**
- * CAUTION: When multiple series are laid out on one axis, relevant ec options effect all series.
- * But for historical reason, these options are configured on each series option, which may
- * introduce confliction. The legacy implementation uses some options (e.g., `defaultBarGap`)
- * from the first declared series, and other options (e.g., `barGap`, `barCategoryGap`) from the last declared
- * series. Nevertheless, We remain this design to avoid breaking change.
- */
- function calcBarWidthAndOffset(seriesInfoOnAxis) {
- var bandWidth = seriesInfoOnAxis.bandWidthResult.w;
- var remainedWidth = bandWidth;
- var autoWidthCount = 0;
- var barCategoryGapOption;
- var barGapOption;
- var stackIdList = [];
- var stackMap = {};
- each(seriesInfoOnAxis.seriesInfo, function (seriesInfo, idx) {
- if (!idx) {
- barGapOption = seriesInfo.defaultBarGap || 0;
- }
- var stackId = seriesInfo.stackId;
- if (!hasOwn(stackMap, stackId)) {
- autoWidthCount++;
- }
- var stackItem = stackMap[stackId];
- if (!stackItem) {
- stackItem = stackMap[stackId] = {
- width: 0,
- maxWidth: 0
- };
- stackIdList.push(stackId);
- }
- var barWidth = seriesInfo.barWidth;
- if (barWidth && !stackItem.width) {
- // See #6312, do not restrict width.
- stackItem.width = barWidth;
- barWidth = mathMin(remainedWidth, barWidth);
- remainedWidth -= barWidth;
- }
- var barMaxWidth = seriesInfo.barMaxWidth;
- barMaxWidth && (stackItem.maxWidth = barMaxWidth);
- var barMinWidth = seriesInfo.barMinWidth;
- barMinWidth && (stackItem.minWidth = barMinWidth);
- var barGap = seriesInfo.barGap;
- barGap != null && (barGapOption = barGap);
- var barCategoryGap = seriesInfo.barCategoryGap;
- barCategoryGap != null && (barCategoryGapOption = barCategoryGap);
- });
- if (barCategoryGapOption == null) {
- // More columns in one group
- // the spaces between group is smaller. Or the column will be too thin.
- barCategoryGapOption = mathMax(35 - stackIdList.length * 4, 15) + '%';
- }
- var barCategoryGapNum = parsePercent(barCategoryGapOption, bandWidth);
- var barGapPercent = parsePercent(barGapOption, 1);
- var autoWidth = (remainedWidth - barCategoryGapNum) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
- autoWidth = mathMax(autoWidth, 0);
- // Find if any auto calculated bar exceeded maxBarWidth
- each(stackIdList, function (stackId) {
- var column = stackMap[stackId];
- var maxWidth = column.maxWidth;
- var minWidth = column.minWidth;
- if (!column.width) {
- var finalWidth = autoWidth;
- if (maxWidth && maxWidth < finalWidth) {
- finalWidth = mathMin(maxWidth, remainedWidth);
- }
- // `minWidth` has higher priority. `minWidth` decide that whether the
- // bar is able to be visible. So `minWidth` should not be restricted
- // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In
- // the extreme cases for `value` axis, bars are allowed to overlap
- // with each other if `minWidth` specified.
- if (minWidth && minWidth > finalWidth) {
- finalWidth = minWidth;
- }
- if (finalWidth !== autoWidth) {
- column.width = finalWidth;
- remainedWidth -= finalWidth + barGapPercent * finalWidth;
- autoWidthCount--;
- }
- } else {
- // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as
- // CSS does. Because barWidth can be a percent value, where
- // `barMaxWidth` can be used to restrict the final width.
- var finalWidth = column.width;
- if (maxWidth) {
- finalWidth = mathMin(finalWidth, maxWidth);
- }
- // `minWidth` has higher priority, as described above
- if (minWidth) {
- finalWidth = mathMax(finalWidth, minWidth);
- }
- column.width = finalWidth;
- remainedWidth -= finalWidth + barGapPercent * finalWidth;
- autoWidthCount--;
- }
- });
- // Recalculate width again
- autoWidth = (remainedWidth - barCategoryGapNum) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
- autoWidth = mathMax(autoWidth, 0);
- var widthSum = 0;
- var lastColumn;
- each(stackIdList, function (stackId) {
- var column = stackMap[stackId];
- if (!column.width) {
- column.width = autoWidth;
- }
- lastColumn = column;
- widthSum += column.width * (1 + barGapPercent);
- });
- if (lastColumn) {
- widthSum -= lastColumn.width * barGapPercent;
- }
- var result = {};
- var offset = -widthSum / 2;
- each(stackIdList, function (stackId) {
- var column = stackMap[stackId];
- result[stackId] = result[stackId] || {
- bandWidth: bandWidth,
- offset: offset,
- width: column.width
- };
- offset += column.width * (1 + barGapPercent);
- });
- return result;
- }
- export function createCrossSeriesLayoutHandler(seriesType) {
- return {
- seriesType: seriesType,
- overallReset: function (ecModel) {
- var axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D);
- eachAxisOnKey(ecModel, axisStatKey, function (axis) {
- if (process.env.NODE_ENV !== 'production') {
- assert(axis instanceof Axis2D);
- }
- var columnLayout = makeColumnLayoutOnAxisReal(axis, seriesType);
- eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel) {
- var columnLayoutInfo = columnLayout.columnMap[getSeriesStackId(seriesModel)];
- seriesModel.getData().setLayout({
- bandWidth: columnLayoutInfo.bandWidth,
- offset: columnLayoutInfo.offset,
- size: columnLayoutInfo.width
- });
- });
- });
- }
- };
- }
- ;
- // TODO: Do not support stack in large mode yet.
- export function createProgressiveLayout(seriesType) {
- return {
- seriesType: seriesType,
- plan: createRenderPlanner(),
- reset: function (seriesModel) {
- if (!isCartesian2DInjectedAsDataCoordSys(seriesModel)) {
- return;
- }
- var data = seriesModel.getData();
- var cartesian = seriesModel.coordinateSystem;
- var baseAxis = cartesian.getBaseAxis();
- var valueAxis = cartesian.getOtherAxis(baseAxis);
- var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim));
- var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));
- var drawBackground = seriesModel.get('showBackground', true);
- var valueDim = data.mapDimension(valueAxis.dim);
- var stackResultDim = data.getCalculationInfo('stackResultDimension');
- var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries');
- var isValueAxisH = valueAxis.isHorizontal();
- var valueAxisStart = valueAxis.toGlobalCoord(valueAxis.dataToCoord(getStartValue(valueAxis)));
- var isLarge = isInLargeMode(seriesModel);
- var barMinHeight = seriesModel.get('barMinHeight') || 0;
- var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim);
- // Layout info.
- var columnWidth = data.getLayout('size');
- var columnOffset = data.getLayout('offset');
- return {
- progress: function (params, data) {
- var count = params.count;
- var largePoints = isLarge && createFloat32Array(count * 3);
- var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3);
- var largeDataIndices = isLarge && createFloat32Array(count);
- var coordLayout = cartesian.master.getRect();
- var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height;
- var dataIndex;
- var store = data.getStore();
- var idxOffset = 0;
- while ((dataIndex = params.next()) != null) {
- var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex);
- var baseValue = store.get(baseDimIdx, dataIndex);
- var baseCoord = valueAxisStart;
- var stackStartValue = void 0;
- // Because of the barMinHeight, we can not use the value in
- // stackResultDimension directly.
- if (stacked) {
- stackStartValue = +value - store.get(valueDimIdx, dataIndex);
- }
- var x = void 0;
- var y = void 0;
- var width = void 0;
- var height = void 0;
- if (isValueAxisH) {
- var coord = cartesian.dataToPoint([value, baseValue]);
- if (stacked) {
- baseCoord = cartesian.dataToPoint([stackStartValue, baseValue])[0];
- }
- x = baseCoord;
- y = coord[1] + columnOffset;
- width = coord[0] - baseCoord;
- height = columnWidth;
- if (mathAbs(width) < barMinHeight) {
- width = (width < 0 ? -1 : 1) * barMinHeight;
- }
- } else {
- var coord = cartesian.dataToPoint([baseValue, value]);
- if (stacked) {
- baseCoord = cartesian.dataToPoint([baseValue, stackStartValue])[1];
- }
- x = coord[0] + columnOffset;
- y = baseCoord;
- width = columnWidth;
- height = coord[1] - baseCoord;
- if (mathAbs(height) < barMinHeight) {
- // Include zero to has a positive bar
- height = (height <= 0 ? -1 : 1) * barMinHeight;
- }
- }
- if (!isLarge) {
- data.setItemLayout(dataIndex, {
- x: x,
- y: y,
- width: width,
- height: height
- });
- } else {
- largePoints[idxOffset] = x;
- largePoints[idxOffset + 1] = y;
- largePoints[idxOffset + 2] = isValueAxisH ? width : height;
- if (largeBackgroundPoints) {
- largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x;
- largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y;
- largeBackgroundPoints[idxOffset + 2] = bgSize;
- }
- largeDataIndices[dataIndex] = dataIndex;
- }
- idxOffset += 3;
- }
- if (isLarge) {
- data.setLayout({
- largePoints: largePoints,
- largeDataIndices: largeDataIndices,
- largeBackgroundPoints: largeBackgroundPoints,
- valueAxisHorizontal: isValueAxisH
- });
- }
- }
- };
- }
- };
- }
- function isInLargeMode(seriesModel) {
- return seriesModel.pipelineContext && seriesModel.pipelineContext.large;
- }
- function barGridCreateAxisContainShapeHandler(seriesType) {
- // See also #6728, #4862, `test/bar-overflow-time-plot.html` `test/bar-overflow-plot2.html`
- // NOTE:
- // Series shapes may overflow `bandWidth` when ec option is set like:
- // - `barWidth` > 100%.
- // - `barWidth` is set as absolute pixel values and `dataZoom` is used, since `bandWidth` is
- // calculated on data space rather than pixel space.
- // We originally used `calcShapeOverflowSupplement` to cover this case, but it still can not
- // resolve pixel `barWidth` case perfectly. A thorough solution may introduce considerable complex,
- // but may not necessary, since users can avoid it by proper ec option settings.
- // Therefore, we use simply `createBandWidthBasedAxisContainShapeHandler` to calculate "containShape"
- // only based on `bandWidth`.
- return createBandWidthBasedAxisContainShapeHandler(makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D));
- // return function (axis, scale, ecModel) {
- // if (!countSeriesOnAxisOnKey(axis, makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D))) {
- // return; // Quick path - in most cases there is no bar on non-ordinal axis.
- // }
- // const columnLayout = makeColumnLayoutOnAxisReal(axis, seriesType);
- // return calcShapeOverflowSupplement(columnLayout);
- // };
- }
- // /**
- // * @see [AXIS_CONTAIN_SHAPE_COMMON_STRATEGY] for more details.
- // */
- // function calcShapeOverflowSupplement(
- // columnLayout: BarGridColumnLayoutOnAxis | NullUndefined
- // ): number[] | NullUndefined {
- // if (columnLayout == null) {
- // return;
- // }
- // const bandWidthResult = columnLayout.bandWidthResult;
- // const invRatio = bandWidthResult.invRatio;
- // if (!isNullableNumberFinite(invRatio)) {
- // return; // No series data or no more than one distinct valid data values.
- // }
- // const barsBoundPx = initExtentForUnion();
- // const bandWidth = bandWidthResult.w;
- // // Union `-bandWidth / 2` and `bandWidth / 2` to provide extra space for visually preferred,
- // // Otherwise the bars on the edges may overlap with axis line.
- // // And it also includes `0`, which ensures `barsBoundPx[0] <= 0 <= barsBoundPx[1]`.
- // unionExtentFromNumber(barsBoundPx, -bandWidth / 2);
- // unionExtentFromNumber(barsBoundPx, bandWidth / 2);
- // // Shapes may overflow the `bandWidth`. For example, that might happen in `pictorialBar`.
- // // Therefore, we also involve shape size (mapped to data scale) in this expansion calculation.
- // each(columnLayout.columnMap, function (item) {
- // unionExtentFromNumber(barsBoundPx, item.offset);
- // unionExtentFromNumber(barsBoundPx, item.offset + item.width);
- // });
- // if (extentHasValue(barsBoundPx)) {
- // // Convert from pixel domain to data domain, since the `barsBoundPx` is calculated based on
- // // `minGap` and extent on data domain.
- // return [barsBoundPx[0] * invRatio, barsBoundPx[1] * invRatio];
- // // If AXIS_BAND_WIDTH_KIND_SINGULAR, extent expansion is not needed.
- // }
- // }
- export function registerBarGridAxisHandlers(registers) {
- callOnlyOnce(registers, function () {
- function register(seriesType) {
- var axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D);
- requireAxisStatisticsForBaseBar(registers, axisStatKey, seriesType, COORD_SYS_TYPE_CARTESIAN_2D);
- registerAxisContainShapeHandler(axisStatKey, barGridCreateAxisContainShapeHandler(seriesType));
- }
- register('bar');
- register('pictorialBar');
- });
- }
|