Skip to content

Commit

Permalink
Enhance circular layout: based on symbol size and fix overlap when so…
Browse files Browse the repository at this point in the history
…me value are very smaller than the max value. Fix #10462, Close #10615
  • Loading branch information
100pah committed Jun 17, 2019
1 parent 4d979f8 commit 1f134a3
Show file tree
Hide file tree
Showing 11 changed files with 919 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/chart/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ echarts.registerVisual(categoryVisual);
echarts.registerVisual(edgeVisual);

echarts.registerLayout(simpleLayout);
echarts.registerLayout(circularLayout);
echarts.registerLayout(echarts.PRIORITY.VISUAL.POST_CHART_LAYOUT, circularLayout);
echarts.registerLayout(forceLayout);

// Graph view coordinate system
Expand Down
27 changes: 5 additions & 22 deletions src/chart/graph/GraphView.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import * as roamHelper from '../../component/helper/roamHelper';
import {onIrrelevantElement} from '../../component/helper/cursorHelper';
import * as graphic from '../../util/graphic';
import adjustEdge from './adjustEdge';
import {getNodeGlobalScale} from './graphHelper';

var FOCUS_ADJACENCY = '__focusNodeAdjacency';
var UNFOCUS_ADJACENCY = '__unfocusNodeAdjacency';
Expand Down Expand Up @@ -96,7 +97,6 @@ export default echarts.extendChartView({
var coordSys = seriesModel.coordinateSystem;

this._model = seriesModel;
this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');

var symbolDraw = this._symbolDraw;
var lineDraw = this._lineDraw;
Expand All @@ -116,7 +116,7 @@ export default echarts.extendChartView({
}
}
// Fix edge contact point with node
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));

var data = seriesModel.getData();
symbolDraw.updateData(data);
Expand Down Expand Up @@ -356,7 +356,7 @@ export default echarts.extendChartView({
originY: e.originY
});
this._updateNodeAndLinkScale();
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));
this._lineDraw.updateLayout();
}, this);
},
Expand All @@ -365,33 +365,16 @@ export default echarts.extendChartView({
var seriesModel = this._model;
var data = seriesModel.getData();

var nodeScale = this._getNodeGlobalScale(seriesModel);
var nodeScale = getNodeGlobalScale(seriesModel);
var invScale = [nodeScale, nodeScale];

data.eachItemGraphicEl(function (el, idx) {
el.attr('scale', invScale);
});
},

_getNodeGlobalScale: function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys.type !== 'view') {
return 1;
}

var nodeScaleRatio = this._nodeScaleRatio;

var groupScale = coordSys.scale;
var groupZoom = (groupScale && groupScale[0]) || 1;
// Scale node when zoom changes
var roamZoom = coordSys.getZoom();
var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;

return nodeScale / groupZoom;
},

updateLayout: function (seriesModel) {
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));

this._symbolDraw.updateLayout();
this._lineDraw.updateLayout();
Expand Down
8 changes: 1 addition & 7 deletions src/chart/graph/adjustEdge.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import * as curveTool from 'zrender/src/core/curve';
import * as vec2 from 'zrender/src/core/vector';
import {getSymbolSize} from './graphHelper';

