bgrapher v1.1.2
bgrapher

Bgrapher presents as much information about a graph as possible in a concise format, while removing all unnecessary noise. This lets you focus on the bigger picture, while still allowing you to focus in on the details as needed.
Bgrapher works particularly well for visualizing large, sparse, directed, hierarchical graphs.
Anything that you might want to draw with dot, but which doesn't neatly fit into a single image, is a good candidate for Bgrapher.
Getting started
Say you already have a bgraph, and would like to draw yourBgraph inside yourElement = document.createElement('div'), you can do so directly:
<script src="https://unpkg.com/bgrapher/dist/bgrapher.min.js"></script>
<script>
let yourBgrapher = new bgrapher.Bgrapher(yourBgraph, yourElement);
</script>Or, if installed via npm:
import { Bgrapher } from 'bgrapher';
let yourBgrapher = new Bgrapher(yourBgraph, yourElement);If your graph is not in bgraph format yet, you can create it using the format described below.
Bgraph format
Bgraphs are like any other graphs, except optimized for speed and flexibility on the frontend. This means that you may need to do a bit more prep work "offline" before rendering a graph.
Bgraph structure
Bgraphs are formatted in JSON. Comments cannot be used.
Nodes are represented with blocks and edges are pairs of edgeEnds.
A bgraph must contain a list of each, as well as all required properties:
| Property | Description |
|---|---|
width & height | The total dimensions of the bgraph. This should be sufficiently large to contain all of your blocks/edgeEnds. |
bgColor | Background color of the bgraph. |
highlightBgColor & highlightFgColor | Highlight colors for highlighting graph interactions. Choosing 2 contrasting colors for these values improves visibility. |
For example, this is a 4 by 4 bgraph with a white background (16777215 corresponds to #ffffff), black on white highlights (0 and 16777215), and no blocks or edgeEnds:
{
"width": 4,
"height": 4,
"bgColor": 16777215,
"highlightFgColor": 0,
"highlightBgColor": 16777215,
"blocks": [],
"edgeEnds": []
}blocks and edgeEnds are objects that can be added to the two lists in order to define a graph.
Bgraph blocks
Each block consists of, crucially, an (x,y) location and an id, as well as other properties that define how it appears in a bgraph:
| Property | Description |
|---|---|
width & height | Dimensions of the rectangle representing the block in the bgraph. |
depth | How the block is ordered relative to others. Higher depth blocks appear above lower depth blocks. |
color | A decimal representation of the block's color (e.g., "#123456" ==> 1193046). |
A block also holds an edgeEnds list of edgeEnd IDs, which helps provide contextual highlighting when interacting with a block using Bgrapher.
Typically, this is a list of edgeEnds that correspond to edges going to and from the given block.
This is an example of a 2 by 1, #000001-colored block, located at the top-left of the bgraph (0,0), which has two edgeEnds (0 and 100) associated with it:
...
"blocks": [
{
"id": 0,
"x": 0, "y": 0,
"width": 2, "height": 1,
"depth": 0, "color": 1,
"edgeEnds": [
0,
100
]
}
],
...Bgraph edgeEnds
Like blocks, each edgeEnd consists of an (x,y) location and an id, as well as some additional properties:
| Property | Description |
|---|---|
isSource | true/false. Whether the edgeEnd represents a start of an edge or the end of an edge. |
direction | 1/2/3/4, which correspond to up/right/down/left. Influences how a highlighted edge appears when drawn. |
color | Same format as for blocks. |
Like a block, an edgeEnd also holds an edgeEnds list of edgeEnd IDs, representing all the edgeEnds that this edgeEnd is coming from/going to.
An edgeEnd usually has at least one other edgeEnd in this list, in order to form a complete edge, with a source edgeEnd and a destination edgeEnd.
Additionally, an edgeEnd can correspond to a particular block, which is represented by the block's ID.
It is best to have the block point back to the corresponding edgeEnds that refer to it.
Below is an example of the edgeEnds from the block above.
The first is at the bottom-left of the block (0,1), while the other is at the bottom-right (1,1). Both are colored black (#000000).
Both are edge ends to one another (as evident from each edgeEnds array), and both correspond to block 0.
One is the source ("isSource": true) and is pointing downward ("direction": 3) while the other is a destination ("isSource": false) and is pointing up ("direction": 1).
...
"edgeEnds": [
{
"id": 0,
"x": 0, "y": 1,
"color": 0,
"direction": 3,
"isSource": true,
"block": 0,
"edgeEnds": [
100
]
},
{
"id": 100,
"x": 1, "y": 1,
"color": 0,
"direction": 1,
"isSource": false,
"block": 0,
"edgeEnds": [
0
]
}
]
...In total, this example bgraph represents a single node with a loop.
Suggested bgraph style guide
Note that the above is only one of the many ways in which this graph could be represented in bgraph format. The following style guide suggests one possible convention for writing out bgraphs, tailored to directed graphs.
Representing nodes in a directed graph:
- Have one
blockfor each node in the graph. - Increment each
block'sidby one from the previousblock, starting from0. - Use at least
1for bothheightandwidth. - Position
blocks at least 3 units away from otherblocks, unless those other blocks are parent hierarchies. - Use the maximum of the number of
edgeEndsthat correspond to theblockand are located to the right or left of theblockas theheightof theblock; use1if none are present. - Use the maximum of the number of
edgeEndsthat correspond to theblockand are located above or below theblockas thewidthof theblock; use1if none are present. - Place all
edgeEndsthat correspond to theblockeither 1 unit below, above, to the left, or to the right of theblock. - Place
edgeEndsthat are to the left or to the right within the height of the block; placeedgeEndsthat are above or below the block within the width of the block. - Ensure that
edgeEndsthat correspond to theblockare listed in theedgeEndslist in theblock.
Representing edges in a directed graph:
- Use two
edgeEnds to represent an edge: oneedgeEndrepresents the beginning of the edge, and anotheredgeEndrepresents the end of the edge. - Increment each
edgeEnd'sidby one from the previousedgeEnd, starting from0. - Position
edgeEnds that are the start of an edge to the right of theblockthey correspond to and usedirectionpointing to the right. - Position
edgeEnds that are the end of an edge to the left of theblockthey correspond to and usedirectionpointing to the right. - Position
edgeEnds that are the start of an edge that crosses hierarchy below theblockthat theedgeEndcorresponds to and usedirectionpointing down. - Position
edgeEnds that are the end of an edge that crosses hierarchy above theblockthat theedgeEndcorresponds to and usedirectionpointing down. - Use
directionto go right for edges that don't cross hierarchy and down for edges that do. - Place
edgeEnds next to each other contiguously and not in the same position. - Use
0for blockdepthwhen no hierarchies exist in the graph.
Representing hierarchies in a directed graph:
- Use
blocks to represent hierarchies. - Use lower
depthvalues for parent hierarchies and higherdepthfor child hierarchies. Use the highestdepthfor theblocks that represent the nodes themselves. - Make sure that the
blocks used to represent hierarchies havewidthandheightthat encompass all the contents of the hierarchy, plus 2 units of padding on all sides. - For edges from/to the hierarchies themselves (rather than nodes within the hierarchies), follow the same convention as for
blocks.
The rest of the info is about the Bgrapher JS object and its various interfaces.
Member variables
A Bgrapher object contains all of the data that you provide to it in the Bgrapher.blocksData and Bgrapher.edgeEndsData member variables, keyed by each block's or edgeEnd's corresponding ID.
All of the metadata is also held in the Bgrapher object itself.
For example:
let yourBgrapher = new Bgrapher({
width: 4,
height: 4,
...
blocks: [
...
{
id: 12,
edgeEnds: [0, 100],
...
},
...
],
...
}, yourElement);
let bgraphWidth = yourBgrapher.width; // 4
let bgraphHeight = yourBgrapher.height; // 4
let edgeEndsOf12 = yourBgrapher.blocksData[12].edgeEnds; // [0, 100]Treat these as read-only to avoid undefined behavior.
Instead use Bgrapher methods to make changes to the bgraph data.
Methods
Bgrapher constructor
Bgrapher([yourBgraph [, yourElement [, yourBgraphState]]])The most convenient form is:
let yourBgrapher = new Bgrapher(yourBgraph, yourElement);Where you simply specify the input bgraph and where you want the bgraph to be displayed.
Parameters
yourBgraph: Either a javascript object or a JSON string containing the bgraph to be displayed.
yourElement: An HTML element within which the Bgrapher will draw the bgraph.
yourBgraphState: Externally-managed state of the user's interaction with the bgraph, such as the user's zoom and offset.
Bgrapher.activeBlocks & Bgrapher.activeEdgeEnds
activeBlocks()
activeEdgeEnds()Return value
Returns either the objects of the active blocks or active edgeEnds in the bgraph.
"Active" blocks/edgeEnds include any which the user selected or hovered over, and which are thus highlighted in the bgraph.
Bgrapher.activeEdges
activeEdges()Return value
Returns a pair of active edgeEnd objects.
Since edges aren't highlighted directly, these are the result of user interaction with blocks and edgeEnds.
Bgrapher.toggleBlock & Bgrapher.toggleEdgeEnd
toggleBlock(blockID)
toggleEdgeEnd(edgeEndID)Toggling a block or edgeEnd using these functions is equivalent to a user clicking on them.
Parameters
blockID/edgeEndID: ID of either the block or edgeEnd which you are toggling.
Bgrapher.selectBlock & Bgrapher.selectEdgeEnd
selectBlock(blockID)
selectEdgeEnd(edgeEndID)Selecting a block or edgeEnd using these functions is equivalent to a user right-clicking on them.
Parameters
blockID/edgeEndID: ID of either the block or edgeEnd which you are toggling.
Bgrapher.hoverBlock & Bgrapher.hoverEdgeEnd
hoverBlock(blockID)
hoverEdgeEnd(edgeEndID)Hovering a block or edgeEnd using these functions is equivalent to a user hovering over them.
Parameters
blockID/edgeEndID: ID of either the block or edgeEnd which you are toggling.
Callbacks
Bgrapher.onHoverBlock & Bgrapher.onHoverEdgeEnd
onHoverBlock(yourCallback)
onHoverEdgeEnd(yourCallback)Parameters
yourCallback: The callback to be called when the user hovers over an element in the graph.
block or edgeEnd object is passed in to the callback.
Bgrapher.onToggleBlock & Bgrapher.onToggleEdgeEnd
onHoverBlock(yourCallback)
onHoverEdgeEnd(yourCallback)Parameters
yourCallback: The callback to be called when the user toggles an element in the graph, meaning that they click to highlight a node and its edges.
block or edgeEnd object is passed in to the callback.
Bgrapher.onSelectBlock & Bgrapher.onSelectEdgeEnd
onHoverBlock(yourCallback)
onHoverEdgeEnd(yourCallback)Parameters
yourCallback: The callback to be called when the user selects an element, seeking to extract more information about it, by default via right-click.
block or edgeEnd object is passed in to the callback.
Shared BgraphState for multiple Bgraphers
It can be convenient to sync user interaction across multiple bgraphers. This can be achieved by using a shared bgraph state.
To use bgraph state that is shared across bgrapher objects:
1. Instantiate a new BgraphState() to be used by all of your bgraphers.
2. Instantiate new Bgrapher()s with yourBgraphState passed in.
For example:
import { Bgrapher, BgraphState } from 'bgrapher';
let yourBgraphState = new BgraphState();
let yourBgrapher1 = new Bgrapher(yourBgraph1, yourElement1, yourBgraphState);
let yourBgrapher2 = new Bgrapher(yourBgraph2, yourElement2, yourBgraphState);When working with a shared state, ensure that both bgraphs have the same dimensions.
BgraphState
new BgraphState()The BgraphState object contains the user's location/zoom level within the bgraph, as well as a list of Bgraphers subscribed to be notified of state changes.
BgraphState.update
update()This is the preferred method to use if you need to force-update all Bgraphers state (e.g., if modifying BgraphState manually).
Any Bgraphers that are subscribed to be notified of state changes are notified when yourBgraphState's update method is called.
To ensure that Bgraphers are subscribed, pass in yourBgraphState to the new Bgrapher() constructor, or to the populateElement call.
BgraphState.center
center()Centers (i.e., places client view in the middle of the bgraph) all bgraphs that are subscribed to the BgraphState.
BgraphState versus React state
Don't let React manage your BgraphState! Bgrapher regenerates only the relevant parts of the graph, while React won't know any better than to refresh the entire HTML element.
In other words, instead of this:
this.state = { myBgraphState: new BgraphState() };Do this:
this.myBgraphState = new BgraphState();Other interfaces
Bgrapher.hoveredBlock & Bgrapher.hoveredEdgeEnd
hoveredBlock()
hoveredEdgeEnd()Return value
Returns only the current hovered block or edgeEnd in the bgraph.
Returns null if no block or edgeEnd is currently hovered.
Prefer to use onHoverBlock & onHoverEdgeEnd instead.
Bgrapher.initBgraph
initBgraph(bgraph)Useful for re-initializing the data used to construct the bgraph.
Parameters
yourBgraph: Either a javascript object or a JSON string containing the bgraph to be displayed.
Bgrapher.populateElement
populateElement(yourElement [, yourBgraphState])Populates yourElement and optionally registers Bgrapher with an external BgraphState.
Useful for moving a bgraph to another element.
You must pass the BgraphState explicitly for it to be preserved.
Parameters
yourElement: An HTML element within which the Bgrapher will draw the bgraph.
yourBgraphState: Externally-managed state of the user's interaction with the bgraph, such as the user's zoom and offset.
Bgrapher.draw
draw()Re-draws the bgrapher using the latest BgraphState.
If using an external BgraphState, prefer to call BgraphState.update() instead.
Bgrapher.centerView
centerView()Re-draws the bgrapher with client view in the center of the bgraph.
If using an external BgraphState, prefer to call BgraphState.center() instead.
Bgrapher.clientWidth & Bgrapher.clientHeight
clientWidth()
clientHeight()Return value
Returns the width or height of the Bgrapher element within yourElement, based on the underlying Bgrapher implementation used.
Bgrapher.updateClientSize
updateClientSize()Changes the size of the bgraph to match the size of yourElement, which contains the bgraph.
Useful if the dimensions of the element change.
This is called automatically whenever the window is re-sized.
Bgrapher.curBlock & Bgrapher.curEdgeEnd
curBlock(cur)
curEdgeEnd(cur)Parameters
cur: Object of the form {x: yourX, y: yourY}, where the x,y coordinate corresponds to a location relative to the bgraph.
For example, this can correspond to a mouse cursor location, but it would not correspond to the x,y coordinate of a block or edgeEnd as specified in the input bgraph object.
Return value
Returns the block or edgeEnd object at the specified location.
Development
Run build
npm install
npm run buildNote that jsdom needs canvas, which may need the following:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfigRun tests
npm run test:covRun example
npm run build:devThen access at localhost:3000.