Giter Site home page Giter Site logo

springy's Introduction

Springy

A force directed graph layout algorithm in JavaScript.

http://getspringy.com/

What is this?

Springy is a force directed graph layout algorithm.

So what does this 'force directed' stuff mean anyway? Excellent question!

It basically means that it uses some real world physics to try and figure out how to show a network graph in a nice way.

Try to imagine it as a bunch of springs connected to each other.

Demo

basic | simplified API | JSON API

Getting Started

springy.js by itself is quite plain and doesn't include any code to do rendering or drag and drop etc. It's just for calculating the layout.

The drawing and interaction stuff is mostly up to you.

However, I've written a little helper jQuery plugin called springyui.js to help get you started. It's got a semi-decent default renderer and some half assed drag and drop.

Basic Usage

See demo.html for the way to add nodes and edges to graph and springyui.js for the rendering example.

Springy 1.1+ supports simplified API for adding nodes and edges, see demo-simple.html:

var graph = new Springy.Graph();
graph.addNodes('mark', 'higgs', 'other', 'etc');
graph.addEdges(
    ['mark', 'higgs'],
    ['mark', 'etc'],
    ['mark', 'other']
);

Springy 1.2+ also accepts JSON, see demo-json.html:

graphJSON = {
  "nodes": ["mark", "higgs", "other", "etc"],
  "edges": [
    ["mark", "higgs"],
    ["mark", "etc"],
    ["mark", "other"]
  ]
};

var graph = new Springy.Graph();
graph.loadJSON(graphJSON);

Advanced Drawing

If you're keen to do your own custom drawing, you'll need to know a few things before you get started.

This is the basic graph API, you can create nodes and edges etc.

// make a new graph
var graph = new Springy.Graph();

// make some nodes
var node1 = graph.newNode({label: '1'});
var node2 = graph.newNode({label: '2'});

// connect them with an edge
graph.newEdge(node1, node2);

So now to draw this graph, lets make a layout object:

var layout = new Springy.Layout.ForceDirected(graph, 400.0, 400.0, 0.5);

I've written a Renderer class, which will handle the rendering loop. You just need to provide some callbacks to do the actual drawing.

var renderer = new Springy.Renderer(layout,
  function clear() {
    // code to clear screen
  },
  function drawEdge(edge, p1, p2) {
    // draw an edge
  },
  function drawNode(node, p) {
    // draw a node
  }
);

Now, just start the rendering loop:

renderer.start();

Further Reading

Have a look at the code in springy.js. Seriously, it's not very much code and it should be pretty easy to understand.

Please let me know if anything is unclear. Feedback is welcome.

Acknowledgements

Thanks to Lachlan Donald for his helpful suggestions and feedback.

springy's People

Contributors

amcameron avatar codr avatar dhotson avatar fabiankessler avatar iangilman avatar jessenaranjo avatar ledsun avatar lgvalent avatar lowerkey avatar mikaa123 avatar mwcz avatar oak-tree avatar slig avatar tdhsmith avatar techtonik avatar tub avatar zcourts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

springy's Issues

Manual tick branch merge

Please, merge your 'manual-tick' branch in to the main one, it's really required feature and currently we need to call all physics independently or write some crazy synchronization stuff for callback.

How to correctly delete / replace a graph

What's the correct way to replace a graph with another. E.G. I have a sexy little graph, the user changes some settings, and I grab the new data via ajax?

At the moment, when I try to delete the graph element and replace it with new data, I seem to create two graphs in the same canvas. Simplified usecase jsfiddle:
http://jsfiddle.net/XPAqX/

Forcing does not work for star-shaped graph

I have a graph with one central and many adjacent nodes. It works fine, if there are not too many nodes. However, if there are more than 33 adjacent nodes (central node excluded), the entire graph just collapses to a line: You only see a single edge with the central node at one end and all other nodes clumped together on the other. It flickers several seconds (the nodes dramatically jump from one end of the line to the other and back, without ever leaving the line though) before the forcing algorithm settles for the line.

