Skip to content

Commit 60a3add

Browse files
Selecting nodes with CTRL key, selected nodes table, data set change, etc
1 parent 4effda1 commit 60a3add

17 files changed

+4131
-17
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A visualizer of iKnow entities.
55
Preview
66
-------
77

8-
![Screenshot](https://cloud.githubusercontent.com/assets/4989256/20240678/2bc3c772-a927-11e6-84fb-01adc6b259df.png)
8+
![Screenshot](https://cloud.githubusercontent.com/assets/4989256/20325678/5efeecde-ab8e-11e6-8d8d-f4955a1afa4d.png)
99

1010
Development
1111
-----------

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "iknow-entity-browser",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"description": "Visualizer for iKnow entities",
55
"main": "gulpfile.babel.js",
66
"scripts": {
2.06 KB
Binary file not shown.
Loading
1.84 KB
Binary file not shown.
1.15 KB
Binary file not shown.

src/static/index.html

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@
99
<script type="text/javascript" src="js/index.js"></script>
1010
</head>
1111
<body onload="init()">
12+
<div id="windows">
1213
<svg id="graph"></svg>
14+
<div id="table">
15+
<h1>Selected Nodes</h1>
16+
<div class="wrapper">
17+
<table>
18+
<thead>
19+
<tr>
20+
<th>ID</th>
21+
<th>Label</th>
22+
<th>Frequency</th>
23+
</tr>
24+
</thead>
25+
<tbody>
26+
27+
</tbody>
28+
</table>
29+
</div>
30+
</div>
31+
</div>
32+
<div class="uiIconGroup topRight">
33+
<i id="tableToggle" class="ui icon-window-list"></i>
34+
</div>
1335
</body>
1436
</html>

src/static/js/index.js

+114-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import sampleData from "./sampleData.json";
1+
import sampleData from "./sample_output2.json";
22

33
var graph = preprocess(sampleData.graph);
44

@@ -7,24 +7,51 @@ function preprocess (graph) {
77
return graph;
88
}
99

10+
function updateSelectedNodes () {
11+
let data = graph.nodes.filter(node => !!node.selected),
12+
table = document.querySelector("#table table tbody");
13+
table.textContent = "";
14+
for (let i = 0; i < data.length; i++) {
15+
let row = table.insertRow(i),
16+
node = data[i];
17+
row.insertCell(0).textContent = node.id;
18+
row.insertCell(1).textContent = node.label;
19+
row.insertCell(2).textContent = node.entities[0].frequency;
20+
}
21+
}
22+
1023
window.init = () => {
1124

12-
var zoom = d3.zoom()
25+
var shiftKey, ctrlKey,
26+
width = window.innerWidth,
27+
height = window.innerHeight;
28+
29+
var zoomer = d3.zoom()
1330
.scaleExtent([1/4, 40])
1431
.on("zoom", () => {
1532
view.attr("transform", d3.event.transform);
1633
});
1734

18-
var svg = d3
19-
.select("svg")
20-
.call(zoom),
21-
width = window.innerWidth,
22-
height = window.innerHeight;
35+
var dragger = d3.drag()
36+
.on("start", dragstarted)
37+
.on("drag", dragged)
38+
.on("end", dragended);
39+
40+
d3.select(window)
41+
.on("keydown", keyDown)
42+
.on("keyup", keyUp);
43+
44+
var svg = d3.select("#graph")
45+
.call(zoomer);
2346

2447
var view = svg
2548
.append("g")
2649
.attr("class", "view");
2750

51+
var brush = view.append("g")
52+
.datum(() => { return { selected: false, previouslySelected: false }; })
53+
.attr("class", "brush");
54+
2855
// var color = d3.scaleOrdinal(d3.schemeCategory20);
2956

3057
var simulation = d3.forceSimulation()
@@ -44,18 +71,29 @@ window.init = () => {
4471
.selectAll("line")
4572
.data(graph.edges)
4673
.enter().append("line")
47-
.attr("stroke", d => d.type === "similar" ? "#f70" : "#000");
74+
.attr("class", d => d.type === "similar"
75+
? "similar"
76+
: d.type === "related"
77+
? "related"
78+
: "other"
79+
);
4880

4981
var node = view.append("g")
5082
.attr("class", "nodes")
5183
.selectAll(".node")
5284
.data(graph.nodes)
5385
.enter().append("g")
5486
.attr("class", "node")
55-
.call(d3.drag()
56-
.on("start", dragstarted)
57-
.on("drag", dragged)
58-
.on("end", dragended));
87+
.call(dragger)
88+
.on("dblclick", () => d3.event.stopPropagation())
89+
.on("click", function (d) {
90+
if (d3.event.defaultPrevented) return;
91+
if (!ctrlKey) {
92+
node.classed("selected", (p) => p.selected = p.previouslySelected = false)
93+
}
94+
d3.select(this).classed("selected", d.selected = !d.previouslySelected);
95+
updateSelectedNodes();
96+
});
5997

6098
var circle = node.append("circle")
6199
.attr("r", d => d.radius);
@@ -72,6 +110,38 @@ window.init = () => {
72110
simulation.force("link")
73111
.links(graph.edges);
74112

113+
var brusher = d3.brush()
114+
.extent([[-9999999, -9999999], [9999999, 9999999]])
115+
.on("start.brush", () => {
116+
if (!d3.event.sourceEvent) return;
117+
node.each((d) => {
118+
d.previouslySelected = ctrlKey && d.selected;
119+
});
120+
})
121+
.on("brush.brush", () => {
122+
if (!d3.event.sourceEvent) return;
123+
var extent = d3.event.selection;
124+
if (!extent)
125+
return;
126+
node.classed("selected", (d) => {
127+
return d.selected = d.previouslySelected ^
128+
(extent[0][0] <= d.x && d.x < extent[1][0]
129+
&& extent[0][1] <= d.y && d.y < extent[1][1]);
130+
});
131+
})
132+
.on("end.brush", () => {
133+
if (!d3.event.sourceEvent) return;
134+
setTimeout(() => {
135+
brush.call(brusher.move, null);
136+
updateSelectedNodes();
137+
}, 25);
138+
});
139+
140+
brush.call(brusher)
141+
.on(".brush", null);
142+
143+
brush.select('.overlay').style('cursor', 'auto');
144+
75145
for (var i = 100; i > 0; --i) simulation.tick();
76146

77147
function ticked () {
@@ -101,4 +171,36 @@ window.init = () => {
101171
d.fy = null;
102172
}
103173

174+
function keyDown () {
175+
shiftKey = d3.event.shiftKey || d3.event.metaKey;
176+
ctrlKey = d3.event.ctrlKey;
177+
178+
if (d3.event.keyCode == 67) { // the 'c' key
179+
// do stuff
180+
}
181+
182+
if (ctrlKey) {
183+
brush.select('.overlay').style('cursor', 'crosshair');
184+
brush.call(brusher);
185+
d3.event.preventDefault();
186+
}
187+
}
188+
189+
function keyUp () {
190+
shiftKey = d3.event.shiftKey || d3.event.metaKey;
191+
ctrlKey = d3.event.ctrlKey;
192+
193+
brush.call(brusher)
194+
.on(".brush", null);
195+
196+
brush.select('.overlay').style('cursor', 'auto');
197+
}
198+
199+
d3.select("#tableToggle")
200+
.data([{ toggled: false }])
201+
.on("click", (d) => {
202+
d.toggled = !d.toggled;
203+
d3.select("#table").classed("active", d.toggled);
204+
});
205+
104206
};

0 commit comments

Comments
 (0)