Skip to content

Commit e544224

Browse files
matekdevAjayThorve
andauthored
Setup Viz App Mock (#305)
Co-authored-by: Ajay Thorve <athorve@nvidia.com>
1 parent bf6893d commit e544224

45 files changed

Lines changed: 1970 additions & 80 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lerna.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"modules/demo/ssr/*",
1616
"modules/demo/tfjs/*",
1717
"modules/demo/client-server",
18+
"modules/demo/viz-app",
1819
"modules/demo/sql/*"
1920
]
2021
}

modules/deck.gl/src/layers/edges.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class EdgeLayer extends (Layer as typeof DeckLayer) {
138138
this.internalState.selectedSourceNodeId = this.internalState.highlightedSourceNodeId;
139139
this.internalState.selectedTargetNodeId = this.internalState.highlightedTargetNodeId;
140140
}
141-
141+
info.object = info.index; // deck.gl uses info.object to check if item has already been added
142142
return info;
143143
}
144144
_getModel({gl, shaderCache}: DeckContext) {

modules/deck.gl/src/layers/nodes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export class NodeLayer extends (Layer as typeof DeckLayer) {
133133
this.internalState.selectedNodeId = this.internalState.highlightedNodeId;
134134
this.internalState.selectedNodeIndex = this.internalState.highlightedNodeIndex;
135135
}
136+
info.object = info.index; // deck.gl uses info.object to check if item has already been added
136137
return info;
137138
}
138139
_getModel({gl, shaderCache}: DeckContext) {

modules/demo/.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"command": "shellCommand.execute",
113113
"args": {
114114
"description": "Select a demo to debug",
115-
"command": "echo client-server luma graph spatial xterm $(find modules/demo/deck modules/demo/tfjs modules/demo/ipc modules/demo/ssr modules/demo/sql -maxdepth 2 -type f -name 'package.json' -print0 | grep -z -v node_modules | tr -d '\\0' | sed -r 's@modules/demo/@@g' | sed -r 's@/package.json@ @g') | sort -Vr | sed -r 's@\\s@\\n@g'",
115+
"command": "echo client-server viz-app luma graph spatial xterm $(find modules/demo/deck modules/demo/tfjs modules/demo/ipc modules/demo/ssr modules/demo/sql -maxdepth 2 -type f -name 'package.json' -print0 | grep -z -v node_modules | tr -d '\\0' | sed -r 's@modules/demo/@@g' | sed -r 's@/package.json@ @g') | sort -Vr | sed -r 's@\\s@\\n@g'",
116116
}
117117
},
118118
]

modules/demo/ssr/graph/plugins/graph/index.js

Lines changed: 123 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const wrtc = require('wrtc');
1616
const {MemoryView} = require('@nvidia/cuda');
1717
const {Float32Buffer} = require('@nvidia/cuda');
1818
const {GraphCOO} = require('@rapidsai/cugraph');
19+
const {Series, Int32} = require('@rapidsai/cudf');
1920