Sample code:

// create and populate graph
var graph = new Springy.Graph();
var center = graph.newNode({
    label: 'ME'
});

for (var i = 0; i < 34; ++i) {
    var node = graph.newNode({label: i});

    // connect nodes with an edge
    graph.newEdge(center, node);
};


var $canvas = jQuery('#graph');
console.assert($canvas.length, 'could not find graph canvas');

jQuery(function(){
  var springy = $canvas.springy({
    graph: graph
  });
});

how to turn on/off the automatic layout?

Thank you so much for this great force-directed layout program.
Is there anyway to turn on or off the force-directed layout when the user wants? In my program, sometimes I need to turn on and off the layout function. I think it would not be a difficult problem. Anybody lets me know how to do so? Thank you.

1000 nodes

I ve been trying springly with 1000 nodes and unfortunately the result is not efficient at all.
http://pastebin.com/H8Q9ij6M
Do you have any recommendations how to make this fast and more elegant with an higher amount of nodes ?

Namespace all of springy under, well, 'springy'

The Graph, Node, Edge, etc objects should all be under a Springy namespace. Here's how backbone does it to play nice with require.js and others:

(function(){

// Save a reference to the global object (window in the browser, global
// on the server).
var root = this;

// Save the previous value of the Backbone variable, so that it can be
// restored later on, if noConflict is used.
var previousBackbone = root.Backbone;

// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both CommonJS and the browser.
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
} else {
Backbone = root.Backbone = {};
}

// Current version of the library. Keep in sync with package.json.
Backbone.VERSION = '0.9.2';

// Runs Backbone.js in noConflict mode, returning the Backbone variable
// to its previous owner. Returns a reference to this Backbone object.
Backbone.noConflict = function() {
root.Backbone = previousBackbone;
return this;
};

/// ... Everything else in the Backbone namespace ///

}).call(this);

Html renderer

Hi,

I am trying to make a renderer that uses html elements (instead of canvas).

