-
Notifications
You must be signed in to change notification settings - Fork 250
/
Copy pathindex.html
107 lines (96 loc) · 3.79 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<br/>
<div style="text-align: center; color: silver">
<b>New node:</b> click on the canvas, <b>New link:</b> drag one node close enough to another one,
<b>Rename</b> node or link by clicking on it, <b>Remove</b> node or link by right-clicking on it
</div>
<div id="graph"></div>
<script>
let nodeIdCounter = 0, linkIdCounter = 0;
let nodes = [], links = [];
let dragSourceNode = null, interimLink = null;
const snapInDistance = 15;
const snapOutDistance = 40;
const updateGraphData = () => {
Graph.graphData({ nodes: nodes, links: links });
};
const distance = (node1, node2) => {
return Math.sqrt(Math.pow(node1.x - node2.x, 2) + Math.pow(node1.y - node2.y, 2));
};
const rename = (nodeOrLink, type) => {
let value = prompt('Name this ' + type + ':', nodeOrLink.name);
if (!value) {
return;
}
nodeOrLink.name = value;
updateGraphData();
};
const setInterimLink = (source, target) => {
let linkId = linkIdCounter ++;
interimLink = { id: linkId, source: source, target: target, name: 'link_' + linkId };
links.push(interimLink);
updateGraphData();
};
const removeLink = link => {
links.splice(links.indexOf(link), 1);
};
const removeInterimLinkWithoutAddingIt = () => {
removeLink(interimLink);
interimLink = null;
updateGraphData();
};
const removeNode = node => {
links.filter(link => link.source === node || link.target === node).forEach(link => removeLink(link));
nodes.splice(nodes.indexOf(node), 1);
};
const Graph = new ForceGraph(document.getElementById('graph'))
.linkDirectionalArrowLength(6)
.linkDirectionalArrowRelPos(1)
.onNodeDrag(dragNode => {
dragSourceNode = dragNode;
for (let node of nodes) {
if (dragNode === node) {
continue;
}
// close enough: snap onto node as target for suggested link
if (!interimLink && distance(dragNode, node) < snapInDistance) {
setInterimLink(dragSourceNode, node);
}
// close enough to other node: snap over to other node as target for suggested link
if (interimLink && node !== interimLink.target && distance(dragNode, node) < snapInDistance) {
removeLink(interimLink);
setInterimLink(dragSourceNode, node);
}
}
// far away enough: snap out of the current target node
if (interimLink && distance(dragNode, interimLink.target) > snapOutDistance) {
removeInterimLinkWithoutAddingIt();
}
})
.onNodeDragEnd(() => {
dragSourceNode = null;
interimLink = null;
updateGraphData();
})
.nodeColor(node => node === dragSourceNode || (interimLink &&
(node === interimLink.source || node === interimLink.target)) ? 'orange' : null)
.linkColor(link => link === interimLink ? 'orange' : '#bbbbbb')
.linkLineDash(link => link === interimLink ? [2, 2] : [])
.onNodeClick((node, event) => rename(node, 'node'))
.onNodeRightClick((node, event) => removeNode(node))
.onLinkClick((link, event) => rename(link, 'link'))
.onLinkRightClick((link, event) => removeLink(link))
.onBackgroundClick(event => {
let coords = Graph.screen2GraphCoords(event.layerX, event.layerY);
let nodeId = nodeIdCounter ++;
nodes.push({ id: nodeId, x: coords.x, y: coords.y, name: 'node_' + nodeId });
updateGraphData();
});
updateGraphData();
</script>
</body>