2021
const {loadNodes, loadEdges} = require('./loader');
2122
const {RenderCluster} = require('../../render/cluster');
@@ -54,12 +55,32 @@ function graphSSRClients(fastify) {
5455

5556
clients[stream.id] = {
5657
video: source,
57-
state: {},
58+
state: {
59+
pickingMode: 'click', // 'click', 'boxSelect'
60+
selectedInfo: {},
61+
boxSelectCoordinates: {rectdata: [{polygon: [[]], show: false}], startPos: null},
62+
clearSelections: false
63+
},
5864
event: {},
5965
props: {width, height, layout},
6066
graph: await loadGraph(graphId),
6167
frame: shmCreate(width * height * 3 / 2),
68+
peer: peer,
6269
};
70+
if (clients[stream.id].graph.dataframes[0]) {
71+
const res = getPaginatedRows(clients[stream.id].graph.dataframes[0]);
72+
peer.send(JSON.stringify({
73+
type: 'data',
74+
data: {nodes: {data: res, length: clients[stream.id].graph.dataframes[0].numRows}}
75+
}));
76+
}
77+
if (clients[stream.id].graph.dataframes[1]) {
78+
const res = getPaginatedRows(clients[stream.id].graph.dataframes[1]);
79+
peer.send(JSON.stringify({
80+
type: 'data',
81+
data: {edges: {data: res, length: clients[stream.id].graph.dataframes[1].numRows}}
82+
}));
83+
}
6384

6485
stream.addTrack(source.createTrack());
6586
peer.streams.push(stream);
@@ -79,6 +100,18 @@ function graphSSRClients(fastify) {
79100
clients[stream.id].event[data.type] = data;
80101
break;
81102
}
103+
case 'pickingMode': {
104+
clients[stream.id].state.pickingMode = data;
105+
break;
106+
}
107+
case 'clearSelections': {
108+
clients[stream.id].state.clearSelections = JSON.parse(data);
109+
break;
110+
}
111+
case 'layout': {
112+
clients[stream.id].props.layout = JSON.parse(data);
113+
break;
114+
}
82115
}
83116
}
84117
}
@@ -95,22 +128,24 @@ function graphSSRClients(fastify) {
95128
}
96129

