|
186 | 186 |
|
187 | 187 | function createForceGraph(popupId, containerName, dot) { |
188 | 188 | // Add event listener for Escape key to close the popup |
189 | | - document.addEventListener('keydown', function(event) { |
| 189 | + document.addEventListener('keydown', function (event) { |
190 | 190 | if (event.key === 'Escape') { |
191 | 191 | hidePopup(); |
192 | 192 | } |
|
202 | 202 | var nodes = []; |
203 | 203 | var links = []; |
204 | 204 |
|
205 | | - graphlibGraph.nodes().forEach(function(node) { |
| 205 | + graphlibGraph.nodes().forEach(function (node) { |
206 | 206 | var nodeData = graphlibGraph.node(node); |
207 | 207 | nodes.push({ |
208 | 208 | id: node, |
209 | 209 | color: nodeData.color || 'white', |
210 | | - |
211 | 210 | }); |
212 | 211 | }); |
213 | 212 |
|
214 | | - graphlibGraph.edges().forEach(function(edge) { |
| 213 | + graphlibGraph.edges().forEach(function (edge) { |
215 | 214 | links.push({ |
216 | 215 | source: edge.v, |
217 | 216 | target: edge.w, |
218 | | - color: graphlibGraph.edge(edge).color || 'white' }); |
| 217 | + color: graphlibGraph.edge(edge).color || 'white', |
| 218 | + weight: graphlibGraph.edge(edge).weight |
| 219 | + }); |
219 | 220 | }); |
220 | 221 |
|
221 | 222 | const gData = { |
|
255 | 256 | const Graph = new ForceGraph3D(container) |
256 | 257 | .graphData(gData) |
257 | 258 | .nodeLabel('id') |
258 | | - .nodeColor(node => highlightNodes.has(node) ? node === hoverNode ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.6)') |
| 259 | + // .nodeColor(node => highlightNodes.has(node) ? node === hoverNode ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.6)') |
259 | 260 | .linkWidth(link => highlightLinks.has(link) ? 4 : 1) |
260 | 261 | .linkDirectionalParticles(link => highlightLinks.has(link) ? 4 : 0) |
261 | 262 | .linkDirectionalParticleWidth(4) |
262 | 263 | .width(container.clientWidth) |
263 | 264 | .height(container.clientHeight) |
264 | 265 | .nodeThreeObject(node => { |
| 266 | + // use node labels instead of spheres |
265 | 267 | const sprite = new SpriteText(node.id); |
266 | 268 | sprite.material.depthWrite = false; // make sprite background transparent |
267 | 269 | sprite.color = node.color; |
268 | | - sprite.textHeight = 2; |
| 270 | + sprite.textHeight = 4; |
269 | 271 | return sprite; |
270 | 272 | }) |
271 | | - .onNodeHover(node => { |
272 | | - // no state change |
273 | | - if ((!node && !highlightNodes.size) || (node && hoverNode === node)) return; |
274 | | - |
275 | | - highlightNodes.clear(); |
276 | | - highlightLinks.clear(); |
277 | | - if (node) { |
278 | | - highlightNodes.add(node); |
279 | | - node.neighbors.forEach(neighbor => highlightNodes.add(neighbor)); |
280 | | - node.links.forEach(link => highlightLinks.add(link)); |
281 | | - } |
282 | | - |
283 | | - hoverNode = node || null; |
284 | | - |
285 | | - updateHighlight(Graph); |
| 273 | + // code to display weight as link text - may be too much for browsers to handle |
| 274 | + .linkThreeObjectExtend(true) |
| 275 | + .linkThreeObject(link => { |
| 276 | + // extend link with text sprite |
| 277 | + const sprite = new SpriteText(`${link.weight}`); |
| 278 | + sprite.color = 'lightgrey'; |
| 279 | + sprite.textHeight = 3; |
| 280 | + return sprite; |
286 | 281 | }) |
287 | | - .onLinkHover(link => { |
288 | | - highlightNodes.clear(); |
289 | | - highlightLinks.clear(); |
| 282 | + .linkPositionUpdate((sprite, { start, end }) => { |
| 283 | + const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({ |
| 284 | + [c]: start[c] + (end[c] - start[c]) / 2 // calc middle point |
| 285 | + }))); |
290 | 286 |
|
291 | | - if (link) { |
292 | | - highlightLinks.add(link); |
293 | | - highlightNodes.add(link.source); |
294 | | - highlightNodes.add(link.target); |
295 | | - } |
| 287 | + // Position sprite |
| 288 | + Object.assign(sprite.position, middlePos); |
| 289 | + }) |
| 290 | + // code to highlight nodes & links |
| 291 | + // TODO: enable via control - see Manipulate Link Force Distance for example |
| 292 | + // .onNodeHover(node => { |
| 293 | + // // no state change |
| 294 | + // if ((!node && !highlightNodes.size) || (node && hoverNode === node)) return; |
| 295 | + // |
| 296 | + // highlightNodes.clear(); |
| 297 | + // highlightLinks.clear(); |
| 298 | + // if (node) { |
| 299 | + // highlightNodes.add(node); |
| 300 | + // node.neighbors.forEach(neighbor => highlightNodes.add(neighbor)); |
| 301 | + // node.links.forEach(link => highlightLinks.add(link)); |
| 302 | + // } |
| 303 | + // |
| 304 | + // hoverNode = node || null; |
| 305 | + // |
| 306 | + // updateHighlight(Graph); |
| 307 | + // }) |
| 308 | + // .onLinkHover(link => { |
| 309 | + // highlightNodes.clear(); |
| 310 | + // highlightLinks.clear(); |
| 311 | + // |
| 312 | + // if (link) { |
| 313 | + // highlightLinks.add(link); |
| 314 | + // highlightNodes.add(link.source); |
| 315 | + // highlightNodes.add(link.target); |
| 316 | + // } |
| 317 | + // |
| 318 | + // updateHighlight(Graph); |
| 319 | + // }) |
| 320 | + ; |
296 | 321 |
|
297 | | - updateHighlight(Graph); |
298 | | - }); |
| 322 | + Graph |
| 323 | + .d3Force('link') |
| 324 | + .distance(link => (1/link.weight) * 200); |
299 | 325 | } |
300 | 326 |
|
| 327 | + // used by highlighting functionality |
301 | 328 | function updateHighlight(Graph) { |
302 | 329 | // trigger update of highlighted objects in scene |
303 | 330 | Graph |
|
313 | 340 | <script> |
314 | 341 | function showPopup(popupId, containerName, dot) { |
315 | 342 | // Add event listener for Escape key to close the popup |
316 | | - document.addEventListener('keydown', function(event) { |
| 343 | + document.addEventListener('keydown', function (event) { |
317 | 344 | if (event.key === 'Escape') { |
318 | 345 | hidePopup(); |
319 | 346 | } |
|
345 | 372 | }); |
346 | 373 |
|
347 | 374 | // Remove the Escape key event listener |
348 | | - document.removeEventListener('keydown', function(event) { |
| 375 | + document.removeEventListener('keydown', function (event) { |
349 | 376 | if (event.key === 'Escape') { |
350 | 377 | hidePopup(); |
351 | 378 | } |
|
372 | 399 | C -> A [color=cyan]; |
373 | 400 | } |
374 | 401 | `; |
375 | | -const mb_dot = `strict digraph G { |
| 402 | + const mb_dot = `strict digraph G { |
376 | 403 | AboutDialog -> MiscTools [ label = "10" weight = "10" color = "red" ]; |
377 | 404 | AboutDialog -> MainPanelView [ label = "1" weight = "1" color = "red" ]; |
378 | 405 | ChunkDownloader -> SecureSingleThreadNotifiable [ label = "1" weight = "1" ]; |
|
0 commit comments