3d-force-graph-vr
Advanced tools
{ | ||
"name": "3d-force-graph-vr", | ||
"version": "0.3.2", | ||
"version": "0.4.0-ngraph", | ||
"description": "UI component for a 3D force-directed graph in VR", | ||
@@ -37,6 +37,4 @@ "main": "dist/3d-force-graph-vr.js", | ||
"d3": "^4.8", | ||
"d3-binarytree": "^0.1", | ||
"d3-force-3d": "^1.0", | ||
"d3-octree": "^0.1", | ||
"lodash": "^4.17.4", | ||
"ngraph.forcelayout3d": "~0.0.16", | ||
"ngraph.graph": "~0.0.12", | ||
"swc": "^0.1" | ||
@@ -43,0 +41,0 @@ }, |
@@ -7,8 +7,7 @@ import './3d-force-graph-vr.css'; | ||
import * as d3Core from 'd3'; | ||
import * as d3Force from 'd3-force-3d'; | ||
import { default as extend } from 'lodash/assign'; | ||
let d3 = {}; | ||
extend(d3, d3Core, d3Force); | ||
import graph from 'ngraph.graph'; | ||
import forcelayout3d from 'ngraph.forcelayout3d'; | ||
const ngraph = { graph, forcelayout3d }; | ||
import * as d3 from 'd3'; | ||
import * as SWC from 'swc'; | ||
@@ -35,5 +34,3 @@ | ||
new SWC.Prop('coolDownTicks', Infinity), | ||
new SWC.Prop('coolDownTime', 15000), // ms | ||
new SWC.Prop('alphaDecay', 0.0228), // cool-down curve | ||
new SWC.Prop('velocityDecay', 0.4) // atmospheric friction | ||
new SWC.Prop('coolDownTime', 15000) // ms | ||
], | ||
@@ -74,11 +71,13 @@ | ||
// Add force-directed layout | ||
state.forceLayout = d3.forceSimulation() | ||
.force('link', d3.forceLink().id(d => d._id)) | ||
.force('charge', d3.forceManyBody()) | ||
.force('center', d3.forceCenter()) | ||
.stop(); | ||
// Setup ticker | ||
(function frameTick() { // IIFE | ||
if (state.onFrame) state.onFrame(); | ||
requestAnimationFrame(frameTick); | ||
})(); | ||
}, | ||
update: state => { | ||
state.onFrame = null; // Pause simulation | ||
// Build graph with data | ||
@@ -94,3 +93,2 @@ const d3Nodes = []; | ||
}); | ||
if (!d3Nodes.length) { return; } | ||
@@ -132,17 +130,13 @@ // Add A-frame objects | ||
// Feed data to force-directed layout | ||
state.forceLayout | ||
.stop() | ||
.alpha(1)// re-heat the simulation | ||
.alphaDecay(state.alphaDecay) | ||
.velocityDecay(state.velocityDecay) | ||
.numDimensions(state.numDimensions) | ||
.nodes(d3Nodes) | ||
.force('link').links(d3Links); | ||
// Add force-directed layout | ||
const graph = ngraph.graph(); | ||
for (let node of d3Nodes) { graph.addNode(node._id); } | ||
for (let link of d3Links) { graph.addLink(link.source, link.target); } | ||
const layout = ngraph.forcelayout3d(graph); | ||
for (let i=0; i<state.warmUpTicks; i++) { state.forceLayout.tick(); } // Initial ticks before starting to render | ||
for (let i=0; i<state.warmUpTicks; i++) { layout.step(); } // Initial ticks before starting to render | ||
let cntTicks = 0; | ||
const startTickTime = new Date(); | ||
state.forceLayout.on("tick", layoutTick).restart(); | ||
state.onFrame = layoutTick; | ||
@@ -153,12 +147,20 @@ // | ||
if (cntTicks++ > state.coolDownTicks || (new Date()) - startTickTime > state.coolDownTime) { | ||
state.forceLayout.stop(); // Stop ticking graph | ||
state.onFrame = null; // Stop ticking graph | ||
} | ||
layout.step(); // Tick it | ||
// Update nodes position | ||
nodes.attr('position', d => `${d.x} ${d.y || 0} ${d.z || 0}`); | ||
nodes.attr('position', d => { | ||
const pos = layout.getNodePosition(d._id); | ||
return `${pos.x} ${pos.y || 0} ${pos.z || 0}`; | ||
}); | ||
//Update links position | ||
links.attr('line', d => `start: ${d.source.x} ${d.source.y || 0} ${d.source.z || 0}; end: ${d.target.x} ${d.target.y || 0} ${d.target.z || 0}`); | ||
links.attr('line', d => { | ||
const pos = layout.getLinkPosition(graph.getLink(d.source, d.target).id); | ||
return `start: ${pos.from.x} ${pos.from.y || 0} ${pos.from.z || 0}; end: ${pos.to.x} ${pos.to.y || 0} ${pos.to.z || 0}`; | ||
}); | ||
} | ||
} | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
6
-25%2517852
-5.43%68485
-5.94%