#graph-node #graphviz #layout #render-graph #rendering #diagram

vizoxide

Vizoxide is a high-level Rust wrapper for Graphviz that provides an idiomatic, builder-pattern interface for creating, styling, laying out, and rendering complex graphs in various output formats

4 stable releases

new 1.0.5 Mar 9, 2025
1.0.3 Mar 2, 2025

#23 in Visualization

Download history 322/week @ 2025-03-01

322 downloads per month

MIT license

97KB
1.5K SLoC

vizoxide - An idiomatic GraphViz wrapper for Rust

Crates.io Documentation License: MIT

A comprehensive, memory-safe Rust wrapper for the GraphViz graph visualization library. This crate provides an ergonomic, idiomatic Rust interface for creating, manipulating, and rendering graphs.

Features

  • Memory-safe wrapping of GraphViz's C API with proper RAII patterns
  • Builder patterns for fluent, expressive graph creation
  • Complete layout control with support for all GraphViz engines
  • Extensive rendering options including SVG, PNG, PDF, and more
  • Rich attribute handling with predefined constants for common graph, node, and edge attributes
  • Comprehensive error handling with descriptive error types
  • Iterator support for traversing nodes and edges

Prerequisites

This crate requires the GraphViz library to be installed on your system. Installation instructions vary by platform:

Linux

sudo apt-get install graphviz libgraphviz-dev

macOS

brew install graphviz

Windows

Download and install from GraphViz's official site or use:

choco install graphviz

Installation

Add this to your Cargo.toml:

[dependencies]
graphviz = "1.0.0"

Basic Usage

use vizoxide::{Graph, Context};
use vizoxide::layout::{apply_layout, Engine};
use vizoxide::render::{render_to_file, Format};
use vizoxide::attr::AttributeContainer;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Create a new GraphViz context
    let context = Context::new()?;
    
    // Create a directed graph
    let mut graph = Graph::new("example", true)?;
    
    // Add nodes
    let a = graph.add_node("A")?;
    let b = graph.add_node("B")?;
    let c = graph.add_node("C")?;
    
    // Add edges
    graph.add_edge(&a, &b, None)?;
    graph.add_edge(&b, &c, None)?;
    graph.add_edge(&c, &a, None)?;
    
    // Set attributes
    graph.set_attribute("rankdir", "LR")?;
    a.set_attribute("shape", "box")?;
    b.set_attribute("style", "filled")?;
    b.set_attribute("fillcolor", "lightblue")?;
    
    // Apply layout
    apply_layout(&context, &mut graph, Engine::Dot)?;
    
    // Render to a file
    render_to_file(&context, &graph, Format::Svg, "example.svg")?;
    
    Ok(())
}

Core Concepts

Context

A Context represents the GraphViz runtime environment. It's required for layout and rendering operations:

let context = Context::new()?;

Graphs

Create directed or undirected graphs:

// Directed graph
let directed = Graph::new("my_graph", true)?;

// Undirected graph
let undirected = Graph::new("my_graph", false)?;

// Strict graph (no duplicate edges)
let strict = Graph::new_with_strictness("my_graph", true, true)?;

Nodes & Edges

Nodes and edges are the building blocks of graphs:

// Add nodes
let node1 = graph.add_node("Node1")?;
let node2 = graph.add_node("Node2")?;

// Add an edge
let edge = graph.add_edge(&node1, &node2, None)?;

Attributes

Customize the appearance of graphs, nodes, and edges with attributes:

// Set attributes directly
graph.set_attribute("bgcolor", "lightgray")?;
node.set_attribute("shape", "box")?;
edge.set_attribute("color", "red")?;

// Check and remove attributes
if node.has_attribute("color")? {
    node.remove_attribute("color")?;
}

// Get attribute value
let shape = node.get_attribute("shape")?;

The attr module provides constants for common attribute names:

use graphviz::attr::{graph, node, edge, values};

// Use predefined attribute constants
graph_obj.set_attribute(graph::RANKDIR, "LR")?;
node_obj.set_attribute(node::SHAPE, values::shape::BOX)?;
edge_obj.set_attribute(edge::STYLE, values::style::DASHED)?;

Builder Pattern

The crate supports a fluent builder pattern for creating graph elements:

// Create a graph with the builder
let graph = Graph::builder("flowchart")
    .directed(true)
    .strict(false)
    .attribute("rankdir", "TD")
    .build()?;

