yog/render/dot
DOT (Graphviz) format export for visualizing graphs.
This module exports graphs to the DOT language,
which is the native format for Graphviz - a powerful open-source
graph visualization tool. The exported files can be rendered to PNG, SVG, PDF, and other
formats using the dot, neato, circo, or other Graphviz layout engines.
Quick Start
import yog/render/dot
// Export with default styling
let dot_string = dot.to_dot(my_graph, dot.default_dot_options())
// Write to file and render with Graphviz CLI
// $ dot -Tpng output.dot -o graph.png
Customization
Use DotOptions to customize:
- Node labels and shapes
- Edge labels and styles
- Per-node and per-edge attributes (custom colors, shapes, etc.)
- Subgraphs/clusters for visual grouping
- Highlight specific nodes or paths
- Graph direction (LR, TB, etc.)
Generic Data Types
The to_dot function works with any node and edge data types. Use
default_dot_options_with_edge_formatter() when your edge data is not a String:
let options = dot.default_dot_options_with_edge_formatter(fn(weight) {
int.to_string(weight)
})
let dot_string = dot.to_dot(my_int_weighted_graph, options)
Per-Element Styling
Provide custom attribute functions for fine-grained control:
let options = DotOptions(
..dot.default_dot_options(),
node_attributes: fn(id, data) {
case id {
1 -> [("fillcolor", "green"), ("shape", "diamond")]
_ -> []
}
},
edge_attributes: fn(from, to, weight) {
case weight > 10 {
True -> [("color", "red"), ("penwidth", "2")]
False -> []
}
},
)
Subgraphs and Clusters
Group nodes visually using subgraphs:
let options = DotOptions(
..dot.default_dot_options(),
subgraphs: Some([
Subgraph(
name: "cluster_0",
label: Some("Cluster A"),
node_ids: [1, 2, 3],
style: Some(dot.Filled),
fillcolor: Some("lightgrey"),
color: None,
),
]),
)
Rendering Options
| Engine | Best For |
|---|---|
dot | Hierarchical layouts (DAGs, trees) |
neato | Spring-based layouts (undirected) |
circo | Circular layouts |
fdp | Force-directed layouts |
sfdp | Large graphs |
References
Types
Arrow head/tail style
pub type ArrowStyle {
Normal
ArrowDot
ArrowDiamond
ODiamond
ArrowBox
Crow
Vee
Inv
Tee
ArrowNone
CustomArrow(String)
}
Constructors
-
NormalStandard arrow
-
ArrowDotDot circle
-
ArrowDiamondFilled diamond
-
ODiamondEmpty diamond
-
ArrowBoxArrowBox
-
CrowCrow’s foot (database notation)
-
VeeV-shaped
-
InvInverted V
-
TeeTee (perpendicular line)
-
ArrowNoneNo arrow
-
CustomArrow(String)Custom arrow (e.g., “ediamond”, “odot”)
Options for customizing DOT (Graphviz) diagram rendering.
This type is generic over node data n and edge data e, allowing it to work
with graphs of any data types. Use default_dot_options() for String edge
data, or default_dot_options_with_edge_formatter() for custom edge types.
Per-Element Styling
Use node_attributes and edge_attributes to set custom DOT attributes
for individual nodes and edges. These functions receive the node/edge data
and should return a list of #(attribute_name, attribute_value) pairs.
Common node attributes: “fillcolor”, “shape”, “width”, “height”, “penwidth” Common edge attributes: “color”, “penwidth”, “style”, “arrowhead”, “constraint”
pub type DotOptions(n, e) {
DotOptions(
node_label: fn(Int, n) -> String,
edge_label: fn(e) -> String,
highlighted_nodes: option.Option(List(Int)),
highlighted_edges: option.Option(List(#(Int, Int))),
node_attributes: fn(Int, n) -> List(#(String, String)),
edge_attributes: fn(Int, Int, e) -> List(#(String, String)),
subgraphs: option.Option(List(Subgraph)),
graph_name: String,
layout: option.Option(Layout),
rankdir: option.Option(RankDir),
bgcolor: option.Option(String),
splines: option.Option(Splines),
overlap: option.Option(Overlap),
nodesep: option.Option(Float),
ranksep: option.Option(Float),
node_shape: NodeShape,
node_color: String,
node_style: Style,
node_fontname: String,
node_fontsize: Int,
node_fontcolor: String,
edge_color: String,
edge_style: Style,
edge_fontname: String,
edge_fontsize: Int,
edge_penwidth: Float,
arrowhead: option.Option(ArrowStyle),
arrowtail: option.Option(ArrowStyle),
highlight_color: String,
highlight_penwidth: Float,
)
}
Constructors
-
DotOptions( node_label: fn(Int, n) -> String, edge_label: fn(e) -> String, highlighted_nodes: option.Option(List(Int)), highlighted_edges: option.Option(List(#(Int, Int))), node_attributes: fn(Int, n) -> List(#(String, String)), edge_attributes: fn(Int, Int, e) -> List(#(String, String)), subgraphs: option.Option(List(Subgraph)), graph_name: String, layout: option.Option(Layout), rankdir: option.Option(RankDir), bgcolor: option.Option(String), splines: option.Option(Splines), overlap: option.Option(Overlap), nodesep: option.Option(Float), ranksep: option.Option(Float), node_shape: NodeShape, node_color: String, node_style: Style, node_fontname: String, node_fontsize: Int, node_fontcolor: String, edge_color: String, edge_style: Style, edge_fontname: String, edge_fontsize: Int, edge_penwidth: Float, arrowhead: option.Option(ArrowStyle), arrowtail: option.Option(ArrowStyle), highlight_color: String, highlight_penwidth: Float, )Arguments
- node_label
-
Function to convert node ID and data to a display label
- edge_label
-
Function to convert edge data to a display label
- highlighted_nodes
-
Optional list of node IDs to highlight
- highlighted_edges
-
Optional list of edges to highlight as (from, to) pairs
- node_attributes
-
Function to provide custom DOT attributes for each node. Returns list of #(attribute_name, attribute_value) pairs. These attributes override any defaults or highlighting.
- edge_attributes
-
Function to provide custom DOT attributes for each edge. Returns list of #(attribute_name, attribute_value) pairs. These attributes override any defaults or highlighting.
- subgraphs
-
Optional list of subgraphs/clusters for visual node grouping.
- graph_name
-
Graph name (default: “G”)
- layout
-
Layout engine (default: None = auto-detect)
- rankdir
-
Graph direction (default: Some(TopToBottom))
- bgcolor
-
Background color (CSS color, default: None)
- splines
-
Edge routing (default: None = Graphviz default)
- overlap
-
Overlap handling (default: None)
- nodesep
-
Minimum space between nodes in inches (default: 0.25)
- ranksep
-
Minimum space between ranks in inches (default: 0.5)
- node_shape
-
Node shape (default: Ellipse)
- node_color
-
Default node fill color (CSS color)
- node_style
-
Node style (default: Filled)
- node_fontname
-
Node font name (default: “Helvetica”)
- node_fontsize
-
Node font size in points (default: 12)
- node_fontcolor
-
Node font color (CSS color, default: “black”)
- edge_color
-
Default edge color (CSS color, default: “black”)
- edge_style
-
Edge style (default: Solid)
- edge_fontname
-
Edge font name (default: “Helvetica”)
- edge_fontsize
-
Edge font size in points (default: 10)
- edge_penwidth
-
Edge line thickness (default: 1.0)
- arrowhead
-
Arrow head style (default: None = Graphviz default)
- arrowtail
-
Arrow tail style (default: None)
- highlight_color
-
Highlight color for nodes/edges (CSS color, default: “red”)
- highlight_penwidth
-
Highlight pen width (default: 2.0)
Graphviz layout engine
pub type Layout {
Dot
Neato
Circo
Fdp
Sfdp
Twopi
Osage
CustomLayout(String)
}
Constructors
-
DotHierarchical layouts (DAGs, trees) - default for most use cases
-
NeatoSpring-based layouts (undirected graphs)
-
CircoCircular layouts
-
FdpForce-directed placement
-
SfdpScalable force-directed (for large graphs)
-
TwopiRadial layouts
-
OsageClustered layouts (array-based)
-
CustomLayout(String)Custom layout engine
Node shapes
pub type NodeShape {
Box
Circle
Ellipse
Diamond
Hexagon
Pentagon
Octagon
Triangle
Rectangle
Square
Rect
InvTriangle
House
InvHouse
Parallelogram
Trapezoid
CustomShape(String)
}
Constructors
-
Box -
Circle -
Ellipse -
Diamond -
Hexagon -
Pentagon -
Octagon -
Triangle -
Rectangle -
Square -
RectRounded rectangle
-
InvTriangleInverted triangle
-
HouseHouse shape (pentagon with flat top)
-
InvHouseInverted house
-
ParallelogramParallelogram
-
TrapezoidTrapezoid
-
CustomShape(String)Custom shape (for advanced Graphviz shapes)
Overlap handling
pub type Overlap {
OverlapTrue
OverlapFalse
Scale
ScaleXY
Prism
CustomOverlap(String)
}
Constructors
-
OverlapTrueAllow overlaps (faster)
-
OverlapFalseRemove all overlaps (slower)
-
ScaleScale graph to remove overlaps
-
ScaleXYScale x and y independently
-
PrismPrism algorithm (Voronoi-based)
-
CustomOverlap(String)Custom overlap mode
Graph direction (rank direction)
pub type RankDir {
TopToBottom
LeftToRight
BottomToTop
RightToLeft
}
Constructors
-
TopToBottomTop to Bottom (vertical, downward)
-
LeftToRightLeft to Right (horizontal)
-
BottomToTopBottom to Top (vertical, upward)
-
RightToLeftRight to Left (horizontal, reversed)
Edge routing style
pub type Splines {
Line
Polyline
Curved
Ortho
Spline
SplinesNone
}
Constructors
-
LineStraight lines
-
PolylinePolyline (bent lines)
-
CurvedCurved edges
-
OrthoOrthogonal (right angles only)
-
SplineBezier splines (smooth curves) - Graphviz default
-
SplinesNoneNo edges
Visual style
pub type Style {
Solid
Dashed
Dotted
Bold
Filled
Rounded
Diagonals
Striped
Wedged
}
Constructors
-
Solid -
Dashed -
Dotted -
Bold -
Filled -
Rounded -
Diagonals -
Striped -
Wedged
A subgraph (cluster) for grouping nodes visually in the diagram.
In Graphviz, subgraphs with names starting with “cluster_” are rendered as bounded rectangles around the contained nodes. This is useful for:
- Visualizing communities or partitions
- Grouping related nodes
- Highlighting logical components
Example
Subgraph(
name: "cluster_0",
label: Some("Module A"),
node_ids: [1, 2, 3],
style: Some(dot.Filled),
fillcolor: Some("lightblue"),
color: Some("blue"),
)
pub type Subgraph {
Subgraph(
name: String,
label: option.Option(String),
node_ids: List(Int),
style: option.Option(Style),
fillcolor: option.Option(String),
color: option.Option(String),
)
}
Constructors
-
Subgraph( name: String, label: option.Option(String), node_ids: List(Int), style: option.Option(Style), fillcolor: option.Option(String), color: option.Option(String), )Arguments
- name
-
Subgraph name. Use “cluster_” prefix for visual clustering.
- label
-
Optional label displayed at the top of the subgraph.
- node_ids
-
List of node IDs to include in this subgraph.
- style
-
Optional style for the subgraph boundary.
- fillcolor
-
Optional fill color for the subgraph background.
- color
-
Optional border color for the subgraph.
Values
pub fn default_dot_options() -> DotOptions(n, String)
Creates default DOT options with simple labeling and sensible styling.
Default configuration:
- Layout: Auto-detected by Graphviz
- Direction: Top-to-bottom
- Node shape: Ellipse
- Colors: Light blue nodes, black edges
- Font: Helvetica 12pt
Note: This function returns DotOptions(n, String), meaning it works
with any node data type (node labels use the ID only) but requires edge
data to be String. For other edge types, use
default_dot_options_with_edge_formatter().
Example
let options = dot.default_dot_options()
let dot_string = dot.to_dot(my_string_graph, options)
pub fn default_dot_options_with(
node_label node_label: fn(Int, n) -> String,
edge_label edge_label: fn(e) -> String,
) -> DotOptions(n, e)
Creates default DOT options with custom label formatters for both nodes and edges.
Use this when you need full control over how both node and edge data are displayed.
Example
let options = dot.default_dot_options_with(
node_label: fn(id, data) { data.name <> " (" <> int.to_string(id) <> ")" },
edge_label: fn(weight) { int.to_string(weight) <> " ms" },
)
pub fn default_dot_options_with_edge_formatter(
edge_formatter: fn(e) -> String,
) -> DotOptions(n, e)
Creates default DOT options with a custom edge formatter.
Use this when your graph has non-String edge data (e.g., Int, Float, custom types). The provided formatter function converts edge data to strings for display.
Example
// For a graph with Int edge weights
let options = dot.default_dot_options_with_edge_formatter(fn(weight) {
int.to_string(weight)
})
// For a graph with custom edge data
let options = dot.default_dot_options_with_edge_formatter(fn(edge_data) {
edge_data.name <> ": " <> float.to_string(edge_data.weight)
})
pub fn path_to_dot_options(
path: path.Path(e),
base_options: DotOptions(n, e),
) -> DotOptions(n, e)
Converts a shortest path result to highlighted DOT options.
Creates a copy of the base options with the path’s nodes and edges set to be highlighted. This is useful for visualizing algorithm results.
Example
case pathfinding.dijkstra(...) {
Some(path) -> {
let options = dot.path_to_dot_options(path, dot.default_dot_options())
let dot_string = dot.to_dot(graph, options)
}
None -> ""
}
pub fn to_dot(
graph: model.Graph(n, e),
options: DotOptions(n, e),
) -> String
Converts a graph to DOT (Graphviz) syntax.
Works with any node data type n and edge data type e. Use the options
to customize labels, styling, and to define subgraphs. Use
default_dot_options() or default_dot_options_with_edge_formatter() to
create appropriate options for your graph.
Time Complexity: O(V + E + S) where S is the total number of nodes across all subgraphs.
Example
let graph =
model.new(Directed)
|> model.add_node(1, "Start")
|> model.add_node(2, "Process")
|> model.add_edge(from: 1, to: 2, with: "5")
let diagram = dot.to_dot(graph, dot.default_dot_options())
// io.println(diagram)
Custom Styling Example
let options = DotOptions(
..dot.default_dot_options(),
node_attributes: fn(id, data) {
case id {
1 -> [("fillcolor", "green"), ("shape", "diamond")]
_ -> []
}
},
subgraphs: Some([
Subgraph(name: "cluster_0", label: Some("Group A"), node_ids: [1, 2]),
]),
)
This output can be processed by Graphviz tools (e.g., dot -Tpng -o graph.png):
digraph G {
node [shape=ellipse];
1 [label="Start"];
2 [label="Process"];
subgraph cluster_0 {
label="Group A";
1; 2;
}
1 -> 2 [label="5"];
}