I would like to know if it is possible to add a renderer option to avoid clear/redraw of nodes/links but only move them.
So I can rely on another graph framework for graphics (like http://jsplumbtoolkit.com/)

Thanks

Labels in JSON

Hi,

I am currently using this to build out a visualization of my create your own adventure game. Currently I have a script that scans and pushes the lvl identifier into the node and edge arrays with no problem. I noticed in other examples not using JSON you are able to provide an alternate label to the node identifier. Is that possible to do in JSON?

like what is done here:

var fir = graph.newNode({label: 'Sicilian Fir'});

but with JSON.

I hope I am posting in the right place.

Load external xml config

Would it be possible for springy or the demo.html to have the option of loading an external configuration file of nodes and links (perhaps as xml or text)? I've tried to modify the demo.html without any luck of getting it working.

2 graph in one page

When I try to draw 2 graph in one page first one started to expand and nodes go out of the canvas boundary. Screenshot is given below
screenshot

Images as nodes

I'm trying to use images with text labels as nodes, but i'm having a problem. Sometimes, the graph flashes
like if there were two graphs, or one graph with two different layouts that alternate in time.

Here are the changes I made in springyui.js:

Node.prototype.getWidth = function() {
    ctx.save();
    var text = typeof(this.data.label) !== 'undefined' ? this.data.label : this.id;
    ctx.font = "12px Verdana, sans-serif";
    var width = ctx.measureText(text).width + 10;
    ctx.restore();
    if(this.image && this.image.width>width)
    {
        width = this.image.width;
    }
    return width;
};

Node.prototype.getHeight = function() {
    if(this.image)
    {
        return 20 + this.image.height;
    }
    return 20;
};

Node.prototype.getImage = function() {
    return this.data.image;
};



[...]
function drawNode(node, p)
    {
        var s = toScreen(p);

        ctx.save();

        var boxWidth = node.getWidth();
        var boxHeight = node.getHeight();
        var textX = s.x - boxWidth/2;
        var textY;
        if(node.getImage()){
            var image = node.getImage();
            // draw node image
            ctx.drawImage(image, s.x - image.width/2, s.y - image.height/2, image.width, image.height);
            textY = s.y + 2 + image.height/2;
        }else{
            textY = s.y - 10;
        }

        // fill background
        ctx.clearRect(textX, textY, boxWidth, 20);

        // fill background
        if (selected !== null && nearest.node !== null && selected.node.id === node.id)
        {
            ctx.fillStyle = "#FFFFE0";
        }
        else if (nearest !== null && nearest.node !== null && nearest.node.id === node.id)
        {
            ctx.fillStyle = "#EEEEEE";
        }
        else
        {
            ctx.fillStyle = "#FFFFFF";
        }

        ctx.fillRect(textX, textY, boxWidth, 20);
        ctx.textAlign = "left";
        ctx.textBaseline = "top";
        ctx.fillStyle = "#222222";
        ctx.font = "12px Verdana, sans-serif";
        //ctx.font = "70% Verdana, Arial, sans-serif";
        var text = typeof(node.data.label) !== 'undefined' ? node.data.label : node.id;
        ctx.fillText(text, textX + 5, textY);

        ctx.restore();
    }
[...]

The image is passed into data parameter and it's and js Image object which has to have
width and height setted.

PS: Excuse me if this is not the right place for this question, i'm a newbie here, in github.

How to set extra node properties?

Hi,

I would like to know how to add these two node features:

1- Moving a node to the center on click?
2- Setting the length, or proportional length of the lines connecting nodes?

Thanks for any pointers to implement this functionality.

Fixed or static node?

I wonder if It is possible to create nodes with static coordinates that dont move or react to force? I would like to glue certain nodes to a static point and played around with some of the parameters.. I think there is already a property "fixed" in the node object?

Sizing Canvas in CSS does not work

It seems that I can only change a graph's size by specifying a width and a height attribute in the canvas html tag. When I try to specify a width or height for the canvas tag with CSS the result is a stretched graph that makes all the graph items look larger rather than providing extra space.

I'm also interested in having the ability to resize the canvas tag dynamically (such as when the browser window size changes).

Do you have any suggestions for how to fix this issue? Thanks

Image quality

Hi guys,

after some readings I can't figure out how to improve the image quality of my graph.
I'd like to show the graph in a canvas with height and width 100% but I can't get a nice resolution
schermata 2015-11-10 alle 23 30 40

Thank you all in advance

EDIT: I forgot: if i click into the canvas the nodes move too fast and too much, i think it's a linked problem due to wrong sizing of the view

getNodeByPos

Hey dhotson,

I have wrote a function for your springyui and want to share it with you. Maybe you like it. I wrote a getNodeByPos function. This function returns the node the mouse is over. If there is no node it returns Null. I used the BoxWidth and BoxHeight to do so. I wrote the BoxSize at the draw method to each node so I don’t have to call the getHeight/Width method all the time. Because I add multilining as well, so the size is Content based (so I only need to get the height/width only once per render cycle not twice). I replaced the usage of the nearest method with this function. The problem is I can’t add it to your springy.js because I need the to/from Screen method from the springyui.js. The coordinate have to be in px, or I try to get the boxsize to vectors as well, but to do this I need the logic of the from/toScreen methods as well. =( Have you an idea?

The other Problem is, it looks like the Click is a little bit aboth the actual box. (Also the arrows connected not to the correct boxSize), Of course i changed the getHight/Width methode by returning the correct Size. The X Vector works well, but the Y dosn't. Have you a clue why?

The function:

var getNodeByPos = function(pos){
    var clickX=pos.x;
    var clickY=pos.y;
    var node={node: null, point: null, distance: null};
    this.graph.nodes.forEach(function(n){
        var point = layout.point(n);
        var pointh=toScreen(point.p);
            //X in Box
            if(clickX>(pointh.x-n.data.boxWidth/2) && clickX<(pointh.x+n.data.boxWidth/2)){
                //Y in Box
                if(clickY>(pointh.y-n.data.boxHeight/2) && clickY<(pointh.y+n.data.boxHeight/2)){
                    node = {node: n, point: point, distance: 0};
                }
            }
    });

    return node;
}

I can use it like the nearest method, without the fromscreen for the position:

var pos = jQuery(this).position();
var p = {x: e.pageX - pos.left, y: e.pageY - pos.top};
selected = nearest = dragged = getNodeByPos(p);

Thanks
Stefan

Add link to a node

Thanks for the great little library!

Would it be possible to add an 'href' property to the Node params?

Ideally, this would make the node double-clickable to navigate to the url.

Thanks!
Evan

Update node or edge color data after it has been initially rendered

Hello again.

How could I go about updating the color data of nodes and edges after they have been added and rendered? It is clear to me how I can specify a color when adding a node or edge, but I do not see how to specify a new color for an existing node or edge.
I basically wrote a shortest path function and I would like to update my directed graph to show the shortest path in a different color.

Thanks for any suggestions

version info

It would be nice to have version info inside the library just to track if it needs to be updated. BTW, the library is awesome.

Explain stiffness, repulsion, damping

"Please let me know if anything is unclear. Feedback is welcome."

Can you explain these variables, preferably on the front page, maybe also hints in the code Layout.ForceDirected = function(graph, stiffness, repulsion, damping){}

Rendering small graphs (like 10 nodes) works well with the defaults of 400, 400, 0.5.
However, when I have for example 40 nodes where 39 have a link to 1, they render on top of each other. When I modify the 0.5 to 0.1 that graph works nicely and there is no overlapping of nodes.

Maybe you can recommend different numbers for different graph size, or what to do with "supernodes"?

But does it work on IE?

The demos all work lovely on FF and Chrome, but when I jump through the hoops necessary to find an IE9 to run I can run all I see is a blank page. I don't know if that's just the rendering method you've used or something in your layout algorithms...

I'm yet to find a JS force-directed dynamic layout engine demo that does work in IE :)

fix the image

Thanks a lot for springy. It helps us a lot.

But every time i refresh page, the image will change. Is ther a way to fix what it looks like. for example, i can set a root node on the left, and the children will expanded to the right.

Problem with merge function

I get an error in the merge function of Graph. I think the lines
nodes.push(graph.addNode(new Node(n.id, n.data))); var edge = graph.addEdge(new Edge(id, from, to, e.data));

should be changed to:
nodes.push(this.addNode(new Node(n.id, n.data))); var edge = this.addEdge(new Edge(id, from, to, e.data));

Here is the final code function:

Graph.prototype.merge = function(data)
 {
    var nodes = [];
    data.nodes.forEach(function(n) {
        nodes.push(this.addNode(new Node(n.id, n.data)));
    }, this);
    data.edges.forEach(function(e) {
        var from = nodes[e.from];
        var to = nodes[e.to];

        var id = (e.directed)
            ? (id = e.type + "-" + from.id + "-" + to.id)
            : (from.id < to.id) // normalise id for non-directed edges
                ? e.type + "-" + from.id + "-" + to.id
                : e.type + "-" + to.id + "-" + from.id;

        var edge = this.addEdge(new Edge(id, from, to, e.data));
        edge.data.type = e.type;
    }, this);
};

How to free memory or destroy springy?

Currently I am working on building a tabbed layout for displaying multiple graphs. Since the users are enabled to create new graph and close a existing graph in the same page, I am wondering if there is a simple way to free memory for spring graph.

IE 8 - Canvas

Hi everyone,

I would like to know how to run the demo on ie8?

ie8 does not support the canvas HTML element. Is there a workaround to make it work on older browsers like ie8?

So far I tried a library called excanvas.js but id did not work either.

Thanks again in advance!

return absolute x,y position of each node

Is there any API can return absolute x,y position of each node? I couldn't find it in layout

var layout = new Springy.Layout.ForceDirected(
  graph,
  400.0, // Spring stiffness
  400.0, // Node repulsion
  0.5 // Damping
);

How to render an image as a node?

Hi,

I would like to know how to render an image as a node? Is there an example?

Thanks in advance and congratulations for the library.

How to modelize a connection between 2 graphs

Hello,

I am using your nice lib to layout HTML divs and I am trying to figure out how to keep 2 distinct areas connected by one node.
I will have 2 divs containing 2 groups of nodes and I want springy to put the connected nodes next to each other.

I think about 2 solutions, please help me to understand how it has to be done:

1/ I make 2 different graphs in each area, I set myself the position of the connected nodes and I make them have more mass to make it fixed.
2/ I make 1 big graph and I try to adjust the node positions to make them stay in their area.

Do I made myself clear? (I do not speak english fuently ;) )
What do you think about my concern?

Where to add an onClick event?

I'm not too great with javascript, let alone the canvas element, and have been unable to add links to the nodes. I suspect I can do some magic inside the drawnode function in springyui.js, but I'm at a loss as to what, exactly.
An onClick event attached to ctx doesn't seem to work, but what would?

Also, it appears that I can't access canvas elements using jQuery (searching for any IDs of elements on the page), which I suppose shows my ignorance in relation to the canvas element.

Some help, or a pointer in the right direction would be much appreciated. Thanks in advance. And thanks for a small js library that's easy to implement :)

Possible error when running detachNode or removeEdge

After detachNode or removeEdge function, the node remains "undefined" in the adjacency list, whereas it should be completely removed. Otherwise it will cause an error every time

        $.each(graph.adjacency[v], function(k){

is executed after detachNode or removeEdge are executed.

How do you permanently remove edges from adjacency?
Thanks,
Gian

Max number of nodes? nodes collapsing

Given this file: https://gist.github.com/4148030

The nodes representation first generates a line with the main "hub" node in 1 side and all the other on top of each other in the other side. When moving the nodes manually they will eventually collapse into the same pattern. This is based of the demo that you have, so I was wondering if I am hitting some weird issue with maximun number of nodes.

Larger graphs...

I did a few experiments with relatively large graphs.

So here is a first one: http://lucy.pkh.me/k200.html which I recommend not to open with firefox (chromium is fine here). It seems to have some little trouble to stabilize. There are 200 base nodes for a total of ~380 nodes.

Now let's add 100 additionnal root nodes (total ~460 nodes): http://lucy.pkh.me/k300.html
Here I guess I reached the limit... Too bad, because I have 1k nodes to place :(

Details about the EdgeNode

Is there any function or a parameter when a Edge is selected and get highlighted will show the information or description about that Edge on a separate canvas tab?

Using Webworker

Hey, what do you think about using Webworker for doing the physics/math stuff? This way, calculating layout for large graph won't interfere user u.i

Grouping of nodes

This is not an issue. This is a feature request. I just want to know if you can add a feature in springy framework. I just need to group set of nodes so that they will stay together inside of a circle or something like that with a group label. For example when a set of peoples names used in your example demo there should be an option to group them as male and female so that all males stay together in one circle labelled aas "Male" and females in another circle labelled as "Female"

Add node/edge click listener

Hi,
I am not sure if this support is already there.

Can you add onClick listener for node and edge?

I need to show set of properties associated with node or edge upon clicking it.

Undirected graphs via JSON

When creating a force directed graph via JSON and springyui, what can I do to ensure that the edges are not directional (i.e. are line segments, don't have arrow heads).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.