JSPM

  • Created
  • Published
  • Downloads 42167
  • Score
    100M100P100Q151901F
  • License BSD-3-Clause

Graphviz DOT rendering and animated transitions for D3

Package Exports

  • d3-graphviz

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (d3-graphviz) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

d3-graphviz

Renders SVG from graphs described in the DOT language using the Viz.js port of Graphviz and does animated transitions between graphs.

Features

  • Rendering of SVG graphs from DOT source
  • Animated transition of one graph into another
  • Edge path tweening
  • Node shape tweening
  • Fade-in and fade-out of entering and exiting nodes and edges
  • Animated growth of entering edges
  • Panning & zooming of the generated graph

Graphviz methods all return the graphviz renderer instance, allowing the concise application of multiple operations on a given graph renderer instance via method chaining.

To render a graph, select an element, call selection.graphviz, and then render from a DOT source string. For example:

d3.select("#graph")
  .graphviz()
    .renderDot('digraph {a -> b}');

It is also possible to call d3.graphviz with a selector as the argument like so:

d3.graphviz("#graph")
    .renderDot('digraph {a -> b}');

This basic example can also bee seen here.

A more colorful demo can be seen here.

Installing

If you use NPM, npm install d3-graphviz. Otherwise, download the latest release.

Principles of Operation

Uses Viz.js to do a layout of a graph specified in the DOT language and generate an SVG text representation, which is analyzed and converted into a data representation that is joined with a selected DOM element and used to render the SVG graph on that element and to animate transitioning of one graph into another.

Contents

API Reference

Creating a Graphviz Renderer

Creating a Graphviz Renderer on an Existing Selection

# selection.graphviz() <>

Returns a new graphviz renderer instance on the given selection.

Creating a Graphviz Renderer Using a Selector String or a Node

# d3.graphviz(selector) <>

Creates a new graphviz renderer instance on the first element matching the given selector string. If the selecor is not a string, instead creates a new graphviz renderer instance on the specified node.

Rendering

# graphviz.renderDot(dotSrc) <>

Renders an SVG graph from the specified dotSrc string and appends it to the selection the grapviz renderer instance was generated on.

It is also possible to do the Graphviz layout in a first separate stage and do the actual rendering of the SVG as a second step like so:

d3.select("#graph")
  .graphviz()
    .dot('digraph {a -> b}')
    .render();

This enables doing the computational intensive layout stages for multiple graphs before doing the potentially synchronized rendering of all the graphs simultaneously.

# graphviz.dot(dotSrc) <>

Computes the layout of a graph from the specified dotSrc string and saves the data for rendering the SVG with graphviz.render at a later stage.

# graphviz.render() <>

Renders an SVG graph from data saved by graphviz.dot and appends it to the selection the grapviz renderer instance was generated on.

# graphviz.engine(engine) <>

Sets the Graphviz layout engine name to the specified engine string. The engine name must be set before attaching the DOT source. If it is changed after this, an eror is thrown. Supports all engines that Viz.js supports. Currently these are:

  • circo
  • dot (default)
  • fdp
  • neato (not supported with viz-lite.js)
  • osage
  • patchwork
  • twopi

sfdp is currently not supported.

Creating Transitions

# graphviz.transition([name]) <>

Applies the specified transition name to subsequent SVG rendering. Accepts the same arguments as selection.transition, but returns the graph renderer instance, not the transition. To attach a configured transition, first create a transition intance with d3.transition, configure it and attach it with graphviz.transition like so:

var t = d3.transition()
    .duration(750)
    .ease(d3.easeLinear);

d3.select("#graph").graphviz()
    .transition(t)
    .renderDot('digraph {a -> b}');

NOTE: Transitions should be named if zooming is enabled. Transitions using the null name will be interrupted by the zoom behavior, causing the graph to be rendered incorrectly.

Controlling Fade-In & Fade-Out

# graphviz.fade(enable) <>

If enable is true (default), enables fade-in and fade-out of nodes and shapes, else disables fade-in and fade-out.

Controlling Animated Growth of Entering Edges

# graphviz.growEnteringEdges(enable) <>

If enable is true (default), enables animated growth of entering edges, else disables animated growth of entering edges.

A demo of animated growth of entering edges can be seen here

Controlling Path Tweening

# graphviz.tweenPaths(enable) <>

If enable is true (default), enables path tweening, else disables path tweening.

# graphviz.tweenPrecision(precision) <>

Sets the precision used during path tweening to precision pixels. Default is 1.

Controlling Shape Tweening

# graphviz.tweenShapes(enable) <>

If enable is true (default), enables shape tweening during transitions, else disables shape tweening. If enable is true, also enables path tweening since shape tweening currently is performed by converting SVG ellipses and polygons to SVG paths and do path tweening on them. At the end of the transition the original SVG shape element is restored.

# graphviz.convertEqualSidedPolygons(enable) <>

If enable is true (default), enables conversion of polygons with equal number of sides during shape tweening, else disables conversion. Not applicable when shape tweening is disabled. At the end of the transition the original SVG shape element is restored.

