/* * 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 } class BoxPath extends Path { 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(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;