| /* | 
 | * 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 { isFunction, extend, createHashMap } from 'zrender/src/core/util'; | 
 | import { StageHandler, CallbackDataParams, ZRColor, Dictionary, InnerDecalObject } | 
 |     from '../util/types'; | 
 | import makeStyleMapper from '../model/mixin/makeStyleMapper'; | 
 | import { ITEM_STYLE_KEY_MAP } from '../model/mixin/itemStyle'; | 
 | import { LINE_STYLE_KEY_MAP } from '../model/mixin/lineStyle'; | 
 | import SeriesModel from '../model/Series'; | 
 | import Model from '../model/Model'; | 
 | import { makeInner } from '../util/model'; | 
 |  | 
 | const inner = makeInner<{scope: object}, SeriesModel>(); | 
 |  | 
 | const defaultStyleMappers = { | 
 |     itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true), | 
 |     lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true) | 
 | }; | 
 |  | 
 | const defaultColorKey = { | 
 |     lineStyle: 'stroke', | 
 |     itemStyle: 'fill' | 
 | } as const; | 
 |  | 
 | function getStyleMapper(seriesModel: SeriesModel, stylePath: string) { | 
 |     const styleMapper = seriesModel.visualStyleMapper | 
 |         || defaultStyleMappers[stylePath as 'itemStyle' | 'lineStyle']; | 
 |     if (!styleMapper) { | 
 |         console.warn(`Unknown style type '${stylePath}'.`); | 
 |         return defaultStyleMappers.itemStyle; | 
 |     } | 
 |     return styleMapper; | 
 | } | 
 |  | 
 | function getDefaultColorKey(seriesModel: SeriesModel, stylePath: string): 'stroke' | 'fill' { | 
 |     // return defaultColorKey[stylePath] || | 
 |     const colorKey = seriesModel.visualDrawType | 
 |         || defaultColorKey[stylePath as 'itemStyle' | 'lineStyle']; | 
 |  | 
 |     if (!colorKey) { | 
 |         console.warn(`Unknown style type '${stylePath}'.`); | 
 |         return 'fill'; | 
 |     } | 
 |  | 
 |     return colorKey; | 
 | } | 
 |  | 
 | type ColorCallback = (params: CallbackDataParams) => ZRColor; | 
 |  | 
 | const seriesStyleTask: StageHandler = { | 
 |     createOnAllSeries: true, | 
 |     performRawSeries: true, | 
 |     reset(seriesModel, ecModel) { | 
 |         const data = seriesModel.getData(); | 
 |         const stylePath = seriesModel.visualStyleAccessPath | 
 |             || 'itemStyle'; | 
 |         // Set in itemStyle | 
 |         const styleModel = seriesModel.getModel(stylePath as any); | 
 |         const getStyle = getStyleMapper(seriesModel, stylePath); | 
 |  | 
 |         const globalStyle = getStyle(styleModel); | 
 |  | 
 |         const decalOption = styleModel.getShallow('decal') as InnerDecalObject; | 
 |         if (decalOption) { | 
 |             data.setVisual('decal', decalOption); | 
 |             decalOption.dirty = true; | 
 |         } | 
 |  | 
 |         // TODO | 
 |         const colorKey = getDefaultColorKey(seriesModel, stylePath); | 
 |         const color = globalStyle[colorKey]; | 
 |  | 
 |         // TODO style callback | 
 |         const colorCallback = isFunction(color) ? color as unknown as ColorCallback : null; | 
 |         const hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; | 
 |         // Get from color palette by default. | 
 |         if (!globalStyle[colorKey] || colorCallback || hasAutoColor) { | 
 |             // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT | 
 |             // make it effect palette. Because some scenarios users need to make some series | 
 |             // transparent or as background, which should better not effect the palette. | 
 |             const colorPalette = seriesModel.getColorFromPalette( | 
 |                 // TODO series count changed. | 
 |                 seriesModel.name, null, ecModel.getSeriesCount() | 
 |             ); | 
 |             if (!globalStyle[colorKey]) { | 
 |                 globalStyle[colorKey] = colorPalette; | 
 |                 data.setVisual('colorFromPalette', true); | 
 |             } | 
 |             globalStyle.fill = (globalStyle.fill === 'auto' || isFunction(globalStyle.fill)) | 
 |                 ? colorPalette | 
 |                 : globalStyle.fill; | 
 |             globalStyle.stroke = (globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke)) | 
 |                 ? colorPalette | 
 |                 : globalStyle.stroke; | 
 |         } | 
 |  | 
 |         data.setVisual('style', globalStyle); | 
 |         data.setVisual('drawType', colorKey); | 
 |  | 
 |         // Only visible series has each data be visual encoded | 
 |         if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) { | 
 |             data.setVisual('colorFromPalette', false); | 
 |  | 
 |             return { | 
 |                 dataEach(data, idx) { | 
 |                     const dataParams = seriesModel.getDataParams(idx); | 
 |                     const itemStyle = extend({}, globalStyle); | 
 |                     itemStyle[colorKey] = colorCallback(dataParams); | 
 |                     data.setItemVisual(idx, 'style', itemStyle); | 
 |                 } | 
 |             }; | 
 |         } | 
 |     } | 
 | }; | 
 |  | 
 | const sharedModel = new Model(); | 
 | const dataStyleTask: StageHandler = { | 
 |     createOnAllSeries: true, | 
 |     performRawSeries: true, | 
 |     reset(seriesModel, ecModel) { | 
 |         if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) { | 
 |             return; | 
 |         } | 
 |  | 
 |         const data = seriesModel.getData(); | 
 |         const stylePath = seriesModel.visualStyleAccessPath | 
 |             || 'itemStyle'; | 
 |         // Set in itemStyle | 
 |         const getStyle = getStyleMapper(seriesModel, stylePath); | 
 |  | 
 |         const colorKey = data.getVisual('drawType'); | 
 |  | 
 |         return { | 
 |             dataEach: data.hasItemOption ? function (data, idx) { | 
 |                 // Not use getItemModel for performance considuration | 
 |                 const rawItem = data.getRawDataItem(idx) as any; | 
 |                 if (rawItem && rawItem[stylePath]) { | 
 |                     sharedModel.option = rawItem[stylePath]; | 
 |                     const style = getStyle(sharedModel); | 
 |  | 
 |                     const existsStyle = data.ensureUniqueItemVisual(idx, 'style'); | 
 |                     extend(existsStyle, style); | 
 |  | 
 |                     if (sharedModel.option.decal) { | 
 |                         data.setItemVisual(idx, 'decal', sharedModel.option.decal); | 
 |                         sharedModel.option.decal.dirty = true; | 
 |                     } | 
 |  | 
 |                     if (colorKey in style) { | 
 |                         data.setItemVisual(idx, 'colorFromPalette', false); | 
 |                     } | 
 |                 } | 
 |             } : null | 
 |         }; | 
 |     } | 
 | }; | 
 |  | 
 | // Pick color from palette for the data which has not been set with color yet. | 
 | // Note: do not support stream rendering. No such cases yet. | 
 | const dataColorPaletteTask: StageHandler = { | 
 |     performRawSeries: true, | 
 |     overallReset(ecModel) { | 
 |         // Each type of series uses one scope. | 
 |         // Pie and funnel are using different scopes. | 
 |         const paletteScopeGroupByType = createHashMap<object>(); | 
 |         ecModel.eachSeries((seriesModel: SeriesModel) => { | 
 |             const colorBy = seriesModel.getColorBy(); | 
 |             if (seriesModel.isColorBySeries()) { | 
 |                 return; | 
 |             } | 
 |             const key = seriesModel.type + '-' + colorBy; | 
 |             let colorScope = paletteScopeGroupByType.get(key); | 
 |             if (!colorScope) { | 
 |                 colorScope = {}; | 
 |                 paletteScopeGroupByType.set(key, colorScope); | 
 |             } | 
 |             inner(seriesModel).scope = colorScope; | 
 |         }); | 
 |  | 
 |  | 
 |         ecModel.eachSeries((seriesModel: SeriesModel) => { | 
 |             if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) { | 
 |                 return; | 
 |             } | 
 |  | 
 |             const dataAll = seriesModel.getRawData(); | 
 |             const idxMap: Dictionary<number> = {}; | 
 |             const data = seriesModel.getData(); | 
 |             const colorScope = inner(seriesModel).scope; | 
 |  | 
 |             const stylePath = seriesModel.visualStyleAccessPath | 
 |                 || 'itemStyle'; | 
 |             const colorKey = getDefaultColorKey(seriesModel, stylePath); | 
 |  | 
 |             data.each(function (idx) { | 
 |                 const rawIdx = data.getRawIndex(idx); | 
 |                 idxMap[rawIdx] = idx; | 
 |             }); | 
 |  | 
 |             // Iterate on data before filtered. To make sure color from palette can be | 
 |             // Consistent when toggling legend. | 
 |             dataAll.each(function (rawIdx) { | 
 |                 const idx = idxMap[rawIdx]; | 
 |                 const fromPalette = data.getItemVisual(idx, 'colorFromPalette'); | 
 |                 // Get color from palette for each data only when the color is inherited from series color, which is | 
 |                 // also picked from color palette. So following situation is not in the case: | 
 |                 // 1. series.itemStyle.color is set | 
 |                 // 2. color is encoded by visualMap | 
 |                 if (fromPalette) { | 
 |                     const itemStyle = data.ensureUniqueItemVisual(idx, 'style'); | 
 |                     const name = dataAll.getName(rawIdx) || (rawIdx + ''); | 
 |                     const dataCount = dataAll.count(); | 
 |                     itemStyle[colorKey] = seriesModel.getColorFromPalette(name, colorScope, dataCount); | 
 |                 } | 
 |             }); | 
 |         }); | 
 |     } | 
 | }; | 
 |  | 
 | export { | 
 |     seriesStyleTask, | 
 |     dataStyleTask, | 
 |     dataColorPaletteTask | 
 | }; |