|  | /* | 
|  | * 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 * as graphic from '../../util/graphic'; | 
|  | import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states'; | 
|  | import * as zrUtil from 'zrender/src/core/util'; | 
|  | import * as symbolUtil from '../../util/symbol'; | 
|  | import ChartView from '../../view/Chart'; | 
|  | import RadarSeriesModel, { RadarSeriesDataItemOption } from './RadarSeries'; | 
|  | import ExtensionAPI from '../../core/ExtensionAPI'; | 
|  | import SeriesData from '../../data/SeriesData'; | 
|  | import { ColorString } from '../../util/types'; | 
|  | import GlobalModel from '../../model/Global'; | 
|  | import { VectorArray } from 'zrender/src/core/vector'; | 
|  | import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle'; | 
|  | import ZRImage from 'zrender/src/graphic/Image'; | 
|  | import { saveOldStyle } from '../../animation/basicTransition'; | 
|  |  | 
|  | type RadarSymbol = ReturnType<typeof symbolUtil.createSymbol> & { | 
|  | __dimIdx: number | 
|  | }; | 
|  |  | 
|  | class RadarView extends ChartView { | 
|  | static type = 'radar'; | 
|  | type = RadarView.type; | 
|  |  | 
|  | private _data: SeriesData<RadarSeriesModel>; | 
|  |  | 
|  | render(seriesModel: RadarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | 
|  | const polar = seriesModel.coordinateSystem; | 
|  | const group = this.group; | 
|  |  | 
|  | const data = seriesModel.getData(); | 
|  | const oldData = this._data; | 
|  |  | 
|  | function createSymbol(data: SeriesData<RadarSeriesModel>, idx: number) { | 
|  | const symbolType = data.getItemVisual(idx, 'symbol') as string || 'circle'; | 
|  | if (symbolType === 'none') { | 
|  | return; | 
|  | } | 
|  | const symbolSize = symbolUtil.normalizeSymbolSize( | 
|  | data.getItemVisual(idx, 'symbolSize') | 
|  | ); | 
|  | const symbolPath = symbolUtil.createSymbol( | 
|  | symbolType, -1, -1, 2, 2 | 
|  | ); | 
|  | const symbolRotate = data.getItemVisual(idx, 'symbolRotate') || 0; | 
|  | symbolPath.attr({ | 
|  | style: { | 
|  | strokeNoScale: true | 
|  | }, | 
|  | z2: 100, | 
|  | scaleX: symbolSize[0] / 2, | 
|  | scaleY: symbolSize[1] / 2, | 
|  | rotation: symbolRotate * Math.PI / 180 || 0 | 
|  | }); | 
|  | return symbolPath as RadarSymbol; | 
|  | } | 
|  |  | 
|  | function updateSymbols( | 
|  | oldPoints: VectorArray[], | 
|  | newPoints: VectorArray[], | 
|  | symbolGroup: graphic.Group, | 
|  | data: SeriesData<RadarSeriesModel>, | 
|  | idx: number, | 
|  | isInit?: boolean | 
|  | ) { | 
|  | // Simply rerender all | 
|  | symbolGroup.removeAll(); | 
|  | for (let i = 0; i < newPoints.length - 1; i++) { | 
|  | const symbolPath = createSymbol(data, idx); | 
|  | if (symbolPath) { | 
|  | symbolPath.__dimIdx = i; | 
|  | if (oldPoints[i]) { | 
|  | symbolPath.setPosition(oldPoints[i]); | 
|  | graphic[isInit ? 'initProps' : 'updateProps']( | 
|  | symbolPath, { | 
|  | x: newPoints[i][0], | 
|  | y: newPoints[i][1] | 
|  | }, seriesModel, idx | 
|  | ); | 
|  | } | 
|  | else { | 
|  | symbolPath.setPosition(newPoints[i]); | 
|  | } | 
|  | symbolGroup.add(symbolPath); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | function getInitialPoints(points: number[][]) { | 
|  | return zrUtil.map(points, function (pt) { | 
|  | return [polar.cx, polar.cy]; | 
|  | }); | 
|  | } | 
|  | data.diff(oldData) | 
|  | .add(function (idx) { | 
|  | const points = data.getItemLayout(idx); | 
|  | if (!points) { | 
|  | return; | 
|  | } | 
|  | const polygon = new graphic.Polygon(); | 
|  | const polyline = new graphic.Polyline(); | 
|  | const target = { | 
|  | shape: { | 
|  | points: points | 
|  | } | 
|  | }; | 
|  |  | 
|  | polygon.shape.points = getInitialPoints(points); | 
|  | polyline.shape.points = getInitialPoints(points); | 
|  | graphic.initProps(polygon, target, seriesModel, idx); | 
|  | graphic.initProps(polyline, target, seriesModel, idx); | 
|  |  | 
|  | const itemGroup = new graphic.Group(); | 
|  | const symbolGroup = new graphic.Group(); | 
|  | itemGroup.add(polyline); | 
|  | itemGroup.add(polygon); | 
|  | itemGroup.add(symbolGroup); | 
|  |  | 
|  | updateSymbols( | 
|  | polyline.shape.points, points, symbolGroup, data, idx, true | 
|  | ); | 
|  |  | 
|  | data.setItemGraphicEl(idx, itemGroup); | 
|  | }) | 
|  | .update(function (newIdx, oldIdx) { | 
|  | const itemGroup = oldData.getItemGraphicEl(oldIdx) as graphic.Group; | 
|  |  | 
|  | const polyline = itemGroup.childAt(0) as graphic.Polyline; | 
|  | const polygon = itemGroup.childAt(1) as graphic.Polygon; | 
|  | const symbolGroup = itemGroup.childAt(2) as graphic.Group; | 
|  | const target = { | 
|  | shape: { | 
|  | points: data.getItemLayout(newIdx) | 
|  | } | 
|  | }; | 
|  |  | 
|  | if (!target.shape.points) { | 
|  | return; | 
|  | } | 
|  | updateSymbols( | 
|  | polyline.shape.points, | 
|  | target.shape.points, | 
|  | symbolGroup, | 
|  | data, | 
|  | newIdx, | 
|  | false | 
|  | ); | 
|  |  | 
|  | saveOldStyle(polygon); | 
|  | saveOldStyle(polyline); | 
|  |  | 
|  | graphic.updateProps(polyline, target, seriesModel); | 
|  | graphic.updateProps(polygon, target, seriesModel); | 
|  |  | 
|  | data.setItemGraphicEl(newIdx, itemGroup); | 
|  | }) | 
|  | .remove(function (idx) { | 
|  | group.remove(oldData.getItemGraphicEl(idx)); | 
|  | }) | 
|  | .execute(); | 
|  |  | 
|  | data.eachItemGraphicEl(function (itemGroup: graphic.Group, idx) { | 
|  | const itemModel = data.getItemModel<RadarSeriesDataItemOption>(idx); | 
|  | const polyline = itemGroup.childAt(0) as graphic.Polyline; | 
|  | const polygon = itemGroup.childAt(1) as graphic.Polygon; | 
|  | const symbolGroup = itemGroup.childAt(2) as graphic.Group; | 
|  | // Radar uses the visual encoded from itemStyle. | 
|  | const itemStyle = data.getItemVisual(idx, 'style'); | 
|  | const color = itemStyle.fill; | 
|  |  | 
|  | group.add(itemGroup); | 
|  |  | 
|  | polyline.useStyle( | 
|  | zrUtil.defaults( | 
|  | itemModel.getModel('lineStyle').getLineStyle(), | 
|  | { | 
|  | fill: 'none', | 
|  | stroke: color | 
|  | } | 
|  | ) | 
|  | ); | 
|  |  | 
|  | setStatesStylesFromModel(polyline, itemModel, 'lineStyle'); | 
|  | setStatesStylesFromModel(polygon, itemModel, 'areaStyle'); | 
|  |  | 
|  | const areaStyleModel = itemModel.getModel('areaStyle'); | 
|  | const polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); | 
|  |  | 
|  | polygon.ignore = polygonIgnore; | 
|  |  | 
|  | zrUtil.each(['emphasis', 'select', 'blur'] as const, function (stateName) { | 
|  | const stateModel = itemModel.getModel([stateName, 'areaStyle']); | 
|  | const stateIgnore = stateModel.isEmpty() && stateModel.parentModel.isEmpty(); | 
|  | // Won't be ignore if normal state is not ignore. | 
|  | polygon.ensureState(stateName).ignore = stateIgnore && polygonIgnore; | 
|  | }); | 
|  |  | 
|  | polygon.useStyle( | 
|  | zrUtil.defaults( | 
|  | areaStyleModel.getAreaStyle(), | 
|  | { | 
|  | fill: color, | 
|  | opacity: 0.7, | 
|  | decal: itemStyle.decal | 
|  | } | 
|  | ) | 
|  | ); | 
|  | const emphasisModel = itemModel.getModel('emphasis'); | 
|  | const itemHoverStyle = emphasisModel.getModel('itemStyle').getItemStyle(); | 
|  | symbolGroup.eachChild(function (symbolPath: RadarSymbol) { | 
|  | if (symbolPath instanceof ZRImage) { | 
|  | const pathStyle = symbolPath.style; | 
|  | symbolPath.useStyle(zrUtil.extend({ | 
|  | // TODO other properties like x, y ? | 
|  | image: pathStyle.image, | 
|  | x: pathStyle.x, y: pathStyle.y, | 
|  | width: pathStyle.width, height: pathStyle.height | 
|  | }, itemStyle)); | 
|  | } | 
|  | else { | 
|  | symbolPath.useStyle(itemStyle); | 
|  | symbolPath.setColor(color); | 
|  | symbolPath.style.strokeNoScale = true; | 
|  | } | 
|  |  | 
|  | const pathEmphasisState = symbolPath.ensureState('emphasis'); | 
|  | pathEmphasisState.style = zrUtil.clone(itemHoverStyle); | 
|  | let defaultText = data.getStore().get(data.getDimensionIndex(symbolPath.__dimIdx), idx); | 
|  | (defaultText == null || isNaN(defaultText as number)) && (defaultText = ''); | 
|  |  | 
|  | setLabelStyle( | 
|  | symbolPath, getLabelStatesModels(itemModel), | 
|  | { | 
|  | labelFetcher: data.hostModel, | 
|  | labelDataIndex: idx, | 
|  | labelDimIndex: symbolPath.__dimIdx, | 
|  | defaultText: defaultText as string, | 
|  | inheritColor: color as ColorString, | 
|  | defaultOpacity: itemStyle.opacity | 
|  | } | 
|  | ); | 
|  | }); | 
|  |  | 
|  | toggleHoverEmphasis( | 
|  | itemGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled') | 
|  | ); | 
|  | }); | 
|  |  | 
|  | this._data = data; | 
|  | } | 
|  |  | 
|  | remove() { | 
|  | this.group.removeAll(); | 
|  | this._data = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | export default RadarView; |