|  | /* | 
|  | * 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 | 
|  | }; |