97130
async function loadGraph(id) {
131+
let dataframes = [];
132+
98133
if (!(id in graphs)) {
99134
const asDeviceMemory = (buf) => new (buf[Symbol.species])(buf);
100-
const [nodes, edges] = await Promise.all([loadNodes(id), loadEdges(id)]);
101-
const src = edges.get('src');
102-
const dst = edges.get('dst');
135+
dataframes = await Promise.all([loadNodes(id), loadEdges(id)]);
136+
const src = dataframes[1].get('src');
137+
const dst = dataframes[1].get('dst');
103138
graphs[id] = {
104139
refCount: 0,
105140
nodes: {
106-
nodeRadius: asDeviceMemory(nodes.get('size').data),
107-
nodeFillColors: asDeviceMemory(nodes.get('color').data),
108-
nodeElementIndices: asDeviceMemory(nodes.get('id').data),
141+
nodeRadius: asDeviceMemory(dataframes[0].get('size').data),
142+
nodeFillColors: asDeviceMemory(dataframes[0].get('color').data),
143+
nodeElementIndices: asDeviceMemory(dataframes[0].get('id').data),
109144
},
110145
edges: {
111-
edgeList: asDeviceMemory(edges.get('edge').data),
112-
edgeColors: asDeviceMemory(edges.get('color').data),
113-
edgeBundles: asDeviceMemory(edges.get('bundle').data),
146+
edgeList: asDeviceMemory(dataframes[1].get('edge').data),
147+
edgeColors: asDeviceMemory(dataframes[1].get('color').data),
148+
edgeBundles: asDeviceMemory(dataframes[1].get('bundle').data),
114149
},
115150
graph: new GraphCOO(src._col, dst._col, {directedEdges: true}),
116151
};
@@ -124,24 +159,25 @@ function graphSSRClients(fastify) {
124159
));
125160

126161
return {
127-
gravity: 1.0,
162+
gravity: 0.0,
128163
linLogMode: false,
129164
scalingRatio: 5.0,
130165
barnesHutTheta: 0.0,
131166
jitterTolerance: 0.05,
132167
strongGravityMode: false,
133168
outboundAttraction: false,
134169
graph: graphs[id].graph,
135-
edges: {
136-
...graphs[id].edges,
137-
length: graphs[id].graph.numEdges(),
138-
},
139170
nodes: {
140171
...graphs[id].nodes,
141172
length: graphs[id].graph.numNodes(),
142173
nodeXPositions: pos.subarray(0, pos.length / 2),
143174
nodeYPositions: pos.subarray(pos.length / 2),
144175
},
176+
edges: {
177+
...graphs[id].edges,
178+
length: graphs[id].graph.numEdges(),
179+
},
180+
dataframes: dataframes
145181
};
146182
}
147183
}
@@ -152,8 +188,17 @@ function layoutAndRenderGraphs(clients) {
152188
return () => {
153189
for (const id in clients) {
154190
const client = clients[id];
191+
const sendToClient =
192+
([nodes, edges]) => {
193+
client.peer.send(JSON.stringify(
194+
{type: 'data', data: {nodes: {data: getPaginatedRows(nodes), length: nodes.numRows}}}));
195+
client.peer.send(JSON.stringify(
196+
{type: 'data', data: {edges: {data: getPaginatedRows(edges), length: edges.numRows}}}));
197+
}
155198

156-
if (client.isRendering) { continue; }
199+
if (client.isRendering) {
200+
continue;
201+
}
157202

158203
const state = {...client.state};
159204
const props = {...client.props};
@@ -170,55 +215,89 @@ function layoutAndRenderGraphs(clients) {
170215
'mouseup',
171216
'mouseleave',
172217
'wheel',
173-
'beforeunload'
218+
'beforeunload',
219+
'shiftKey',
220+
'dragStart',
221+
'dragOver'
174222
].map((x) => client.event[x])
175223
.filter(Boolean);
176224

177225
if (event.length === 0 && !props.layout) { continue; }
178226
if (event.length !== 0) { client.event = Object.create(null); }
179-
if (props.layout) { client.graph = forceAtlas2(client.graph); }
227+
if (props.layout == true) { client.graph = forceAtlas2(client.graph); }
180228

181229
const {
182230
width = client.props.width ?? 800,
183231
height = client.props.height ?? 600,
184232
} = client.state;
185233

234+
state.window = {width: width, height: height, ...client.state.window};
235+
186236
if (client.frame?.byteLength !== (width * height * 3 / 2)) {
187237
shmDetach(client.frame.key, true);
188238
client.frame = shmCreate(width * height * 3 / 2);
189239
}
190-
191240
client.isRendering = true;
192241

193-
renderer.render(id,
194-
{
195-
state,
196-
props,
197-
event,
198-
frame: client.frame.key,
199-
graph: {
200-
...client.graph,
201-
graph: undefined,
202-
edges: getIpcHandles(client.graph.edges),
203-
nodes: getIpcHandles(client.graph.nodes),
204-
},
205-
},
206-
(error, result) => {
207-
client.isRendering = false;
208-
if (id in clients) {
209-
if (error) { throw error; }
210-
result?.state && Object.assign(client.state, result.state);
211-
// console.log(state?.deck?.props?.initialViewState);
212-
// if (state?.deck?.props) {
213-
// client.props.initialViewState = state.deck.props.initialViewState;
214-
// }
215-
client.video.onFrame({...result.frame, data: client.frame.buffer});
216-
}
217-
});
242+
renderer.render(
243+
id,
244+
{
245+
state,
246+
props,
247+
event,
248+
frame: client.frame.key,
249+
graph: {
250+
...client.graph,
251+
graph: undefined,
252+
edges: getIpcHandles(client.graph.edges),
253+
nodes: getIpcHandles(client.graph.nodes),
254+
},
255+
},
256+
(error, result) => {
257+
client.isRendering = false;
258+
if (id in clients) {
259+
if (error) { throw error; }
260+
if (client.state.clearSelections == true) {
261+
// clear selection is called once
262+
result.state.clearSelections = false;
263+
264+
// reset selected state
265+
result.state.selectedInfo.selectedNodes = [];
266+
result.state.selectedInfo.selectedEdges = [];
267+
result.state.selectedInfo.selectedCoordinates = {};
268+
result.state.boxSelectCoordinates.rectdata = [{polygon: [[]], show: false}];
269+
270+
// send to client
271+
if (client.graph.dataframes) { sendToClient(client.graph.dataframes); }
272+
} else if (JSON.stringify(client.state.selectedInfo.selectedCoordinates) !==
273+
JSON.stringify(result.state.selectedInfo.selectedCoordinates)) {
274+
// selections updated
275+
const nodes =
276+
Series.new({type: new Int32, data: result.state.selectedInfo.selectedNodes});
277+
const edges =
278+
Series.new({type: new Int32, data: result.state.selectedInfo.selectedEdges});
279+
if (client.graph.dataframes) {
280+
sendToClient([
281+
client.graph.dataframes[0].gather(nodes),
282+
client.graph.dataframes[1].gather(edges)
283+
]);
284+
}
285+
}
286+
// copy result state to client's current state
287+
result?.state && Object.assign(client.state, result.state);
288+
289+
client.video.onFrame({...result.frame, data: client.frame.buffer});
290+
}
291+
});
218292
}
219293
}
220294
}
221295

