| /* |
| * 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/src/core/util'; |
| import { mathAbs, mathMax, mathMin, parsePercent } from '../util/number'; |
| import { isDimensionStacked } from '../data/helper/dataStackHelper'; |
| import createRenderPlanner from '../chart/helper/createRenderPlanner'; |
| import Axis2D from '../coord/cartesian/Axis2D'; |
| import Cartesian2D from '../coord/cartesian/Cartesian2D'; |
| import { StageHandler, NullUndefined } from '../util/types'; |
| import { createFloat32Array } from '../util/vendor'; |
| import { |
| makeCallOnlyOnce, |
| } from '../util/model'; |
| import { isOrdinalScale } from '../scale/helper'; |
| import { |
| isCartesian2DInjectedAsDataCoordSys |
| } from '../coord/cartesian/cartesianAxisHelper'; |
| import type BaseBarSeriesModel from '../chart/bar/BaseBarSeries'; |
| import type BarSeriesModel from '../chart/bar/BarSeries'; |
| import { |
| AxisContainShapeHandler, registerAxisContainShapeHandler, |
| } from '../coord/scaleRawExtentInfo'; |
| import { EChartsExtensionInstallRegisters } from '../extension'; |
| import { |
| eachAxisOnKey, |
| eachSeriesOnAxisOnKey, |
| } from '../coord/axisStatistics'; |
| import { |
| AxisBandWidthResult, calcBandWidth |
| } from '../coord/axisBand'; |
| import { BaseBarSeriesSubType, getStartValue, requireAxisStatisticsForBaseBar } from './barCommon'; |
| import { COORD_SYS_TYPE_CARTESIAN_2D } from '../coord/cartesian/GridModel'; |
| import { createBandWidthBasedAxisContainShapeHandler, makeAxisStatKey2 } from '../chart/helper/axisSnippets'; |
| |
| |
| const callOnlyOnce = makeCallOnlyOnce(); |
| |
| const STACK_PREFIX = '__ec_stack_'; |
| |
| function getSeriesStackId(seriesModel: BaseBarSeriesModel): StackId { |
| return ((seriesModel as BarSeriesModel).get('stack') || STACK_PREFIX + seriesModel.seriesIndex) as StackId; |
| } |
| |
| interface BarGridLayoutAxisInfo { |
| seriesInfo: BarGridLayoutAxisSeriesInfo[]; |
| // Calculated layout width for a single bars group. |
| bandWidthResult: AxisBandWidthResult; |
| } |
| |
| interface BarGridLayoutAxisSeriesInfo { |
| barWidth: number |
| barMaxWidth: number |
| barMinWidth: number |
| barGap: number | string |
| defaultBarGap?: number | string |
| barCategoryGap: number | string |
| stackId: StackId |
| } |
| |
| type StackId = string & {_: 'barGridStackId'}; |
| |
| |
| export interface BarGridLayoutOptionForCustomSeries { |
| count: number |
| |
| barWidth?: number | string |
| barMaxWidth?: number | string |
| barMinWidth?: number | string |
| barGap?: number | string |
| barCategoryGap?: number | string |
| } |
| interface BarGridLayoutOption extends BarGridLayoutOptionForCustomSeries { |
| axis: Axis2D |
| } |
| |
| // The layout of a bar group (may come from different series but on the same value on the base axis). |
| // The bars with the same `StackId` are stacked; otherwise they are placed side by side, following |
| // series declaration order. |
| type BarWidthAndOffsetOnAxis = Record<StackId, BarGridLayoutResultItemInternal>; |
| export type BarGridColumnLayoutOnAxis = BarGridLayoutAxisInfo & { |
| columnMap: BarWidthAndOffsetOnAxis; |
| }; |
| |
| type BarGridLayoutResultItemInternal = { |
| bandWidth: BarGridLayoutAxisInfo['bandWidthResult']['w'] |
| offset: number // An offset with respect to `dataToPoint` |
| width: number |
| }; |
| type BarGridLayoutResultItem = BarGridLayoutResultItemInternal & { |
| offsetCenter: number |
| }; |
| export type BarGridLayoutResultForCustomSeries = BarGridLayoutResultItem[] | NullUndefined; |
| |
| /** |
| * 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: BarGridLayoutOption): BarGridLayoutResultForCustomSeries { |
| if (!isOrdinalScale(opt.axis.scale)) { |
| return; |
| } |
| |
| const bandWidthResult = calcBandWidth(opt.axis); |
| |
| const params: BarGridLayoutAxisSeriesInfo[] = []; |
| for (let i = 0; i < opt.count || 0; i++) { |
| params.push(defaults({ |
| stackId: STACK_PREFIX + i as StackId |
| }, opt) as BarGridLayoutAxisSeriesInfo); |
| } |
| const widthAndOffsets = calcBarWidthAndOffset({ |
| bandWidthResult, |
| seriesInfo: params, |
| }); |
| |
| const result: BarGridLayoutResultItem[] = []; |
| for (let i = 0; i < opt.count; i++) { |
| const item = widthAndOffsets[STACK_PREFIX + i as StackId] as BarGridLayoutResultItem; |
| 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: Axis2D, |
| seriesType: BaseBarSeriesSubType |
| ): BarGridColumnLayoutOnAxis { |
| const seriesInfoListOnAxis = createLayoutInfoListOnAxis( |
| baseAxis, seriesType |
| ) as BarGridColumnLayoutOnAxis; |
| seriesInfoListOnAxis.columnMap = calcBarWidthAndOffset(seriesInfoListOnAxis); |
| return seriesInfoListOnAxis; |
| } |
| |
| function createLayoutInfoListOnAxis( |
| baseAxis: Axis2D, |
| seriesType: BaseBarSeriesSubType |
| ): BarGridLayoutAxisInfo { |
| |
| const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D); |
| const seriesInfoOnAxis: BarGridLayoutAxisSeriesInfo[] = []; |
| const bandWidthResult = calcBandWidth( |
| baseAxis, |
| {fromStat: {key: axisStatKey}, min: 1} |
| ); |
| |
| eachSeriesOnAxisOnKey(baseAxis, axisStatKey, function (seriesModel: BaseBarSeriesModel) { |
| 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, |
| 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: BarGridLayoutAxisInfo |
| ): BarWidthAndOffsetOnAxis { |
| interface StackInfo { |
| width: number |
| maxWidth: number |
| minWidth?: number |
| } |
| |
| const bandWidth = seriesInfoOnAxis.bandWidthResult.w; |
| let remainedWidth = bandWidth; |
| let autoWidthCount: number = 0; |
| let barCategoryGapOption: number | string; |
| let barGapOption: number | string; |
| const stackIdList: StackId[] = []; |
| const stackMap: Record<StackId, StackInfo> = {}; |
| |
| each(seriesInfoOnAxis.seriesInfo, function (seriesInfo, idx) { |
| if (!idx) { |
| barGapOption = seriesInfo.defaultBarGap || 0; |
| } |
| const stackId = seriesInfo.stackId; |
| |
| if (!hasOwn(stackMap, stackId)) { |
| autoWidthCount++; |
| } |
| let stackItem = stackMap[stackId]; |
| if (!stackItem) { |
| stackItem = stackMap[stackId] = { |
| width: 0, |
| maxWidth: 0 |
| }; |
| stackIdList.push(stackId); |
| } |
| |
| let barWidth = seriesInfo.barWidth; |
| if (barWidth && !stackItem.width) { |
| // See #6312, do not restrict width. |
| stackItem.width = barWidth; |
| barWidth = mathMin(remainedWidth, barWidth); |
| remainedWidth -= barWidth; |
| } |
| |
| const barMaxWidth = seriesInfo.barMaxWidth; |
| barMaxWidth && (stackItem.maxWidth = barMaxWidth); |
| const barMinWidth = seriesInfo.barMinWidth; |
| barMinWidth && (stackItem.minWidth = barMinWidth); |
| const barGap = seriesInfo.barGap; |
| (barGap != null) && (barGapOption = barGap); |
| const 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) + '%'; |
| } |
| |
| const barCategoryGapNum = parsePercent(barCategoryGapOption, bandWidth); |
| const barGapPercent = parsePercent(barGapOption, 1); |
| |
| let autoWidth = (remainedWidth - barCategoryGapNum) |
| / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); |
| autoWidth = mathMax(autoWidth, 0); |
| |
| // Find if any auto calculated bar exceeded maxBarWidth |
| each(stackIdList, function (stackId) { |
| const column = stackMap[stackId]; |
| const maxWidth = column.maxWidth; |
| const minWidth = column.minWidth; |
| |
| if (!column.width) { |
| let 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. |
| let 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); |
| |
| let widthSum = 0; |
| let lastColumn: StackInfo; |
| each(stackIdList, function (stackId) { |
| const column = stackMap[stackId]; |
| if (!column.width) { |
| column.width = autoWidth; |
| } |
| lastColumn = column; |
| widthSum += column.width * (1 + barGapPercent); |
| }); |
| if (lastColumn) { |
| widthSum -= lastColumn.width * barGapPercent; |
| } |
| |
| const result: BarWidthAndOffsetOnAxis = {}; |
| |
| let offset = -widthSum / 2; |
| each(stackIdList, function (stackId) { |
| const 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: BaseBarSeriesSubType): StageHandler { |
| return { |
| |
| seriesType, |
| |
| overallReset(ecModel) { |
| const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D); |
| eachAxisOnKey(ecModel, axisStatKey, function (axis: Axis2D) { |
| if (__DEV__) { |
| assert(axis instanceof Axis2D); |
| } |
| const columnLayout = makeColumnLayoutOnAxisReal(axis, seriesType); |
| |
| eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel) { |
| const 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: string): StageHandler { |
| return { |
| seriesType, |
| |
| plan: createRenderPlanner(), |
| |
| reset: function (seriesModel: BaseBarSeriesModel) { |
| if (!isCartesian2DInjectedAsDataCoordSys(seriesModel)) { |
| return; |
| } |
| |
| const data = seriesModel.getData(); |
| |
| const cartesian = seriesModel.coordinateSystem as Cartesian2D; |
| const baseAxis = cartesian.getBaseAxis(); |
| const valueAxis = cartesian.getOtherAxis(baseAxis); |
| const valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim)); |
| const baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); |
| const drawBackground = (seriesModel as BarSeriesModel).get('showBackground', true); |
| const valueDim = data.mapDimension(valueAxis.dim); |
| const stackResultDim = data.getCalculationInfo('stackResultDimension'); |
| const stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries'); |
| const isValueAxisH = valueAxis.isHorizontal(); |
| |
| const valueAxisStart = valueAxis.toGlobalCoord(valueAxis.dataToCoord(getStartValue(valueAxis))); |
| |
| const isLarge = isInLargeMode(seriesModel); |
| const barMinHeight = seriesModel.get('barMinHeight') || 0; |
| |
| const stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); |
| |
| // Layout info. |
| const columnWidth = data.getLayout('size'); |
| const columnOffset = data.getLayout('offset'); |
| |
| return { |
| progress: function (params, data) { |
| const count = params.count; |
| const largePoints = isLarge && createFloat32Array(count * 3); |
| const largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3); |
| const largeDataIndices = isLarge && createFloat32Array(count); |
| const coordLayout = cartesian.master.getRect(); |
| const bgSize = isValueAxisH ? coordLayout.width : coordLayout.height; |
| |
| let dataIndex; |
| const store = data.getStore(); |
| |
| let idxOffset = 0; |
| |
| while ((dataIndex = params.next()) != null) { |
| const value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex); |
| const baseValue = store.get(baseDimIdx, dataIndex) as number; |
| let baseCoord = valueAxisStart; |
| let stackStartValue; |
| |
| // Because of the barMinHeight, we can not use the value in |
| // stackResultDimension directly. |
| if (stacked) { |
| stackStartValue = +value - (store.get(valueDimIdx, dataIndex) as number); |
| } |
| |
| let x; |
| let y; |
| let width; |
| let height; |
| |
| if (isValueAxisH) { |
| const 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 { |
| const 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, y, width, 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, |
| largeDataIndices, |
| largeBackgroundPoints, |
| valueAxisHorizontal: isValueAxisH |
| }); |
| } |
| } |
| }; |
| } |
| }; |
| } |
| |
| function isInLargeMode(seriesModel: BaseBarSeriesModel) { |
| return seriesModel.pipelineContext && seriesModel.pipelineContext.large; |
| } |
| |
| function barGridCreateAxisContainShapeHandler(seriesType: BaseBarSeriesSubType): AxisContainShapeHandler { |
| // 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: EChartsExtensionInstallRegisters) { |
| callOnlyOnce(registers, function () { |
| |
| function register(seriesType: BaseBarSeriesSubType): void { |
| const 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'); |
| }); |
| } |