blob: c0b57142a411866d9bee03cc5417c49744d9505d [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 graphic from '../../util/graphic';
import { setStatesStylesFromModel, enableHoverEmphasis } from '../../util/states';
import ChartView from '../../view/Chart';
import List from '../../data/List';
import ParallelSeriesModel, { ParallelSeriesDataItemOption } from './ParallelSeries';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI';
import { StageHandlerProgressParams, ParsedValue, Payload } from '../../util/types';
import Parallel from '../../coord/parallel/Parallel';
import { OptionAxisType } from '../../coord/axisCommonTypes';
import { numericToNumber } from '../../util/number';
import { eqNaN } from 'zrender/src/core/util';
import { saveOldStyle } from '../../animation/basicTrasition';
const DEFAULT_SMOOTH = 0.3;
interface ParallelDrawSeriesScope {
smooth: number
}
class ParallelView extends ChartView {
static type = 'parallel';
type = ParallelView.type;
private _dataGroup = new graphic.Group();
private _data: List;
private _initialized = false;
init() {
this.group.add(this._dataGroup);
}
/**
* @override
*/
render(
seriesModel: ParallelSeriesModel,
ecModel: GlobalModel,
api: ExtensionAPI,
payload: Payload
) {
const dataGroup = this._dataGroup;
const data = seriesModel.getData();
const oldData = this._data;
const coordSys = seriesModel.coordinateSystem;
const dimensions = coordSys.dimensions;
const seriesScope = makeSeriesScope(seriesModel);
data.diff(oldData)
.add(add)
.update(update)
.remove(remove)
.execute();
function add(newDataIndex: number) {
const line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys);
updateElCommon(line, data, newDataIndex, seriesScope);
}
function update(newDataIndex: number, oldDataIndex: number) {
const line = oldData.getItemGraphicEl(oldDataIndex) as graphic.Polyline;
const points = createLinePoints(data, newDataIndex, dimensions, coordSys);
data.setItemGraphicEl(newDataIndex, line);
graphic.updateProps(line, {shape: {points: points}}, seriesModel, newDataIndex);
saveOldStyle(line);
updateElCommon(line, data, newDataIndex, seriesScope);
}
function remove(oldDataIndex: number) {
const line = oldData.getItemGraphicEl(oldDataIndex);
dataGroup.remove(line);
}
// First create
if (!this._initialized) {
this._initialized = true;
const clipPath = createGridClipShape(
coordSys, seriesModel, function () {
// Callback will be invoked immediately if there is no animation
setTimeout(function () {
dataGroup.removeClipPath();
});
}
);
dataGroup.setClipPath(clipPath);
}
this._data = data;
}
incrementalPrepareRender(seriesModel: ParallelSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
this._initialized = true;
this._data = null;
this._dataGroup.removeAll();
}
incrementalRender(taskParams: StageHandlerProgressParams, seriesModel: ParallelSeriesModel, ecModel: GlobalModel) {
const data = seriesModel.getData();
const coordSys = seriesModel.coordinateSystem;
const dimensions = coordSys.dimensions;
const seriesScope = makeSeriesScope(seriesModel);
for (let dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) {
const line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys);
line.incremental = true;
updateElCommon(line, data, dataIndex, seriesScope);
}
}
remove() {
this._dataGroup && this._dataGroup.removeAll();
this._data = null;
}
}
function createGridClipShape(coordSys: Parallel, seriesModel: ParallelSeriesModel, cb: () => void) {
const parallelModel = coordSys.model;
const rect = coordSys.getRect();
const rectEl = new graphic.Rect({
shape: {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
}
});
const dim = parallelModel.get('layout') === 'horizontal' ? 'width' as const : 'height' as const;
rectEl.setShape(dim, 0);
graphic.initProps(rectEl, {
shape: {
width: rect.width,
height: rect.height
}
}, seriesModel, cb);
return rectEl;
}
function createLinePoints(data: List, dataIndex: number, dimensions: string[], coordSys: Parallel) {
const points = [];
for (let i = 0; i < dimensions.length; i++) {
const dimName = dimensions[i];
const value = data.get(data.mapDimension(dimName), dataIndex);
if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) {
points.push(coordSys.dataToPoint(value, dimName));
}
}
return points;
}
function addEl(data: List, dataGroup: graphic.Group, dataIndex: number, dimensions: string[], coordSys: Parallel) {
const points = createLinePoints(data, dataIndex, dimensions, coordSys);
const line = new graphic.Polyline({
shape: {points: points},
// silent: true,
z2: 10
});
dataGroup.add(line);
data.setItemGraphicEl(dataIndex, line);
return line;
}
function makeSeriesScope(seriesModel: ParallelSeriesModel): ParallelDrawSeriesScope {
let smooth = seriesModel.get('smooth', true);
smooth === true && (smooth = DEFAULT_SMOOTH);
smooth = numericToNumber(smooth);
eqNaN(smooth) && (smooth = 0);
return { smooth };
}
function updateElCommon(
el: graphic.Polyline,
data: List,
dataIndex: number,
seriesScope: ParallelDrawSeriesScope
) {
el.useStyle(data.getItemVisual(dataIndex, 'style'));
el.style.fill = null;
el.setShape('smooth', seriesScope.smooth);
const itemModel = data.getItemModel<ParallelSeriesDataItemOption>(dataIndex);
const emphasisModel = itemModel.getModel('emphasis');
setStatesStylesFromModel(el, itemModel, 'lineStyle');
enableHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
}
// function simpleDiff(oldData, newData, dimensions) {
// let oldLen;
// if (!oldData
// || !oldData.__plProgressive
// || (oldLen = oldData.count()) !== newData.count()
// ) {
// return true;
// }
// let dimLen = dimensions.length;
// for (let i = 0; i < oldLen; i++) {
// for (let j = 0; j < dimLen; j++) {
// if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) {
// return true;
// }
// }
// }
// return false;
// }
// FIXME put in common util?
function isEmptyValue(val: ParsedValue, axisType: OptionAxisType) {
return axisType === 'category'
? val == null
: (val == null || isNaN(val as number)); // axisType === 'value'
}
export default ParallelView;