View on GitHub

GraphFellow

GraphFellow: directed graphs in JavaScript

Download this project as a .zip file Download this project as a tar.gz file

beta: GraphFellow is still in development!

Programming graph behaviour

The behaviour of the graph is controlled by named JavaScript functions. There are a few already available, but you can also write your own, and add them to the graph with the add_function function.

You should make sure you add all the functions you need before you initialise your graphs with GraphFellow.init() or GraphFellow.create_graph().

The functions which control the behaviour of the graph are triggered by the following events:

event type on description
on_init graph runs when the graph is initialised
on_tick graph runs every ‘tick’, if tick_period is not zero
on_departure traveller run by a traveller just before it sets off on a journey along an edge
on_arrival traveller run by a traveller when it arrives at the end of its journey along an edge
on_click vertex, edge, traveller runs when user clicks or taps on component
on_mouseover vertex, edge, traveller runs when user’s pointer moves over component

The function is called with two arguments: event and graph. You can also use the this keyword to access the specific component that triggered the event (for init and tick events, this is the graph).

Use event.type to determine what triggered the function. The graph object provides access to the whole graph.

The on_click and on_mouseover events are triggered by pointertap and pointerover events in the browser, so work with touch as well as mouse interfaces.

Built-in functions

Still in development! These are likely to change:

function description
_vertex_transmit_to_random creates and despatches a traveller to travel away from this vertex along an edge chosen at random
_vertex_transmit_to_all creates and despatches a traveller along every edge out of this vertex
_print_payload (for debugging) prints the payload value for this
_pulse animate a pulse on this
_set_color colour this with its initial colour
_send_travellers_on_all_random executes _vertex_transmit_to_random on every vertex
_transmit_from_all_vertices_random executes _vertex_transmit_to_all on every vertex
_traveller_deliver_max_payload sets the payload of the vertex this traveller’s current at_vertex to be the traveller’s payload, if it’s greater

Adding your own functions

After the GraphFellow library has been loaded, use add_function() to make your own function available.

GraphFellow.add_function("alert_payload", function(event, graph){
  alert("I am a " + this.component_type + " with payload " + this.payload.value);
});

Then, in your config, do something like this to have this event triggered by clicking on any vertex or traveller:

"config": {
  "vertices": {"on_click": "alert_payload"},
  "travellers": {"on_click": "alert_payload"}
}

Note: add_function() will not let you add a function with a name that starts with an underscore, because those are reserved for the built-ins.

Component objects

Depending on what triggered the event you’re programming, this may be a GraphVertex, GraphEdge, or Traveller (or sometimes the Graph object itself). The following overview is a jumping-off place for working with these objects — if you need to get deeply familiar with them, look in the graphfellow.js source.

These fields are common to all three component types:

field description
.component_type use this.component_type to determine what type of thing it is — returns a string with a value one of vertex, edge, or traveller (or even graph)
.json_type string with a value one of vertices, edges or travellers, matching the collection names in the graph object
.diagram the visual representation of the object on the graph — it’s a PIXI.DisplayObject. For example, the .position of that gives the (before-scaling) coordinates on the graph (which by default is 1000 wide — see graph.config.grid_width to check).
.payload the payload (which includes a diagram and a value)
.payload.value payload value

Always use payload.set(value) to set the value, because this updates the diagram too.

The GraphVertex object

Useful fields:

field description
.id the unique ID for this vertex
.payload.value payload value
.edges_out array of edges (GraphEdge objects) going out from this vertex
.edges_in array of edges coming into this vertex

The order of edges in the edges_out array matches the order in which they were declared in the config that was used to create the vertex (see last in the get_edge_to() method below).

This vertex might not be the from vertex of an edge you find in the edges_out array if that edge is bi-directional — if it isn’t, it will be the to vertex (and, similarly, be attentive with bi-directional edges in edges_in).

Useful methods:

method description
get_edge_to(to_vertex, chooser) get edge from here to to_vertex, based on chooser
get_random_edge_out() pick a random edge leading out from here
set_color(color) set colour of vertex to color, or initial colour if no argument
pulse(color) do a pulse animation (use config setting if no argument)
stop_pulse() stop pulse animation (if any)
payload.set(value) update payload value

You can select an edge out of a vertex using the vertex’s get_edge_to(to_vertex, chooser) method. The method effectively searches the edges_out array for you and returns one edge that leads from the current vertex to the vertex specified by the to_vertex argument (a GraphVertex object). The returned edge may be null if no such edge is available. The optional chooser argument is a string indicating how that edge will be selected if there is more than one available that leads to the to_vertex:

chooser description
none the first edge found (the default), using the ordering of edges_out array
last the last edge found, using the ordering of edges_out array
random a random choice from all possible edges (made with equal distribution)

The method get_random_edge_out() will choose one of the edges from edges_out, without consideration for which destination that is.

The GraphVertex object has a pulse(color) function that you can use to make it pulse (if the vertex has not been configured with has_pulse then this function has no effect).

The GraphEdge object

Useful fields:

field description
.from the vertex (GraphVertex object) from which this edge starts
.to the vertex at which this edge ends
.is_bidirectional true if travellers can traverse this edge both ways, in which case: be careful about what you infer from from and to

