about arbor

Arbor is a graph visualization library built with web workers and jQuery. Rather than trying to be an all-encompassing framework, arbor provides an efficient, force-directed layout algorithm plus abstractions for graph organization and screen refresh handling.

It leaves the actual screen-drawing to you. This means you can use it with canvas, SVG, or even positioned HTML elements; whatever display approach is appropriate for your project and your performance needs.

As a result, the code you write with it can be focused on the things that make your project unique – the graph data and your visual style – rather than spending time on the physics math that makes the layouts possible.

installation

To use the particle system, get jQuery and add the file at lib/arbor.js
to your path somewhere and include them in your HTML:

If you want to let arbor handle realtime color and value tweens for you,
include the arbor-tween.js file as well. this will add a pair of new
tweening methods to the ParticleSystem object (see the docs to decide if
this appeals to you or not).

getting started

The source distribution contains a sample project
that demonstrates some of the basic idioms for working with the library to build a
visualization. More detailed documentation can be found in the reference section.

In addition, the demos folder of the source distribution contains standalone versions
of the demos on the site. But since all of them use xhr to fetch their data, you'll
still need to view them from an http server. If you don't have a copy of
apache handy, use the demo-server.sh script to create a local server.

Contribute

Code submissions are greatly appreciated and highly encouraged. Please send
pull requests with fixes, enhancements, etc. to samizdatco
on github. The oldschool may also pipe their diff -u output to gro.sjrobra@ofni.

license

colophon

Arbor’s design is heavily influenced by Jeffrey Bernstein’s excellent
Traer Physics
library for Processing. In addition,
much of the brute-force physics code was originally adapted from Dennis Hotson’s
springy.js.

The
Barnes-Hut n-body implementation is based on Tom Ventimiglia and Kevin Wayne’s vivid
description of the algorithm.
Thanks to all for such elegantly simple and comprehensible code.

ParticleSystem

The particle system stores your nodes
and edges and handles updating their coordinates as the simulation progresses.

rendering

The particle system doesn’t do any drawing on its own; you need to provide those
routines in a separate object that will be triggered by the system when it’s time
to redraw the screen. To set this up, create an object with two methods (.init
and .redraw), then set the particle system’s renderer
attribute to your new object:

The .init method will be called once before the first pass through the
draw loop. Then the .redraw method will be called each time the screen
needs to be re-plotted. Take a look at the sample project for a
slightly more elaborated example of how this works.

nodes

addNode(name, data)

name is a string identifier that will be used in talking to the particle
system about this node.

data is an object with keys and values set by the user. you can use it to
store additional information about the node for later use in e.g., drawing.

Creates a new node in the particle system and returns the resulting Node object.

getNode(name)

name is an identifier for a node already in the system

Returns the corresponding Node object or
undefined if none is found. If called with a node as an
argument, it will return that same node (for idempotence).

pruneNode(node)

node is either an identifier string or a Node object

Removes the corresponding Node from the
particle system (as well as any Edges in which it is a participant).

edges

addEdge(source, target, data)

source and target are either identifier strings or a Node objects.

data is a user data object with additional information about the edge.

Creates a new edge connecting the source and target nodes
then returns the resulting Edge object.

Returns an array containing all Edge objects in which the
node is the source.
If no connections exist, returns [].

getEdgesTo(node)

node is a string identifier or Node object

Returns an array containing all Edge objects in which the
node is the target.
If no connections exist, returns [].

pruneEdge(edge)

edge is an Edge object.

Removes the corresponding Edge from the particle system.

iteration

eachNode(callback)

callback is a function with the signature ƒ(node, pt) where
node is a Node object and pt is a Point object with its current
location.

The callback function will be invoked once for each Node in the system.

Note that while the node.p attribute is always in the coordinate system of
the particle system, the pt argument is transformed into pixel coordinates
(provided you have called .screenSize to specify the screen bounding box).

eachEdge(callback)

callback is a function with the signature ƒ(edge, pt1, pt2) where
edge is an Edge object and pt1 and pt2 are Point objects
with the current endpoint locations.

The callback function will be invoked once for each Edge in the system.

