|   | 
| /* | 
| * 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, keys } from 'zrender/lib/core/util.js'; | 
| import { parsePercent } from '../util/number.js'; | 
| import { isDimensionStacked } from '../data/helper/dataStackHelper.js'; | 
| import createRenderPlanner from '../chart/helper/createRenderPlanner.js'; | 
| import { createFloat32Array } from '../util/vendor.js'; | 
| var STACK_PREFIX = '__ec_stack_'; | 
| function getSeriesStackId(seriesModel) { | 
|   return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex; | 
| } | 
| function getAxisKey(axis) { | 
|   return axis.dim + axis.index; | 
| } | 
| /** | 
|  * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined. | 
|  */ | 
| export function getLayoutOnAxis(opt) { | 
|   var params = []; | 
|   var baseAxis = opt.axis; | 
|   var axisKey = 'axis0'; | 
|   if (baseAxis.type !== 'category') { | 
|     return; | 
|   } | 
|   var bandWidth = baseAxis.getBandWidth(); | 
|   for (var i = 0; i < opt.count || 0; i++) { | 
|     params.push(defaults({ | 
|       bandWidth: bandWidth, | 
|       axisKey: axisKey, | 
|       stackId: STACK_PREFIX + i | 
|     }, opt)); | 
|   } | 
|   var widthAndOffsets = doCalBarWidthAndOffset(params); | 
|   var result = []; | 
|   for (var i = 0; i < opt.count; i++) { | 
|     var item = widthAndOffsets[axisKey][STACK_PREFIX + i]; | 
|     item.offsetCenter = item.offset + item.width / 2; | 
|     result.push(item); | 
|   } | 
|   return result; | 
| } | 
| export function prepareLayoutBarSeries(seriesType, ecModel) { | 
|   var seriesModels = []; | 
|   ecModel.eachSeriesByType(seriesType, function (seriesModel) { | 
|     // Check series coordinate, do layout for cartesian2d only | 
|     if (isOnCartesian(seriesModel)) { | 
|       seriesModels.push(seriesModel); | 
|     } | 
|   }); | 
|   return seriesModels; | 
| } | 
| /** | 
|  * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent | 
|  * values. | 
|  * This works for time axes, value axes, and log axes. | 
|  * For a single time axis, return value is in the form like | 
|  * {'x_0': [1000000]}. | 
|  * The value of 1000000 is in milliseconds. | 
|  */ | 
| function getValueAxesMinGaps(barSeries) { | 
|   /** | 
|    * Map from axis.index to values. | 
|    * For a single time axis, axisValues is in the form like | 
|    * {'x_0': [1495555200000, 1495641600000, 1495728000000]}. | 
|    * Items in axisValues[x], e.g. 1495555200000, are time values of all | 
|    * series. | 
|    */ | 
|   var axisValues = {}; | 
|   each(barSeries, function (seriesModel) { | 
|     var cartesian = seriesModel.coordinateSystem; | 
|     var baseAxis = cartesian.getBaseAxis(); | 
|     if (baseAxis.type !== 'time' && baseAxis.type !== 'value') { | 
|       return; | 
|     } | 
|     var data = seriesModel.getData(); | 
|     var key = baseAxis.dim + '_' + baseAxis.index; | 
|     var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); | 
|     var store = data.getStore(); | 
|     for (var i = 0, cnt = store.count(); i < cnt; ++i) { | 
|       var value = store.get(dimIdx, i); | 
|       if (!axisValues[key]) { | 
|         // No previous data for the axis | 
|         axisValues[key] = [value]; | 
|       } else { | 
|         // No value in previous series | 
|         axisValues[key].push(value); | 
|       } | 
|       // Ignore duplicated time values in the same axis | 
|     } | 
|   }); | 
|   var axisMinGaps = {}; | 
|   for (var key in axisValues) { | 
|     if (axisValues.hasOwnProperty(key)) { | 
|       var valuesInAxis = axisValues[key]; | 
|       if (valuesInAxis) { | 
|         // Sort axis values into ascending order to calculate gaps | 
|         valuesInAxis.sort(function (a, b) { | 
|           return a - b; | 
|         }); | 
|         var min = null; | 
|         for (var j = 1; j < valuesInAxis.length; ++j) { | 
|           var delta = valuesInAxis[j] - valuesInAxis[j - 1]; | 
|           if (delta > 0) { | 
|             // Ignore 0 delta because they are of the same axis value | 
|             min = min === null ? delta : Math.min(min, delta); | 
|           } | 
|         } | 
|         // Set to null if only have one data | 
|         axisMinGaps[key] = min; | 
|       } | 
|     } | 
|   } | 
|   return axisMinGaps; | 
| } | 
| export function makeColumnLayout(barSeries) { | 
|   var axisMinGaps = getValueAxesMinGaps(barSeries); | 
|   var seriesInfoList = []; | 
|   each(barSeries, function (seriesModel) { | 
|     var cartesian = seriesModel.coordinateSystem; | 
|     var baseAxis = cartesian.getBaseAxis(); | 
|     var axisExtent = baseAxis.getExtent(); | 
|     var bandWidth; | 
|     if (baseAxis.type === 'category') { | 
|       bandWidth = baseAxis.getBandWidth(); | 
|     } else if (baseAxis.type === 'value' || baseAxis.type === 'time') { | 
|       var key = baseAxis.dim + '_' + baseAxis.index; | 
|       var minGap = axisMinGaps[key]; | 
|       var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]); | 
|       var scale = baseAxis.scale.getExtent(); | 
|       var scaleSpan = Math.abs(scale[1] - scale[0]); | 
|       bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value | 
|     } else { | 
|       var data = seriesModel.getData(); | 
|       bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count(); | 
|     } | 
|     var barWidth = parsePercent(seriesModel.get('barWidth'), bandWidth); | 
|     var barMaxWidth = parsePercent(seriesModel.get('barMaxWidth'), bandWidth); | 
|     var 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), bandWidth); | 
|     var barGap = seriesModel.get('barGap'); | 
|     var barCategoryGap = seriesModel.get('barCategoryGap'); | 
|     seriesInfoList.push({ | 
|       bandWidth: bandWidth, | 
|       barWidth: barWidth, | 
|       barMaxWidth: barMaxWidth, | 
|       barMinWidth: barMinWidth, | 
|       barGap: barGap, | 
|       barCategoryGap: barCategoryGap, | 
|       axisKey: getAxisKey(baseAxis), | 
|       stackId: getSeriesStackId(seriesModel) | 
|     }); | 
|   }); | 
|   return doCalBarWidthAndOffset(seriesInfoList); | 
| } | 
| function doCalBarWidthAndOffset(seriesInfoList) { | 
|   // Columns info on each category axis. Key is cartesian name | 
|   var columnsMap = {}; | 
|   each(seriesInfoList, function (seriesInfo, idx) { | 
|     var axisKey = seriesInfo.axisKey; | 
|     var bandWidth = seriesInfo.bandWidth; | 
|     var columnsOnAxis = columnsMap[axisKey] || { | 
|       bandWidth: bandWidth, | 
|       remainedWidth: bandWidth, | 
|       autoWidthCount: 0, | 
|       categoryGap: null, | 
|       gap: '20%', | 
|       stacks: {} | 
|     }; | 
|     var stacks = columnsOnAxis.stacks; | 
|     columnsMap[axisKey] = columnsOnAxis; | 
|     var stackId = seriesInfo.stackId; | 
|     if (!stacks[stackId]) { | 
|       columnsOnAxis.autoWidthCount++; | 
|     } | 
|     stacks[stackId] = stacks[stackId] || { | 
|       width: 0, | 
|       maxWidth: 0 | 
|     }; | 
|     // Caution: In a single coordinate system, these barGrid attributes | 
|     // will be shared by series. Consider that they have default values, | 
|     // only the attributes set on the last series will work. | 
|     // Do not change this fact unless there will be a break change. | 
|     var barWidth = seriesInfo.barWidth; | 
|     if (barWidth && !stacks[stackId].width) { | 
|       // See #6312, do not restrict width. | 
|       stacks[stackId].width = barWidth; | 
|       barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); | 
|       columnsOnAxis.remainedWidth -= barWidth; | 
|     } | 
|     var barMaxWidth = seriesInfo.barMaxWidth; | 
|     barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); | 
|     var barMinWidth = seriesInfo.barMinWidth; | 
|     barMinWidth && (stacks[stackId].minWidth = barMinWidth); | 
|     var barGap = seriesInfo.barGap; | 
|     barGap != null && (columnsOnAxis.gap = barGap); | 
|     var barCategoryGap = seriesInfo.barCategoryGap; | 
|     barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap); | 
|   }); | 
|   var result = {}; | 
|   each(columnsMap, function (columnsOnAxis, coordSysName) { | 
|     result[coordSysName] = {}; | 
|     var stacks = columnsOnAxis.stacks; | 
|     var bandWidth = columnsOnAxis.bandWidth; | 
|     var categoryGapPercent = columnsOnAxis.categoryGap; | 
|     if (categoryGapPercent == null) { | 
|       var columnCount = keys(stacks).length; | 
|       // More columns in one group | 
|       // the spaces between group is smaller. Or the column will be too thin. | 
|       categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%'; | 
|     } | 
|     var categoryGap = parsePercent(categoryGapPercent, bandWidth); | 
|     var barGapPercent = parsePercent(columnsOnAxis.gap, 1); | 
|     var remainedWidth = columnsOnAxis.remainedWidth; | 
|     var autoWidthCount = columnsOnAxis.autoWidthCount; | 
|     var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); | 
|     autoWidth = Math.max(autoWidth, 0); | 
|     // Find if any auto calculated bar exceeded maxBarWidth | 
|     each(stacks, function (column) { | 
|       var maxWidth = column.maxWidth; | 
|       var minWidth = column.minWidth; | 
|       if (!column.width) { | 
|         var finalWidth = autoWidth; | 
|         if (maxWidth && maxWidth < finalWidth) { | 
|           finalWidth = Math.min(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 = Math.min(finalWidth, maxWidth); | 
|         } | 
|         // `minWidth` has higher priority, as described above | 
|         if (minWidth) { | 
|           finalWidth = Math.max(finalWidth, minWidth); | 
|         } | 
|         column.width = finalWidth; | 
|         remainedWidth -= finalWidth + barGapPercent * finalWidth; | 
|         autoWidthCount--; | 
|       } | 
|     }); | 
|     // Recalculate width again | 
|     autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); | 
|     autoWidth = Math.max(autoWidth, 0); | 
|     var widthSum = 0; | 
|     var lastColumn; | 
|     each(stacks, function (column, idx) { | 
|       if (!column.width) { | 
|         column.width = autoWidth; | 
|       } | 
|       lastColumn = column; | 
|       widthSum += column.width * (1 + barGapPercent); | 
|     }); | 
|     if (lastColumn) { | 
|       widthSum -= lastColumn.width * barGapPercent; | 
|     } | 
|     var offset = -widthSum / 2; | 
|     each(stacks, function (column, stackId) { | 
|       result[coordSysName][stackId] = result[coordSysName][stackId] || { | 
|         bandWidth: bandWidth, | 
|         offset: offset, | 
|         width: column.width | 
|       }; | 
|       offset += column.width * (1 + barGapPercent); | 
|     }); | 
|   }); | 
|   return result; | 
| } | 
| function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) { | 
|   if (barWidthAndOffset && axis) { | 
|     var result = barWidthAndOffset[getAxisKey(axis)]; | 
|     if (result != null && seriesModel != null) { | 
|       return result[getSeriesStackId(seriesModel)]; | 
|     } | 
|     return result; | 
|   } | 
| } | 
| export { retrieveColumnLayout }; | 
| export function layout(seriesType, ecModel) { | 
|   var seriesModels = prepareLayoutBarSeries(seriesType, ecModel); | 
|   var barWidthAndOffset = makeColumnLayout(seriesModels); | 
|   each(seriesModels, function (seriesModel) { | 
|     var data = seriesModel.getData(); | 
|     var cartesian = seriesModel.coordinateSystem; | 
|     var baseAxis = cartesian.getBaseAxis(); | 
|     var stackId = getSeriesStackId(seriesModel); | 
|     var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; | 
|     var columnOffset = columnLayoutInfo.offset; | 
|     var columnWidth = columnLayoutInfo.width; | 
|     data.setLayout({ | 
|       bandWidth: columnLayoutInfo.bandWidth, | 
|       offset: columnOffset, | 
|       size: columnWidth | 
|     }); | 
|   }); | 
| } | 
| // TODO: Do not support stack in large mode yet. | 
| export function createProgressiveLayout(seriesType) { | 
|   return { | 
|     seriesType: seriesType, | 
|     plan: createRenderPlanner(), | 
|     reset: function (seriesModel) { | 
|       if (!isOnCartesian(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 = getValueAxisStart(baseAxis, 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) { | 
|                 var startCoord = cartesian.dataToPoint([stackStartValue, baseValue]); | 
|                 baseCoord = startCoord[0]; | 
|               } | 
|               x = baseCoord; | 
|               y = coord[1] + columnOffset; | 
|               width = coord[0] - baseCoord; | 
|               height = columnWidth; | 
|               if (Math.abs(width) < barMinHeight) { | 
|                 width = (width < 0 ? -1 : 1) * barMinHeight; | 
|               } | 
|             } else { | 
|               var coord = cartesian.dataToPoint([baseValue, value]); | 
|               if (stacked) { | 
|                 var startCoord = cartesian.dataToPoint([baseValue, stackStartValue]); | 
|                 baseCoord = startCoord[1]; | 
|               } | 
|               x = coord[0] + columnOffset; | 
|               y = baseCoord; | 
|               width = columnWidth; | 
|               height = coord[1] - baseCoord; | 
|               if (Math.abs(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 isOnCartesian(seriesModel) { | 
|   return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d'; | 
| } | 
| function isInLargeMode(seriesModel) { | 
|   return seriesModel.pipelineContext && seriesModel.pipelineContext.large; | 
| } | 
| // See cases in `test/bar-start.html` and `#7412`, `#8747`. | 
| function getValueAxisStart(baseAxis, valueAxis) { | 
|   var startValue = valueAxis.model.get('startValue'); | 
|   if (!startValue) { | 
|     startValue = 0; | 
|   } | 
|   return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? startValue > 0 ? startValue : 1 : startValue)); | 
| } |