Useful methods:

method description
set_color(color) set colour of the edge to color, or reset to initial colour if no argument
is_edge_from(vertex) true if this edge leads from vertex
is_edge_to(vertex) true if this edge leads to vertex
calculate_midpoint() {x, y} object representing the midpoint of the edge (will be an approximation if the edge is curved)
payload.set(value) update payload value

Note that if the edge is bidirectional, is_edge_from(v) and is_edge_from(v) will both return true if v is at either end of the edge regardless of which end that is.

The Traveller object

Useful fields:

field description
.from the vertex (GraphVertex object) from which this traveller started its journey (null if it is not travelling)
.to the vertex at which this traveller ended (or will end) its journey (null if it is not travelling)
.following_edge the edge (GraphEdge object) that this traveller is following (null if it is not travelling)
.at_vertex the vertex at which this traveller is currently resting (or at which it has just arrvied — see note below)
.qty_journeys count of journeys this traveller has completed (see below)

The from, to, and following_edge fields are set before the traveller’s on_departure event, and cleared after its on_arrival event. Additionally, the at_vertex field is cleared after the on_departure event and populated just before the on_arrival event — so all those fields are useful to you when programming the traveller’s journey-based event functions.

Useful methods:

method description
travel(edge) commence a journey along edge: returns false if journey is denied (see below)
set_color(color) set colour of traveller to color, or reset to initial colour if no argument
destroy() destroys traveller
payload.set(value) update payload value

The Traveller object has a travel(edge) method, which you can use to set a traveller on its journey. The journey will only happen if the traveller can travel along that edge based on its current location. That is, the edge specified must start from the traveller’s at_vertex (which could be at either end if the edge is bi-directional).

If the edge doesn’t start from the traveller’s current location, the call will return false, and no journey is commenced.

Otherwise, travel() triggers the on_departure event if this traveller has one defined, starts the animation, and returns true. Note that the duration of the journey is determined by multiplying the edge’s journey_duration (in seconds) by the traveller’s speed.

To programmatically create a traveller, use Graph.create_traveller(), which takes a config object that must include an at_vertex GraphVertex object, and the graph to which it belongs). This automatically renders the diagram on the stage and adds the traveller to the graph’s travellers array.

  let t = graph.create_traveller({
    "at_vertex": graph.get_vertex_by_id("foo"), 
    "journey_lifespan": 1
  });

The number of journeys a traveller has made is recorded in its qty_journeys property. This is updated on arrival at a vertex, before the on_arrival function (if any) is called.

You can destroy a traveller with its destroy() function. This is called automatically if the number of journeys the traveller has made equals its journey_lifespan, after the on_arrival event of its final journey has been executed. If you call destroy() on a traveller that is already on a journey, it will be destroyed and its on_arrival event will not be triggered.

Accessing the graph or graphs

The GraphFellow methods init() and create_graph() each return the graph they create. In the case of init(), if there is more than one, you’ll get an array of graphs instead. Often you don’t need this, but sometimes it’s helpful (for example, see how the reset button is implemented in the regexp example).

<div class="graphfellow" data-graph-src="example.json"></div>
let my_graph = GraphFellow.init(); // my_graph contains graph object

Note that you may get a graph object back from init() or create_graph() before it is ready to use, because it still needs to load its config from the JSON file linked from its container’s data-graph-src attribute. You can check the graph’s is_ready attribute, which is true when config has loaded. If you provided config explicitly with create_graph() this will be true immediately. Otherwise, you may have to wait for the AJAX call to complete. That call is automatically made as soon as the graph is created (that is, you don’t have to trigger it youself).

All graphs are added to the GraphFellow.graphs array in the order they are created. For example, if there’s only one graph on the page you can access it with GraphFellow.graphs[0].

The Graph object

You might be accessing the Graph object via the result you got back when you created it, or via GraphFellow.graphs (see above), or — more commonly — in an event handler triggered from within the graph. All the event handlers are called with two arguments, event and graph.

The Graph object gives you access to all the components in the graph.

Useful fields:

field description
.vertices array of all vertices
.edges array of all edges
.travellers array of all travellers
.config the current config settings (see settings for detail)
.container the DOM element containing the graph
.component_type returns the string with value “graph
.json_type returns the string with value “graphs
.is_ready set to true when configuration is complete, which might not be immediately upon creation if it’s being loaded, via AJAX, from a JSON file

For example, if graph is the Graph object, you can pulse all vertices red:

for (let i=0; i < graph.vertices.length; i++) {
  graph.vertices[i].pulse(0xff0000);
}

Useful methods:

method description
get_vertex_by_id(id) find vertex by its (string) id
create_traveller(config) make a new traveller (see Traveller class above)

The names of the arrays of components correspond to what you find by inspecting a component’s json_type field. This may be useful if you’re writing event handlers that are generic, because you can use a construction like:

// "this" may be a vertex, edge, or traveller
let number_of_similar_items_in_graph = graph[this.json_type].length

Caution: be careful when iterating over the graph’s travellers array if any of those travellers might be destroyed or created while you’re doing it. The destroy() method in particular can cause unexpected results, because the indices of the array may change when a traveller is removed from it.