blob: 68ac23a0a42e75a920fad2eb1c35efd98ad185d5 [file] [log] [blame]
/*
* 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, enableHoverEmphasis } 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 List from '../../data/List';
import { BoxplotItemLayout } from './boxplotLayout';
import { saveOldStyle } from '../../animation/basicTrasition';
class BoxplotView extends ChartView {
static type = 'boxplot';
type = BoxplotView.type;
private _data: List;
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: List,
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: List,
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);
setStatesStylesFromModel(el, itemModel);
enableHoverEmphasis(el, itemModel.get(['emphasis', 'focus']), itemModel.get(['emphasis', 'blurScope']));
}
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;