|  | /* | 
|  | * 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 zrUtil from 'zrender/src/core/util'; | 
|  | import ChartView from '../../view/Chart'; | 
|  | import * as graphic from '../../util/graphic'; | 
|  | import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states'; | 
|  | import Path, { PathProps } from 'zrender/src/graphic/Path'; | 
|  | import BoxplotSeriesModel, { BoxplotDataItemOption } from './BoxplotSeries'; | 
|  | import GlobalModel from '../../model/Global'; | 
|  | import ExtensionAPI from '../../core/ExtensionAPI'; | 
|  | import SeriesData from '../../data/SeriesData'; | 
|  | import { BoxplotItemLayout } from './boxplotLayout'; | 
|  | import { saveOldStyle } from '../../animation/basicTransition'; | 
|  |  | 
|  | class BoxplotView extends ChartView { | 
|  | static type = 'boxplot'; | 
|  | type = BoxplotView.type; | 
|  |  | 
|  | private _data: SeriesData; | 
|  |  | 
|  | render(seriesModel: BoxplotSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | 
|  | const data = seriesModel.getData(); | 
|  | const group = this.group; | 
|  | const oldData = this._data; | 
|  |  | 
|  | // There is no old data only when first rendering or switching from | 
|  | // stream mode to normal mode, where previous elements should be removed. | 
|  | if (!this._data) { | 
|  | group.removeAll(); | 
|  | } | 
|  |  | 
|  | const constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; | 
|  |  | 
|  | data.diff(oldData) | 
|  | .add(function (newIdx) { | 
|  | if (data.hasValue(newIdx)) { | 
|  | const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; | 
|  | const symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true); | 
|  | data.setItemGraphicEl(newIdx, symbolEl); | 
|  | group.add(symbolEl); | 
|  | } | 
|  | }) | 
|  | .update(function (newIdx, oldIdx) { | 
|  | let symbolEl = oldData.getItemGraphicEl(oldIdx) as BoxPath; | 
|  |  | 
|  | // Empty data | 
|  | if (!data.hasValue(newIdx)) { | 
|  | group.remove(symbolEl); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; | 
|  | if (!symbolEl) { | 
|  | symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); | 
|  | } | 
|  | else { | 
|  | saveOldStyle(symbolEl); | 
|  | updateNormalBoxData(itemLayout, symbolEl, data, newIdx); | 
|  | } | 
|  |  | 
|  | group.add(symbolEl); | 
|  |  | 
|  | data.setItemGraphicEl(newIdx, symbolEl); | 
|  | }) | 
|  | .remove(function (oldIdx) { | 
|  | const el = oldData.getItemGraphicEl(oldIdx); | 
|  | el && group.remove(el); | 
|  | }) | 
|  | .execute(); | 
|  |  | 
|  | this._data = data; | 
|  | } | 
|  |  | 
|  | remove(ecModel: GlobalModel) { | 
|  | const group = this.group; | 
|  | const data = this._data; | 
|  | this._data = null; | 
|  | data && data.eachItemGraphicEl(function (el) { | 
|  | el && group.remove(el); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | class BoxPathShape { | 
|  | points: number[][]; | 
|  | } | 
|  |  | 
|  | interface BoxPathProps extends PathProps { | 
|  | shape?: Partial<BoxPathShape> | 
|  | } | 
|  |  | 
|  | class BoxPath extends Path<BoxPathProps> { | 
|  |  | 
|  | readonly type = 'boxplotBoxPath'; | 
|  | shape: BoxPathShape; | 
|  |  | 
|  | constructor(opts?: BoxPathProps) { | 
|  | super(opts); | 
|  | } | 
|  |  | 
|  | getDefaultShape() { | 
|  | return new BoxPathShape(); | 
|  | } | 
|  |  | 
|  | buildPath(ctx: CanvasRenderingContext2D, shape: BoxPathShape) { | 
|  | const ends = shape.points; | 
|  |  | 
|  | let i = 0; | 
|  | ctx.moveTo(ends[i][0], ends[i][1]); | 
|  | i++; | 
|  | for (; i < 4; i++) { | 
|  | ctx.lineTo(ends[i][0], ends[i][1]); | 
|  | } | 
|  | ctx.closePath(); | 
|  |  | 
|  | for (; i < ends.length; i++) { | 
|  | ctx.moveTo(ends[i][0], ends[i][1]); | 
|  | i++; | 
|  | ctx.lineTo(ends[i][0], ends[i][1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | function createNormalBox( | 
|  | itemLayout: BoxplotItemLayout, | 
|  | data: SeriesData, | 
|  | dataIndex: number, | 
|  | constDim: number, | 
|  | isInit?: boolean | 
|  | ) { | 
|  | const ends = itemLayout.ends; | 
|  |  | 
|  | const el = new BoxPath({ | 
|  | shape: { | 
|  | points: isInit | 
|  | ? transInit(ends, constDim, itemLayout) | 
|  | : ends | 
|  | } | 
|  | }); | 
|  |  | 
|  | updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); | 
|  |  | 
|  | return el; | 
|  | } | 
|  |  | 
|  | function updateNormalBoxData( | 
|  | itemLayout: BoxplotItemLayout, | 
|  | el: BoxPath, | 
|  | data: SeriesData, | 
|  | dataIndex: number, | 
|  | isInit?: boolean | 
|  | ) { | 
|  | const seriesModel = data.hostModel; | 
|  | const updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; | 
|  |  | 
|  | updateMethod( | 
|  | el, | 
|  | {shape: {points: itemLayout.ends}}, | 
|  | seriesModel, | 
|  | dataIndex | 
|  | ); | 
|  |  | 
|  | el.useStyle(data.getItemVisual(dataIndex, 'style')); | 
|  | el.style.strokeNoScale = true; | 
|  |  | 
|  | el.z2 = 100; | 
|  |  | 
|  | const itemModel = data.getItemModel<BoxplotDataItemOption>(dataIndex); | 
|  | const emphasisModel = itemModel.getModel('emphasis'); | 
|  |  | 
|  | setStatesStylesFromModel(el, itemModel); | 
|  |  | 
|  | toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); | 
|  | } | 
|  |  | 
|  | function transInit(points: number[][], dim: number, itemLayout: BoxplotItemLayout) { | 
|  | return zrUtil.map(points, function (point) { | 
|  | point = point.slice(); | 
|  | point[dim] = itemLayout.initBaseline; | 
|  | return point; | 
|  | }); | 
|  | } | 
|  |  | 
|  | export default BoxplotView; |