Similar to the behavior of .eachNode, the edge.source.p and
edge.target.p attributes are always in the coordinate system of
the particle system, while pt1 and pt2 will be transformed into pixel coordinates
(provided you have called .screenSize to specify the screen bounding box).

modification

graft(branch)

branch is an object of the form {nodes:{}, edges:{}}.

the nodes attribute contains a mapping of node names to data objects. For example,

Adds nodes and edges to the current set in the particle system. The
leaf object values in the branch argument will be accessible through the
.data attribute of the resulting Nodes and Edges.

merge(branch)

branch is an object of the form {nodes:{}, edges:{}}
(see .graft for details).

Adds nodes and edges to the current set in the particle system and removes
any that are not present in the new branch. Conserved nodes will maintain their position
and state.

prune(callback)

callback is a function with the signature ƒ(node, from, to) where
node is a Node object and from and to are arrays
of edges for which node is the source and target respectively.

The callback function will be invoked once for each Node
in the system and should return true if the node should be pruned or do
nothing if the node should remain unaltered. Note that pruning a node will also remove all edges in
which it participates

system settings

parameters( ) or (params)

if present, params is an object containing new settings values
for the particle system. Valid keys are the same as for the ParticleSystem
constructor function:
repulsion, stiffness, friction, gravity, fps, and dt.

If called with no arguments, returns an object with the current system
parameters as keys and values. If an argument is supplied, any values specified will
be used as the new parameters (omitted values will remain unchanged).

fps( ) or (fps)

if present, the fps argument is a positive integer.

If called with no arguments, returns the frame rate achieved over
the last few seconds of drawing. Otherwise the argument will set the new target
frame rate. This affects the frequency with which the particle system iterates
its simulation as well as the frequency with which the ParticleSystem calls the
.redraw method of the object pointed to by its
.renderer attribute.

bounds( )

Returns a bounding box containing all nodes using system
coordinates. The return value is of the form:

{ topleft:{x:, y:}, bottomright:{x:, y:} }

energy( )

Returns some basic stats on the state of activity in the
system. The values are in terms of velocity within the system’s coordinate
frame. This can be a useful measure of when the layout has stabilized.
The return value is of the form:

{sum:, max:, mean:, n:}

start( )

Manually start the system running. By default the system
will run and pause on its own based on the level of energy in the particles.
You should only need to manually start after having previously called the .stop method.

stop( )

Pauses the particle simulation until .start is called.
Since the system begins running as soon as it is supplied with nodes and edges, you
may wish to call .stop shortly after creating the system object if it
will not be displayed until later in the page lifetime (e.g., until a user action takes place).

coordinate helpers

screenSize(width, height)

width and height are positive integers defining the
dimensions of the screen area you will be drawing in.

Calling this method enables automatic coordinate transformations
from the particle system’s coordinate system to your display’s. This can be seen
in the points supplied by the .eachNode and .eachEdge
iterators as well as the to/fromScreen and nearest
methods in this section.

You will nearly always want to call this once when setting up your
ParticleSystem and renderer as well as whenever the dimensions
of the display area change.

screenPadding(top, right, bottom, left)

All arguments are integers defining the number of pixels that should be left
blank along each edge of the display area. Either 1, 2, or 4 arguments are
expected and are interpreted similarly to the CSS padding: property.

When the system transforms points
between coordinate systems it will factor the padding into the locations it
provides to .eachNode and company.

screenStep(stepsize)

stepsize is a number between 0 and 1 defining the
amount the bounding box should move from one frame to the next.

As the nodes move and the bounding box changes, the system
applies a variable amount of smoothing to the ‘camera’ movements. As stepsize
approaches 1 the amount of smoothing decreases as the bounds updates become more
instantaneous.

screen() or (opts)

If present, opts is an object of the form:

{ size:{width:400, height:300},
padding:[1,2,3,4],
step:.1 }

This is a shorthand method combining the functions of the prior three.
If called without an argument, returns the current screen size/padding/scaling. If an
argument is supplied, updates the settings accordingly.

toScreen(systemPoint)