var v1 = [];
var v2 = [];
Expand Down Expand Up @@ -97,13 +98,6 @@ export default function (graph, scale) {
var v = [];
scale /= 2;

function getSymbolSize(node) {
var symbolSize = node.getVisual('symbolSize');
if (symbolSize instanceof Array) {
symbolSize = (symbolSize[0] + symbolSize[1]) / 2;
}
return symbolSize;
}
graph.eachEdge(function (edge, idx) {
var linePoints = edge.getLayout();
var fromSymbol = edge.getVisual('fromSymbol');
Expand Down
2 changes: 1 addition & 1 deletion src/chart/graph/circularLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {circularLayout} from './circularLayoutHelper';
export default function (ecModel) {
ecModel.eachSeriesByType('graph', function (seriesModel) {
if (seriesModel.get('layout') === 'circular') {
circularLayout(seriesModel);
circularLayout(seriesModel, 'symbolSize');
}
});
}
115 changes: 95 additions & 20 deletions src/chart/graph/circularLayoutHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,35 @@
*/

import * as vec2 from 'zrender/src/core/vector';
import {getSymbolSize, getNodeGlobalScale} from './graphHelper';

export function circularLayout(seriesModel) {
var PI = Math.PI;

var _symbolRadiansHalf = [];

/**
* `basedOn` can be:
* 'value':
* This layout is not accurate and have same bad case. For example,
* if the min value is very smaller than the max value, the nodes
* with the min value probably overlap even though there is enough
* space to layout them. So we only use this approach in the as the
* init layout of the force layout.
* FIXME
* Probably we do not need this method any more but use
* `basedOn: 'symbolSize'` in force layout if
* delay its init operations to GraphView.
* 'symbolSize':
* This approach work only if all of the symbol size calculated.
* That is, the progressive rendering is not applied to graph.
* FIXME
* If progressive rendering is applied to graph some day,
* probably we have to use `basedOn: 'value'`.
*
* @param {module:echarts/src/model/Series} seriesModel
* @param {string} basedOn 'value' or 'symbolSize'
*/
export function circularLayout(seriesModel, basedOn) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.type !== 'view') {
return;
Expand All @@ -30,33 +57,22 @@ export function circularLayout(seriesModel) {
var nodeData = seriesModel.getData();
var graph = nodeData.graph;

var angle = 0;
var sum = nodeData.getSum('value');
var unitAngle = Math.PI * 2 / (sum || nodeData.count());

var cx = rect.width / 2 + rect.x;
var cy = rect.height / 2 + rect.y;

var r = Math.min(rect.width, rect.height) / 2;

graph.eachNode(function (node) {
var value = node.getValue('value');

angle += unitAngle * (sum ? value : 1) / 2;

node.setLayout([
r * Math.cos(angle) + cx,
r * Math.sin(angle) + cy
]);

angle += unitAngle * (sum ? value : 1) / 2;
});
var count = nodeData.count();

nodeData.setLayout({
cx: cx,
cy: cy
});

if (!count) {
return;
}

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

graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.curveness') || 0;
var p1 = vec2.clone(edge.node1.getLayout());
Expand All @@ -73,4 +89,63 @@ export function circularLayout(seriesModel) {
}
edge.setLayout([p1, p2, cp1]);
});
}
}

var _layoutNodesBasedOn = {

value: function (seriesModel, coordSys, graph, nodeData, r, cx, cy, count) {
var angle = 0;
var sum = nodeData.getSum('value');
var unitAngle = Math.PI * 2 / (sum || count);

graph.eachNode(function (node) {
var value = node.getValue('value');
var radianHalf = unitAngle * (sum ? value : 1) / 2;

angle += radianHalf;
node.setLayout([
r * Math.cos(angle) + cx,
r * Math.sin(angle) + cy
]);
angle += radianHalf;
});
},

symbolSize: function (seriesModel, coordSys, graph, nodeData, r, cx, cy, count) {
var sumRadian = 0;
_symbolRadiansHalf.length = count;

var nodeScale = getNodeGlobalScale(seriesModel);

graph.eachNode(function (node) {
var symbolSize = getSymbolSize(node);

// Normally this case will not happen, but we still add
// some the defensive code (2px is an arbitrary value).
isNaN(symbolSize) && (symbolSize = 2);
symbolSize < 0 && (symbolSize = 0);

symbolSize *= nodeScale;

var symbolRadianHalf = Math.asin(symbolSize / 2 / r);
// when `symbolSize / 2` is bigger than `r`.
isNaN(symbolRadianHalf) && (symbolRadianHalf = PI / 2);
_symbolRadiansHalf[node.dataIndex] = symbolRadianHalf;
sumRadian += symbolRadianHalf * 2;
});

var halfRemainRadian = (2 * PI - sumRadian) / count / 2;

var angle = 0;
graph.eachNode(function (node) {
var radianHalf = halfRemainRadian + _symbolRadiansHalf[node.dataIndex];

angle += radianHalf;
node.setLayout([
r * Math.cos(angle) + cx,
r * Math.sin(angle) + cy
]);
angle += radianHalf;
});
}
};
2 changes: 1 addition & 1 deletion src/chart/graph/forceLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function (ecModel) {
simpleLayout(graphSeries);
}
else if (initLayout === 'circular') {
circularLayout(graphSeries);
circularLayout(graphSeries, 'value');
}

var nodeDataExtent = nodeData.getDataExtent('value');
Expand Down
44 changes: 44 additions & 0 deletions src/chart/graph/graphHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.
*/

export function getNodeGlobalScale(seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys.type !== 'view') {
return 1;
}

var nodeScaleRatio = seriesModel.option.nodeScaleRatio;

var groupScale = coordSys.scale;
var groupZoom = (groupScale && groupScale[0]) || 1;
// Scale node when zoom changes
var roamZoom = coordSys.getZoom();
var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;

return nodeScale / groupZoom;
}

export function getSymbolSize(node) {
var symbolSize = node.getVisual('symbolSize');
if (symbolSize instanceof Array) {
symbolSize = (symbolSize[0] + symbolSize[1]) / 2;
}
return +symbolSize;
}

2 changes: 2 additions & 0 deletions src/echarts.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ var PRIORITY_VISUAL_LAYOUT = 1000;
var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;
var PRIORITY_VISUAL_GLOBAL = 2000;
var PRIORITY_VISUAL_CHART = 3000;
var PRIORITY_VISUAL_POST_CHART_LAYOUT = 3500;
var PRIORITY_VISUAL_COMPONENT = 4000;
// FIXME
// necessary?
Expand All @@ -81,6 +82,7 @@ export var PRIORITY = {
PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,
GLOBAL: PRIORITY_VISUAL_GLOBAL,
CHART: PRIORITY_VISUAL_CHART,
POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,
COMPONENT: PRIORITY_VISUAL_COMPONENT,
BRUSH: PRIORITY_VISUAL_BRUSH
}
Expand Down
2 changes: 1 addition & 1 deletion test/force.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
width: '25%',
height: '25%',
force: {
// initLayout: 'circular'
// initLayout: 'circular',
// gravity: 0
repulsion: 100,
edgeLength: 5
Expand Down
2 changes: 1 addition & 1 deletion test/force3.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
animation: false,
data: data,
force: {
// initLayout: 'circular'
// initLayout: 'circular',
// gravity: 0
repulsion: 100,
edgeLength: 5
Expand Down
Loading

0 comments on commit 1f134a3

Please sign in to comment.