Documentation

Documentation

Don’t hesitate to dive into the source code! It is well described with comments and the library archive also includes some example files worth checking out.

Example

This commented example demonstrates most of what you can do with the Dracula library. It also serves as a quick tutorial on the usage.

var g = new Graph();

/* add a simple node */
g.addNode('strawberry');
g.addNode('cherry');

/* add a node with a customized label */
g.addNode('id34', { label: 'Tomato' });

/* Advanced node example: Add a node with a customized shape
 * (the Raphael graph drawing implementation can draw this shape, please
 * consult the RaphaelJS reference for details http://raphaeljs.com/)
 * Note: Here's the place for even more tweaking! */


/* First: Write a custom node render function. <em>/
var render = (r, n) => {
  /</em> the Raphael set is obligatory, containing all you want to display <em>/
  var set = r.set().push(
    /</em> custom objects go here <em>/
    r.rect(n.point[0] - 30, n.point[1] - 13, 62, 86)
      .attr({ fill: '#fa8', 'stroke-width': 2, r: '9px'}))
      .push(r.text(n.point[0], n.point[1] + 30, n.label)
      .attr({ 'font-size': '20px' }));
    /</em> custom tooltip attached to the set */

    set.items.forEach((el) => {
      el.tooltip(r.set().push(r.rect(0, 0, 30, 30)
        .attr({
          fill: '#fec', 'stroke-width': 1, r: '9px'
        })
      ));
    });
    return set;
  };

g.addNode('id35', {
  label: 'meat\nand\ngreed' ,
  /* filling the shape with a color makes it easier to be dragged */
  render: render
});

/* connect nodes with edges */
g.addEdge('strawberry', 'cherry');
g.addEdge('cherry', 'apple');

/* a directed connection, using an arrow */
g.addEdge('id34', 'cherry', { directed : true } );

/* customize the colors of that edge */
g.addEdge('id35', 'apple', {
  style: {
    stroke: '#bfa',
    fill: '#56f',
    label: 'Label'
  }
});

/* add an unknown node implicitly by adding an edge */
g.addEdge('strawberry', 'apple');

/* layout the graph using the Spring layout implementation */
var layouter = new Graph.Layout.Spring(g);
layouter.layout();

/* draw the graph using the RaphaelJS draw implementation */
var renderer = new Graph.Renderer.Raphael('canvas', g, width, height);
renderer.draw();

78 replies on “Documentation”

I am trying to use dracula JS in a react application. But it says, Dracula is not defined.
How do I add & use dracula js in react?

from within the file where you want to use the library, just add the following at the top:

“`javascript
import Dracula from ‘graphdracula’
“`

Hi,

I am new to dracula js, i am trying to add the edge and node dynamically on the click of the button, but Edge is going seperate from the source.

Below is the code:

var g = new Graph();
var renderer;
var layouter;
window.onload = function() {

var redraw;
window.height = 600;
window.width = 1000;

g.addEdge(“cherry”, “apple”);
g.addEdge(“cherry”, “strawberry”);
g.addEdge(“strawberry”, “kiwi”);
g.addEdge(“banana”, “kiwi”);

g.addNode(‘id34’, { label: ‘Tomato’ });
g.addEdge(‘id34’, ‘cherry’, { directed : true } );

/* layout the graph using the Spring layout implementation */
layouter = new Graph.Layout.Spring(g);
layouter.layout();

/* draw the graph using the RaphaelJS draw implementation */
renderer = new Graph.Renderer.Raphael(‘canvas’, g, width, height);
renderer.draw();

function myFunction() {
g.addNode(‘drinks’);
}

}
function redraw() {
g.addEdge(“drinks”, “kiwi”);
/* draw the graph using the RaphaelJS draw implementation */
renderer = new Graph.Renderer.Raphael(‘canvas’, g, width, height);
renderer.draw();
};

Re Draw