A demo of shape tweening can be seen here.

Controlling Panning & Zooming

# graphviz.zoom(enable) <>

If enable is true (default), enables panning and zooming, else disables panning & zooming. Note that panning and zooming during transitions may be queued until after the transition.

Maintaining Object Constancy

In order to acheive meaningful transitions, the D3 default join-by-index key function is not sufficient. Four different key modes are available that may be useful in different situations:

  • title (default) - Uses the text of the SVG title element for each node and edge g element as generated by Graphviz. For nodes, this is "node_id" (not to be confused with the node attribute id) and for edges it is "node_id edgeop node_id", e.g. "a -> b". For node and edge sub-elements, the tag-index key mode is used, see below.
  • id - Uses the id attribute of the node and edge SVG g elements as generated by Graphviz. Note that unless the graph author specifies id attributes for nodes and edges, Graphviz generates a unique internal id that is unpredictable by the graph writer, making the id key mode not very useful. For node and edge sub-elements, the tag-index key mode is used, see below.
  • tag-index - Uses a key composed of the SVG element tag, followed by a dash (-) and the relative index of that element within all sibling elements with the same tag. For example: ellipse-0. Normally not very useful for other than static graphs, since all nodes and edges are siblings and are generated as SVG g elements.
  • index - Uses the D3 default join-by-index key function. Not useful for other than static graphs.

# graphviz.keyMode(mode) <>

Sets the key mode to the specified mode string. If mode is not one of the defined key modes above, an error is thrown. The key mode must be set before attaching the DOT source. If it is changed after this, an eror is thrown.

Customizing Graph Attributes

# graphviz.attributer(function) <>

If the function is a function, it is evaluated for each SVG element, before applying attributes and transitions, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]). If function is null, removes the attributer. For example, to set the fill color of ellipses to yellow and fade to red during transition:

var t = d3.transition()
    .duration(2000)
    .ease(d3.easeLinear);

d3.select("#graph").graphviz()
    .transition(t)
    .attributer(function(d) {
        if (d.tag == "ellipse") {
            d3.select(this)
                .attr("fill", "yellow");
            d.attributes.fill = "red";
        }
    })
    .renderDot('digraph {a -> b}');

Large Graphs

For very large graphs it might be necessary to increase the amount of memory available to Viz.js.

# graphviz.totalMemory(size) <>

Sets the total memory available to Viz.js to size bytes, which should be a power of 2. See the Viz.js API for details.

Examples

Building Applications with d3-graphviz

SVG structure

The generated SVG graph has exactly the same structure as the SVG generated by Viz.js, so applications utilizing knowledge about this structure should be able to use d3-graphviz without adaptations. If path tweening or shape tweening is used, some SVG elements may be converted during transitions, but they are restored to the original shape after the transition.

NOTE: avoid selection.select

When selecting elements within the graph, selection.select must not be used if additional rendering is going to be performed on the same graph renderer instance. This is due to the fact that selection.select propagates data from the elements in the selection to the corresponding selected elements, causing already bound data to be overwritten with incorrect data and subsequent errors. Use selection.selectAll, which does not propagate data or selection.node and querySelector. For example, to select the first g element within the first svg element within a specified div element:

var div = d3.select("#graph");
var svg = d3.select(div.node().querySelector("svg"));
var g = d3.select(svg.node().querySelector("g"));

For more, read this issue and this Stack Overflow post.

Data Format

The data bound to each DOM node is an object containing the following fields:

  • tag - The DOM node tag.
  • attributes - An object containing attributes as properties.
  • children - An array of data for the node's children.
  • key - The key used when binding data to nodes with the key function. See graphviz.keyMode for more.
  • text - Contains the text if the DOM node is a Text node. A text node has the tag "#text", not to be confused with the tag "text", which is an SVG 'text' element.
  • comment - Contains the comment if the DOM element is a Comment node. A comment node has the tag "#comment".

Other fields are used internally, but may be subject to change between releases and should not by used an external applications.

To inspect data:

d3.select("#graph").graphviz()
    .renderDot('digraph  {a -> b}');
console.log(JSON.stringify(d3.select("svg").datum(), null, 4));

Performance

The shape- and path-tweening operations are quite computational intensive and can be disabled with graphviz.tweenShapes and graphviz.tweenPaths to improve performance if they are not needed. Even if enabled, performance gains can be made by turning off conversion of equally sided polygons with graphviz.convertEqualSidedPolygons or by reducing tween precision by setting a larger value with graphviz.tweenPrecision.

In order for animated transitions to be smooth, special considerations has been made to do the computational intensive operations before transitions start. Use transition.delay to reserve time for those computations.

Since the author is new to both Javascipt and D3, there are probably a lot of things that can be improved. Suggestions are welcome.

Requirements

d3-graphviz uses a few ES6 language features, so it must be used with a modern browser.

Development

In order to run the tests you need Node.js 6.x or later.

Credits