296+
function getPaginatedRows(df, page = 1, rowsPerPage = 400) {
297+
if (!df) { return {}; }
298+
return df.head(page * rowsPerPage).tail(rowsPerPage).toArrow().toArray();
299+
}
300+
222301
function forceAtlas2({graph, nodes, edges, ...params}) {
223302
graph.forceAtlas2({...params, positions: nodes.nodeXPositions.buffer});
224303
return {

modules/demo/ssr/graph/plugins/graph/loader.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,7 @@ function getDefaultNodes() {
5858
'ustr::c',
5959
'ustr::d'
6060
])),
61-
id: Series.new({
62-
type: new Uint32,
63-
data: [
64-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
65-
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
66-
]
67-
}),
61+
id: Series.sequence({type: new Uint32, init: 0, step: 1, size: 40}),
6862
color: Series.new({
6963
type: new Uint32,
7064
data: [

modules/demo/ssr/graph/public/video.html

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,21 @@
99
</head>
1010

1111
<body style="background:#2e2e2e; margin:0;">
12+
1213
<video autoplay muted width="800" height="600"></video>
14+
<div>
15+
<input type="checkbox" id="box-select" onchange="updatePan()">
16+
<label for="box-select">box-select</label>
17+
</div>
18+
19+
<div>
20+
<button onclick="updateClearSelections()">clear selections</button>
21+
</div>
22+
1323
<script src="https://cdn.jsdelivr.net/npm/simple-peer@9.11.0/simplepeer.min.js"></script>
1424
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.3/socket.io.js"></script>
1525
<script>
16-
const sock = io({ transports: ['websocket'], reconnection: false });
26+
const sock = io({ transports: ['websocket'], reconnection: true });
1727
const video = document.querySelector('video');
1828
const peer = new SimplePeer({
1929
trickle: true,
@@ -31,6 +41,11 @@
3141
// Negotiate handshake
3242
sock.on('signal', (data) => peer.signal(data));
3343
peer.on('signal', (data) => sock.emit('signal', data));
44+
peer.on('data', (data) => {
45+
var decoded = new TextDecoder().decode(data);
46+
var decodedjson = JSON.parse(decoded);
47+
console.log("got data from peer: ", decodedjson.data);
48+
});
3449

3550
// Server video stream
3651
peer.on('stream', (stream) => {
@@ -64,6 +79,14 @@
6479
});
6580
}
6681

82+
function updatePan(){
83+
let value = document.getElementById("box-select").checked;
84+
peer.send(JSON.stringify({type: 'pickingMode', data: value? "boxSelect" : "click"}));
85+
}
86+
function updateClearSelections(){
87+
peer.send(JSON.stringify({ type: 'clearSelections', data: true}));
88+
}
89+
6790
function serializeEvent(original) {
6891
return Object
6992
.getOwnPropertyNames(Object.getPrototypeOf(original))

0 commit comments

Comments
 (0)