dagre-rs
A complete Rust port of dagre.js -- hierarchical directed graph layout using the Sugiyama method.
Features
- Full 27-step layout pipeline matching dagre.js behavior
- All four rank directions: TB (top-to-bottom), BT, LR, RL
- Compound (nested) graph support with subgraph boundaries
- Edge label placement and self-loop routing
- Network simplex rank assignment
- Barycenter-based crossing minimization
- Brandes-Koepf coordinate assignment (4-direction sweep + median balance)
- Graph data structure with directed/undirected, multigraph, and compound modes
- Graph algorithms: topological sort, Dijkstra, Bellman-Ford, Floyd-Warshall, Tarjan SCC, Prim MST, and more
- JSON serialization via serde
- Zero runtime dependencies (only
logand optionalserde)
Compatibility
This crate tracks @dagrejs/dagre v3.0.1-pre (commit 4713b59). The reference data in cross-validate/reference_data.json is generated against this exact commit; see cross-validate/SETUP.md for how to reproduce.
Cross-validated against dagre.js on 20 reference graphs covering single nodes, chains, diamonds, cycles, fan-outs, disconnected components, edge labels, self-loops, compound subgraphs, all four rank directions, custom separators, margins, parallel edges, and varying node sizes. All produce identical coordinates, ranks, orders, and graph dimensions.
Downstream using dagre-d3-es or dagre.js v0.8.5 (mermaid, Go d2)
@dagrejs/dagre changed its NetworkSimplex tie-breaking behavior between v0.8.5 and the current 3.x line: when two crossing-reduction sweeps tie on crossing count, the new behavior keeps the last tied layering, while v0.8.5 (still used by dagre-d3-es and Go d2) keeps the first. Set LayoutOptions { tie_keep_first: true, .. } to opt into the v0.8.5 behavior — required for byte-identical layouts when interoperating with those downstreams.
Usage
use ;
use ;
// Create a directed graph
let mut g = with_options;
// Add nodes with dimensions
let mut a = default;
a.width = 50.0;
a.height = 50.0;
g.set_node;
let mut b = default;
b.width = 50.0;
b.height = 50.0;
g.set_node;
// Add an edge
g.set_edge;
// Run layout
layout;
// Read results
let node_a = g.node.unwrap;
println!;
Configuration
use ;
let opts = LayoutOptions ;
layout;
Project stats
| Metric | Value |
|---|---|
| Source code | ~8,600 lines |
| Test code | ~8,800 lines |
| Test functions | 528 |
| Test pass rate | 100% (0 ignored, 0 failures) |
| Cross-validation | 20/20 match with dagre.js |
| Clippy warnings | 0 (enforced by CI via clippy --all-targets -- -D warnings) |
| Runtime dependencies | 0 |
CI runs cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features on every push and pull request — see .github/workflows/ci.yml.
License
Apache-2.0