You should add examples of performance, this is on of the most important when looking for good graph library. Currently I use VivaGraph.js for it`s performance.

Hi,
Wonderful works~
Is there the possibility to add two lines(both-way connections) with arrow between two nodes? Thanks~

Hi sir, really great library. I want to mousemove for edges, so that on hovering the edges, label show up as tooltip. Is there any way to do it. I tried it using nearest node, but this is not helping.

I replace nodes with images… but how do I change the Color of text?

var set = r.set().push(
r.image(“images/server.png”, n.point[0], n.point[1]+10, 80, 80)).push(r.text(n.point[0]+40, n.point[1]+80, n.label)
.attr({“font-size”:”15px”}));

a simple jquery script like this will work:

$(document).on('click', '#canvas ellipse', function () {
var id = $(this).attr('id');
window.location.href  = 'http://localhost:default.aspx?page=id';
});

Hi ,
I want to make a distance between two nodes and that should be ordered and aligned properly. I don’t know how to resolve this issue. Please do reply to get it resolved this issue.

Thanks in advance.

This is a grate easy to understand library.
I just try this with raphael latest version. Seems connections are not rendering. Have you fixed it.

Thank you in advance

Yeah, I know. It’s mainly because of the use of floating point numbers for the layout algorithm. I plan to switch to integers and scale the coordinates after the layout has been calculated. Simpler shapes might also help (e.g. rectangles without borders).

Hi ,
I want to make a distance between two nodes and that should be ordered and aligned properly. I don’t know how to resolve this issue. Please do reply to get it resolved this issue.

Thanks in advance.

Hi good works!!!
I’ve a problem for the label, I want icon for the node and this is ok, but label not appear
what is wrong?

var g = new Graph();
var render = function(r, n) {
var set = r.set().push(
r.image(“icon_user.png”, 5,5, 20, 20)
);
return set;
};

/* add a simple node */
g.addNode(“strawberry”, {label:”hello”,render:render});

Great library, thanks! Is there a way to generate graphs where we could display more than 1 edge between two nodes. If I define 2 edges between A and B, they will overlap and appear as a single edge.

Thanks.

Dear Johann and Dracula users,
I try to set default colour for nodes with the possibility to change colour for few nodes only.
I imagine it would be on the stage of adding node:
g.addNode(“node_name”, {_option to change default node colour} );

Anybody would be able to help? I would be grateful.
Thanks!

In :dracula_graph.js file:
var color = “green”;
… var ellipse = r.ellipse…

and in working file:
g.addNode(“_node_name_”, { stroke : “red” , fill : “red”});

🙂

I would like to develop a solitaire game using your library.
The game is as follows.
In the initial state every node of a graph is white.
The player selects a node by clicking on it. The selected node become orange and the border size of the adjacent nodes increases.
If the user confirms his choice, by clicking for a second time, the selected node turns red and the neighborhood becomes yellow. The number of non-white nodes is reported.
After a while (less than one second), the red node disappears (the same happens for its incident edges).
The player will continue in eliminating nodes until the graph will be empty.
The goal is to keep the number of non-white nodes as low as possible.
Of course, an undo button permits the player to go back to the previous states.

Can you give me some hints to develop the program? For instance, how to add events handlers for the nodes.

Thanks in advance for your cooperation.

PS: I know that this game could seem to be not very actractive but it represents an optimisation problem and can help the researchers to develop a better solution strategy.

Hi Philipp!

Thank you so much for the great tool you created!
It helped me alot.

But I got a dumb question regarding custom nodes and edges. When I make a custom node render function like in your example – edges disappear. How to fix that?

Thank you again!
BR,
Anastasia

This is a great library — I’m just playing around with it and I love it so far.

I am having the same problem as Anastasia — when I add a custom node renderer; my edges disappear.

Thanks!

Actually, the edges stayed if I kept using an ellipse instead of a circle. So, I changed the shape to an ellipse with the same horizontal and vertical radius to make a circle; then the edges stayed.

Hi!
Is it possible to customize the label on the arcs making them appear only when mouse gets over it?
thanks!

Also is there any way to make the lines that point both ways clearer? E.g. not curved so they point to different places or in bold if they point both ways?

Hi,
How can I add click event to any node in the graph. I need to do it urgently. Please answer.
Also i cannot see any ID in the eclipse created for the nodes? Is there any way to add IDs to the node elements?

Thanks.

this works for me:
var yourSet = r.set();
yourSet.push(r.rect(x,y,w,h));
yourSet.click(function() {
alert(” rect clicked “);
});
best,
jens

When I use the code below I found it’s not easy to locate the rectangle and the text position exactly where I can see on my screen , especially when the n.label is very long or has many word break.

I try to modify the parameters on the functions : r.rect() and r.text() .
After I do that, the UI behavior is so strange that I can’t display the node normally.

var render = function(r, n) {
/* the Raphael set is obligatory, containing all you want to display */
var set = r.set().push(
/* custom objects go here */
r.rect(n.point[0], n.point[1]+13, 162, 86)
.attr({“fill”: “#fa8”, “stroke-width”: 2, r : “9px”}))
.push(r.text(n.point[0] + 80, n.point[1] + 60, n.label)
.attr({“font-size”:”20px”}));
return set;
};

g.addNode(“bkn”, {label:”12tdkfiekdasfjldfdsadfB1″,render:render});
g.addNode(“bkn”,{label:”123\n456\n789234234\n”,render:render});

I was playing with your library and I think it is great. Very powerful and I love the direct representation.

One thing I did notice is that when dealing with directed edges, if more than one terminates at a particular node, it is sometimes impossible to tell which edge is directed. This is because they both terminate at the same point on the node and the arrows overlap. Is this something you have looked into fixing?

Does anyone knows how to change the color of the nodes? I need to sort the nodes using color, but I don’t know how to do that yet :/ I really appreciate if anyone can help me.
Thanks.

hi!
Really great library! I noticed that the nodes usually start from right area (parent) to left area (children) and bottom area (parent) to top area(children). Is there any way to do the opposite? From top to bottom and left to right? Really appreciate your help.

Is there any possibility to add custom objects into the node, like a check box, or a button?

Any Idea! thanks!

is there any way to clear graph?
i have a list of records for each it will generate graph when i will click on next for next record’s graph it will still showing old one’s nodes and edges.

so how to clear the old graph or replace with new graph?

Say you have a node:
g.addNode(“c2”, { label: “Work”})

and your svg will look like:

I thought label is for display purposes, and id is the identity. Am I wrong?

Last try: note that the id is Work

<ellipse cx=”640.5″ cy=”359.5″ rx=”30″ ry=”20″ fill=”#bf5600″ stroke=”#bf5600″ stroke-width=”2″ id=”Work” style=”cursor: move;” fill-opacity=”0.6″/>

How would I add auxiliary boxes for each node with text and html tags on it.
I was reading the render proc bellow and it actually make a extra box to pop up once we hover the mouse under the node.
How ever I could not figure out how to amend it in order to write html text within those boxes.
Where can I find samples or tutorials for it?
var render = function(r, n) {
/* the Raphael set is obligatory, containing all you want to display */
var set = r.set().push(
/* custom objects go here */
r.rect(n.point[0]-30, n.point[1]-16, 80, 80)
.attr({“fill”: “#fa8”, “stroke-width”: 2, r : “9px”}))
.push(r.text(n.point[0], n.point[1] + 30, n.label)
.attr({“font-size”:”20px”}));
/* custom tooltip attached to the set */
set.items.forEach(
function(el) {
el.tooltip(r.set().push(r.rect(0, 0, 50, 100)
.attr({“fill”: “#fdddc”, “stroke-width”: 1, r : “10px”})
.attr({“font-size”:”20px”})))});
return set;
};

Here’s a link on the representation library that I used, it’s SVG instead of HTML but you can do similar things with it, check it out: http://raphaeljs.com/ (in the example, r is the Raphael object)

Really nice job! I was wondering whether there is a way to customize the edges, say I want change the length of the edge. Could you provide me a way of doing that?

Thanks!

Hey

Really great job!

I was just wondering if there is a nice way to do edges from one node to itself. So far, the edge will be a really small loop, I think it would be better to have a bigger loop, that one can actually see.

Hi Philip

I have become a big Fan of your simple still elegant call graph API.So, I have used your call graph API in one of my application.I need to make the connections and nodes to be more organized with out over lapping to each other.Shuffle function which is given in above example is not avoiding the
over lapping nodes and connections.
I’m looking for some solution to draw the call graphs in a neat way,may be to make the less the size of a node, and to maintain the length of a connection lines.
Also as I earlier asked about grouping..
Please consider following scenario of dependency analysis and if I’m using call graphs for that represent such dependencies in a call graph..

1. Class A calls Class B and Class C
2. If Class A changed ,B and C will be impacted
3. There is another set of classes P,Q and R ,P has Changed ,which consumes B.
4. But B,P,Q are from Same Module,A and C are separately for A,B, C and P,Q,R those will be two separated call graphs groups.
5. If I wanted to indicate and group again to show what node(A,B,C,D,P,R,Q) by Module on top of already drew graphs and on the same screen as an additional information (Page),What can be done?
can we show B,P,Q highlighted ? sparking with a flash line while they are attached to the initial grouping of changed impacts?

Really appreciate your comments
Thanks
Kanchana

Is there a way to have two distinct nodes with identical labels? All the documented examples use the node label to uniquely identify the node..

I found the same need when working with the lib. What I did is that I created a separate data structure to manage the graph information, with two arrays, one for the nodes and other for the edges. Whenever something changes on my custom data struct, it plots the whole graph. The bad thing is that it redraws everything, changing colors and positions each time. If it works for you you can do that.

/*
 * Edge
 */
function Edge(node,target){
    this.node=undefined;
    this.targets=new Array();
    node ? this.node=node : null;
    target ? this.addTarget(target) : null;
}
Edge.prototype = {
    constructor:Edge,
    create:function(node,targets){
        var r = null;
        if(node&amp;&amp;targets){
            this.node=node;
            this.addTarget(targets);
            r=node;
        }
        return r;
    },
    addTarget:function(target){
        var r = false;
        if(target&amp;&amp;this.node!=undefined){
            if(typeof target == "object"){
                r = new Array();
                for(var i=0;i&lt;target.length;i++){
                    var j=0,found=false;
                    while(j&lt;this.targets.length&amp;&amp;!found){
                        this.targets[j]==target[i] ? found=true : null;
                        j++;
                    }
                    if(j==this.targets.length){
                        this.targets.push(target[i]);
                        r.push(target[i]);
                    }
                }
            }
            else{
                var i=0,found=false;
                while(i&lt;this.targets.length&amp;&amp;!found){
                    this.targets[i]==target ? found=true : null;
                    i++;
                }
                if(!found){
                    this.targets.push(target);
                    r = target;
                }
            }
        }
        return r;
    },
    removeTarget:function(target){
        var r = null;
        if(target){
            if(typeof target == "object"){
                r = new Array();
                for(var i=0;i&lt;target.length;i++){
                    var j=0;
                    while(this.targets[j]!=undefined){
                        if(this.targets[j]==target[i]){
                            r.push(target[i]);
                            this.targets.splice(j,1);
                            j--;
                        }
                        j++;
                    }
                }
            }
            else{
                var i=0;
                while(this.targets[i]!=undefined){
                    if(this.targets[i]==target){
                        r = target;
                        this.targets.splice(i,1);
                        i--;
                    }
                    i++;
                }
            }
        }
        return r;
    },
    hasTarget:function(target){
        if(typeof target != "object"){
            for(var i=0;i&lt;this.targets.length;i++)
                if(this.targets[i] == target)
                    return true;
            return false;
        }
        else{
            for(var i=0;i&lt;target.length;i++)
                if(!this.hasTarget(target[i]))
                    return false;
            return true;
        }
    }
}

/*
 * DirectedGraph
 */
function DirectedGraph(init){
    this.nodes=new Array();
    this.edges=new Array();
    this.isBidirectional = false;
    this.matrix = new Array();
    if(typeof init == "object"){
        init.type=="bidirectional" ? this.setType("bidirectional") : null;
        typeof init.nodes == "object" ? this.addNode(init.nodes) : null;
        typeof init.edges == "object" ? this.addEdges(init.edges) : null;
        /*if(typeof init.buildMatrix == "boolean" &amp;&amp; this.nodes.length&gt;0){
           
        }*/
    }
}
DirectedGraph.prototype = {
    constructor: DirectedGraph,
    /*
    * setType
    * Sets the type of Graph. It must be empty to
    * set the type. Can be: unidirectional (set by default),
    * or bydirectional.
    */
    setType: function(how){
        if(this.isEmpty()){
            switch(how){
                case "unidirectional":
                    this._isBidirectional=false;
                    break;
                case "bidirectional":
                    this._isBidirectional=true;
                    break;
                default:
                    return false;
            }
            return true;
        }
        return false;
    },
    /*
    * isEmpty
    * Checks if the array of nodes is empty
    * @return True if is empty, false if it isn't
    */
    isEmpty: function(){
        if(this._nodes._length==0)
            return true;
        return false;
    },
    /*
    * reset
    * Creates a new array for the nodes and for the
    * edges (it empties the Graph), and leaves only
    * the type of Graph value
    */
    reset: function(){
        this._nodes = new LinkedList();
        this._edges = new LinkedList();
    },
    /*
    * addNode
    * Adds a node to the Graph, verifying first if the
    * value of the node exists already.
    * @return true if the node was inserted, and
    * false if it wasn't because it was already there
    */
    addNode: function(a){
        if(typeof a != "object"){
            if(!this.hasNode(a)){
                this.nodes.push(a);
                return a;
            }
            return false;
        }
        else{
            var r = new Array();
            for(var i=0;i&lt;a.length;i++)
                if(this.addNode(a[i]))
                    r.push(a[i]);
        }
        return r;
    },
    /*
    * hasNode
    * Checks if the given value is contained on the Graph
    * @return true if it is. False if it isn't.
    */
    hasNode: function(a){
        if(typeof a != "object"){
            for(var i=0;i&lt;this.nodes.length;i++)
                if(this.nodes[i] == a)
                    return true;
            return false;
        }
        else{
            for(var i=0;i&lt;a.length;i++)
                if(!this.hasNode(a[i]))
                    return false;
            return true;
        }
    },
    /*
    * removeNode
    */
    removeNode: function(a){
        if(typeof a != "object"){
            var i=0;
            while(i&lt;this.nodes.length){
                if(this.nodes[i]==a){
                    this.nodes.splice(i,1);
                    i--;
                    return a;
                }
                i++;
            }
            return false;
        }
        else{
            var r = new Array();
            for(var i=0;i&lt;a.length;i++)
                if(this.removeNode(a[i]))
                    r.push(a[i]);
        }
        return r;
    },
    /*
    * addEdge
    */
    addEdge: function(from,to){
        if(from&amp;&amp;to){
            if(!this.hasNode(from))
                this.addNode(from);
            if(typeof to == "object"){
                if(this.hasEdge(from)){
                    var arr = this.edges[this.indexOfEdge(from)].targets.slice();
                    for(var i=0;i&lt;arr.length;i++)
                        to.push(arr[i]);
                }
                to.sort();
                for(var i=1;i&lt;to.length;i++)
                    if(to[i]==to[i-1]){
                        to.splice(i,1);
                        i--;
                    }
                for(var i=0;i&lt;to.length;i++)
                    !this.hasNode(to[i]) ? this.addNode(to[i]) : null;
            }
            else if(!this.hasNode(to))
                this.addNode(to);
            if(!this.hasEdge(from)){
                this.edges.push(new Edge(from,to));
                return "added edge:["+from+"]-&gt;["+to.toString()+"]";
            }
            else{
                if(this.edges[this.indexOfEdge(from)].addTarget(to))
                    return "updated edge:["+from+"] with ["+to.toString()+"]";
            }
        }
        return false;
    },
    /*
    * addEdges
    */
    addEdges: function(edges){
        for(var i=0;i&lt;edges.length;i++)
            this.addEdge(edges[i][0],edges[i][1]);
    },
    /*
    * indexOfEdge
    */
    indexOfEdge: function(from){
        for(var i=0;i&lt;this.edges.length;i++)
            if(from==this.edges[i].node)
                return i;
        return false;
    },
    /*
    * hasEdge
    */
    hasEdge: function(which){
        for(var i=0;i&lt;this.edges.length;i++)
            if(which==this.edges[i].node)
                return true;
        return false;
    },
    /*
    * removeEdge
    */
    removeEdge: function(which){
        if(this.hasEdge(which)){
            this.edges.splice(this.indexOfEdge(which),1);
            return which;
        }
        return false;
    },
    /*
    *
    */
    removeEdgeTarget: function(node,target){
        if(this.hasEdge(node)){
            if(typeof target != "object"){
                var arr = this.edges[this.indexOfEdge(node)].targets.slice();
                for(var i=0;i&lt;arr.length;i++)
                    if(arr[i] == target){
                        arr.splice(i,1);
                        this.edges[this.indexOfEdge(node)].targets = arr;
                        return target;
                    }
                return false;
            }
            else{
                var r = new Array();
                for(var i=0;i&lt;target.length;i++)
                    if(this.removeEdgeTarget(node,target[i]))
                        r.push(target[i]);
                if(r.length==0)
                    return false;
                else
                    return r;
            }
        }
        else
            return false;
    }
    /*
        TODO: {} Add functions to:
       
                Build matrix
                Draw matrix
               
              Update functions:
               
                removeNode:
                    - Find if there's an edge object whos node is the node to be
                    removed, and remove that edge object.
                    - Find if there's edges pointing to the node to be removed and
                      remove those targets from their edge objects
        }
    */
}

Hi,

great lib!

I want to use your library for the visualization of protein interactions. But i think i noticed a bug. When you display a graph and scroll the page afterwards the tooltips are not placed where they should. They change position according to the visual part of the page.

Do you know this problem or may it be my mistake.

Hopefully you can fix it.

I just figured out, that this only happens when a page is scrollable, so only when the graph is stored on a big page or a too small iframe. When you use a big enough iframe it works well.

I fixed that without needing to resort to an iframe.
I changed the tooltip code to this:

Raphael.el.tooltip = function (tp) {
    this.tp = tp;
    this.tp.o = {x: 0, y: 0};
    this.tp.hide();
    this.hover(
        function(event){
            this.mousemove(function(event){
                this.tp.translate(event.clientX -
                                  this.tp.o.x,event.clientY - this.tp.o.y + $(window).scrollTop());
                this.tp.o = {x: event.clientX, y: event.clientY + $(window).scrollTop()};
            });
            this.tp.show().toFront();
        },
        function(event){
            this.tp.hide();
            this.unmousemove();
        });
    return this;
};

Hi entropy,

yes this fixes the problem of a page moving with a scrollbar, but only if the Raphael svg starts at the coordinates x=o,y=0 of the page, otherwise you have to adjust the position of a tooltip in the render function with negative values. E.g. if you have a toolbar on the left side.

but thanks, it helps a lot.

Peter

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.