Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 12 additions & 98 deletions src/chart/helper/Line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@

import { isArray, each, retrieve2 } from 'zrender/src/core/util';
import * as vector from 'zrender/src/core/vector';
import * as symbolUtil from '../../util/symbol';
import ECLinePath from './LinePath';
import * as graphic from '../../util/graphic';
import { toggleHoverEmphasis, enterEmphasis, leaveEmphasis, SPECIAL_STATES } from '../../util/states';
import { toggleHoverEmphasis, enterEmphasis, leaveEmphasis } from '../../util/states';
import {getLabelStatesModels, setLabelStyle} from '../../label/labelStyle';
import {round} from '../../util/number';
import SeriesData from '../../data/SeriesData';
Expand All @@ -37,19 +36,18 @@ import {
import SeriesModel from '../../model/Series';
import type { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
import { TextStyleProps } from 'zrender/src/graphic/Text';
import { LineDataVisual } from '../../visual/commonVisualTypes';
import Model from '../../model/Model';
import tokens from '../../visual/tokens';

const SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'] as const;

type ECSymbol = ReturnType<typeof createSymbol>;

type LineECSymbol = ECSymbol & {
__specifiedRotation: number
};

type LineList = SeriesData<SeriesModel, LineDataVisual>;
import {
SYMBOL_CATEGORIES,
LineECSymbol,
LineList,
createSymbol,
makeSymbolTypeKey,
makeSymbolTypeValue,
ECSymbol,
updateSymbol
} from './lineSymbolHelper';

export interface LineLabel extends graphic.Text {
lineLabelOriginalOpacity: number
Expand All @@ -62,64 +60,6 @@ interface InnerLineLabel extends LineLabel {
__labelDistance: number[]
}

function makeSymbolTypeKey(symbolCategory: 'fromSymbol' | 'toSymbol') {
return '_' + symbolCategory + 'Type' as '_fromSymbolType' | '_toSymbolType';
}
function makeSymbolTypeValue(name: 'fromSymbol' | 'toSymbol', lineData: LineList, idx: number) {
const symbolType = lineData.getItemVisual(idx, name);
if (!symbolType || symbolType === 'none') {
return symbolType;
}
const symbolSize = lineData.getItemVisual(idx, name + 'Size' as 'fromSymbolSize' | 'toSymbolSize');
const symbolRotate = lineData.getItemVisual(idx, name + 'Rotate' as 'fromSymbolRotate' | 'toSymbolRotate');
const symbolOffset = lineData.getItemVisual(idx, name + 'Offset' as 'fromSymbolOffset' | 'toSymbolOffset');
const symbolKeepAspect = lineData.getItemVisual(idx,
name + 'KeepAspect' as 'fromSymbolKeepAspect' | 'toSymbolKeepAspect');
const symbolSizeArr = symbolUtil.normalizeSymbolSize(symbolSize);

const symbolOffsetArr = symbolUtil.normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr);

return symbolType + symbolSizeArr + symbolOffsetArr + (symbolRotate || '') + (symbolKeepAspect || '');
}

/**
* @inner
*/
function createSymbol(name: 'fromSymbol' | 'toSymbol', lineData: LineList, idx: number) {
const symbolType = lineData.getItemVisual(idx, name);
if (!symbolType || symbolType === 'none') {
return;
}

const symbolSize = lineData.getItemVisual(idx, name + 'Size' as 'fromSymbolSize' | 'toSymbolSize');
const symbolRotate = lineData.getItemVisual(idx, name + 'Rotate' as 'fromSymbolRotate' | 'toSymbolRotate');
const symbolOffset = lineData.getItemVisual(idx, name + 'Offset' as 'fromSymbolOffset' | 'toSymbolOffset');
const symbolKeepAspect = lineData.getItemVisual(idx,
name + 'KeepAspect' as 'fromSymbolKeepAspect' | 'toSymbolKeepAspect');

const symbolSizeArr = symbolUtil.normalizeSymbolSize(symbolSize);

const symbolOffsetArr = symbolUtil.normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr);

const symbolPath = symbolUtil.createSymbol(
symbolType,
-symbolSizeArr[0] / 2 + (symbolOffsetArr as number[])[0],
-symbolSizeArr[1] / 2 + (symbolOffsetArr as number[])[1],
symbolSizeArr[0],
symbolSizeArr[1],
null,
symbolKeepAspect
);

(symbolPath as LineECSymbol).__specifiedRotation = symbolRotate == null || isNaN(symbolRotate)
? void 0
: +symbolRotate * Math.PI / 180 || 0;

symbolPath.name = name;

return symbolPath;
}

function createLine(points: number[][]) {
const line = new ECLinePath({
name: 'line',
Expand Down Expand Up @@ -262,33 +202,7 @@ class Line extends graphic.Group {
line.ensureState('blur').style = blurLineStyle;
line.ensureState('select').style = selectLineStyle;

// Update symbol
each(SYMBOL_CATEGORIES, function (symbolCategory) {
const symbol = this.childOfName(symbolCategory) as ECSymbol;
if (symbol) {
// Share opacity and color with line.
symbol.setColor(visualColor);
symbol.style.opacity = lineStyle.opacity;

for (let i = 0; i < SPECIAL_STATES.length; i++) {
const stateName = SPECIAL_STATES[i];
const lineState = line.getState(stateName);
if (lineState) {
const lineStateStyle = lineState.style || {};
const state = symbol.ensureState(stateName);
const stateStyle = state.style || (state.style = {});
if (lineStateStyle.stroke != null) {
stateStyle[symbol.__isEmptyBrush ? 'stroke' : 'fill'] = lineStateStyle.stroke;
}
if (lineStateStyle.opacity != null) {
stateStyle.opacity = lineStateStyle.opacity;
}
}
}

symbol.markRedraw();
}
}, this);
updateSymbol.bind(this)(lineStyle, line);

const rawVal = seriesModel.getRawValue(idx) as number;
setLabelStyle(this, labelStatesModels, {
Expand Down
2 changes: 1 addition & 1 deletion src/chart/helper/LineDraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ function isPointNaN(pt: number[]) {
}

function lineNeedsDraw(pts: number[][]) {
return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
return pts && pts.length > 1 && !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
}


Expand Down
129 changes: 125 additions & 4 deletions src/chart/helper/Polyline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,61 @@
* under the License.
*/

import { each } from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import { toggleHoverEmphasis } from '../../util/states';
import type { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
import type SeriesData from '../../data/SeriesData';
import { BlurScope, DefaultEmphasisFocus } from '../../util/types';
import SeriesModel from '../../model/Series';
import { LineDataVisual } from '../../visual/commonVisualTypes';
import { ECSymbol } from '../../util/symbol';
import * as vector from 'zrender/src/core/vector';
import {
SYMBOL_CATEGORIES,
LineECSymbol,
LineList,
createSymbol,
makeSymbolTypeKey,
makeSymbolTypeValue,
updateSymbol
} from './lineSymbolHelper';

class Polyline extends graphic.Group {

private _fromSymbolType: string;
private _toSymbolType: string;

constructor(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
super();
this._createPolyline(lineData, idx, seriesScope);
this._createPolyline(lineData as SeriesData<SeriesModel, LineDataVisual>, idx, seriesScope);
}

private _createPolyline(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
private _createPolyline(
lineData: SeriesData<SeriesModel, LineDataVisual>,
idx: number,
seriesScope: LineDrawSeriesScope) {
// let seriesModel = lineData.hostModel;
const points = lineData.getItemLayout(idx);

const line = new graphic.Polyline({
name: 'polyline',
shape: {
points: points
}
});

this.add(line);

each(SYMBOL_CATEGORIES, function (symbolCategory) {
const symbol = createSymbol(symbolCategory, lineData, idx);
// Symbols must added after line to make sure
// it will be updated after line#update.
// Or symbol position and rotation update in line#beforeUpdate will be one frame slow
this.add(symbol);
this[makeSymbolTypeKey(symbolCategory)] = makeSymbolTypeValue(symbolCategory, lineData, idx);
}, this);

this._updateCommonStl(lineData, idx, seriesScope);
};

Expand All @@ -55,11 +86,23 @@ class Polyline extends graphic.Group {
};
graphic.updateProps(line, target, seriesModel, idx);

each(SYMBOL_CATEGORIES, function (symbolCategory) {
const symbolType = makeSymbolTypeValue(symbolCategory, lineData as LineList, idx);
const key = makeSymbolTypeKey(symbolCategory);
// Symbol changed
if (this[key] !== symbolType) {
this.remove(this.childOfName(symbolCategory));
const symbol = createSymbol(symbolCategory, lineData as LineList, idx);
this.add(symbol);
}
this[key] = symbolType;
}, this);

this._updateCommonStl(lineData, idx, seriesScope);
};

_updateCommonStl(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
const line = this.childAt(0) as graphic.Polyline;
const line = this.childOfName('polyline') as graphic.Polyline;
const itemModel = lineData.getItemModel<LineDrawModelOption>(idx);


Expand All @@ -76,16 +119,94 @@ class Polyline extends graphic.Group {
focus = emphasisModel.get('focus');
blurScope = emphasisModel.get('blurScope');
}
line.useStyle(lineData.getItemVisual(idx, 'style'));
const lineStyle = lineData.getItemVisual(idx, 'style');
line.useStyle(lineStyle);
line.style.fill = null;
line.style.strokeNoScale = true;

const lineEmphasisState = line.ensureState('emphasis');
lineEmphasisState.style = emphasisLineStyle;

updateSymbol.bind(this)(lineStyle, line);

toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);
};

beforeUpdate() {
const lineGroup = this;
const symbolFrom = lineGroup.childOfName('fromSymbol') as ECSymbol;
const symbolTo = lineGroup.childOfName('toSymbol') as ECSymbol;

if (!symbolFrom && !symbolTo) {
return;
}

let invScale = 1;
let parentNode = this.parent;
while (parentNode) {
if (parentNode.scaleX) {
invScale /= parentNode.scaleX;
}
parentNode = parentNode.parent;
}

const polyline = lineGroup.childOfName('polyline') as graphic.Polyline;
// If line wasn't changed
if (!this.__dirty && !polyline.__dirty) {
return;
}

const points = polyline.shape.points;
function setSymbolRotation(symbol: ECSymbol, percent: 0 | 1) {
const specifiedRotation = (symbol as LineECSymbol).__specifiedRotation;
if (specifiedRotation == null) {
const d = points.length;
if (d < 2) {
return;
}

let tangent = [0, 0];
let p1: vector.VectorArray;
let p2: vector.VectorArray;

if (percent === 0) {
p1 = points[0];
p2 = points[1];
}
else {
p1 = points[d - 2];
p2 = points[d - 1];
}

const p = [ p2[0] - p1[0], p2[1] - p1[1] ];
tangent = vector.normalize(p, p);
symbol.attr('rotation', (percent === 1 ? -1 : 1) * Math.PI / 2 - Math.atan2(
tangent[1], tangent[0]
));
}
else {
symbol.attr('rotation', specifiedRotation);
}
}

const percent = polyline.shape.percent;
const fromPos = points[0];
if (symbolFrom) {
symbolFrom.setPosition(fromPos);
setSymbolRotation(symbolFrom, 0);
symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent;
symbolFrom.markRedraw();
}

const toPos = points[polyline.shape.points.length - 1];
if (symbolTo) {
symbolTo.setPosition(toPos);
setSymbolRotation(symbolTo, 1);
symbolTo.scaleX = symbolTo.scaleY = invScale * percent;
symbolTo.markRedraw();
}
}

updateLayout(lineData: SeriesData, idx: number) {
const polyline = this.childAt(0) as graphic.Polyline;
polyline.setShape('points', lineData.getItemLayout(idx));
Expand Down
Loading