Skip to content

Commit

Permalink
Merge pull request #12590 from wf123537200/feat-6811
Browse files Browse the repository at this point in the history
feat(graph): graph support multiple edges, for #6811
  • Loading branch information
100pah authored Jun 29, 2020
2 parents 1f31de3 + fd25c1a commit baae12e
Show file tree
Hide file tree
Showing 8 changed files with 628 additions and 19 deletions.
13 changes: 10 additions & 3 deletions src/chart/graph/GraphSeries.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import Model from '../../model/Model';
import {encodeHTML} from '../../util/format';
import createGraphFromNodeEdge from '../helper/createGraphFromNodeEdge';
import LegendVisualProvider from '../../visual/LegendVisualProvider';
import {initCurvenessList} from '../helper/multipleGraphEdgeHelper';
import {createEdgeMapForCurveness} from '../../chart/helper/multipleGraphEdgeHelper';

var GraphSeries = echarts.extendSeriesModel({

Expand Down Expand Up @@ -66,7 +68,13 @@ var GraphSeries = echarts.extendSeriesModel({
var self = this;

if (nodes && edges) {
return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data;
// auto curveness
initCurvenessList(this);
var graph = createGraphFromNodeEdge(nodes, edges, this, true, beforeLink);
zrUtil.each(graph.edges, function (edge) {
createEdgeMapForCurveness(edge.node1, edge.node2, this, edge.dataIndex);
}, this);
return graph.data;
}

function beforeLink(nodeData, edgeData) {
Expand Down Expand Up @@ -278,7 +286,6 @@ var GraphSeries = echarts.extendSeriesModel({
lineStyle: {
color: '#aaa',
width: 1,
curveness: 0,
opacity: 0.5
},
emphasis: {
Expand All @@ -289,4 +296,4 @@ var GraphSeries = echarts.extendSeriesModel({
}
});

export default GraphSeries;
export default GraphSeries;
10 changes: 8 additions & 2 deletions src/chart/graph/circularLayoutHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import * as vec2 from 'zrender/src/core/vector';
import {getSymbolSize, getNodeGlobalScale} from './graphHelper';
import * as zrUtil from 'zrender/src/core/util';
import {getCurvenessForEdge} from '../helper/multipleGraphEdgeHelper';

var PI = Math.PI;

Expand Down Expand Up @@ -73,8 +75,12 @@ export function circularLayout(seriesModel, basedOn) {

_layoutNodesBasedOn[basedOn](seriesModel, coordSys, graph, nodeData, r, cx, cy, count);

graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.curveness') || 0;
graph.eachEdge(function (edge, index) {
var curveness = zrUtil.retrieve3(
edge.getModel().get('lineStyle.curveness'),
getCurvenessForEdge(edge, seriesModel, index),
0
);
var p1 = vec2.clone(edge.node1.getLayout());
var p2 = vec2.clone(edge.node2.getLayout());
var cp1;
Expand Down
8 changes: 7 additions & 1 deletion src/chart/graph/forceLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {circularLayout} from './circularLayoutHelper';
import {linearMap} from '../../util/number';
import * as vec2 from 'zrender/src/core/vector';
import * as zrUtil from 'zrender/src/core/util';
import {getCurvenessForEdge} from '../helper/multipleGraphEdgeHelper';

export default function (ecModel) {
ecModel.eachSeriesByType('graph', function (graphSeries) {
Expand Down Expand Up @@ -84,11 +85,16 @@ export default function (ecModel) {
d = (edgeLength[0] + edgeLength[1]) / 2;
}
var edgeModel = edge.getModel();
var curveness = zrUtil.retrieve3(
edge.getModel().get('lineStyle.curveness'),
-getCurvenessForEdge(edge, graphSeries, idx, true),
0
);
return {
n1: nodes[edge.node1.dataIndex],
n2: nodes[edge.node2.dataIndex],
d: d,
curveness: edgeModel.get('lineStyle.curveness') || 0,
curveness: curveness,
ignoreForceLayout: edgeModel.get('ignoreForceLayout')
};
});
Expand Down
17 changes: 12 additions & 5 deletions src/chart/graph/simpleLayoutHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/

import * as vec2 from 'zrender/src/core/vector';
import * as zrUtil from 'zrender/src/core/util';
import {getCurvenessForEdge} from '../helper/multipleGraphEdgeHelper';


export function simpleLayout(seriesModel) {
var coordSys = seriesModel.coordinateSystem;
Expand All @@ -31,12 +34,16 @@ export function simpleLayout(seriesModel) {
node.setLayout([+model.get('x'), +model.get('y')]);
});

simpleLayoutEdge(graph);
simpleLayoutEdge(graph, seriesModel);
}

export function simpleLayoutEdge(graph) {
graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.curveness') || 0;
export function simpleLayoutEdge(graph, seriesModel) {
graph.eachEdge(function (edge, index) {
var curveness = zrUtil.retrieve3(
edge.getModel().get('lineStyle.curveness'),
-getCurvenessForEdge(edge, seriesModel, index, true),
0
);
var p1 = vec2.clone(edge.node1.getLayout());
var p2 = vec2.clone(edge.node2.getLayout());
var points = [p1, p2];
Expand All @@ -48,4 +55,4 @@ export function simpleLayoutEdge(graph) {
}
edge.setLayout(points);
});
}
}
5 changes: 3 additions & 2 deletions src/chart/helper/createGraphFromNodeEdge.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ export default function (nodes, edges, seriesModel, directed, beforeLink) {
var linkNameList = [];
var validEdges = [];
var linkCount = 0;

for (var i = 0; i < edges.length; i++) {
var link = edges[i];
var source = link.source;
var target = link.target;

// addEdge may fail when source or target not exists
if (graph.addEdge(source, target, linkCount)) {
if (graph.addEdge(source, target, linkCount, seriesModel)) {
validEdges.push(link);
linkNameList.push(zrUtil.retrieve(link.id, source + ' > ' + target));
linkCount++;
Expand Down Expand Up @@ -89,6 +91,5 @@ export default function (nodes, edges, seriesModel, directed, beforeLink) {

// Update dataIndex of nodes and edges because invalid edge may be removed
graph.update();

return graph;
}
229 changes: 229 additions & 0 deletions src/chart/helper/multipleGraphEdgeHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* 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';

var KEY_DELIMITER = '-->';
/**
* params handler
* @param {module:echarts/model/SeriesModel} seriesModel
* @returns {*}
*/
var getAutoCurvenessParams = function (seriesModel) {
return seriesModel.get('autoCurveness') || null;
};

/**
* Generate a list of edge curvatures, 20 is the default
* @param {module:echarts/model/SeriesModel} seriesModel
* @param {number} appendLength
* @return 20 => [0, -0.2, 0.2, -0.4, 0.4, -0.6, 0.6, -0.8, 0.8, -1, 1, -1.2, 1.2, -1.4, 1.4, -1.6, 1.6, -1.8, 1.8, -2]
*/
var createCurveness = function (seriesModel, appendLength) {
var autoCurvenessParmas = getAutoCurvenessParams(seriesModel);
var length = 20;
var curvenessList = [];

// handler the function set
if (typeof autoCurvenessParmas === 'number') {
length = autoCurvenessParmas;
}
else if (zrUtil.isArray(autoCurvenessParmas)) {
seriesModel.__curvenessList = autoCurvenessParmas;
return;
}

// append length
if (appendLength > length) {
length = appendLength;
}

// make sure the length is even
var len = length % 2 ? length + 2 : length + 3;
curvenessList = [];

for (var i = 0; i < len; i++) {
curvenessList.push((i % 2 ? i + 1 : i) / 10 * (i % 2 ? -1 : 1));
}
seriesModel.__curvenessList = curvenessList;
};

/**
* Create different cache key data in the positive and negative directions, in order to set the curvature later
* @param {number|string|module:echarts/data/Graph.Node} n1
* @param {number|string|module:echarts/data/Graph.Node} n2
* @param {module:echarts/model/SeriesModel} seriesModel
* @returns {string} key
*/
var getKeyOfEdges = function (n1, n2, seriesModel) {
var source = [n1.id, n1.dataIndex].join('.');
var target = [n2.id, n2.dataIndex].join('.');
return [seriesModel.uid, source, target].join(KEY_DELIMITER);
};

/**
* get opposite key
* @param {string} key
* @returns {string}
*/
var getOppositeKey = function (key) {
var keys = key.split(KEY_DELIMITER);
return [keys[0], keys[2], keys[1]].join(KEY_DELIMITER);
};

/**
* get edgeMap with key
* @param edge
* @param {module:echarts/model/SeriesModel} seriesModel
*/
var getEdgeFromMap = function (edge, seriesModel) {
var key = getKeyOfEdges(edge.node1, edge.node2, seriesModel);
return seriesModel.__edgeMap[key];
};

/**
* calculate all cases total length
* @param edge
* @param seriesModel
* @returns {number}
*/
var getTotalLengthBetweenNodes = function (edge, seriesModel) {
var len = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node1, edge.node2, seriesModel), seriesModel);
var lenV = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node2, edge.node1, seriesModel), seriesModel);

return len + lenV;
};

/**
*
* @param key
*/
var getEdgeMapLengthWithKey = function (key, seriesModel) {
var edgeMap = seriesModel.__edgeMap;
return edgeMap[key] ? edgeMap[key].length : 0;
};

/**
* Count the number of edges between the same two points, used to obtain the curvature table and the parity of the edge
* @see /graph/GraphSeries.js@getInitialData
* @param {module:echarts/model/SeriesModel} seriesModel
*/
export function initCurvenessList(seriesModel) {
if (!getAutoCurvenessParams(seriesModel)) {
return;
}

seriesModel.__curvenessList = [];
seriesModel.__edgeMap = {};
// calc the array of curveness List
createCurveness(seriesModel);
}

/**
* set edgeMap with key
* @param {number|string|module:echarts/data/Graph.Node} n1
* @param {number|string|module:echarts/data/Graph.Node} n2
* @param {module:echarts/model/SeriesModel} seriesModel
* @param {number} index
*/
export function createEdgeMapForCurveness(n1, n2, seriesModel, index) {
if (!getAutoCurvenessParams(seriesModel)) {
return;
}

var key = getKeyOfEdges(n1, n2, seriesModel);
var edgeMap = seriesModel.__edgeMap;
var oppositeEdges = edgeMap[getOppositeKey(key)];
// set direction
if (edgeMap[key] && !oppositeEdges) {
edgeMap[key].isForward = true;
}
else if (oppositeEdges && edgeMap[key]) {
oppositeEdges.isForward = true;
edgeMap[key].isForward = false;
}

edgeMap[key] = edgeMap[key] || [];
edgeMap[key].push(index);
}

/**
* get curvature for edge
* @param edge
* @param {module:echarts/model/SeriesModel} seriesModel
* @param index
*/
export function getCurvenessForEdge(edge, seriesModel, index, needReverse) {
const autoCurvenessParams = getAutoCurvenessParams(seriesModel);
const isArrayParam = zrUtil.isArray(autoCurvenessParams);
if (!autoCurvenessParams) {
return null;
}

var edgeArray = getEdgeFromMap(edge, seriesModel);
if (!edgeArray) {
return null;
}

var edgeIndex = -1;
for (var i = 0; i < edgeArray.length; i++) {
if (edgeArray[i] === index) {
edgeIndex = i;
break;
}
}
// if totalLen is Longer createCurveness
var totalLen = getTotalLengthBetweenNodes(edge, seriesModel);
createCurveness(seriesModel, totalLen);

edge.lineStyle = edge.lineStyle || {};
// if is opposite edge, must set curvenss to opposite number
var curKey = getKeyOfEdges(edge.node1, edge.node2, seriesModel);
var curvenessList = seriesModel.__curvenessList;
// if pass array no need parity
var parityCorrection = isArrayParam ? 0 : totalLen % 2 ? 0 : 1;

if (!edgeArray.isForward) {
// the opposite edge show outside
var oppositeKey = getOppositeKey(curKey);
var len = getEdgeMapLengthWithKey(oppositeKey, seriesModel);
var resValue = curvenessList[edgeIndex + len + parityCorrection];
// isNeedReverse, simple, force type need reverse the curveness in the junction of the forword and the opposite
if (needReverse) {
// set as array may make the parity handle with the len of opposite
if (isArrayParam) {
if (autoCurvenessParams && autoCurvenessParams[0] === 0) {
return (len + parityCorrection) % 2 ? resValue : -resValue;
}
else {
return ((len % 2 ? 0 : 1) + parityCorrection) % 2 ? resValue : -resValue;
}
}
else {
return (len + parityCorrection) % 2 ? resValue : -resValue;
}
}
else {
return curvenessList[edgeIndex + len + parityCorrection];
}
}
else {
return curvenessList[parityCorrection + edgeIndex];
}
}
Loading

0 comments on commit baae12e

Please sign in to comment.