// Create a node with attributes
let start = graph.create_node("start")
    .attribute("shape", "ellipse")
    .attribute("style", "filled")
    .attribute("fillcolor", "lightblue")
    .build()?;

// Create an edge with attributes
let edge = graph.create_edge(&start, &process, None)
    .attribute("label", "Begin")
    .attribute("color", "blue")
    .build()?;

Layout Engines

GraphViz supports various layout algorithms:

// Basic layout application
apply_layout(&context, &mut graph, Engine::Dot)?;

// Other available engines
apply_layout(&context, &mut graph, Engine::Neato)?;  // Spring model
apply_layout(&context, &mut graph, Engine::Fdp)?;    // Force-directed
apply_layout(&context, &mut graph, Engine::Circo)?;  // Circular
apply_layout(&context, &mut graph, Engine::Twopi)?;  // Radial
apply_layout(&context, &mut graph, Engine::Sfdp)?;   // Multiscale

Layout Settings

Customize layout behavior with predefined or custom settings:

// Using predefined layout settings
let radial_settings = radial_layout();
radial_settings.apply(&graph)?;

// Using custom layout settings
let custom_settings = LayoutSettings::new()
    .with_overlap("false")
    .with_splines("ortho")
    .with_nodesep(0.75)
    .with_ranksep(1.0);
custom_settings.apply(&graph)?;

Rendering

Render to File

render_to_file(&context, &graph, Format::Svg, "output.svg")?;
render_to_file(&context, &graph, Format::Png, "output.png")?;
render_to_file(&context, &graph, Format::Pdf, "output.pdf")?;

Render to Memory

// Render to string (for text formats like SVG, DOT)
let svg_string = render_to_string(&context, &graph, Format::Svg)?;

// Render to bytes (for any format)
let png_bytes = render_to_bytes(&context, &graph, Format::Png)?;

// Render to a writer
let mut buffer = Cursor::new(Vec::new());
render_to_writer(&context, &graph, Format::Svg, &mut buffer)?;

Supported Formats

The crate supports all GraphViz output formats including:

  • Vector formats: SVG, PDF, PS, EPS
  • Raster formats: PNG, JPEG, GIF, BMP
  • Text formats: DOT, XDOT, JSON, Plain
  • And many more accessible via Format enum

Graph Traversal

Iterate over nodes and edges:

// Iterate through all nodes
for node in graph.nodes() {
    println!("Node: {}", node.name()?);
    
    // Iterate through outgoing edges
    for edge in graph.out_edges(&node) {
        let target = edge.to_node().name()?;
        println!("  -> {}", target);
    }
}

// Check graph properties
println!("Node count: {}", graph.node_count());
println!("Edge count: {}", graph.edge_count());
println!("Directed: {}", graph.is_directed());

Advanced Usage

Custom Plugins

// Create a context with custom plugin settings
let context = Context::new_with_plugins(true, false)?;

Render Options

// Customize rendering with options
let options = RenderOptions::new()
    .with_anti_alias(true)
    .with_transparency(true)
    .with_dpi(300.0)
    .with_background("white")
    .with_scale(2.0);

// Apply options when rendering
render_with_options(&context, &graph, Format::Png, "high_quality.png", &options)?;

Examples

The crate includes several examples demonstrating various features:

  • simple_graph - Basic graph creation and rendering
  • complex_graph_with_build_pattern - Using the builder pattern
  • edge_builder_with_multiple_attributes - Working with edge attributes
  • predefined_layout_settings - Using predefined layouts
  • creating_context_with_plugins - Working with GraphViz plugins
  • iterating_over_nodes_and_edges - Graph traversal
  • render_graph_to_string_and_bytes - In-memory rendering
  • custom_layout_settings - Advanced layout configuration
  • modifying_attributes_and_removing_them - Attribute manipulation
  • render_graph_to_writer - Rendering to custom output

Run any example with:

cargo run --example example_name

Error Handling

The crate uses the GraphvizError type to represent various error conditions:

match result {
    Ok(graph) => println!("Graph created successfully"),
    Err(GraphvizError::GraphCreationFailed) => println!("Failed to create graph"),
    Err(GraphvizError::LayoutFailed) => println!("Failed to apply layout"),
    Err(GraphvizError::RenderFailed) => println!("Failed to render graph"),
    Err(e) => println!("Other error: {}", e),
}

License

This crate is available under the MIT License. See the LICENSE file for more details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Dependencies

~0.3–2.2MB
~43K SLoC