systemPoint is a Point object whose x and y
values are set using the system’s internal coordinate scheme.

Converts the x and y to screen coordinates on the basis of the current
screen size and padding, returning the result as a new Point object. If the
size hasn’t been set or the system hasn’t started yet, undefined will be returned
instead.

fromScreen(screenPoint)

screenPoint is a Point object whose x and y
values are using the screen’s pixel coordinates.

Converts the x and y to system coordinates on the basis of the current
screen size and padding, returning the result as a new Point object. If the
size hasn’t been set or the system hasn’t started yet, undefined will be returned
instead.

nearest(screenPoint)

screenPoint is a Point object whose x and y
values are using the screen’s pixel coordinates.

Returns a reference to the node nearest the argument’s screen position
in an object of the form:

{node:, point:, distance:}

node and point will either be the eponymous
objects or null depending on whether any such node exists. distance
is measured in pixels.

tweening

tweenNode(node, duration, opts)

node is a Node object or an identifier string
for the node whose values you wish to tween.

duration is the time (in seconds) the transition should last.

opts is a mapping of names and target values.

This method allows you to initiate gradual transitions of values in the .data
object of a given Node. For instance consider a node whose .data
object looks like:

{color:"#00ff00", radius:1}

The following call will make this node quadruple in size and change color to light blue
over the course of 3 seconds:

sys.tweenNode(myNode, 3, {color:"cyan", radius:4})

The system can handle tweening numerical values or colors which are expressed as either
named CSS colors or hex strings beginning with a “#”.

There is also a pair of ‘magic’ keys that can be included in the opts
argument to modify the tween behavior. delay specifies the time
in seconds before the tween should begin. ease can be set to
one of the names seen in the src/easing.js file to control the dynamics
of the transition.

!

Note that the tween methods are disabled unless you include the arbor-tween.js
file in your page.

tweenEdge(edge, duration, opts)

edge is the Edge whose values you wish
to tween.

duration is the time (in seconds) the transition should last.

opts is a mapping of names and target values.

Identical in behavior to .tweenNode except that it operates on the .data
attribute of an Edge instead of a Node.

!

Note that the tween methods are disabled unless you include the arbor-tween.js
file in your page.

datastructures

Node

Node objects encapsulate the current physics state of a point in the particle
system as well as giving you a place to attach associated non-physics metadata.

This will create a new Node object with a .data field
containing {myColor:"goldenrod"}. Note that the mass value was stripped
out of the data object and used for the node’s mass in the simulation.

The ‘magic’
variables you can use in this way (and their defaults):

mass 1.0 the node’s resistance to movement and repulsive power

fixed false if true, the node will be unaffected by other particles

x auto the starting x position (in system coordinates)

y auto the starting y position (in system coordinates)

using nodes

With each tick of the simulation the values in .p will be updated based on
the repulsion and spring forces in the system. To alter the node’s properties (in response
to, say, a mouse click), simply reset its values and the system will use the new values
in its next tick:

console.log( node.p.x, node.p.y )
>> 1.2, 0.4

node.p = arbor.Point(1, 1)
console.log( node.p.x, node.p.y )
>> 1, 1

node.p.y = 13
console.log( node.p.x, node.p.y )
>> 1, 13

Each node contains an attribute called .data whose contents
and use are entirely up to you. Typically it is used for storing metadata about
the node so your rendering code can know how to draw it, what its label text
should be, which url to go to on click, etc.

system values

name

String (read only)

mass

Number

fixed

Boolean

p

Point

user values

data

{ … }

Edge

Edge objects hold references to the source and target nodes they connect and have
a preferred ‘resting’ length. They will apply forces on their endpoint nodes in an
attempt to attain this optimal distance.

This creates a pair of Node objects then creates an Edge from
the first to the second. The length key is a special variable that will be
used for setting the edge’s resting length. Any other keys in the object passed to
.addEdge will be placed in the resulting Edge’s .data
attribute.

Note that .addEdge can be called with either actual
Node objects or simply their .names as arguments. If a name is
used but a node with that identifier does not yet exist, the system will automatically create
one before creating the edge. For instance, the above code could be simplified to: