|  | /* | 
|  | * 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} from 'zrender/src/core/util'; | 
|  | import GlobalModel from '../model/Global'; | 
|  | import SeriesModel from '../model/Series'; | 
|  | import { SeriesOption, SeriesStackOptionMixin } from '../util/types'; | 
|  | import SeriesData, { DataCalculationInfo } from '../data/SeriesData'; | 
|  | import { addSafe } from '../util/number'; | 
|  |  | 
|  | type StackInfo = Pick< | 
|  | DataCalculationInfo<SeriesOption & SeriesStackOptionMixin>, | 
|  | 'stackedDimension' | 
|  | | 'isStackedByIndex' | 
|  | | 'stackedByDimension' | 
|  | | 'stackResultDimension' | 
|  | | 'stackedOverDimension' | 
|  | > & { | 
|  | data: SeriesData | 
|  | seriesModel: SeriesModel<SeriesOption & SeriesStackOptionMixin> | 
|  | }; | 
|  |  | 
|  | // (1) [Caution]: the logic is correct based on the premises: | 
|  | //     data processing stage is blocked in stream. | 
|  | //     See <module:echarts/stream/Scheduler#performDataProcessorTasks> | 
|  | // (2) Only register once when import repeatedly. | 
|  | //     Should be executed after series is filtered and before stack calculation. | 
|  | export default function dataStack(ecModel: GlobalModel) { | 
|  | const stackInfoMap = createHashMap<StackInfo[]>(); | 
|  | ecModel.eachSeries(function (seriesModel: SeriesModel<SeriesOption & SeriesStackOptionMixin>) { | 
|  | const stack = seriesModel.get('stack'); | 
|  | // Compatible: when `stack` is set as '', do not stack. | 
|  | if (stack) { | 
|  | const stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []); | 
|  | const data = seriesModel.getData(); | 
|  |  | 
|  | const stackInfo: StackInfo = { | 
|  | // Used for calculate axis extent automatically. | 
|  | // TODO: Type getCalculationInfo return more specific type? | 
|  | stackResultDimension: data.getCalculationInfo('stackResultDimension'), | 
|  | stackedOverDimension: data.getCalculationInfo('stackedOverDimension'), | 
|  | stackedDimension: data.getCalculationInfo('stackedDimension'), | 
|  | stackedByDimension: data.getCalculationInfo('stackedByDimension'), | 
|  | isStackedByIndex: data.getCalculationInfo('isStackedByIndex'), | 
|  | data: data, | 
|  | seriesModel: seriesModel | 
|  | }; | 
|  |  | 
|  | // If stacked on axis that do not support data stack. | 
|  | if (!stackInfo.stackedDimension | 
|  | || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension) | 
|  | ) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | stackInfoList.length && data.setCalculationInfo( | 
|  | 'stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel | 
|  | ); | 
|  |  | 
|  | stackInfoList.push(stackInfo); | 
|  | } | 
|  | }); | 
|  |  | 
|  | stackInfoMap.each(calculateStack); | 
|  | } | 
|  |  | 
|  | function calculateStack(stackInfoList: StackInfo[]) { | 
|  | each(stackInfoList, function (targetStackInfo, idxInStack) { | 
|  | const resultVal: number[] = []; | 
|  | const resultNaN = [NaN, NaN]; | 
|  | const dims: [string, string] = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension]; | 
|  | const targetData = targetStackInfo.data; | 
|  | const isStackedByIndex = targetStackInfo.isStackedByIndex; | 
|  | const stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; | 
|  |  | 
|  | // Should not write on raw data, because stack series model list changes | 
|  | // depending on legend selection. | 
|  | targetData.modify(dims, function (v0, v1, dataIndex) { | 
|  | let sum = targetData.get(targetStackInfo.stackedDimension, dataIndex) as number; | 
|  |  | 
|  | // Consider `connectNulls` of line area, if value is NaN, stackedOver | 
|  | // should also be NaN, to draw a appropriate belt area. | 
|  | if (isNaN(sum)) { | 
|  | return resultNaN; | 
|  | } | 
|  |  | 
|  | let byValue: number; | 
|  | let stackedDataRawIndex; | 
|  |  | 
|  | if (isStackedByIndex) { | 
|  | stackedDataRawIndex = targetData.getRawIndex(dataIndex); | 
|  | } | 
|  | else { | 
|  | byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex) as number; | 
|  | } | 
|  |  | 
|  | // If stackOver is NaN, chart view will render point on value start. | 
|  | let stackedOver = NaN; | 
|  |  | 
|  | for (let j = idxInStack - 1; j >= 0; j--) { | 
|  | const stackInfo = stackInfoList[j]; | 
|  |  | 
|  | // Has been optimized by inverted indices on `stackedByDimension`. | 
|  | if (!isStackedByIndex) { | 
|  | stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue); | 
|  | } | 
|  |  | 
|  | if (stackedDataRawIndex >= 0) { | 
|  | const val = stackInfo.data.getByRawIndex( | 
|  | stackInfo.stackResultDimension, stackedDataRawIndex | 
|  | ) as number; | 
|  |  | 
|  | // Considering positive stack, negative stack and empty data | 
|  | if ( | 
|  | stackStrategy === 'all' // single stack group | 
|  | || (stackStrategy === 'positive' && val > 0) | 
|  | || (stackStrategy === 'negative' && val < 0) | 
|  | || (stackStrategy === 'samesign' && sum > 0 && val > 0) // All positive stack | 
|  | || (stackStrategy === 'samesign' && sum < 0 && val < 0) // All negative stack | 
|  | ) { | 
|  | // The sum has to be very small to be affected by the | 
|  | // floating arithmetic problem. An incorrect result will probably | 
|  | // cause axis min/max to be filtered incorrectly. | 
|  | sum = addSafe(sum, val); | 
|  | stackedOver = val; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | resultVal[0] = sum; | 
|  | resultVal[1] = stackedOver; | 
|  |  | 
|  | return resultVal; | 
|  | }); | 
|  | }); | 
|  | } |