patrickfuller / jgraph

An embeddable webGL graph visualization library.
http://patrickfuller.github.io/jgraph
MIT License
133 stars 31 forks source link

Incremental addNode() calls #7

Open d0znpp opened 8 years ago

d0znpp commented 8 years ago

It would be great if you can add incremental addNode function. Like https://cdn.rawgit.com/frewsxcv/graphosaurus/HEAD/examples/incremental/index.html

patrickfuller commented 8 years ago

This should be straightforward. I can give it a try when I get some free time (likely in two weeks), but let me know if you try it before then.

From draw (here), we could add a node with something like:

jgraph.addNode = function (node) {
    var material, mesh, self = jgraph;
    if (node.hasOwnProperty('color')) {
        node.color = node.color.toLowerCase();
        if (!self.materials.hasOwnProperty(node.color)) {
            self.materials[node.color] = self.makeMaterial(node.color);
        }
    }
    material = self.materials[node.hasOwnProperty('color') ?
            node.color : self.defaultNodeColor];
    mesh = new THREE.Mesh(self.sphereGeometry, material);
    mesh.position.fromArray(node.hasOwnProperty('location') ?
                            node.location : [0, 0, 0]);
    if (node.hasOwnProperty('size')) {
        mesh.scale.set(node.size, node.size, node.size);
    }
    self.scene.add(mesh);
    self.nodes.push(mesh);
}

where node is an object of the form:

{color: optional, size: optional, location: [x, y, z]}

It may require some tweaking, but something like that should work. This could be tested as a patch in google dev console in one of the example graphs (this would be a good test).

Once this is working, you can use the same logic to add in edges. Take this part, strip out unnecessary lines, make sure the variables and names line up, and you should be all set.

d0znpp commented 8 years ago

Great! Thanks. It works. But I have no idea how to calculate node position automatically.

Original function puts nodes automatically, but incremental is not.

    mesh.position.fromArray(node.hasOwnProperty('location') ?
                            node.location : [0, 0, 0]);
patrickfuller commented 8 years ago

The node position depends on the effect you're going for. It's hard to tell without knowing more about what you're trying to do, but here are some ideas:

Random

The link you sent uses what looks like a random layout. In this setup, you would generate a random location and check for collisions. A python implementation is here, but you could also do it in javascript. Three.js has some useful vector math that would make this simple.

Force-directed layout after every node

You can run jgraph.optimize() after every addNode call. This will re-run the optimization jiggling, so may not be the right approach if you want to quickly add many nodes.

Force-directed layout, pre-rendered

If you're trying to gradually reveal a known graph, you can run the force-directed optimization on the whole graph. Then, save it with something like this in the console:

var data = JSON.stringify(jgraph.current);
var url = 'data:text/json;charset=utf8,' + encodeURIComponent(data);
window.open(url, '_blank');
window.focus();

From here, initialize jgraph as jgraph.create('selector', {runOptimization: false}) to disable the optimization from running. You can then loop through your graph, calling addNode to slowly reveal nodes.

d0znpp commented 8 years ago

Perfect! Thanks.