mirror of
https://gitee.com/antv/g6.git
synced 2024-12-05 21:28:33 +08:00
test: tool.mapper-spec
This commit is contained in:
commit
0bea2f6f29
@ -58,7 +58,7 @@
|
|||||||
graph = new G6.Graph({
|
graph = new G6.Graph({
|
||||||
id: 'mountNode', // dom id
|
id: 'mountNode', // dom id
|
||||||
fitView: 'autoZoom',
|
fitView: 'autoZoom',
|
||||||
plugins: [new Plugin({max_iteration: 1000, kg: 2, kr: 40, prev_overlapping: true}),
|
plugins: [new Plugin({maxIteration: 1000, kg: 2, kr: 40, prevOverlapping: true}),
|
||||||
new FisheyePlugin({radius: 200}),
|
new FisheyePlugin({radius: 200}),
|
||||||
nodeSizeMapper, nodeColorMapper
|
nodeSizeMapper, nodeColorMapper
|
||||||
],
|
],
|
||||||
|
@ -25,11 +25,11 @@
|
|||||||
const Mapper = G6.Plugins['tool.mapper'];
|
const Mapper = G6.Plugins['tool.mapper'];
|
||||||
const MaxSpanningForestPlugin = G6.Plugins['template.maxSpanningForest'];
|
const MaxSpanningForestPlugin = G6.Plugins['template.maxSpanningForest'];
|
||||||
//the instances of plugins
|
//the instances of plugins
|
||||||
const maxSpanningForest = new MaxSpanningForestPlugin({
|
let maxSpanningForest = new MaxSpanningForestPlugin({
|
||||||
layoutCfg: {
|
layoutCfg: {
|
||||||
max_iteration: 600,
|
maxIteration: 600,
|
||||||
kg: 10,
|
kg: 10,
|
||||||
prev_overlapping: true,
|
prevOverlapping: true,
|
||||||
onLayoutComplete: function () {
|
onLayoutComplete: function () {
|
||||||
const minimap = document.getElementById('minimap');
|
const minimap = document.getElementById('minimap');
|
||||||
const legend = document.getElementById('legend');
|
const legend = document.getElementById('legend');
|
||||||
@ -38,24 +38,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const nodeSizeMapper = new Mapper('node', 'userview', 'size', [20, 50], {
|
let nodeSizeMapper = new Mapper('node', 'userview', 'size', [20, 50], {
|
||||||
legendCfg: {
|
legendCfg: {
|
||||||
containerId: 'legend',
|
containerId: 'legend',
|
||||||
title: 'UV',
|
title: 'UV',
|
||||||
layout: 'horizontal'
|
layout: 'horizontal',
|
||||||
|
// formatter: function(num) {
|
||||||
|
// console.log(num);
|
||||||
|
// num = num * num;
|
||||||
|
// if (num >= 100000000) {
|
||||||
|
// return num / 100000000 + '亿';
|
||||||
|
// } if (num >= 10000) {
|
||||||
|
// return num / 10000 + '万';
|
||||||
|
// }
|
||||||
|
// return num;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const edgeSizeMapper = new Mapper('edge', 'userview', 'size', [1, 16], {
|
let edgeSizeMapper = new Mapper('edge', 'userview', 'size', [1, 16], {
|
||||||
legendCfg: null
|
legendCfg: null
|
||||||
});
|
});
|
||||||
const nodeColorMapper = new Mapper('node', 'stayTime', 'color', ['#BAE7FF', '#1890FF', '#0050B3'], {
|
let nodeColorMapper = new Mapper('node', 'stayTime', 'color', ['#BAE7FF', '#1890FF', '#0050B3'], {
|
||||||
legendCfg: {
|
legendCfg: {
|
||||||
containerId: 'legend',
|
containerId: 'legend',
|
||||||
title: 'Stay Time',
|
title: 'Stay Time',
|
||||||
layout: 'horizontal'
|
layout: 'horizontal'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const minimapPlugin = new G6.Plugins['tool.minimap']({
|
let minimapPlugin = new G6.Plugins['tool.minimap']({
|
||||||
container: 'minimap',
|
container: 'minimap',
|
||||||
width: 180,
|
width: 180,
|
||||||
height: 120
|
height: 120
|
||||||
@ -67,14 +77,14 @@
|
|||||||
id: 'mountNode', // dom id
|
id: 'mountNode', // dom id
|
||||||
fitView: 'autoZoom',
|
fitView: 'autoZoom',
|
||||||
plugins: [
|
plugins: [
|
||||||
maxSpanningForest, nodeSizeMapper, nodeColorMapper, edgeSizeMapper, minimapPlugin
|
maxSpanningForest, nodeColorMapper, minimapPlugin, edgeSizeMapper, nodeSizeMapper,
|
||||||
],
|
],
|
||||||
modes: {
|
modes: {
|
||||||
default: ['panCanvas', 'wheelZoom']
|
default: ['panCanvas', 'wheelZoom']
|
||||||
},
|
},
|
||||||
height: 500,
|
height: 500,
|
||||||
});
|
});
|
||||||
graph.read(data);
|
graph.read(Util.cloneDeep(data));
|
||||||
const minimap = document.getElementById('minimap');
|
const minimap = document.getElementById('minimap');
|
||||||
const legend = document.getElementById('legend');
|
const legend = document.getElementById('legend');
|
||||||
if (minimap !== undefined) minimap.style.display = 'none';
|
if (minimap !== undefined) minimap.style.display = 'none';
|
||||||
|
@ -32,13 +32,13 @@
|
|||||||
nodes.forEach((node, index) => {
|
nodes.forEach((node, index) => {
|
||||||
if (parseInt(index / col) % 2 === 0) {
|
if (parseInt(index / col) % 2 === 0) {
|
||||||
node.x = (col - index % col) * hgap;
|
node.x = (col - index % col) * hgap;
|
||||||
node.ori_x = node.x;
|
node.oriX = node.x;
|
||||||
} else {
|
} else {
|
||||||
node.x = index % col * hgap + hgap;
|
node.x = index % col * hgap + hgap;
|
||||||
node.ori_x = node.x;
|
node.oriX = node.x;
|
||||||
}
|
}
|
||||||
node.y = parseInt(index / col) * vgap + vgap / 2;
|
node.y = parseInt(index / col) * vgap + vgap / 2;
|
||||||
node.ori_y = node.y;
|
node.oriY = node.y;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -15,19 +15,19 @@
|
|||||||
<script>
|
<script>
|
||||||
$.getJSON('../../test/fixtures/viralMarketing.json', data => {
|
$.getJSON('../../test/fixtures/viralMarketing.json', data => {
|
||||||
const Plugin = G6.Plugins['layout.forceAtlas2'];
|
const Plugin = G6.Plugins['layout.forceAtlas2'];
|
||||||
const layout_params = {
|
const layoutParams = {
|
||||||
max_iteration: 1500,
|
maxIteration: 1500,
|
||||||
prev_overlapping: true,
|
prevOverlapping: true,
|
||||||
kr: 15,
|
kr: 15,
|
||||||
mode: 'normal',
|
mode: 'normal',
|
||||||
barnes_hut: false, // may be counter-productive on small networks
|
barnesHut: false, // may be counter-productive on small networks
|
||||||
ks: 0.1,
|
ks: 0.1,
|
||||||
dissuade_hubs: false
|
dissuadeHubs: false
|
||||||
};
|
};
|
||||||
graph = new G6.Graph({
|
graph = new G6.Graph({
|
||||||
id: 'mountNode', // dom id
|
id: 'mountNode', // dom id
|
||||||
fitView: 'autoZoom',
|
fitView: 'autoZoom',
|
||||||
plugins: [new Plugin(layout_params)],
|
plugins: [new Plugin(layoutParams)],
|
||||||
minZoom: 0,
|
minZoom: 0,
|
||||||
modes: {
|
modes: {
|
||||||
default: ['panCanvas']
|
default: ['panCanvas']
|
||||||
|
@ -54,11 +54,11 @@
|
|||||||
graph.read(data);
|
graph.read(data);
|
||||||
const nodes = graph.getNodes();
|
const nodes = graph.getNodes();
|
||||||
const edges = graph.getEdges();
|
const edges = graph.getEdges();
|
||||||
const re_nodes = [nodes[0], nodes[1]];
|
const reNodes = [nodes[0], nodes[1]];
|
||||||
const re_edges = [edges[0]];
|
const reEdges = [edges[0]];
|
||||||
graph.highlightSubgraph({
|
graph.highlightSubgraph({
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
scale: 0.5
|
scale: 0.5
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const nodeSizeMapper = new Mapper('node', 'weight', 'size', [20, 40]);
|
||||||
const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [2, 20], {
|
const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [2, 20], {
|
||||||
legendCfg: null
|
legendCfg: null
|
||||||
});
|
});
|
||||||
@ -28,21 +29,25 @@
|
|||||||
id: 'node1',
|
id: 'node1',
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 200,
|
y: 200,
|
||||||
|
weight: 10,
|
||||||
class: 'class_1'
|
class: 'class_1'
|
||||||
}, {
|
}, {
|
||||||
id: 'node2',
|
id: 'node2',
|
||||||
x: 300,
|
x: 300,
|
||||||
y: 200,
|
y: 200,
|
||||||
|
weight: 2,
|
||||||
class: 'class_1'
|
class: 'class_1'
|
||||||
}, {
|
}, {
|
||||||
id: 'node3',
|
id: 'node3',
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 100,
|
y: 100,
|
||||||
|
weight: 15,
|
||||||
class: 'class_2'
|
class: 'class_2'
|
||||||
}, {
|
}, {
|
||||||
id: 'node4',
|
id: 'node4',
|
||||||
x: 300,
|
x: 300,
|
||||||
y: 100,
|
y: 100,
|
||||||
|
weight: 5,
|
||||||
class: 'class_2'
|
class: 'class_2'
|
||||||
}],
|
}],
|
||||||
edges: [{
|
edges: [{
|
||||||
@ -61,7 +66,7 @@
|
|||||||
};
|
};
|
||||||
const graph = new G6.Graph({
|
const graph = new G6.Graph({
|
||||||
id: 'mountNode', // dom id
|
id: 'mountNode', // dom id
|
||||||
plugins: [nodeColorMapper, edgeSizeMapper],
|
plugins: [nodeColorMapper, edgeSizeMapper, nodeSizeMapper],
|
||||||
height: 1000,
|
height: 1000,
|
||||||
});
|
});
|
||||||
graph.read(data);
|
graph.read(data);
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<title>快速上手-网图-SVG </title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="mountNode"></div>
|
|
||||||
<script src="../build/g6.js"></script>
|
|
||||||
<script>
|
|
||||||
const data = {
|
|
||||||
nodes: [{
|
|
||||||
id: 'node1',
|
|
||||||
x: 100,
|
|
||||||
y: 200
|
|
||||||
}, {
|
|
||||||
id: 'node2',
|
|
||||||
x: 300,
|
|
||||||
y: 200
|
|
||||||
}],
|
|
||||||
edges: [{
|
|
||||||
target: 'node2',
|
|
||||||
source: 'node1'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
const graph = new G6.Graph({
|
|
||||||
container: 'mountNode',
|
|
||||||
width: 500,
|
|
||||||
height: 500
|
|
||||||
});
|
|
||||||
graph.read(data);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@antv/g6",
|
"name": "@antv/g6",
|
||||||
"version": "2.1.0-beta",
|
"version": "2.1.0-beta.3",
|
||||||
"description": "graph visualization frame work",
|
"description": "graph visualization frame work",
|
||||||
"main": "build/g6.js",
|
"main": "build/g6.js",
|
||||||
"homepage": "https://github.com/antvis/g6",
|
"homepage": "https://github.com/antvis/g6",
|
||||||
@ -97,7 +97,7 @@
|
|||||||
"screenshot": "node ./bin/screenshot.js",
|
"screenshot": "node ./bin/screenshot.js",
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"test": "torch --compile --renderer --recursive ./test/unit",
|
"test": "torch --compile --renderer --recursive ./test/unit",
|
||||||
"test-live": "torch --compile --interactive --watch --recursive ./test/unit/plugins/tool.mapper-spec.js",
|
"test-live": "torch --compile --interactive --watch --recursive ./test/unit/plugins/layout.forceAtlas2-spec.js",
|
||||||
"watch": "webpack --config webpack-dev.config.js",
|
"watch": "webpack --config webpack-dev.config.js",
|
||||||
"win-dev": "node ./bin/win-dev.js"
|
"win-dev": "node ./bin/win-dev.js"
|
||||||
},
|
},
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
/* BlobBuilder.js
|
|
||||||
* A complete BlobBuilder shim
|
|
||||||
* By Eli Grey
|
|
||||||
* License: MIT/X11
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || function(view) {
|
|
||||||
|
|
||||||
let
|
|
||||||
get_class = function(object) {
|
|
||||||
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
|
||||||
},
|
|
||||||
FakeBlobBuilder = view.BlobBuilder = function() {},
|
|
||||||
FakeBlob = view.Blob = function(data, type) {
|
|
||||||
this.data = data;
|
|
||||||
this.size = data.length;
|
|
||||||
this.type = type;
|
|
||||||
},
|
|
||||||
FBB_proto = FakeBlobBuilder.prototype = [],
|
|
||||||
FB_proto = FakeBlob.prototype,
|
|
||||||
FileReaderSync = view.FileReaderSync,
|
|
||||||
FileException = function(type) {
|
|
||||||
this.code = this[this.name = type];
|
|
||||||
},
|
|
||||||
file_ex_codes = (
|
|
||||||
'NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR ' +
|
|
||||||
'NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR'
|
|
||||||
).split(' '),
|
|
||||||
file_ex_code = file_ex_codes.length,
|
|
||||||
URL = view.URL = view.URL || view.webkitURL || view,
|
|
||||||
real_create_object_url,
|
|
||||||
real_revoke_object_url,
|
|
||||||
btoa = view.btoa,
|
|
||||||
ArrayBuffer = view.ArrayBuffer,
|
|
||||||
can_apply_typed_arrays = false,
|
|
||||||
can_apply_typed_arrays_test = function(pass) {
|
|
||||||
can_apply_typed_arrays = !pass;
|
|
||||||
};
|
|
||||||
while (file_ex_code--) {
|
|
||||||
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (ArrayBuffer) {
|
|
||||||
can_apply_typed_arrays_test.apply(0, new Uint8Array(1));
|
|
||||||
}
|
|
||||||
} catch (ex) {}
|
|
||||||
if (!URL.createObjectURL) {
|
|
||||||
URL = {};
|
|
||||||
}
|
|
||||||
real_create_object_url = URL.createObjectURL;
|
|
||||||
real_revoke_object_url = URL.revokeObjectURL;
|
|
||||||
URL.createObjectURL = function(blob) {
|
|
||||||
let type = blob.type;
|
|
||||||
if (type === null) {
|
|
||||||
type = 'application/octet-stream';
|
|
||||||
}
|
|
||||||
if (blob instanceof FakeBlob) {
|
|
||||||
if (btoa) {
|
|
||||||
return 'data:' + type + ';base64,' + btoa(blob.data);
|
|
||||||
}
|
|
||||||
return 'data:' + type + ',' + encodeURIComponent(blob.data);
|
|
||||||
|
|
||||||
} else if (real_create_object_url) {
|
|
||||||
return real_create_object_url.call(URL, blob);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
URL.revokeObjectURL = function(object_url) {
|
|
||||||
if (object_url.substring(0, 5) !== 'data:' && real_revoke_object_url) {
|
|
||||||
real_revoke_object_url.call(URL, object_url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FBB_proto.append = function(data /* , endings*/) {
|
|
||||||
const bb = this;
|
|
||||||
// decode data to a binary string
|
|
||||||
if (ArrayBuffer && data instanceof ArrayBuffer) {
|
|
||||||
if (can_apply_typed_arrays) {
|
|
||||||
bb.push(String.fromCharCode.apply(String, new Uint8Array(data)));
|
|
||||||
} else {
|
|
||||||
let
|
|
||||||
str = '',
|
|
||||||
buf = new Uint8Array(data),
|
|
||||||
i = 0,
|
|
||||||
buf_len = buf.length;
|
|
||||||
for (; i < buf_len; i++) {
|
|
||||||
str += String.fromCharCode(buf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (get_class(data) === 'Blob' || get_class(data) === 'File') {
|
|
||||||
if (FileReaderSync) {
|
|
||||||
const fr = new FileReaderSync();
|
|
||||||
bb.push(fr.readAsBinaryString(data));
|
|
||||||
} else {
|
|
||||||
// async FileReader won't work as BlobBuilder is sync
|
|
||||||
throw new FileException('NOT_READABLE_ERR');
|
|
||||||
}
|
|
||||||
} else if (data instanceof FakeBlob) {
|
|
||||||
bb.push(data.data);
|
|
||||||
} else {
|
|
||||||
if (typeof data !== 'string') {
|
|
||||||
data += ''; // convert unsupported types to strings
|
|
||||||
}
|
|
||||||
// decode UTF-16 to binary string
|
|
||||||
bb.push(unescape(encodeURIComponent(data)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FBB_proto.getBlob = function(type) {
|
|
||||||
if (!arguments.length) {
|
|
||||||
type = null;
|
|
||||||
}
|
|
||||||
return new FakeBlob(this.join(''), type);
|
|
||||||
};
|
|
||||||
FBB_proto.toString = function() {
|
|
||||||
return '[object BlobBuilder]';
|
|
||||||
};
|
|
||||||
FB_proto.slice = function(start, end, type) {
|
|
||||||
const args = arguments.length;
|
|
||||||
if (args < 3) {
|
|
||||||
type = null;
|
|
||||||
}
|
|
||||||
return new FakeBlob(
|
|
||||||
this.data.slice(start, args > 1 ? end : this.data.length), type
|
|
||||||
);
|
|
||||||
};
|
|
||||||
FB_proto.toString = function() {
|
|
||||||
return '[object Blob]';
|
|
||||||
};
|
|
||||||
return FakeBlobBuilder;
|
|
||||||
}(self);
|
|
@ -7,17 +7,17 @@ ForceAtlas2(https://medialab.sciencespo.fr/publications/Jacomy_Heymann_Venturini
|
|||||||
- mode: 'normal'/'linlog'
|
- mode: 'normal'/'linlog'
|
||||||
'normal': normal layout
|
'normal': normal layout
|
||||||
'linlog': the cluster will be more compact
|
'linlog': the cluster will be more compact
|
||||||
- pre_overlapping: true/false.
|
- preOverlapping: true/false.
|
||||||
true: prevents node overlapping, result in non-node-overlapping layout
|
true: prevents node overlapping, result in non-node-overlapping layout
|
||||||
false: allows node overlapping.
|
false: allows node overlapping.
|
||||||
- dissuade_hubs: true/false. whether active the dissuade hub mode
|
- dissuadeHubs: true/false. whether active the dissuade hub mode
|
||||||
true: grant authorities(nodes with a high indegree) a more central position than hubs(nodes with a high outdegree)
|
true: grant authorities(nodes with a high indegree) a more central position than hubs(nodes with a high outdegree)
|
||||||
false: without dissuade hub mode
|
false: without dissuade hub mode
|
||||||
- barnes_hut: true/false. Whether active the barnes hut optimization on computing repulsive foces. We implemented the paper 'A hierarchical O(N log N) force-calculation algorithm strategy'(https://www.nature.com/articles/324446a0).
|
- barnesHut: true/false. Whether active the barnes hut optimization on computing repulsive foces. We implemented the paper 'A hierarchical O(N log N) force-calculation algorithm strategy'(https://www.nature.com/articles/324446a0).
|
||||||
- ks: controls the global velocity. Default: 0.1
|
- ks: controls the global velocity. Default: 0.1
|
||||||
- ksmax: the max global velocity. Default: 10
|
- ksmax: the max global velocity. Default: 10
|
||||||
- tao: the tolerance for the global swinging. Default: 0.1
|
- tao: the tolerance for the global swinging. Default: 0.1
|
||||||
- max_iteration: the iteration to terminate the algorithm. We suggest 700 for the graph with less than 50 nodes; 1000 for 100 nodes; Upper than 1500 for more nodes.
|
- maxIteration: the iteration to terminate the algorithm. We suggest 700 for the graph with less than 50 nodes; 1000 for 100 nodes; Upper than 1500 for more nodes.
|
||||||
|
|
||||||
## use
|
## use
|
||||||
|
|
||||||
@ -26,18 +26,18 @@ simple use.
|
|||||||
```js
|
```js
|
||||||
$.getJSON('../../test/fixtures/viralMarketing.json', data => {
|
$.getJSON('../../test/fixtures/viralMarketing.json', data => {
|
||||||
const Plugin = G6.Plugins['layout.forceAtlas2'];
|
const Plugin = G6.Plugins['layout.forceAtlas2'];
|
||||||
const layout_params = {
|
const layoutParams = {
|
||||||
max_iteration: 1500,
|
maxIteration: 1500,
|
||||||
prev_overlapping: true,
|
prevOverlapping: true,
|
||||||
kr: 15,
|
kr: 15,
|
||||||
mode: 'normal',
|
mode: 'normal',
|
||||||
barnes_hut: false, // may be counter-productive on small networks
|
barnesHut: false, // may be counter-productive on small networks
|
||||||
ks: 0.1,
|
ks: 0.1,
|
||||||
dissuade_hubs: false
|
dissuadeHubs: false
|
||||||
};
|
};
|
||||||
graph = new G6.Graph({
|
graph = new G6.Graph({
|
||||||
id: 'mountNode', // dom id
|
id: 'mountNode', // dom id
|
||||||
plugins: [new Plugin(layout_params)],
|
plugins: [new Plugin(layoutParams)],
|
||||||
height: 1000,
|
height: 1000,
|
||||||
});
|
});
|
||||||
graph.read(data);
|
graph.read(data);
|
||||||
|
@ -77,14 +77,14 @@ class Body {
|
|||||||
}
|
}
|
||||||
// returns a new body
|
// returns a new body
|
||||||
add(bo) {
|
add(bo) {
|
||||||
const nenw_mass = this.mass + bo.mass;
|
const nenwMass = this.mass + bo.mass;
|
||||||
const x = (this.rx * this.mass + bo.rx * bo.mass) / nenw_mass;
|
const x = (this.rx * this.mass + bo.rx * bo.mass) / nenwMass;
|
||||||
const y = (this.ry * this.mass + bo.ry * bo.mass) / nenw_mass;
|
const y = (this.ry * this.mass + bo.ry * bo.mass) / nenwMass;
|
||||||
const dg = this.degree + bo.degree;
|
const dg = this.degree + bo.degree;
|
||||||
const params = {
|
const params = {
|
||||||
rx: x,
|
rx: x,
|
||||||
ry: y,
|
ry: y,
|
||||||
mass: nenw_mass,
|
mass: nenwMass,
|
||||||
degree: dg
|
degree: dg
|
||||||
};
|
};
|
||||||
return new Body(params);
|
return new Body(params);
|
||||||
|
@ -54,7 +54,7 @@ class Layout {
|
|||||||
* whether preventing the node overlapping
|
* whether preventing the node overlapping
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
prev_overlapping: false,
|
prevOverlapping: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* whether active the dissuade hub mode
|
* whether active the dissuade hub mode
|
||||||
@ -62,19 +62,19 @@ class Layout {
|
|||||||
* a more central position than hubs (nodes with a high outdegree)
|
* a more central position than hubs (nodes with a high outdegree)
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
dissuade_hubs: false,
|
dissuadeHubs: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* whether active the barnes hut optimization on computing repulsive forces
|
* whether active the barnes hut optimization on computing repulsive forces
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
barnes_hut: false,
|
barnesHut: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the max iteration number
|
* the max iteration number
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
max_iteration: 1500,
|
maxIteration: 1500,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* control the global velocity
|
* control the global velocity
|
||||||
@ -111,18 +111,18 @@ class Layout {
|
|||||||
kr,
|
kr,
|
||||||
kg,
|
kg,
|
||||||
mode,
|
mode,
|
||||||
prev_overlapping,
|
prevOverlapping,
|
||||||
dissuade_hubs,
|
dissuadeHubs,
|
||||||
barnes_hut,
|
barnesHut,
|
||||||
max_iteration,
|
maxIteration,
|
||||||
ks,
|
ks,
|
||||||
ksmax,
|
ksmax,
|
||||||
tao,
|
tao,
|
||||||
onLayoutComplete
|
onLayoutComplete
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
if (!barnes_hut && nodes.length > 300) barnes_hut = true;
|
if (!barnesHut && nodes.length > 300) barnesHut = true;
|
||||||
else if (barnes_hut && nodes.length <= 300) barnes_hut = false;
|
else if (barnesHut && nodes.length <= 300) barnesHut = false;
|
||||||
|
|
||||||
const width = this.width ? this.width : graph.getWidth();
|
const width = this.width ? this.width : graph.getWidth();
|
||||||
const height = this.height ? this.height : graph.getHeight();
|
const height = this.height ? this.height : graph.getHeight();
|
||||||
@ -142,10 +142,10 @@ class Layout {
|
|||||||
kr,
|
kr,
|
||||||
kg,
|
kg,
|
||||||
mode,
|
mode,
|
||||||
prev_overlapping,
|
prevOverlapping,
|
||||||
dissuade_hubs,
|
dissuadeHubs,
|
||||||
barnes_hut,
|
barnesHut,
|
||||||
max_iteration,
|
maxIteration,
|
||||||
ks,
|
ks,
|
||||||
ksmax,
|
ksmax,
|
||||||
tao,
|
tao,
|
||||||
@ -153,38 +153,39 @@ class Layout {
|
|||||||
widths
|
widths
|
||||||
};
|
};
|
||||||
|
|
||||||
// a loading dom before worker
|
// a loading dom before worker
|
||||||
const loading = document.createElement('div');
|
const loading = document.createElement('div');
|
||||||
loading.id = 'loading';
|
loading.id = 'loading';
|
||||||
loading.style.setProperty('background-color', '#fff');
|
loading.style.setProperty('background-color', '#fff');
|
||||||
loading.style.setProperty('position', 'absolute');
|
loading.style.setProperty('position', 'absolute');
|
||||||
const parent = graph.getGraphContainer();
|
const parent = graph.getGraphContainer().parentNode;
|
||||||
loading.style.setProperty('width', parent.width() + 'px');
|
loading.style.setProperty('width', parent.offsetWidth + 'px');
|
||||||
loading.style.setProperty('height', parent.height() + 'px');
|
loading.style.setProperty('height', parent.offsetHeight + 'px');
|
||||||
loading.style.setProperty('margin-top', (-parent.height()) + 'px');
|
loading.style.setProperty('margin-top', (-parent.offsetHeight) + 'px');
|
||||||
|
loading.style.zIndex = 999;
|
||||||
parent.appendChild(loading);
|
parent.appendChild(loading);
|
||||||
// the loading image
|
// the loading image
|
||||||
const loading_img = document.createElement('img');
|
const loadingImg = document.createElement('img');
|
||||||
loading_img.src = 'https://gw.alipayobjects.com/zos/rmsportal/mnEmjOmrHbghTsZNeTmI.gif';
|
loadingImg.src = 'https://gw.alipayobjects.com/zos/rmsportal/mnEmjOmrHbghTsZNeTmI.gif';
|
||||||
loading_img.style.setProperty('width', 120 + 'px');
|
loadingImg.style.setProperty('width', 200 + 'px');
|
||||||
loading_img.style.setProperty('height', 120 + 'px');
|
loadingImg.style.setProperty('height', 200 + 'px');
|
||||||
const Cw = 120;
|
const Cw = loadingImg.offsetWidth;
|
||||||
const Pw = parent.width();
|
const Pw = loading.offsetWidth;
|
||||||
const left = (Pw - Cw) / 2;
|
const left = (Pw - Cw) / 2;
|
||||||
loading_img.style.setProperty('margin-left', left + 'px');
|
loadingImg.style.setProperty('margin-left', left + 'px');
|
||||||
const Ch = 120;
|
const Ch = loadingImg.offsetHeight;
|
||||||
const Ph = parent.height();
|
const Ph = loading.offsetHeight;
|
||||||
const top = (Ph - Ch) / 2;
|
const top = (Ph - Ch) / 2;
|
||||||
loading_img.style.setProperty('margin-top', top + 'px');
|
loadingImg.style.setProperty('margin-top', top + 'px');
|
||||||
loading.appendChild(loading_img);
|
loading.appendChild(loadingImg);
|
||||||
|
|
||||||
const worker = new Worker();// { type: 'module' }
|
const worker = new Worker();// { type: 'module' }
|
||||||
worker.postMessage(obj);
|
worker.postMessage(obj);
|
||||||
worker.onmessage = function(event) {
|
worker.onmessage = function(event) {
|
||||||
this.nodes = event.data;
|
this.nodes = event.data;
|
||||||
const graph_nodes = graph.getNodes();
|
const graphNodes = graph.getNodes();
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
const model = graph_nodes[i].getModel();
|
const model = graphNodes[i].getModel();
|
||||||
model.x = this.nodes[i].x;
|
model.x = this.nodes[i].x;
|
||||||
model.y = this.nodes[i].y;
|
model.y = this.nodes[i].y;
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ onmessage = function(event) {
|
|||||||
kr,
|
kr,
|
||||||
kg,
|
kg,
|
||||||
mode,
|
mode,
|
||||||
prev_overlapping,
|
prevOverlapping,
|
||||||
dissuade_hubs,
|
dissuadeHubs,
|
||||||
barnes_hut,
|
barnesHut,
|
||||||
max_iteration,
|
maxIteration,
|
||||||
ks,
|
ks,
|
||||||
ksmax,
|
ksmax,
|
||||||
tao,
|
tao,
|
||||||
@ -30,8 +30,6 @@ onmessage = function(event) {
|
|||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
idmap[nodes[i].id] = i;
|
idmap[nodes[i].id] = i;
|
||||||
degrees[i] = 0;
|
degrees[i] = 0;
|
||||||
// nodes[i].index = i;
|
|
||||||
// nodes[i].degree = 0;
|
|
||||||
nodes[i].x = Math.random() * 1000;
|
nodes[i].x = Math.random() * 1000;
|
||||||
nodes[i].y = Math.random() * 1000;
|
nodes[i].y = Math.random() * 1000;
|
||||||
}
|
}
|
||||||
@ -45,24 +43,20 @@ onmessage = function(event) {
|
|||||||
node2 = nodes[j];
|
node2 = nodes[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// // const node1 = graph.find(edges[i].source).getModel();
|
|
||||||
// // const node2 = graph.find(edges[i].target).getModel();
|
|
||||||
// nodes[node1.index].degree += 1;
|
|
||||||
// nodes[node2.index].degree += 1;
|
|
||||||
degrees[idmap[node1.id]] += 1;
|
degrees[idmap[node1.id]] += 1;
|
||||||
degrees[idmap[node2.id]] += 1;
|
degrees[idmap[node2.id]] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const kr_prime = 100;
|
const krPrime = 100;
|
||||||
let iter = max_iteration;
|
let iter = maxIteration;
|
||||||
const prevo_iter = 50;
|
const prevoIter = 50;
|
||||||
let Forces = [];
|
let Forces = [];
|
||||||
const pre_Forces = [];
|
const preForces = [];
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
Forces[2 * i] = 0;
|
Forces[2 * i] = 0;
|
||||||
Forces[2 * i + 1] = 0;
|
Forces[2 * i + 1] = 0;
|
||||||
|
|
||||||
if (barnes_hut) {
|
if (barnesHut) {
|
||||||
let params = {
|
let params = {
|
||||||
id: i,
|
id: i,
|
||||||
rx: nodes[i].x,
|
rx: nodes[i].x,
|
||||||
@ -78,22 +72,22 @@ onmessage = function(event) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
pre_Forces[2 * i] = Forces[2 * i];
|
preForces[2 * i] = Forces[2 * i];
|
||||||
pre_Forces[2 * i + 1] = Forces[2 * i + 1];
|
preForces[2 * i + 1] = Forces[2 * i + 1];
|
||||||
Forces[2 * i] = 0;
|
Forces[2 * i] = 0;
|
||||||
Forces[2 * i + 1] = 0;
|
Forces[2 * i + 1] = 0;
|
||||||
}
|
}
|
||||||
// // attractive forces, existing on every actual edge
|
// // attractive forces, existing on every actual edge
|
||||||
Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees);
|
Forces = getAttrForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, widths, idmap, degrees);
|
||||||
// // // repulsive forces and Gravity, existing on every node pair
|
// // // repulsive forces and Gravity, existing on every node pair
|
||||||
// // // if prev_overlapping, using the no-optimized method in the last prevo_iter instead.
|
// // // if prevOverlapping, using the no-optimized method in the last prevoIter instead.
|
||||||
if (barnes_hut && ((prev_overlapping && iter > prevo_iter) || !prev_overlapping)) {
|
if (barnesHut && ((prevOverlapping && iter > prevoIter) || !prevOverlapping)) {
|
||||||
Forces = getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies, degrees);
|
Forces = getOptRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, bodies, degrees);
|
||||||
} else {
|
} else {
|
||||||
Forces = getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees);
|
Forces = getRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, widths, degrees);
|
||||||
}
|
}
|
||||||
// // update the positions
|
// // update the positions
|
||||||
const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees);
|
const res = updatePos(size, nodes, Forces, preForces, SG, ks, ksmax, tao, degrees);
|
||||||
nodes = res[0];
|
nodes = res[0];
|
||||||
SG = res[1];
|
SG = res[1];
|
||||||
iter -= 1;
|
iter -= 1;
|
||||||
@ -101,81 +95,75 @@ onmessage = function(event) {
|
|||||||
self.postMessage(nodes);
|
self.postMessage(nodes);
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees) {
|
function getAttrForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, widths, idmap, degrees) {
|
||||||
for (let i = 0; i < esize; i += 1) {
|
for (let i = 0; i < esize; i += 1) {
|
||||||
// const source_node = graph.find(edges[i].source).getModel();
|
// const sourceNode = graph.find(edges[i].source).getModel();
|
||||||
// const target_node = graph.find(edges[i].target).getModel();
|
// const targetNode = graph.find(edges[i].target).getModel();
|
||||||
let source_node;
|
let sourceNode;
|
||||||
let target_node;
|
let targetNode;
|
||||||
let source_idx;
|
let sourceIdx;
|
||||||
let target_idx;
|
let targetIdx;
|
||||||
for (let j = 0; j < size; j += 1) {
|
for (let j = 0; j < size; j += 1) {
|
||||||
if (nodes[j].id === edges[i].source) {
|
if (nodes[j].id === edges[i].source) {
|
||||||
source_node = nodes[j];
|
sourceNode = nodes[j];
|
||||||
source_idx = j;
|
sourceIdx = j;
|
||||||
} else if (nodes[j].id === edges[i].target) {
|
} else if (nodes[j].id === edges[i].target) {
|
||||||
target_node = nodes[j];
|
targetNode = nodes[j];
|
||||||
target_idx = j;
|
targetIdx = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let dir = [ target_node.x - source_node.x, target_node.y - source_node.y ];
|
let dir = [ targetNode.x - sourceNode.x, targetNode.y - sourceNode.y ];
|
||||||
let eucli_dis = Math.hypot(dir[0], dir[1]);
|
let eucliDis = Math.hypot(dir[0], dir[1]);
|
||||||
eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis;
|
||||||
dir[0] = dir[0] / eucli_dis;
|
dir[0] = dir[0] / eucliDis;
|
||||||
dir[1] = dir[1] / eucli_dis;
|
dir[1] = dir[1] / eucliDis;
|
||||||
// the force
|
// the force
|
||||||
if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[source_idx] - widths[target_idx];
|
if (prevOverlapping && iter < prevoIter) eucliDis = eucliDis - widths[sourceIdx] - widths[targetIdx];
|
||||||
let Fa1 = eucli_dis;
|
let Fa1 = eucliDis;
|
||||||
let Fa2 = Fa1;
|
let Fa2 = Fa1;
|
||||||
if (mode === 'linlog') {
|
if (mode === 'linlog') {
|
||||||
Fa1 = Math.log(1 + eucli_dis);
|
Fa1 = Math.log(1 + eucliDis);
|
||||||
Fa2 = Fa1;
|
Fa2 = Fa1;
|
||||||
}
|
}
|
||||||
if (dissuade_hubs) {
|
if (dissuadeHubs) {
|
||||||
// Fa1 = eucli_dis / source_node.degree;
|
Fa1 = eucliDis / degrees[sourceIdx];
|
||||||
// Fa2 = eucli_dis / target_node.degree;
|
Fa2 = eucliDis / degrees[targetIdx];
|
||||||
Fa1 = eucli_dis / degrees[source_idx];
|
|
||||||
Fa2 = eucli_dis / degrees[target_idx];
|
|
||||||
}
|
}
|
||||||
if (prev_overlapping && iter < prevo_iter && eucli_dis <= 0) {
|
if (prevOverlapping && iter < prevoIter && eucliDis <= 0) {
|
||||||
Fa1 = 0;
|
Fa1 = 0;
|
||||||
Fa2 = 0;
|
Fa2 = 0;
|
||||||
} else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) {
|
} else if (prevOverlapping && iter < prevoIter && eucliDis > 0) {
|
||||||
Fa1 = eucli_dis;
|
Fa1 = eucliDis;
|
||||||
Fa2 = eucli_dis;
|
Fa2 = eucliDis;
|
||||||
}
|
}
|
||||||
// Forces[2 * source_node.index] += Fa1 * dir[0];
|
Forces[2 * idmap[sourceNode.id]] += Fa1 * dir[0];
|
||||||
// Forces[2 * target_node.index] -= Fa2 * dir[0];
|
Forces[2 * idmap[targetNode.id]] -= Fa2 * dir[0];
|
||||||
// Forces[2 * source_node.index + 1] += Fa1 * dir[1];
|
Forces[2 * idmap[sourceNode.id] + 1] += Fa1 * dir[1];
|
||||||
// Forces[2 * target_node.index + 1] -= Fa2 * dir[1];
|
Forces[2 * idmap[targetNode.id] + 1] -= Fa2 * dir[1];
|
||||||
Forces[2 * idmap[source_node.id]] += Fa1 * dir[0];
|
|
||||||
Forces[2 * idmap[target_node.id]] -= Fa2 * dir[0];
|
|
||||||
Forces[2 * idmap[source_node.id] + 1] += Fa1 * dir[1];
|
|
||||||
Forces[2 * idmap[target_node.id] + 1] -= Fa2 * dir[1];
|
|
||||||
dir = null;
|
dir = null;
|
||||||
}
|
}
|
||||||
return Forces;
|
return Forces;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees) {
|
function getRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, center, widths, degrees) {
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
for (let j = i + 1; j < size; j += 1) {
|
for (let j = i + 1; j < size; j += 1) {
|
||||||
let dir = [ nodes[j].x - nodes[i].x, nodes[j].y - nodes[i].y ];
|
let dir = [ nodes[j].x - nodes[i].x, nodes[j].y - nodes[i].y ];
|
||||||
let eucli_dis = Math.hypot(dir[0], dir[1]);
|
let eucliDis = Math.hypot(dir[0], dir[1]);
|
||||||
eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis;
|
||||||
dir[0] = dir[0] / eucli_dis;
|
dir[0] = dir[0] / eucliDis;
|
||||||
dir[1] = dir[1] / eucli_dis;
|
dir[1] = dir[1] / eucliDis;
|
||||||
|
|
||||||
if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[i] - widths[j];
|
if (prevOverlapping && iter < prevoIter) eucliDis = eucliDis - widths[i] - widths[j];
|
||||||
|
|
||||||
let Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis;
|
let Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucliDis;
|
||||||
|
|
||||||
if (prev_overlapping && iter < prevo_iter && eucli_dis < 0) {
|
if (prevOverlapping && iter < prevoIter && eucliDis < 0) {
|
||||||
Fr = kr_prime * (degrees[i] + 1) * (degrees[j] + 1);
|
Fr = krPrime * (degrees[i] + 1) * (degrees[j] + 1);
|
||||||
} else if (prev_overlapping && iter < prevo_iter && eucli_dis === 0) {
|
} else if (prevOverlapping && iter < prevoIter && eucliDis === 0) {
|
||||||
Fr = 0;
|
Fr = 0;
|
||||||
} else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) {
|
} else if (prevOverlapping && iter < prevoIter && eucliDis > 0) {
|
||||||
Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis;
|
Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucliDis;
|
||||||
}
|
}
|
||||||
Forces[2 * i] -= Fr * dir[0];
|
Forces[2 * i] -= Fr * dir[0];
|
||||||
Forces[2 * j] += Fr * dir[0];
|
Forces[2 * j] += Fr * dir[0];
|
||||||
@ -186,9 +174,9 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h
|
|||||||
|
|
||||||
// gravity
|
// gravity
|
||||||
let dir = [ nodes[i].x - center.x, nodes[i].y - center.y ];
|
let dir = [ nodes[i].x - center.x, nodes[i].y - center.y ];
|
||||||
const eucli_dis = Math.hypot(dir[0], dir[1]);
|
const eucliDis = Math.hypot(dir[0], dir[1]);
|
||||||
dir[0] = dir[0] / eucli_dis;
|
dir[0] = dir[0] / eucliDis;
|
||||||
dir[1] = dir[1] / eucli_dis;
|
dir[1] = dir[1] / eucliDis;
|
||||||
const Fg = kg * (degrees[i] + 1);
|
const Fg = kg * (degrees[i] + 1);
|
||||||
Forces[2 * i] -= Fg * dir[0];
|
Forces[2 * i] -= Fg * dir[0];
|
||||||
Forces[2 * i + 1] -= Fg * dir[1];
|
Forces[2 * i + 1] -= Fg * dir[1];
|
||||||
@ -197,7 +185,7 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h
|
|||||||
return Forces;
|
return Forces;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, ct, bodies, degrees) {
|
function getOptRepGraForces(nodes, edges, size, esize, prevOverlapping, dissuadeHubs, mode, iter, prevoIter, Forces, kr, krPrime, kg, ct, bodies, degrees) {
|
||||||
let minx = 9e10,
|
let minx = 9e10,
|
||||||
maxx = -9e10,
|
maxx = -9e10,
|
||||||
miny = 9e10,
|
miny = 9e10,
|
||||||
@ -212,75 +200,75 @@ function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuad
|
|||||||
|
|
||||||
let width = Math.max(maxx - minx, maxy - miny);
|
let width = Math.max(maxx - minx, maxy - miny);
|
||||||
|
|
||||||
let quad_params = {
|
let quadParams = {
|
||||||
xmid: (maxx + minx) / 2,
|
xmid: (maxx + minx) / 2,
|
||||||
ymid: (maxy + miny) / 2,
|
ymid: (maxy + miny) / 2,
|
||||||
length: width,
|
length: width,
|
||||||
mass_center: ct,
|
massCenter: ct,
|
||||||
mass: size
|
mass: size
|
||||||
};
|
};
|
||||||
let quad = new Quad(quad_params);
|
let quad = new Quad(quadParams);
|
||||||
let quad_tree = new QuadTree(quad);
|
let quadTree = new QuadTree(quad);
|
||||||
|
|
||||||
// build the tree, insert the nodes(quads) into the tree
|
// build the tree, insert the nodes(quads) into the tree
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
if (bodies[i].in(quad)) quad_tree.insert(bodies[i]);
|
if (bodies[i].in(quad)) quadTree.insert(bodies[i]);
|
||||||
}
|
}
|
||||||
// update the repulsive forces and the gravity.
|
// update the repulsive forces and the gravity.
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
bodies[i].resetForce();
|
bodies[i].resetForce();
|
||||||
quad_tree.updateForce(bodies[i]);
|
quadTree.updateForce(bodies[i]);
|
||||||
Forces[2 * i] -= bodies[i].fx;
|
Forces[2 * i] -= bodies[i].fx;
|
||||||
Forces[2 * i + 1] -= bodies[i].fy;
|
Forces[2 * i + 1] -= bodies[i].fy;
|
||||||
|
|
||||||
// gravity
|
// gravity
|
||||||
let dir = [ nodes[i].x - ct.x, nodes[i].y - ct.y ];
|
let dir = [ nodes[i].x - ct.x, nodes[i].y - ct.y ];
|
||||||
let eucli_dis = Math.hypot(dir[0], dir[1]);
|
let eucliDis = Math.hypot(dir[0], dir[1]);
|
||||||
eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
eucliDis = eucliDis < 0.0001 ? 0.0001 : eucliDis;
|
||||||
dir[0] = dir[0] / eucli_dis;
|
dir[0] = dir[0] / eucliDis;
|
||||||
dir[1] = dir[1] / eucli_dis;
|
dir[1] = dir[1] / eucliDis;
|
||||||
let Fg = kg * (degrees[i] + 1);
|
let Fg = kg * (degrees[i] + 1);
|
||||||
Forces[2 * i] -= Fg * dir[0];
|
Forces[2 * i] -= Fg * dir[0];
|
||||||
Forces[2 * i + 1] -= Fg * dir[1];
|
Forces[2 * i + 1] -= Fg * dir[1];
|
||||||
|
|
||||||
eucli_dis = null;
|
eucliDis = null;
|
||||||
Fg = null;
|
Fg = null;
|
||||||
dir = null;
|
dir = null;
|
||||||
}
|
}
|
||||||
quad_params = null;
|
quadParams = null;
|
||||||
quad = null;
|
quad = null;
|
||||||
quad_tree = null;
|
quadTree = null;
|
||||||
width = null;
|
width = null;
|
||||||
return Forces;
|
return Forces;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees) {
|
function updatePos(size, nodes, Forces, preForces, SG, ks, ksmax, tao, degrees) {
|
||||||
let swgns = [];
|
let swgns = [];
|
||||||
let trans = [];
|
let trans = [];
|
||||||
// swg(G) and tra(G)
|
// swg(G) and tra(G)
|
||||||
let swgG = 0;
|
let swgG = 0;
|
||||||
let traG = 0;
|
let traG = 0;
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
const minus = [ Forces[2 * i] - pre_Forces[2 * i],
|
const minus = [ Forces[2 * i] - preForces[2 * i],
|
||||||
Forces[2 * i + 1] - pre_Forces[2 * i + 1]
|
Forces[2 * i + 1] - preForces[2 * i + 1]
|
||||||
];
|
];
|
||||||
const minus_norm = Math.hypot(minus[0], minus[1]);
|
const minusNorm = Math.hypot(minus[0], minus[1]);
|
||||||
const add = [ Forces[2 * i] + pre_Forces[2 * i],
|
const add = [ Forces[2 * i] + preForces[2 * i],
|
||||||
Forces[2 * i + 1] + pre_Forces[2 * i + 1]
|
Forces[2 * i + 1] + preForces[2 * i + 1]
|
||||||
];
|
];
|
||||||
const add_norm = Math.hypot(add[0], add[1]);
|
const addNorm = Math.hypot(add[0], add[1]);
|
||||||
|
|
||||||
swgns[i] = minus_norm;
|
swgns[i] = minusNorm;
|
||||||
trans[i] = add_norm / 2;
|
trans[i] = addNorm / 2;
|
||||||
|
|
||||||
swgG += (degrees[i] + 1) * swgns[i];
|
swgG += (degrees[i] + 1) * swgns[i];
|
||||||
traG += (degrees[i] + 1) * trans[i];
|
traG += (degrees[i] + 1) * trans[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
let pre_SG = SG;
|
let preSG = SG;
|
||||||
SG = tao * traG / swgG;
|
SG = tao * traG / swgG;
|
||||||
if (pre_SG !== 0) {
|
if (preSG !== 0) {
|
||||||
SG = SG > (1.5 * pre_SG) ? (1.5 * pre_SG) : SG;
|
SG = SG > (1.5 * preSG) ? (1.5 * preSG) : SG;
|
||||||
}
|
}
|
||||||
// update the node positions
|
// update the node positions
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
@ -289,13 +277,13 @@ function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees)
|
|||||||
absForce = absForce < 0.0001 ? 0.0001 : absForce;
|
absForce = absForce < 0.0001 ? 0.0001 : absForce;
|
||||||
const max = ksmax / absForce;
|
const max = ksmax / absForce;
|
||||||
Sn = Sn > max ? max : Sn;
|
Sn = Sn > max ? max : Sn;
|
||||||
const Dn_x = Sn * Forces[2 * i];
|
const Dnx = Sn * Forces[2 * i];
|
||||||
const Dn_y = Sn * Forces[2 * i + 1];
|
const Dny = Sn * Forces[2 * i + 1];
|
||||||
nodes[i].x += Dn_x;
|
nodes[i].x += Dnx;
|
||||||
nodes[i].y += Dn_y;
|
nodes[i].y += Dny;
|
||||||
}
|
}
|
||||||
swgns = null;
|
swgns = null;
|
||||||
trans = null;
|
trans = null;
|
||||||
pre_SG = null;
|
preSG = null;
|
||||||
return [ nodes, SG ];
|
return [ nodes, SG ];
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class Quad {
|
|||||||
* the mass center of this quad
|
* the mass center of this quad
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.mass_center = params.mass_center;
|
this.massCenter = params.massCenter;
|
||||||
/**
|
/**
|
||||||
* the mass of this quad
|
* the mass of this quad
|
||||||
* @type {number}
|
* @type {number}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileOverview body
|
|
||||||
* @author shiwu.wyy@antfin.com
|
|
||||||
*/
|
|
||||||
const G6 = require('@antv/g6');
|
|
||||||
const Util = G6.Util;
|
|
||||||
|
|
||||||
// represents a body(a point mass) and its position
|
|
||||||
class Body {
|
|
||||||
constructor(options) {
|
|
||||||
Util.mix(this, {
|
|
||||||
/**
|
|
||||||
* the id of this body, the same with the node id
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
id: -1,
|
|
||||||
/**
|
|
||||||
* the position of this body
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
rx: null,
|
|
||||||
/**
|
|
||||||
* the position of this body
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
ry: null,
|
|
||||||
/**
|
|
||||||
* the force acting on this body
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
fx: 0,
|
|
||||||
/**
|
|
||||||
* the force acting on this body
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
fy: 0,
|
|
||||||
/**
|
|
||||||
* the mass of this body, =1 for a node
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
mass: 1,
|
|
||||||
/**
|
|
||||||
* the degree of the node represented by this body
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
degree: 1,
|
|
||||||
/**
|
|
||||||
* the parameter for repulsive force, = kr
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
G: 1
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
// returns the euclidean distance
|
|
||||||
distanceTo(bo) {
|
|
||||||
const dx = this.rx - bo.rx;
|
|
||||||
const dy = this.ry - bo.ry;
|
|
||||||
return Math.hypot(dx, dy);
|
|
||||||
}
|
|
||||||
setPos(x, y) {
|
|
||||||
this.rx = x;
|
|
||||||
this.ry = y;
|
|
||||||
}
|
|
||||||
// resets the forces
|
|
||||||
resetForce() {
|
|
||||||
this.fx = 0;
|
|
||||||
this.fy = 0;
|
|
||||||
}
|
|
||||||
addForce(b) {
|
|
||||||
const dx = b.rx - this.rx;
|
|
||||||
const dy = b.ry - this.ry;
|
|
||||||
let dist = Math.hypot(dx, dy);
|
|
||||||
dist = dist < 0.0001 ? 0.0001 : dist;
|
|
||||||
// the repulsive defined by force atlas 2
|
|
||||||
const F = (this.G * (this.degree + 1) * (b.degree + 1)) / dist;
|
|
||||||
this.fx += F * dx / dist;
|
|
||||||
this.fy += F * dy / dist;
|
|
||||||
}
|
|
||||||
// if quad contains this body
|
|
||||||
in(quad) {
|
|
||||||
return quad.contains(this.rx, this.ry);
|
|
||||||
}
|
|
||||||
// returns a new body
|
|
||||||
add(bo) {
|
|
||||||
const nenw_mass = this.mass + bo.mass;
|
|
||||||
const x = (this.rx * this.mass + bo.rx * bo.mass) / nenw_mass;
|
|
||||||
const y = (this.ry * this.mass + bo.ry * bo.mass) / nenw_mass;
|
|
||||||
const dg = this.degree + bo.degree;
|
|
||||||
const params = { rx: x, ry: y, mass: nenw_mass, degree: dg };
|
|
||||||
return new Body(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Body;
|
|
@ -1,85 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileOverview quad
|
|
||||||
* @author shiwu.wyy@antfin.com
|
|
||||||
*/
|
|
||||||
const G6 = require('@antv/g6');
|
|
||||||
const Util = G6.Util;
|
|
||||||
|
|
||||||
class Quad {
|
|
||||||
constructor(options) {
|
|
||||||
Util.mix(this, {
|
|
||||||
/**
|
|
||||||
* the center position of this quad
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
xmid: null,
|
|
||||||
/**
|
|
||||||
* the center position of this quad
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
ymid: null,
|
|
||||||
/**
|
|
||||||
* the length of this quad
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
length: 0,
|
|
||||||
/**
|
|
||||||
* the mass center of this quad
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
mass_center: [ 0, 0 ],
|
|
||||||
/**
|
|
||||||
* the mass of this quad
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
mass: 0
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
getLength() {
|
|
||||||
return this.length;
|
|
||||||
}
|
|
||||||
contains(x, y) {
|
|
||||||
const halfLen = this.length / 2;
|
|
||||||
return (x <= this.xmid + halfLen &&
|
|
||||||
x >= this.xmid - halfLen &&
|
|
||||||
y <= this.ymid + halfLen &&
|
|
||||||
y >= this.ymid - halfLen);
|
|
||||||
}
|
|
||||||
// northwest quadrant
|
|
||||||
NW() {
|
|
||||||
const x = this.xmid - this.length / 4;
|
|
||||||
const y = this.ymid + this.length / 4;
|
|
||||||
const len = this.length / 2;
|
|
||||||
const params = { xmid: x, ymid: y, length: len };
|
|
||||||
const NW = new Quad(params);
|
|
||||||
return NW;
|
|
||||||
}
|
|
||||||
// northeast
|
|
||||||
NE() {
|
|
||||||
const x = this.xmid + this.length / 4;
|
|
||||||
const y = this.ymid + this.length / 4;
|
|
||||||
const len = this.length / 2;
|
|
||||||
const params = { xmid: x, ymid: y, length: len };
|
|
||||||
const NE = new Quad(params);
|
|
||||||
return NE;
|
|
||||||
}
|
|
||||||
// southwest
|
|
||||||
SW() {
|
|
||||||
const x = this.xmid - this.length / 4;
|
|
||||||
const y = this.ymid - this.length / 4;
|
|
||||||
const len = this.length / 2;
|
|
||||||
const params = { xmid: x, ymid: y, length: len };
|
|
||||||
const SW = new Quad(params);
|
|
||||||
return SW;
|
|
||||||
}
|
|
||||||
// southeast
|
|
||||||
SE() {
|
|
||||||
const x = this.xmid + this.length / 4;
|
|
||||||
const y = this.ymid - this.length / 4;
|
|
||||||
const len = this.length / 2;
|
|
||||||
const params = { xmid: x, ymid: y, length: len };
|
|
||||||
const SE = new Quad(params);
|
|
||||||
return SE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = Quad;
|
|
@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileOverview quadTree
|
|
||||||
* @author shiwu.wyy@antfin.com
|
|
||||||
*/
|
|
||||||
const G6 = require('@antv/g6');
|
|
||||||
const Util = G6.Util;
|
|
||||||
class QuadTree {
|
|
||||||
// each quadtree represents a quadrant and an aggregate body
|
|
||||||
// that represents all bodies inside the quadrant
|
|
||||||
constructor(options) {
|
|
||||||
Util.mix(this, {
|
|
||||||
/**
|
|
||||||
* (aggregated) body in this quad
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
body: null,
|
|
||||||
/**
|
|
||||||
* tree representing the northwest quadrant
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
quad: null,
|
|
||||||
NW: null,
|
|
||||||
NE: null,
|
|
||||||
SW: null,
|
|
||||||
SE: null,
|
|
||||||
/**
|
|
||||||
* threshold
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
theta: 0.5
|
|
||||||
}, options);
|
|
||||||
if (options != null) this.quad = options;
|
|
||||||
}
|
|
||||||
// insert a body(node) into the tree
|
|
||||||
insert(bo) {
|
|
||||||
// if this node does not contain a body, put the new body bo here
|
|
||||||
if (this.body == null) {
|
|
||||||
this.body = bo;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// internal node
|
|
||||||
if (!this._isExternal()) {
|
|
||||||
// update mass info
|
|
||||||
this.body = this.body.add(bo);
|
|
||||||
// insert body into quadrant
|
|
||||||
this._putBody(bo);
|
|
||||||
} else { // external node
|
|
||||||
// divide this region into four children
|
|
||||||
this.NW = new QuadTree(this.quad.NW());
|
|
||||||
this.NE = new QuadTree(this.quad.NE());
|
|
||||||
this.SW = new QuadTree(this.quad.SW());
|
|
||||||
this.SE = new QuadTree(this.quad.SE());
|
|
||||||
|
|
||||||
// insert this body and bo
|
|
||||||
this._putBody(this.body);
|
|
||||||
this._putBody(bo);
|
|
||||||
// update the mass info
|
|
||||||
this.body = this.body.add(bo);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// inserts bo into a quad
|
|
||||||
_putBody(bo) {
|
|
||||||
if (bo.in(this.quad.NW())) this.NW.insert(bo);
|
|
||||||
else if (bo.in(this.quad.NE())) this.NE.insert(bo);
|
|
||||||
else if (bo.in(this.quad.SW())) this.SW.insert(bo);
|
|
||||||
else if (bo.in(this.quad.SE())) this.SE.insert(bo);
|
|
||||||
}
|
|
||||||
_isExternal() {
|
|
||||||
// four children are null
|
|
||||||
return (this.NW == null && this.NE == null && this.SW == null && this.SE == null);
|
|
||||||
}
|
|
||||||
// update the forces
|
|
||||||
updateForce(bo) {
|
|
||||||
if (this.body == null || bo === this.body) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if the current node is external
|
|
||||||
if (this._isExternal()) bo.addForce(this.body);
|
|
||||||
// internal nodes
|
|
||||||
else {
|
|
||||||
const s = this.quad.getLength();
|
|
||||||
const d = this.body.distanceTo(bo);
|
|
||||||
// b is far enough
|
|
||||||
if ((s / d) < this.theta) bo.addForce(this.body);
|
|
||||||
else {
|
|
||||||
this.NW.updateForce(bo);
|
|
||||||
this.NE.updateForce(bo);
|
|
||||||
this.SW.updateForce(bo);
|
|
||||||
this.SE.updateForce(bo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = QuadTree;
|
|
@ -13,9 +13,9 @@ template.maxSpanningForest for jiuselu graph analyzer, which is a plugin for gra
|
|||||||
|
|
||||||
parameter for this plugin:
|
parameter for this plugin:
|
||||||
- layoutCfg: the configuration for layout.
|
- layoutCfg: the configuration for layout.
|
||||||
- max_iteration: the number of iteration for terminating the layout algorithm
|
- maxIteration: the number of iteration for terminating the layout algorithm
|
||||||
- kg: the gravity parameter for layout. The larger kg, more compact the graph, expecialy for the isolated subgraphs.
|
- kg: the gravity parameter for layout. The larger kg, more compact the graph, expecialy for the isolated subgraphs.
|
||||||
- prev_overlapping: whether preventing the node overlapping
|
- prevOverlapping: whether preventing the node overlapping
|
||||||
- onLayoutComplete: a listener for layout completement. When the layout is complete, the loading div and img disappear.
|
- onLayoutComplete: a listener for layout completement. When the layout is complete, the loading div and img disappear.
|
||||||
- menuCfg: the configuration for menu
|
- menuCfg: the configuration for menu
|
||||||
example:
|
example:
|
||||||
@ -46,10 +46,10 @@ To navigate an item (a node or edge) by id or item, if this item is not in the v
|
|||||||
graph.navigate(item); // item or id
|
graph.navigate(item); // item or id
|
||||||
|
|
||||||
To create the menu which follows the mouse click:
|
To create the menu which follows the mouse click:
|
||||||
graph.createMenu(func, container_id);
|
graph.createMenu(func, containerId);
|
||||||
params:
|
params:
|
||||||
- func is the onclick listener for the li '查看单页分析详情'
|
- func is the onclick listener for the li '查看单页分析详情'
|
||||||
- container_id is the DOM id of the menu container.
|
- containerId is the DOM id of the menu container.
|
||||||
|
|
||||||
## use
|
## use
|
||||||
|
|
||||||
@ -60,9 +60,9 @@ const MaxSpanningForestPlugin = G6.Plugins['template.maxSpanningForest'];
|
|||||||
//the instances of plugins
|
//the instances of plugins
|
||||||
const maxSpanningForest = new MaxSpanningForestPlugin({
|
const maxSpanningForest = new MaxSpanningForestPlugin({
|
||||||
layoutCfg: {
|
layoutCfg: {
|
||||||
max_iteration: 600,
|
maxIteration: 600,
|
||||||
kg: 10,
|
kg: 10,
|
||||||
prev_overlapping: true,
|
prevOverlapping: true,
|
||||||
onLayoutComplete: function () {
|
onLayoutComplete: function () {
|
||||||
const minimap = document.getElementById('minimap');
|
const minimap = document.getElementById('minimap');
|
||||||
const legend = document.getElementById('legend');
|
const legend = document.getElementById('legend');
|
||||||
|
@ -43,10 +43,10 @@ class Plugin {
|
|||||||
kr: 120,
|
kr: 120,
|
||||||
kg: 8.0,
|
kg: 8.0,
|
||||||
mode: 'common',
|
mode: 'common',
|
||||||
prev_overlapping: true,
|
prevOverlapping: true,
|
||||||
dissuade_hubs: false,
|
dissuadeHubs: false,
|
||||||
max_iteration: 1000,
|
maxIteration: 1000,
|
||||||
barnes_hut: true,
|
barnesHut: true,
|
||||||
ks: 0.1,
|
ks: 0.1,
|
||||||
ksmax: 10,
|
ksmax: 10,
|
||||||
tao: 0.1,
|
tao: 0.1,
|
||||||
@ -55,13 +55,13 @@ class Plugin {
|
|||||||
},
|
},
|
||||||
fisheye: true,
|
fisheye: true,
|
||||||
menu: null,
|
menu: null,
|
||||||
pre_navi: {},
|
preNavi: {},
|
||||||
edge_style: {
|
edgeStyle: {
|
||||||
endArrow: true,
|
endArrow: true,
|
||||||
stroke: '#4F7DAB',
|
stroke: '#4F7DAB',
|
||||||
strokeOpacity: 0.65
|
strokeOpacity: 0.65
|
||||||
},
|
},
|
||||||
node_style: {
|
nodeStyle: {
|
||||||
stroke: '#696969',
|
stroke: '#696969',
|
||||||
strokeOpacity: 0.4,
|
strokeOpacity: 0.4,
|
||||||
lineWidth: 1
|
lineWidth: 1
|
||||||
@ -148,21 +148,21 @@ class Plugin {
|
|||||||
if (!model.isTreeEdge || typeof model.isTreeEdge === 'undefined') model.shape = 'quadraticCurve';
|
if (!model.isTreeEdge || typeof model.isTreeEdge === 'undefined') model.shape = 'quadraticCurve';
|
||||||
}
|
}
|
||||||
graph.edge({
|
graph.edge({
|
||||||
style: this.edge_style
|
style: this.edgeStyle
|
||||||
});
|
});
|
||||||
graph.node({
|
graph.node({
|
||||||
label: this.node_label,
|
label: this.node_label,
|
||||||
style: this.node_style
|
style: this.nodeStyle
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
activeItem(item) {
|
activeItem(item) {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const pre_navi = this.pre_navi;
|
const preNavi = this.preNavi;
|
||||||
if (Util.isString(item)) {
|
if (Util.isString(item)) {
|
||||||
item = graph.find(item);
|
item = graph.find(item);
|
||||||
}
|
}
|
||||||
let style = {};
|
let style = {};
|
||||||
let pre_style = {};
|
let preStyle = {};
|
||||||
if (item.type === 'node') {
|
if (item.type === 'node') {
|
||||||
style = {
|
style = {
|
||||||
stroke: '#fff',
|
stroke: '#fff',
|
||||||
@ -170,29 +170,29 @@ class Plugin {
|
|||||||
shadowColor: '#6a80aa',
|
shadowColor: '#6a80aa',
|
||||||
shadowBlur: 20
|
shadowBlur: 20
|
||||||
};
|
};
|
||||||
pre_style = this.node_style;
|
preStyle = this.nodeStyle;
|
||||||
} else if (item.type === 'edge') {
|
} else if (item.type === 'edge') {
|
||||||
style = {
|
style = {
|
||||||
endArrow: true,
|
endArrow: true,
|
||||||
stroke: '#4C7295',
|
stroke: '#4C7295',
|
||||||
strokeOpacity: 1
|
strokeOpacity: 1
|
||||||
};
|
};
|
||||||
pre_style = this.edge_style;
|
preStyle = this.edgeStyle;
|
||||||
} else return;
|
} else return;
|
||||||
|
|
||||||
// unactive the previous navigate node
|
// unactive the previous navigate node
|
||||||
if (pre_navi !== {} && pre_navi !== null && pre_navi !== undefined && pre_navi.item !== null) {
|
if (preNavi !== {} && preNavi !== null && preNavi !== undefined && preNavi.item !== null) {
|
||||||
graph.update(pre_navi.item, {
|
graph.update(preNavi.item, {
|
||||||
style: pre_navi.style
|
style: preNavi.style
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.update(item, {
|
graph.update(item, {
|
||||||
style
|
style
|
||||||
});
|
});
|
||||||
this.pre_navi = {
|
this.preNavi = {
|
||||||
item,
|
item,
|
||||||
style: pre_style
|
style: preStyle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
setListener() {
|
setListener() {
|
||||||
@ -207,7 +207,7 @@ class Plugin {
|
|||||||
});
|
});
|
||||||
graph.on('node:mouseleave', ev => {
|
graph.on('node:mouseleave', ev => {
|
||||||
graph.update(ev.item, {
|
graph.update(ev.item, {
|
||||||
style: this.node_style
|
style: this.nodeStyle
|
||||||
});
|
});
|
||||||
graph.css({
|
graph.css({
|
||||||
cursor: '-webkit-grab'
|
cursor: '-webkit-grab'
|
||||||
@ -221,7 +221,7 @@ class Plugin {
|
|||||||
});
|
});
|
||||||
graph.on('edge:mouseleave', ev => {
|
graph.on('edge:mouseleave', ev => {
|
||||||
graph.update(ev.item, {
|
graph.update(ev.item, {
|
||||||
style: this.edge_style
|
style: this.edgeStyle
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -230,9 +230,9 @@ class Plugin {
|
|||||||
item
|
item
|
||||||
}) => {
|
}) => {
|
||||||
if (shape && item.isNode) {
|
if (shape && item.isNode) {
|
||||||
const menu_x = item.getModel().x * graph.getMatrix()[0] + graph.getMatrix()[6];
|
const menuX = item.getModel().x * graph.getMatrix()[0] + graph.getMatrix()[6];
|
||||||
const menu_y = item.getModel().y * graph.getMatrix()[0] + graph.getMatrix()[7];
|
const menuY = item.getModel().y * graph.getMatrix()[0] + graph.getMatrix()[7];
|
||||||
this.menu.show(item, menu_x, menu_y);
|
this.menu.show(item, menuX, menuY);
|
||||||
graph.draw();
|
graph.draw();
|
||||||
} else {
|
} else {
|
||||||
this.menu.hide();
|
this.menu.hide();
|
||||||
|
@ -9,7 +9,7 @@ const Util = G6.Util;
|
|||||||
class Menu {
|
class Menu {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
Util.mix(this, {
|
Util.mix(this, {
|
||||||
container_id: '',
|
containerId: '',
|
||||||
clickType: 'in'
|
clickType: 'in'
|
||||||
}, options);
|
}, options);
|
||||||
this.createMenu();
|
this.createMenu();
|
||||||
@ -26,15 +26,15 @@ class Menu {
|
|||||||
createMenu() {
|
createMenu() {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const menuCfg = this.menuCfg;
|
const menuCfg = this.menuCfg;
|
||||||
const container_id = this.container_id;
|
const containerId = this.containerId;
|
||||||
|
|
||||||
const menuHtml = `<ul class="menu" style = "
|
const menuHtml = `<ul class="menu" style = "
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: none;"></ul>`;
|
display: none;"></ul>`;
|
||||||
const menu = Util.createDOM(menuHtml);
|
const menu = Util.createDOM(menuHtml);
|
||||||
let parent = graph.getGraphContainer();
|
let parent = graph.getGraphContainer();
|
||||||
if (container_id !== '' && container_id !== undefined) {
|
if (containerId !== '' && containerId !== undefined) {
|
||||||
parent = document.getElementById(container_id);
|
parent = document.getElementById(containerId);
|
||||||
}
|
}
|
||||||
parent.appendChild(menu);
|
parent.appendChild(menu);
|
||||||
|
|
||||||
@ -62,12 +62,12 @@ class Menu {
|
|||||||
showSource(node) {
|
showSource(node) {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const {
|
const {
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
} = Util.extract(graph, 'in', 1, [ node ]);
|
} = Util.extract(graph, 'in', 1, [ node ]);
|
||||||
graph.highlightSubgraph({
|
graph.highlightSubgraph({
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
});
|
});
|
||||||
// show the hided edge, which is not tree edge and it is in the es
|
// show the hided edge, which is not tree edge and it is in the es
|
||||||
// and the source and targert of the edge are both visible
|
// and the source and targert of the edge are both visible
|
||||||
@ -75,7 +75,7 @@ class Menu {
|
|||||||
Util.each(edges, edge => {
|
Util.each(edges, edge => {
|
||||||
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
||||||
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
||||||
Util.each(re_edges, e => {
|
Util.each(reEdges, e => {
|
||||||
if (edge.id === e.id) {
|
if (edge.id === e.id) {
|
||||||
edge.show();
|
edge.show();
|
||||||
}
|
}
|
||||||
@ -89,12 +89,12 @@ class Menu {
|
|||||||
showTargets(node) {
|
showTargets(node) {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const {
|
const {
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
} = Util.extract(graph, 'out', 1, [ node ]);
|
} = Util.extract(graph, 'out', 1, [ node ]);
|
||||||
graph.highlightSubgraph({
|
graph.highlightSubgraph({
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
});
|
});
|
||||||
// show the hided edge, which is not tree edge and it is in the es
|
// show the hided edge, which is not tree edge and it is in the es
|
||||||
// and the source and targert of the edge are both visible
|
// and the source and targert of the edge are both visible
|
||||||
@ -102,7 +102,7 @@ class Menu {
|
|||||||
Util.each(edges, edge => {
|
Util.each(edges, edge => {
|
||||||
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
||||||
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
||||||
Util.each(re_edges, e => {
|
Util.each(reEdges, e => {
|
||||||
if (edge.id === e.id) {
|
if (edge.id === e.id) {
|
||||||
edge.show();
|
edge.show();
|
||||||
}
|
}
|
||||||
@ -115,12 +115,12 @@ class Menu {
|
|||||||
showAll(node) {
|
showAll(node) {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const {
|
const {
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
} = Util.extract(graph, 'bi', 1, [ node ]);
|
} = Util.extract(graph, 'bi', 1, [ node ]);
|
||||||
graph.highlightSubgraph({
|
graph.highlightSubgraph({
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
});
|
});
|
||||||
// show the hided edge, which is not tree edge and it is in the es
|
// show the hided edge, which is not tree edge and it is in the es
|
||||||
// and the source and targert of the edge are both visible
|
// and the source and targert of the edge are both visible
|
||||||
@ -128,7 +128,7 @@ class Menu {
|
|||||||
Util.each(edges, edge => {
|
Util.each(edges, edge => {
|
||||||
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
if (!edge.isVisible() && !edge.getModel().isTreeEdge &&
|
||||||
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
edge.getSource().isVisible() && edge.getTarget().isVisible()) {
|
||||||
Util.each(re_edges, e => {
|
Util.each(reEdges, e => {
|
||||||
if (edge.id === e.id) {
|
if (edge.id === e.id) {
|
||||||
edge.show();
|
edge.show();
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ const Fisheye = require('./tool');
|
|||||||
class Plugin {
|
class Plugin {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
Util.mix(this, {
|
Util.mix(this, {
|
||||||
ori_xs: [],
|
oriXs: [],
|
||||||
ori_ys: []
|
oriYs: []
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
init() {
|
init() {
|
||||||
@ -23,10 +23,10 @@ class Plugin {
|
|||||||
graph.on('mousemove', Util.throttle(ev => {
|
graph.on('mousemove', Util.throttle(ev => {
|
||||||
const nodes = graph.getNodes();
|
const nodes = graph.getNodes();
|
||||||
const size = nodes.length;
|
const size = nodes.length;
|
||||||
if (this.ori_xs.length !== size) return;
|
if (this.oriXs.length !== size) return;
|
||||||
for (let i = 0; i < size; i += 1) {
|
for (let i = 0; i < size; i += 1) {
|
||||||
nodes[i].getModel().x = this.ori_xs[i];
|
nodes[i].getModel().x = this.oriXs[i];
|
||||||
nodes[i].getModel().y = this.ori_ys[i];
|
nodes[i].getModel().y = this.oriYs[i];
|
||||||
}
|
}
|
||||||
fisheye.zoom(ev.x, ev.y);
|
fisheye.zoom(ev.x, ev.y);
|
||||||
graph.updateNodePosition();
|
graph.updateNodePosition();
|
||||||
@ -39,16 +39,16 @@ class Plugin {
|
|||||||
if (ev === undefined || ev.item === undefined) {
|
if (ev === undefined || ev.item === undefined) {
|
||||||
for (let i = 0; i < size; i++) {
|
for (let i = 0; i < size; i++) {
|
||||||
if (nodes[i].getModel().x === undefined) return;
|
if (nodes[i].getModel().x === undefined) return;
|
||||||
this.ori_xs[i] = nodes[i].getModel().x;
|
this.oriXs[i] = nodes[i].getModel().x;
|
||||||
this.ori_ys[i] = nodes[i].getModel().y;
|
this.oriYs[i] = nodes[i].getModel().y;
|
||||||
}
|
}
|
||||||
} else if (ev.item.type !== 'node' || (!ev.updateModel.x && !ev.updateModel.y)) return;
|
} else if (ev.item.type !== 'node' || (!ev.updateModel.x && !ev.updateModel.y)) return;
|
||||||
else {
|
else {
|
||||||
const item = graph.find(ev.originModel.id);
|
const item = graph.find(ev.originModel.id);
|
||||||
for (let i = 0; i < size; i++) {
|
for (let i = 0; i < size; i++) {
|
||||||
if (nodes[i].getModel().id === item.id) {
|
if (nodes[i].getModel().id === item.id) {
|
||||||
this.ori_x[i] = ev.updateModel.x;
|
this.oriXs[i] = ev.updateModel.x;
|
||||||
this.ori_y[i] = ev.updateModel.y;
|
this.oriYs[i] = ev.updateModel.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ class Tool {
|
|||||||
const dist = Math.hypot(node.x - center[0], node.y - center[1]);
|
const dist = Math.hypot(node.x - center[0], node.y - center[1]);
|
||||||
if (dist < radius) {
|
if (dist < radius) {
|
||||||
// take the center as the origin
|
// take the center as the origin
|
||||||
const moved_coords = [ node.x - center[0], node.y - center[1] ];
|
const movedCoords = [ node.x - center[0], node.y - center[1] ];
|
||||||
// transform to polar coordinates
|
// transform to polar coordinates
|
||||||
const { p, theta } = Rect2Polar(moved_coords[0], moved_coords[1]);
|
const { p, theta } = Rect2Polar(movedCoords[0], movedCoords[1]);
|
||||||
const pf = radius * (((d + 1) * (p / radius)) / (d * (p / radius) + 1)); // after fisheye zooming
|
const pf = radius * (((d + 1) * (p / radius)) / (d * (p / radius) + 1)); // after fisheye zooming
|
||||||
// transform to rect coordinates
|
// transform to rect coordinates
|
||||||
const { x, y } = Polar2Rect(pf, theta);
|
const { x, y } = Polar2Rect(pf, theta);
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
Highlight a subgraph and weaken the rest of the graph.
|
Highlight a subgraph and weaken the rest of the graph.
|
||||||
|
|
||||||
interface:
|
interface:
|
||||||
- highlightSubgraph(hl_items)
|
- highlightSubgraph(hlItems)
|
||||||
hightlight a subgraph
|
hightlight a subgraph
|
||||||
params:
|
params:
|
||||||
- hl_items: the items which will be highlighted
|
- hlItems: the items which will be highlighted
|
||||||
|
|
||||||
- restoreGraph()
|
- restoreGraph()
|
||||||
restore the graph to the un-lighlighted style.
|
restore the graph to the un-lighlighted style.
|
||||||
@ -58,7 +58,7 @@ const graph = new G6.Graph({
|
|||||||
graph.read(data);
|
graph.read(data);
|
||||||
const nodes = graph.getNodes();
|
const nodes = graph.getNodes();
|
||||||
const edges = graph.getEdges();
|
const edges = graph.getEdges();
|
||||||
const re_nodes = [nodes[0], nodes[1]];
|
const reNodes = [nodes[0], nodes[1]];
|
||||||
const re_edges = [edges[0]];
|
const reEdges = [edges[0]];
|
||||||
graph.highlightSubgraph({re_nodes, re_edges});
|
graph.highlightSubgraph({reNodes, reEdges});
|
||||||
```
|
```
|
@ -35,11 +35,11 @@ class Plugin {
|
|||||||
this.graph.restoreGraph = this.restoreGraph;
|
this.graph.restoreGraph = this.restoreGraph;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
highlightSubgraph(hl_items) {
|
highlightSubgraph(hlItems) {
|
||||||
this.restoreGraph();
|
this.restoreGraph();
|
||||||
// sort the group items
|
// sort the group items
|
||||||
const ns = hl_items.re_nodes;
|
const ns = hlItems.reNodes;
|
||||||
const es = hl_items.re_edges;
|
const es = hlItems.reEdges;
|
||||||
const group = this.getItemGroup();
|
const group = this.getItemGroup();
|
||||||
const items = this.getItems();
|
const items = this.getItems();
|
||||||
Util.each(items, i => {
|
Util.each(items, i => {
|
||||||
|
@ -58,7 +58,8 @@ class Plugin {
|
|||||||
* 是否数据对齐
|
* 是否数据对齐
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
nice: true
|
nice: true,
|
||||||
|
curRange: [ 0, 100 ]
|
||||||
}, {
|
}, {
|
||||||
itemType,
|
itemType,
|
||||||
dim,
|
dim,
|
||||||
@ -145,16 +146,12 @@ class Plugin {
|
|||||||
domain = this._trainNumberScale(itemType, data);
|
domain = this._trainNumberScale(itemType, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rangeLength = range.length;
|
const rangeLength = range.length;
|
||||||
const domainLength = domain.length;
|
const domainLength = domain.length;
|
||||||
if (rangeLength !== domainLength) {
|
if (rangeLength !== domainLength && scaleType === 'Category') {
|
||||||
const domainStep = (domain[1] - domain[0]) / (rangeLength - 1);
|
throw new Error('please set the same length of range to the domain!');
|
||||||
Util.each(range, (v, i) => {
|
|
||||||
domain[i] = domain[0] + i * domainStep;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (domain[0] === domain[1]) {
|
if (domainLength === 2 && domain[0] === domain[1]) {
|
||||||
if (domain[0] > 0) {
|
if (domain[0] > 0) {
|
||||||
domain[0] = 0;
|
domain[0] = 0;
|
||||||
} else if (domain[0] < 0) {
|
} else if (domain[0] < 0) {
|
||||||
@ -176,10 +173,18 @@ class Plugin {
|
|||||||
const scaleType = this._getScaleType(data);
|
const scaleType = this._getScaleType(data);
|
||||||
const channel = this.channel;
|
const channel = this.channel;
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
|
|
||||||
const containerId = this.legendCfg.containerId;
|
const containerId = this.legendCfg.containerId;
|
||||||
let legendContainer = this.legendCfg.container;
|
let legendContainer = this.legendCfg.container;
|
||||||
if (containerId === undefined && legendContainer === undefined) {
|
if (legendContainer === undefined) {
|
||||||
legendContainer = Util.createDOM('<div class="legend-container"></div>');
|
if (containerId === undefined) {
|
||||||
|
legendContainer = Util.createDOM('<div class="legend-container"></div>');
|
||||||
|
} else {
|
||||||
|
legendContainer = document.getElementById(containerId);
|
||||||
|
if (legendContainer === undefined || legendContainer === null) {
|
||||||
|
throw new Error('please set the container for the graph !');
|
||||||
|
}
|
||||||
|
}
|
||||||
const container = graph.getGraphContainer();
|
const container = graph.getGraphContainer();
|
||||||
container.appendChild(legendContainer);
|
container.appendChild(legendContainer);
|
||||||
}
|
}
|
||||||
@ -201,27 +206,30 @@ class Plugin {
|
|||||||
}
|
}
|
||||||
// the listener to filter nodes and edges
|
// the listener to filter nodes and edges
|
||||||
const slider = legend.get('slider');
|
const slider = legend.get('slider');
|
||||||
slider.on('sliderchange', Util.throttle(ev => {
|
const domain = this.scale.values;
|
||||||
const domain = this.scale.values;
|
const dim = this.dim;
|
||||||
const cur_range = ev.range;
|
graph.addFilter(item => {
|
||||||
const dim = this.dim;
|
if (item.isNode) {
|
||||||
graph.addFilter(item => {
|
const val = item.model[dim];
|
||||||
if (item.isNode) {
|
const percent = 100 * (val - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
||||||
const val = item.model[dim];
|
if (percent > this.curRange[1] || percent < this.curRange[0]) {
|
||||||
const percent = 100 * (val - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
return false;
|
||||||
if (percent > cur_range[1] || percent < cur_range[0]) return false;
|
|
||||||
return true;
|
|
||||||
} else if (item.isEdge) {
|
|
||||||
const source_val = item.source.model[dim];
|
|
||||||
const source_percent = 100 * (source_val - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
|
||||||
const source_visible = (source_percent <= cur_range[1] && source_percent >= cur_range[0]);
|
|
||||||
const target_val = item.target.model[dim];
|
|
||||||
const target_percent = 100 * (target_val - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
|
||||||
const target_visible = (target_percent <= cur_range[1] && target_percent >= cur_range[0]);
|
|
||||||
if (!source_visible || !target_visible) return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
return true;
|
||||||
|
} else if (item.isEdge) {
|
||||||
|
const sourceVal = item.source.model[dim];
|
||||||
|
const sourcePercent = 100 * (sourceVal - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
||||||
|
const sourceVisible = (sourcePercent <= this.curRange[1] && sourcePercent >= this.curRange[0]);
|
||||||
|
const targetVal = item.target.model[dim];
|
||||||
|
const targetPercent = 100 * (targetVal - domain[0]) / (domain[domain.length - 1] - domain[0]);
|
||||||
|
const targetVisible = (targetPercent <= this.curRange[1] && targetPercent >= this.curRange[0]);
|
||||||
|
if (!sourceVisible || !targetVisible) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
slider.on('sliderchange', Util.throttle(ev => {
|
||||||
|
this.curRange = ev.range;
|
||||||
graph.filter();
|
graph.filter();
|
||||||
}, 100));
|
}, 100));
|
||||||
}
|
}
|
||||||
@ -306,14 +314,14 @@ class Plugin {
|
|||||||
const items = [];
|
const items = [];
|
||||||
Util.each(range, (val, i) => {
|
Util.each(range, (val, i) => {
|
||||||
const percent = (domain[i] - scale.min) / (scale.max - scale.min);
|
const percent = (domain[i] - scale.min) / (scale.max - scale.min);
|
||||||
let item_text = domain[i];
|
let itemText = domain[i];
|
||||||
if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) {
|
if (legendCfg.formatter !== undefined && legendCfg.formatter !== null) {
|
||||||
item_text = legendCfg.formatter(domain[i]);
|
itemText = legendCfg.formatter(domain[i]);
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
text: domain[i],
|
text: domain[i],
|
||||||
attrValue: val,
|
attrValue: val,
|
||||||
value: item_text, // the number label of the slider
|
value: itemText, // the number label of the slider
|
||||||
scaleValue: percent
|
scaleValue: percent
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -361,14 +369,14 @@ class Plugin {
|
|||||||
const items = [];
|
const items = [];
|
||||||
Util.each(range, (val, i) => {
|
Util.each(range, (val, i) => {
|
||||||
const dom = domain[0] + domainStep * i;
|
const dom = domain[0] + domainStep * i;
|
||||||
let item_text = dom;
|
let itemText = dom;
|
||||||
if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) {
|
if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) {
|
||||||
item_text = legendCfg.formatter(dom);
|
itemText = legendCfg.formatter(dom);
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
text: dom,
|
text: dom,
|
||||||
attrValue: val,
|
attrValue: val,
|
||||||
value: item_text // the number label of the slider
|
value: itemText // the number label of the slider
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const cfg = {
|
const cfg = {
|
||||||
@ -407,9 +415,8 @@ class Plugin {
|
|||||||
return scale.scale(model[dim]) * 2;
|
return scale.scale(model[dim]) * 2;
|
||||||
} else if (channel === 'color') {
|
} else if (channel === 'color') {
|
||||||
return color.mapping(model[dim])[0];
|
return color.mapping(model[dim])[0];
|
||||||
} else if (itemType === 'edge' && channel === 'size') {
|
|
||||||
return scale.scale(model[dim]);
|
|
||||||
}
|
}
|
||||||
|
// itemType === 'edge' && channel === 'size'
|
||||||
return scale.scale(model[dim]);
|
return scale.scale(model[dim]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -419,11 +426,6 @@ class Plugin {
|
|||||||
max: domain[domain.length - 1]
|
max: domain[domain.length - 1]
|
||||||
};
|
};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Linear':
|
|
||||||
return Scale.linear({
|
|
||||||
min: domain[0],
|
|
||||||
max: domain[domain.length - 1]
|
|
||||||
});
|
|
||||||
case 'Category':
|
case 'Category':
|
||||||
return Scale.cat({
|
return Scale.cat({
|
||||||
values: domain
|
values: domain
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## textDisplay
|
## textDisplay
|
||||||
|
|
||||||
Hide the labels when the width of the text are 2 times bigger than the parent node.
|
Hide the labels when the width of the text is 2 times bigger than the parent node.
|
||||||
|
|
||||||
|
|
||||||
## use
|
## use
|
||||||
|
@ -29,10 +29,10 @@ class Plugin {
|
|||||||
const label = node.getLabel();
|
const label = node.getLabel();
|
||||||
const model = node.getModel();
|
const model = node.getModel();
|
||||||
const labelBox = label.getBBox();
|
const labelBox = label.getBBox();
|
||||||
const label_width = labelBox.maxX - labelBox.minX;
|
const labelWidth = labelBox.maxX - labelBox.minX;
|
||||||
const node_width = model.size * scale;
|
const nodeWidth = model.size * scale;
|
||||||
if (label_width === 0) return;
|
if (labelWidth === 0) return;
|
||||||
const ratio = label_width / node_width;
|
const ratio = labelWidth / nodeWidth;
|
||||||
if (ratio > 2) {
|
if (ratio > 2) {
|
||||||
label.hide();
|
label.hide();
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,58 +10,58 @@ const Util = G6.Util;
|
|||||||
|
|
||||||
const extractSubgraph = {
|
const extractSubgraph = {
|
||||||
extract(graph, type, step, focusNodes) {
|
extract(graph, type, step, focusNodes) {
|
||||||
const re_edges = [];
|
const reEdges = [];
|
||||||
Util.each(focusNodes, fn => {
|
Util.each(focusNodes, fn => {
|
||||||
if (type === 'in') {
|
if (type === 'in') {
|
||||||
const in_edges = fn.getInEdges();
|
const inEdges = fn.getInEdges();
|
||||||
Util.each(in_edges, ie => {
|
Util.each(inEdges, ie => {
|
||||||
re_edges.push(ie);
|
reEdges.push(ie);
|
||||||
});
|
});
|
||||||
} else if (type === 'out') {
|
} else if (type === 'out') {
|
||||||
const out_edges = fn.getOutEdges();
|
const outEdges = fn.getOutEdges();
|
||||||
Util.each(out_edges, oe => {
|
Util.each(outEdges, oe => {
|
||||||
re_edges.push(oe);
|
reEdges.push(oe);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const in_edges = fn.getInEdges();
|
const inEdges = fn.getInEdges();
|
||||||
Util.each(in_edges, ie => {
|
Util.each(inEdges, ie => {
|
||||||
re_edges.push(ie);
|
reEdges.push(ie);
|
||||||
});
|
});
|
||||||
const out_edges = fn.getOutEdges();
|
const outEdges = fn.getOutEdges();
|
||||||
Util.each(out_edges, oe => {
|
Util.each(outEdges, oe => {
|
||||||
re_edges.push(oe);
|
reEdges.push(oe);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const re_nodes = [];
|
const reNodes = [];
|
||||||
Util.each(focusNodes, fn => {
|
Util.each(focusNodes, fn => {
|
||||||
re_nodes.push(fn);
|
reNodes.push(fn);
|
||||||
Util.each(re_edges, e => {
|
Util.each(reEdges, e => {
|
||||||
const source = e.getSource();
|
const source = e.getSource();
|
||||||
const target = e.getTarget();
|
const target = e.getTarget();
|
||||||
if (source.id !== fn.id) re_nodes.push(source);
|
if (source.id !== fn.id) reNodes.push(source);
|
||||||
if (target.id !== fn.id) re_nodes.push(target);
|
if (target.id !== fn.id) reNodes.push(target);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
re_nodes,
|
reNodes,
|
||||||
re_edges
|
reEdges
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setZIndex(ns, es, min_z) {
|
setZIndex(ns, es, minZ) {
|
||||||
const graph = this.graph;
|
const graph = this.graph;
|
||||||
const nodes = graph.getNodes();
|
const nodes = graph.getNodes();
|
||||||
const edges = graph.getEdges();
|
const edges = graph.getEdges();
|
||||||
Util.each(nodes, node => {
|
Util.each(nodes, node => {
|
||||||
Util.each(ns, n => {
|
Util.each(ns, n => {
|
||||||
if (node.id === n.id) node.getGraphicGroup().setSilent('zIndex', min_z + 2);
|
if (node.id === n.id) node.getGraphicGroup().setSilent('zIndex', minZ + 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Util.each(edges, edge => {
|
Util.each(edges, edge => {
|
||||||
Util.each(es, e => {
|
Util.each(es, e => {
|
||||||
if (edge.id === e.id) edge.getGraphicGroup().setSilent('zIndex', min_z + 1);
|
if (edge.id === e.id) edge.getGraphicGroup().setSilent('zIndex', minZ + 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
graph.getItemGroup().sort();
|
graph.getItemGroup().sort();
|
||||||
|
@ -106,7 +106,7 @@ class Graph extends Base {
|
|||||||
const cfg = {};
|
const cfg = {};
|
||||||
|
|
||||||
Mixins.forEach(Mixin => {
|
Mixins.forEach(Mixin => {
|
||||||
Util.mix(cfg, Mixin.CFG, inputCfg);
|
Util.mix(cfg, Util.cloneDeep(Mixin.CFG), inputCfg);
|
||||||
});
|
});
|
||||||
super(cfg);
|
super(cfg);
|
||||||
// plugin should init before all
|
// plugin should init before all
|
||||||
|
@ -1 +1 @@
|
|||||||
module.exports = '2.1.0-beta';
|
module.exports = '2.1.0-beta.3';
|
||||||
|
@ -3,130 +3,145 @@ const Layout = require('../../../build/plugin.layout.forceAtlas2');
|
|||||||
const expect = require('chai').expect;
|
const expect = require('chai').expect;
|
||||||
const Util = G6.Util;
|
const Util = G6.Util;
|
||||||
|
|
||||||
// document.body.appendChild(Util.createDOM(`
|
document.body.appendChild(Util.createDOM(`
|
||||||
// <div id='mountNode'></div>
|
<div id='mountNode'></div>
|
||||||
// `));
|
`));
|
||||||
|
|
||||||
// describe('custom layout test', () => {
|
describe('custom layout test', () => {
|
||||||
// const layout = new Layout();
|
const originInnerHTML = document.getElementById('mountNode').innerHTML;
|
||||||
// const data = {
|
const layout = new Layout();
|
||||||
// nodes: [{
|
const data = {
|
||||||
// id: 'node1'
|
nodes: [{
|
||||||
// }, {
|
id: 'node1'
|
||||||
// id: 'node2'
|
}, {
|
||||||
// }, {
|
id: 'node2'
|
||||||
// id: 'node3'
|
}, {
|
||||||
// }],
|
id: 'node3'
|
||||||
// edges: [{
|
}],
|
||||||
// target: 'node2',
|
edges: [{
|
||||||
// source: 'node1'
|
target: 'node2',
|
||||||
// }, {
|
source: 'node1'
|
||||||
// target: 'node2',
|
}, {
|
||||||
// source: 'node3'
|
target: 'node2',
|
||||||
// }]
|
source: 'node3'
|
||||||
// };
|
}]
|
||||||
// const graph = new G6.Graph({
|
};
|
||||||
// container: 'mountNode',
|
const graph = new G6.Graph({
|
||||||
// width: 500,
|
container: 'mountNode',
|
||||||
// height: 500,
|
width: 500,
|
||||||
// plugins: [ layout ]
|
height: 500,
|
||||||
// });
|
plugins: [ layout ]
|
||||||
|
});
|
||||||
|
|
||||||
// graph.read(data);
|
graph.read(data);
|
||||||
// it('layout node positions', () => {
|
it('graph render', () => {
|
||||||
// const node1Model = graph.find('node1').getModel();
|
expect(document.getElementById('mountNode').innerHTML).not.eql(originInnerHTML);
|
||||||
// expect(node1Model.x).not.eql(undefined);
|
});
|
||||||
// expect(node1Model.y).not.eql(undefined);
|
it('layout node positions', () => {
|
||||||
// });
|
graph.on('afterlayout', () => {
|
||||||
// it('graph destroy', () => {
|
const node1Model = graph.find('node1').getModel();
|
||||||
// graph.destroy();
|
expect(node1Model.x).not.eql(undefined);
|
||||||
// });
|
expect(node1Model.y).not.eql(undefined);
|
||||||
// });
|
});
|
||||||
|
});
|
||||||
|
it('graph destroy', () => {
|
||||||
|
graph.on('afterlayout', () => {
|
||||||
|
graph.destroy();
|
||||||
|
expect(document.getElementById('mountNode').innerHTML).eql(originInnerHTML);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// describe('node nonoverlapping test', () => {
|
describe('node nonoverlapping test', () => {
|
||||||
// const layout = new Layout({ prevOverlapping: true });
|
const layout = new Layout({ prevOverlapping: true });
|
||||||
// const data = {
|
const data = {
|
||||||
// nodes: [{
|
nodes: [{
|
||||||
// id: 'node1'
|
id: 'node1'
|
||||||
// }, {
|
}, {
|
||||||
// id: 'node2'
|
id: 'node2'
|
||||||
// }, {
|
}, {
|
||||||
// id: 'node3'
|
id: 'node3'
|
||||||
// }],
|
}],
|
||||||
// edges: [{
|
edges: [{
|
||||||
// target: 'node2',
|
target: 'node2',
|
||||||
// source: 'node1'
|
source: 'node1'
|
||||||
// }, {
|
}, {
|
||||||
// target: 'node2',
|
target: 'node2',
|
||||||
// source: 'node3'
|
source: 'node3'
|
||||||
// }]
|
}]
|
||||||
// };
|
};
|
||||||
// const graph = new G6.Graph({
|
const graph = new G6.Graph({
|
||||||
// container: 'mountNode',
|
container: 'mountNode',
|
||||||
// width: 500,
|
width: 500,
|
||||||
// height: 500,
|
height: 500,
|
||||||
// plugins: [ layout ]
|
plugins: [ layout ]
|
||||||
// });
|
});
|
||||||
// graph.read(data);
|
graph.read(data);
|
||||||
// it('overlapping', () => {
|
it('overlapping', () => {
|
||||||
// const node1Model = graph.find('node1').getModel();
|
graph.on('afterlayout', () => {
|
||||||
// const node2Model = graph.find('node2').getModel();
|
const node1Model = graph.find('node1').getModel();
|
||||||
// const node3Model = graph.find('node3').getModel();
|
const node2Model = graph.find('node2').getModel();
|
||||||
// const node1BBox = node1Model.getBBox();
|
const node3Model = graph.find('node3').getModel();
|
||||||
// const node2BBox = node2Model.getBBox();
|
const node1BBox = node1Model.getBBox();
|
||||||
// const node3BBox = node3Model.getBBox();
|
const node2BBox = node2Model.getBBox();
|
||||||
// const node1Radius = (node1BBox.maxX - node1BBox.minX) / 2;
|
const node3BBox = node3Model.getBBox();
|
||||||
// const node2Radius = (node2BBox.maxX - node2BBox.minX) / 2;
|
const node1Radius = (node1BBox.maxX - node1BBox.minX) / 2;
|
||||||
// const node3Radius = (node3BBox.maxX - node3BBox.minX) / 2;
|
const node2Radius = (node2BBox.maxX - node2BBox.minX) / 2;
|
||||||
// const dist12 = Math.plot(node1Model.x - node2Model.x, node1Model.y - node2Model.y);
|
const node3Radius = (node3BBox.maxX - node3BBox.minX) / 2;
|
||||||
// const dist23 = Math.plot(node2Model.x - node3Model.x, node2Model.y - node3Model.y);
|
const dist12 = Math.plot(node1Model.x - node2Model.x, node1Model.y - node2Model.y);
|
||||||
// const dist13 = Math.plot(node1Model.x - node3Model.x, node1Model.y - node3Model.y);
|
const dist23 = Math.plot(node2Model.x - node3Model.x, node2Model.y - node3Model.y);
|
||||||
// expect(node1Radius - node2Radius <= dist12).eql(true);
|
const dist13 = Math.plot(node1Model.x - node3Model.x, node1Model.y - node3Model.y);
|
||||||
// expect(node2Radius - node3Radius <= dist23).eql(true);
|
expect(node1Radius - node2Radius <= dist12).eql(true);
|
||||||
// expect(node1Radius - node3Radius <= dist13).eql(true);
|
expect(node2Radius - node3Radius <= dist23).eql(true);
|
||||||
// });
|
expect(node1Radius - node3Radius <= dist13).eql(true);
|
||||||
// it('layout node positions', () => {
|
});
|
||||||
// const node1Model = graph.find('node1').getModel();
|
});
|
||||||
// expect(node1Model.x).not.eql(undefined);
|
it('layout node positions', () => {
|
||||||
// expect(node1Model.y).not.eql(undefined);
|
graph.on('afterlayout', () => {
|
||||||
// });
|
const node1Model = graph.find('node1').getModel();
|
||||||
// it('graph destroy', () => {
|
expect(node1Model.x).not.eql(undefined);
|
||||||
// graph.destroy();
|
expect(node1Model.y).not.eql(undefined);
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
it('graph destroy', () => {
|
||||||
|
graph.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// describe('barnes hut optimiazation test', () => {
|
describe('barnes hut optimiazation test', () => {
|
||||||
// const layout = new Layout();
|
const layout = new Layout();
|
||||||
// const data = {
|
const data = {
|
||||||
// nodes: [{
|
nodes: [{
|
||||||
// id: 'node1'
|
id: 'node1'
|
||||||
// }, {
|
}, {
|
||||||
// id: 'node2'
|
id: 'node2'
|
||||||
// }, {
|
}, {
|
||||||
// id: 'node3'
|
id: 'node3'
|
||||||
// }],
|
}],
|
||||||
// edges: [{
|
edges: [{
|
||||||
// target: 'node2',
|
target: 'node2',
|
||||||
// source: 'node1'
|
source: 'node1'
|
||||||
// }, {
|
}, {
|
||||||
// target: 'node2',
|
target: 'node2',
|
||||||
// source: 'node3'
|
source: 'node3'
|
||||||
// }]
|
}]
|
||||||
// };
|
};
|
||||||
// const graph = new G6.Graph({
|
const graph = new G6.Graph({
|
||||||
// container: 'mountNode',
|
container: 'mountNode',
|
||||||
// width: 500,
|
width: 500,
|
||||||
// height: 500,
|
height: 500,
|
||||||
// plugins: [ layout ]
|
plugins: [ layout ]
|
||||||
// });
|
});
|
||||||
|
|
||||||
// graph.read(data);
|
graph.read(data);
|
||||||
// it('layout node positions', () => {
|
it('layout node positions', () => {
|
||||||
// const node1Model = graph.find('node1').getModel();
|
graph.on('afterlayout', () => {
|
||||||
// expect(node1Model.x).not.eql(undefined);
|
const node1Model = graph.find('node1').getModel();
|
||||||
// expect(node1Model.y).not.eql(undefined);
|
expect(node1Model.x).not.eql(undefined);
|
||||||
// });
|
expect(node1Model.y).not.eql(undefined);
|
||||||
// it('graph destroy', () => {
|
});
|
||||||
// graph.destroy();
|
});
|
||||||
// });
|
it('graph destroy', () => {
|
||||||
// });
|
graph.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -2,15 +2,18 @@ const G6 = require('../../../src/index');
|
|||||||
const Mapper = require('../../../plugins/tool.mapper/');
|
const Mapper = require('../../../plugins/tool.mapper/');
|
||||||
const expect = require('chai').expect;
|
const expect = require('chai').expect;
|
||||||
const Util = G6.Util;
|
const Util = G6.Util;
|
||||||
const Simulate = require('event-simulate');
|
|
||||||
|
|
||||||
document.body.appendChild(Util.createDOM(`
|
document.body.appendChild(Util.createDOM(`
|
||||||
<div>
|
<div>
|
||||||
<div id='mountNode'></div>
|
<div id='mountNode'></div>
|
||||||
<div id="nodeSizeLegend"></div>
|
<div id="nodeSizeLegend"></div>
|
||||||
<div id="nodeColorLegend"></div>
|
<div id="nodeColorLegend"></div>
|
||||||
|
<div id="nodeColorLegend1"></div>
|
||||||
|
<div id="nodeColorLegend2"></div>
|
||||||
|
<div id="nodeColorLegend3"></div>
|
||||||
<div id="nodeColorCatLegend"></div>
|
<div id="nodeColorCatLegend"></div>
|
||||||
<div id="nodeSizeFormatLegend"></div>
|
<div id="nodeSizeFormatLegend"></div>
|
||||||
|
<div id="sliderChangeTestDiv"></div>
|
||||||
</div>
|
</div>
|
||||||
`));
|
`));
|
||||||
|
|
||||||
@ -62,15 +65,61 @@ describe('node size mapper test', () => {
|
|||||||
});
|
});
|
||||||
it('legend destroy', () => {
|
it('legend destroy', () => {
|
||||||
graph.destroy();
|
graph.destroy();
|
||||||
expect(document.getElementById('nodeSizeLegend').innerHTML).eql(originInnerHTML);
|
expect(document.getElementById('nodeSizeLegend')).eql(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('node color mapper test', () => {
|
describe('node color mapper domain length test', () => {
|
||||||
const originInnerHTML = document.getElementById('nodeColorLegend').innerHTML;
|
const fn = function() {
|
||||||
|
const nodeSizeMapper = new Mapper('node', 'class', 'color', [ '#ff0000', '#00ff00' ], {
|
||||||
|
legendCfg: {
|
||||||
|
layout: 'vertical'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'node1',
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
class: 'class1'
|
||||||
|
}, {
|
||||||
|
id: 'node2',
|
||||||
|
x: 300,
|
||||||
|
y: 200,
|
||||||
|
class: 'class2'
|
||||||
|
}, {
|
||||||
|
id: 'node3',
|
||||||
|
x: 400,
|
||||||
|
y: 200,
|
||||||
|
class: 'class3'
|
||||||
|
}],
|
||||||
|
edges: [{
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node1'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
plugins: [ nodeSizeMapper ]
|
||||||
|
});
|
||||||
|
graph.read(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('legend render', () => {
|
||||||
|
expect(fn).to.Throw();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('node color mapper domian equals 1 test', () => {
|
||||||
|
const originInnerHTML = document.getElementById('nodeColorLegend1').innerHTML;
|
||||||
const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], {
|
const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], {
|
||||||
|
scaleCfg: {
|
||||||
|
type: 'pow'
|
||||||
|
},
|
||||||
legendCfg: {
|
legendCfg: {
|
||||||
containerId: 'nodeColorLegend',
|
containerId: 'nodeColorLegend1',
|
||||||
layout: 'vertical'
|
layout: 'vertical'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -84,7 +133,7 @@ describe('node color mapper test', () => {
|
|||||||
id: 'node2',
|
id: 'node2',
|
||||||
x: 300,
|
x: 300,
|
||||||
y: 200,
|
y: 200,
|
||||||
weight: 2
|
weight: 1
|
||||||
}],
|
}],
|
||||||
edges: [{
|
edges: [{
|
||||||
target: 'node2',
|
target: 'node2',
|
||||||
@ -99,20 +148,123 @@ describe('node color mapper test', () => {
|
|||||||
});
|
});
|
||||||
graph.read(data);
|
graph.read(data);
|
||||||
it('legend render', () => {
|
it('legend render', () => {
|
||||||
expect(document.getElementById('nodeColorLegend').innerHTML).not.eql(originInnerHTML);
|
expect(document.getElementById('nodeColorLegend1').innerHTML).not.eql(originInnerHTML);
|
||||||
|
});
|
||||||
|
it('node color mapper', () => {
|
||||||
|
const node1Model = graph.find('node1').getModel();
|
||||||
|
const node2Model = graph.find('node2').getModel();
|
||||||
|
expect(node1Model.color).eql('#00ff00');
|
||||||
|
expect(node2Model.color).eql('#00ff00');
|
||||||
|
});
|
||||||
|
it('legend destroy', () => {
|
||||||
|
graph.destroy();
|
||||||
|
expect(document.getElementById('nodeColorLegend1')).eql(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('node color mapper domian equals 0 test', () => {
|
||||||
|
const originInnerHTML = document.getElementById('nodeColorLegend2').innerHTML;
|
||||||
|
const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], {
|
||||||
|
scaleCfg: {
|
||||||
|
type: 'pow'
|
||||||
|
},
|
||||||
|
legendCfg: {
|
||||||
|
containerId: 'nodeColorLegend2',
|
||||||
|
layout: '',
|
||||||
|
formatter: num => {
|
||||||
|
return num * num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'node1',
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
weight: 0
|
||||||
|
}, {
|
||||||
|
id: 'node2',
|
||||||
|
x: 300,
|
||||||
|
y: 200,
|
||||||
|
weight: 0
|
||||||
|
}],
|
||||||
|
edges: [{
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node1'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
plugins: [ nodeSizeMapper ]
|
||||||
|
});
|
||||||
|
graph.read(data);
|
||||||
|
it('legend render', () => {
|
||||||
|
expect(document.getElementById('nodeColorLegend2').innerHTML).not.eql(originInnerHTML);
|
||||||
|
});
|
||||||
|
it('node color mapper', () => {
|
||||||
|
const node1Model = graph.find('node1').getModel();
|
||||||
|
const node2Model = graph.find('node2').getModel();
|
||||||
|
expect(node1Model.color).eql('#00ff00');
|
||||||
|
expect(node2Model.color).eql('#00ff00');
|
||||||
|
});
|
||||||
|
it('legend destroy', () => {
|
||||||
|
graph.destroy();
|
||||||
|
expect(document.getElementById('nodeColorLegend2')).eql(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('node color mapper domian equals -1 test', () => {
|
||||||
|
const originInnerHTML = document.getElementById('nodeColorLegend3').innerHTML;
|
||||||
|
const nodeSizeMapper = new Mapper('node', 'weight', 'color', [ '#ff0000', '#00ff00' ], {
|
||||||
|
scaleCfg: {
|
||||||
|
type: 'pow'
|
||||||
|
},
|
||||||
|
legendCfg: {
|
||||||
|
containerId: 'nodeColorLegend3'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'node1',
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
weight: -1
|
||||||
|
}, {
|
||||||
|
id: 'node2',
|
||||||
|
x: 300,
|
||||||
|
y: 200,
|
||||||
|
weight: -1
|
||||||
|
}],
|
||||||
|
edges: [{
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node1'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
plugins: [ nodeSizeMapper ]
|
||||||
|
});
|
||||||
|
graph.read(data);
|
||||||
|
it('legend render', () => {
|
||||||
|
expect(document.getElementById('nodeColorLegend3').innerHTML).not.eql(originInnerHTML);
|
||||||
});
|
});
|
||||||
it('node color mapper', () => {
|
it('node color mapper', () => {
|
||||||
const node1Model = graph.find('node1').getModel();
|
const node1Model = graph.find('node1').getModel();
|
||||||
const node2Model = graph.find('node2').getModel();
|
const node2Model = graph.find('node2').getModel();
|
||||||
expect(node1Model.color).eql('#ff0000');
|
expect(node1Model.color).eql('#ff0000');
|
||||||
expect(node2Model.color).eql('#00ff00');
|
expect(node2Model.color).eql('#ff0000');
|
||||||
});
|
});
|
||||||
it('legend destroy', () => {
|
it('legend destroy', () => {
|
||||||
graph.destroy();
|
graph.destroy();
|
||||||
expect(document.getElementById('nodeColorLegend').innerHTML).eql(originInnerHTML);
|
expect(document.getElementById('nodeColorLegend3')).eql(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('edge size mapper test', () => {
|
describe('edge size mapper test', () => {
|
||||||
const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [ 10, 50 ], {
|
const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [ 10, 50 ], {
|
||||||
legendCfg: null
|
legendCfg: null
|
||||||
@ -159,6 +311,53 @@ describe('edge size mapper test', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('edge size mapper vertical test', () => {
|
||||||
|
const edgeSizeMapper = new Mapper('edge', 'weight', 'size', [ 10, 50 ], {
|
||||||
|
legendCfg: {
|
||||||
|
layout: 'vertical'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'node1',
|
||||||
|
x: 100,
|
||||||
|
y: 200
|
||||||
|
}, {
|
||||||
|
id: 'node2',
|
||||||
|
x: 300,
|
||||||
|
y: 200
|
||||||
|
}, {
|
||||||
|
id: 'node3',
|
||||||
|
x: 400,
|
||||||
|
y: 200
|
||||||
|
}],
|
||||||
|
edges: [{
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node1',
|
||||||
|
weight: 1
|
||||||
|
}, {
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node3',
|
||||||
|
weight: 2
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
plugins: [ edgeSizeMapper ]
|
||||||
|
});
|
||||||
|
graph.read(data);
|
||||||
|
it('edge size mapper', () => {
|
||||||
|
const edges = graph.getEdges();
|
||||||
|
const edge1Model = edges[0].getModel();
|
||||||
|
const edge2Model = edges[1].getModel();
|
||||||
|
const size1 = edge1Model.size;
|
||||||
|
const size2 = edge2Model.size;
|
||||||
|
expect(size1).eql(10);
|
||||||
|
expect(size2).eql(50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('node color category mapper test', () => {
|
describe('node color category mapper test', () => {
|
||||||
const originInnerHTML = document.getElementById('nodeColorCatLegend').innerHTML;
|
const originInnerHTML = document.getElementById('nodeColorCatLegend').innerHTML;
|
||||||
@ -202,12 +401,11 @@ describe('node color category mapper test', () => {
|
|||||||
});
|
});
|
||||||
it('legend destroy', () => {
|
it('legend destroy', () => {
|
||||||
graph.destroy();
|
graph.destroy();
|
||||||
expect(document.getElementById('nodeColorCatLegend').innerHTML).eql(originInnerHTML);
|
expect(document.getElementById('nodeColorCatLegend')).eql(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('node size mapper with formatter test', () => {
|
describe('node size mapper with formatter test', () => {
|
||||||
const originInnerHTML = document.getElementById('nodeSizeFormatLegend').innerHTML;
|
|
||||||
const nodeSizeMapper = new Mapper('node', 'weight', 'size', [ 10, 50 ], {
|
const nodeSizeMapper = new Mapper('node', 'weight', 'size', [ 10, 50 ], {
|
||||||
legendCfg: {
|
legendCfg: {
|
||||||
formatter: num => {
|
formatter: num => {
|
||||||
@ -239,9 +437,6 @@ describe('node size mapper with formatter test', () => {
|
|||||||
plugins: [ nodeSizeMapper ]
|
plugins: [ nodeSizeMapper ]
|
||||||
});
|
});
|
||||||
graph.read(data);
|
graph.read(data);
|
||||||
it('legend render', () => {
|
|
||||||
expect(document.getElementById('nodeSizeFormatLegend').innerHTML).not.eql(originInnerHTML);
|
|
||||||
});
|
|
||||||
it('node size mapper', () => {
|
it('node size mapper', () => {
|
||||||
const node1Model = graph.find('node1').getModel();
|
const node1Model = graph.find('node1').getModel();
|
||||||
const node2Model = graph.find('node2').getModel();
|
const node2Model = graph.find('node2').getModel();
|
||||||
@ -250,9 +445,45 @@ describe('node size mapper with formatter test', () => {
|
|||||||
expect(size1).eql(10);
|
expect(size1).eql(10);
|
||||||
expect(size2).eql(50);
|
expect(size2).eql(50);
|
||||||
});
|
});
|
||||||
it('legend destroy', () => {
|
});
|
||||||
graph.destroy();
|
|
||||||
expect(document.getElementById('nodeSizeFormatLegend').innerHTML).eql(originInnerHTML);
|
|
||||||
|
describe('container undefined test', () => {
|
||||||
|
const fn = function() {
|
||||||
|
const nodeSizeMapper = new Mapper('node', 'weight', 'size', [ 10, 50 ], {
|
||||||
|
legendCfg: {
|
||||||
|
containerId: 'undefinedDOM'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'node1',
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
weight: 2
|
||||||
|
}, {
|
||||||
|
id: 'node2',
|
||||||
|
x: 300,
|
||||||
|
y: 200,
|
||||||
|
weight: 3
|
||||||
|
}],
|
||||||
|
edges: [{
|
||||||
|
target: 'node2',
|
||||||
|
source: 'node1'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
plugins: [ nodeSizeMapper ]
|
||||||
|
});
|
||||||
|
graph.read(data);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
it('legend render', () => {
|
||||||
|
expect(fn).to.Throw();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -268,7 +499,7 @@ describe('slider test', () => {
|
|||||||
id: 'node1',
|
id: 'node1',
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 200,
|
y: 200,
|
||||||
weight: 2
|
weight: 1
|
||||||
}, {
|
}, {
|
||||||
id: 'node2',
|
id: 'node2',
|
||||||
x: 300,
|
x: 300,
|
||||||
@ -286,16 +517,11 @@ describe('slider test', () => {
|
|||||||
height: 500,
|
height: 500,
|
||||||
plugins: [ nodeSizeMapper ]
|
plugins: [ nodeSizeMapper ]
|
||||||
});
|
});
|
||||||
const mouseEventWrapper = graph.getMouseEventWrapper();
|
|
||||||
graph.read(data);
|
graph.read(data);
|
||||||
|
|
||||||
it('legend sliderchange', () => {
|
it('legend sliderchange', () => {
|
||||||
const node1Model = graph.find('node1').getModel();
|
const slider = nodeSizeMapper.legend.get('slider');
|
||||||
const clientPoint = graph.getClientPoint(node1Model);
|
slider.emit('sliderchange', { range: [ 0, 50 ] });
|
||||||
Simulate.simulate(mouseEventWrapper, 'sliderchange', {
|
expect(document.getElementById('sliderChangeTestDiv')).not.eql(undefined);
|
||||||
clientX: clientPoint.x - 50,
|
|
||||||
clientY: clientPoint.y
|
|
||||||
});
|
|
||||||
expect(document.getElementsById('sliderChangeTestDiv')).not.eql(undefined);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user