Current section
Files
Jump to
Current section
Files
src/yog@render@dot.erl
-module(yog@render@dot).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog/render/dot.gleam").
-export([default_dot_options/0, default_dot_options_with_edge_formatter/1, default_dot_options_with/2, path_to_dot_options/2, to_dot/2]).
-export_type([subgraph/0, layout/0, rank_dir/0, node_shape/0, style/0, splines/0, arrow_style/0, overlap/0, dot_options/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" DOT (Graphviz) format export for visualizing graphs.\n"
"\n"
" This module exports graphs to the [DOT language](https://graphviz.org/doc/info/lang.html),\n"
" which is the native format for [Graphviz](https://graphviz.org/) - a powerful open-source\n"
" graph visualization tool. The exported files can be rendered to PNG, SVG, PDF, and other\n"
" formats using the `dot`, `neato`, `circo`, or other Graphviz layout engines.\n"
"\n"
" ## Quick Start\n"
"\n"
" ```gleam\n"
" import yog/render/dot\n"
"\n"
" // Export with default styling\n"
" let dot_string = dot.to_dot(my_graph, dot.default_dot_options())\n"
"\n"
" // Write to file and render with Graphviz CLI\n"
" // $ dot -Tpng output.dot -o graph.png\n"
" ```\n"
"\n"
" ## Customization\n"
"\n"
" Use `DotOptions` to customize:\n"
" - Node labels and shapes\n"
" - Edge labels and styles\n"
" - **Per-node and per-edge attributes** (custom colors, shapes, etc.)\n"
" - **Subgraphs/clusters** for visual grouping\n"
" - Highlight specific nodes or paths\n"
" - Graph direction (LR, TB, etc.)\n"
"\n"
" ## Generic Data Types\n"
"\n"
" The `to_dot` function works with any node and edge data types. Use \n"
" `default_dot_options_with_edge_formatter()` when your edge data is not a String:\n"
"\n"
" ```gleam\n"
" let options = dot.default_dot_options_with_edge_formatter(fn(weight) {\n"
" int.to_string(weight)\n"
" })\n"
" let dot_string = dot.to_dot(my_int_weighted_graph, options)\n"
" ```\n"
"\n"
" ## Per-Element Styling\n"
"\n"
" Provide custom attribute functions for fine-grained control:\n"
"\n"
" ```gleam\n"
" let options = DotOptions(\n"
" ..dot.default_dot_options(),\n"
" node_attributes: fn(id, data) {\n"
" case id {\n"
" 1 -> [(\"fillcolor\", \"green\"), (\"shape\", \"diamond\")]\n"
" _ -> []\n"
" }\n"
" },\n"
" edge_attributes: fn(from, to, weight) {\n"
" case weight > 10 {\n"
" True -> [(\"color\", \"red\"), (\"penwidth\", \"2\")]\n"
" False -> []\n"
" }\n"
" },\n"
" )\n"
" ```\n"
"\n"
" ## Subgraphs and Clusters\n"
"\n"
" Group nodes visually using subgraphs:\n"
"\n"
" ```gleam\n"
" let options = DotOptions(\n"
" ..dot.default_dot_options(),\n"
" subgraphs: Some([\n"
" Subgraph(\n"
" name: \"cluster_0\",\n"
" label: Some(\"Cluster A\"),\n"
" node_ids: [1, 2, 3],\n"
" style: Some(dot.Filled),\n"
" fillcolor: Some(\"lightgrey\"),\n"
" color: None,\n"
" ),\n"
" ]),\n"
" )\n"
" ```\n"
"\n"
" ## Rendering Options\n"
"\n"
" | Engine | Best For |\n"
" |--------|----------|\n"
" | `dot` | Hierarchical layouts (DAGs, trees) |\n"
" | `neato` | Spring-based layouts (undirected) |\n"
" | `circo` | Circular layouts |\n"
" | `fdp` | Force-directed layouts |\n"
" | `sfdp` | Large graphs |\n"
"\n"
" ## References\n"
"\n"
" - [Graphviz Documentation](https://graphviz.org/documentation/)\n"
" - [DOT Language Guide](https://graphviz.org/doc/info/lang.html)\n"
" - [Node Shapes](https://graphviz.org/doc/info/shapes.html)\n"
" - [Arrow Styles](https://graphviz.org/doc/info/arrows.html)\n"
" - [Cluster/Subgraph Syntax](https://graphviz.org/docs/attrs/cluster/)\n"
).
-type subgraph() :: {subgraph,
binary(),
gleam@option:option(binary()),
list(integer()),
gleam@option:option(style()),
gleam@option:option(binary()),
gleam@option:option(binary())}.
-type layout() :: dot |
neato |
circo |
fdp |
sfdp |
twopi |
osage |
{custom_layout, binary()}.
-type rank_dir() :: top_to_bottom |
left_to_right |
bottom_to_top |
right_to_left.
-type node_shape() :: box |
circle |
ellipse |
diamond |
hexagon |
pentagon |
octagon |
triangle |
rectangle |
square |
rect |
inv_triangle |
house |
inv_house |
parallelogram |
trapezoid |
{custom_shape, binary()}.
-type style() :: solid |
dashed |
dotted |
bold |
filled |
rounded |
diagonals |
striped |
wedged.
-type splines() :: line | polyline | curved | ortho | spline | splines_none.
-type arrow_style() :: normal |
arrow_dot |
arrow_diamond |
o_diamond |
arrow_box |
crow |
vee |
inv |
tee |
arrow_none |
{custom_arrow, binary()}.
-type overlap() :: overlap_true |
overlap_false |
scale |
scale_x_y |
prism |
{custom_overlap, binary()}.
-type dot_options(YXR, YXS) :: {dot_options,
fun((integer(), YXR) -> binary()),
fun((YXS) -> binary()),
gleam@option:option(list(integer())),
gleam@option:option(list({integer(), integer()})),
fun((integer(), YXR) -> list({binary(), binary()})),
fun((integer(), integer(), YXS) -> list({binary(), binary()})),
gleam@option:option(list(subgraph())),
binary(),
gleam@option:option(layout()),
gleam@option:option(rank_dir()),
gleam@option:option(binary()),
gleam@option:option(splines()),
gleam@option:option(overlap()),
gleam@option:option(float()),
gleam@option:option(float()),
node_shape(),
binary(),
style(),
binary(),
integer(),
binary(),
binary(),
style(),
binary(),
integer(),
float(),
gleam@option:option(arrow_style()),
gleam@option:option(arrow_style()),
binary(),
float()}.
-file("src/yog/render/dot.gleam", 455).
-spec create_dot_options(
fun((integer(), YYE) -> binary()),
fun((YYF) -> binary())
) -> dot_options(YYE, YYF).
create_dot_options(Node_label, Edge_label) ->
{dot_options,
Node_label,
Edge_label,
none,
none,
fun(_, _) -> [] end,
fun(_, _, _) -> [] end,
none,
<<"G"/utf8>>,
none,
{some, top_to_bottom},
none,
none,
none,
none,
none,
ellipse,
<<"lightblue"/utf8>>,
filled,
<<"Helvetica"/utf8>>,
12,
<<"black"/utf8>>,
<<"black"/utf8>>,
solid,
<<"Helvetica"/utf8>>,
10,
1.0,
none,
none,
<<"red"/utf8>>,
2.0}.
-file("src/yog/render/dot.gleam", 401).
?DOC(
" Creates default DOT options with simple labeling and sensible styling.\n"
"\n"
" Default configuration:\n"
" - Layout: Auto-detected by Graphviz\n"
" - Direction: Top-to-bottom\n"
" - Node shape: Ellipse\n"
" - Colors: Light blue nodes, black edges\n"
" - Font: Helvetica 12pt\n"
"\n"
" **Note:** This function returns `DotOptions(n, String)`, meaning it works\n"
" with any node data type (node labels use the ID only) but requires edge\n"
" data to be `String`. For other edge types, use\n"
" `default_dot_options_with_edge_formatter()`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = dot.default_dot_options()\n"
" let dot_string = dot.to_dot(my_string_graph, options)\n"
" ```\n"
).
-spec default_dot_options() -> dot_options(any(), binary()).
default_dot_options() ->
create_dot_options(
fun(Id, _) -> erlang:integer_to_binary(Id) end,
fun(Weight) -> Weight end
).
-file("src/yog/render/dot.gleam", 426).
?DOC(
" Creates default DOT options with a custom edge formatter.\n"
"\n"
" Use this when your graph has non-String edge data (e.g., Int, Float, custom types).\n"
" The provided formatter function converts edge data to strings for display.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // For a graph with Int edge weights\n"
" let options = dot.default_dot_options_with_edge_formatter(fn(weight) {\n"
" int.to_string(weight)\n"
" })\n"
"\n"
" // For a graph with custom edge data\n"
" let options = dot.default_dot_options_with_edge_formatter(fn(edge_data) {\n"
" edge_data.name <> \": \" <> float.to_string(edge_data.weight)\n"
" })\n"
" ```\n"
).
-spec default_dot_options_with_edge_formatter(fun((YXW) -> binary())) -> dot_options(any(), YXW).
default_dot_options_with_edge_formatter(Edge_formatter) ->
create_dot_options(
fun(Id, _) -> erlang:integer_to_binary(Id) end,
Edge_formatter
).
-file("src/yog/render/dot.gleam", 447).
?DOC(
" Creates default DOT options with custom label formatters for both nodes and edges.\n"
"\n"
" Use this when you need full control over how both node and edge data are displayed.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = dot.default_dot_options_with(\n"
" node_label: fn(id, data) { data.name <> \" (\" <> int.to_string(id) <> \")\" },\n"
" edge_label: fn(weight) { int.to_string(weight) <> \" ms\" },\n"
" )\n"
" ```\n"
).
-spec default_dot_options_with(
fun((integer(), YYA) -> binary()),
fun((YYB) -> binary())
) -> dot_options(YYA, YYB).
default_dot_options_with(Node_label, Edge_label) ->
create_dot_options(Node_label, Edge_label).
-file("src/yog/render/dot.gleam", 772).
-spec merge_attributes_list(
list({binary(), binary()}),
list({binary(), binary()})
) -> list({binary(), binary()}).
merge_attributes_list(Base, Override) ->
gleam@list:fold(
Override,
Base,
fun(Acc, Pair) ->
Filtered = gleam@list:filter(
Acc,
fun(Existing) ->
erlang:element(1, Existing) /= erlang:element(1, Pair)
end
),
[Pair | Filtered]
end
).
-file("src/yog/render/dot.gleam", 786).
-spec format_attributes_list(list({binary(), binary()})) -> binary().
format_attributes_list(Attrs) ->
_pipe = Attrs,
_pipe@1 = lists:reverse(_pipe),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Pair) ->
<<<<<<(erlang:element(1, Pair))/binary, "=\""/utf8>>/binary,
(erlang:element(2, Pair))/binary>>/binary,
"\""/utf8>>
end
),
gleam@string:join(_pipe@2, <<", "/utf8>>).
-file("src/yog/render/dot.gleam", 824).
-spec path_to_edges(list(integer())) -> list({integer(), integer()}).
path_to_edges(Nodes) ->
case Nodes of
[] ->
[];
[_] ->
[];
[First, Second | Rest] ->
[{First, Second} | path_to_edges([Second | Rest])]
end.
-file("src/yog/render/dot.gleam", 809).
?DOC(
" Converts a shortest path result to highlighted DOT options.\n"
"\n"
" Creates a copy of the base options with the path's nodes and edges\n"
" set to be highlighted. This is useful for visualizing algorithm results.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case pathfinding.dijkstra(...) {\n"
" Some(path) -> {\n"
" let options = dot.path_to_dot_options(path, dot.default_dot_options())\n"
" let dot_string = dot.to_dot(graph, options)\n"
" }\n"
" None -> \"\"\n"
" }\n"
" ```\n"
).
-spec path_to_dot_options(
yog@pathfinding@utils:path(YYS),
dot_options(YYU, YYS)
) -> dot_options(YYU, YYS).
path_to_dot_options(Path, Base_options) ->
Nodes = erlang:element(2, Path),
Edges = path_to_edges(Nodes),
{dot_options,
erlang:element(2, Base_options),
erlang:element(3, Base_options),
{some, Nodes},
{some, Edges},
erlang:element(6, Base_options),
erlang:element(7, Base_options),
erlang:element(8, Base_options),
erlang:element(9, Base_options),
erlang:element(10, Base_options),
erlang:element(11, Base_options),
erlang:element(12, Base_options),
erlang:element(13, Base_options),
erlang:element(14, Base_options),
erlang:element(15, Base_options),
erlang:element(16, Base_options),
erlang:element(17, Base_options),
erlang:element(18, Base_options),
erlang:element(19, Base_options),
erlang:element(20, Base_options),
erlang:element(21, Base_options),
erlang:element(22, Base_options),
erlang:element(23, Base_options),
erlang:element(24, Base_options),
erlang:element(25, Base_options),
erlang:element(26, Base_options),
erlang:element(27, Base_options),
erlang:element(28, Base_options),
erlang:element(29, Base_options),
erlang:element(30, Base_options),
erlang:element(31, Base_options)}.
-file("src/yog/render/dot.gleam", 839).
?DOC(" Convert Layout to Graphviz string\n").
-spec layout_to_string(layout()) -> binary().
layout_to_string(Layout) ->
case Layout of
dot ->
<<"dot"/utf8>>;
neato ->
<<"neato"/utf8>>;
circo ->
<<"circo"/utf8>>;
fdp ->
<<"fdp"/utf8>>;
sfdp ->
<<"sfdp"/utf8>>;
twopi ->
<<"twopi"/utf8>>;
osage ->
<<"osage"/utf8>>;
{custom_layout, S} ->
S
end.
-file("src/yog/render/dot.gleam", 853).
?DOC(" Convert RankDir to Graphviz string\n").
-spec rankdir_to_string(rank_dir()) -> binary().
rankdir_to_string(Rd) ->
case Rd of
top_to_bottom ->
<<"TB"/utf8>>;
left_to_right ->
<<"LR"/utf8>>;
bottom_to_top ->
<<"BT"/utf8>>;
right_to_left ->
<<"RL"/utf8>>
end.
-file("src/yog/render/dot.gleam", 863).
?DOC(" Convert NodeShape to Graphviz string\n").
-spec node_shape_to_string(node_shape()) -> binary().
node_shape_to_string(Shape) ->
case Shape of
box ->
<<"box"/utf8>>;
circle ->
<<"circle"/utf8>>;
ellipse ->
<<"ellipse"/utf8>>;
diamond ->
<<"diamond"/utf8>>;
hexagon ->
<<"hexagon"/utf8>>;
pentagon ->
<<"pentagon"/utf8>>;
octagon ->
<<"octagon"/utf8>>;
triangle ->
<<"triangle"/utf8>>;
rectangle ->
<<"rectangle"/utf8>>;
square ->
<<"square"/utf8>>;
rect ->
<<"rect"/utf8>>;
inv_triangle ->
<<"invtriangle"/utf8>>;
house ->
<<"house"/utf8>>;
inv_house ->
<<"invhouse"/utf8>>;
parallelogram ->
<<"parallelogram"/utf8>>;
trapezoid ->
<<"trapezoid"/utf8>>;
{custom_shape, S} ->
S
end.
-file("src/yog/render/dot.gleam", 886).
?DOC(" Convert Style to Graphviz string\n").
-spec style_to_string(style()) -> binary().
style_to_string(Style) ->
case Style of
solid ->
<<"solid"/utf8>>;
dashed ->
<<"dashed"/utf8>>;
dotted ->
<<"dotted"/utf8>>;
bold ->
<<"bold"/utf8>>;
filled ->
<<"filled"/utf8>>;
rounded ->
<<"rounded"/utf8>>;
diagonals ->
<<"diagonals"/utf8>>;
striped ->
<<"striped"/utf8>>;
wedged ->
<<"wedged"/utf8>>
end.
-file("src/yog/render/dot.gleam", 901).
?DOC(" Convert Splines to Graphviz string\n").
-spec splines_to_string(splines()) -> binary().
splines_to_string(Splines) ->
case Splines of
line ->
<<"line"/utf8>>;
polyline ->
<<"polyline"/utf8>>;
curved ->
<<"curved"/utf8>>;
ortho ->
<<"ortho"/utf8>>;
spline ->
<<"spline"/utf8>>;
splines_none ->
<<"none"/utf8>>
end.
-file("src/yog/render/dot.gleam", 913).
?DOC(" Convert ArrowStyle to Graphviz string\n").
-spec arrow_style_to_string(arrow_style()) -> binary().
arrow_style_to_string(Arrow) ->
case Arrow of
normal ->
<<"normal"/utf8>>;
arrow_dot ->
<<"dot"/utf8>>;
arrow_diamond ->
<<"diamond"/utf8>>;
o_diamond ->
<<"odiamond"/utf8>>;
arrow_box ->
<<"box"/utf8>>;
crow ->
<<"crow"/utf8>>;
vee ->
<<"vee"/utf8>>;
inv ->
<<"inv"/utf8>>;
tee ->
<<"tee"/utf8>>;
arrow_none ->
<<"none"/utf8>>;
{custom_arrow, S} ->
S
end.
-file("src/yog/render/dot.gleam", 930).
?DOC(" Convert Overlap to Graphviz string\n").
-spec overlap_to_string(overlap()) -> binary().
overlap_to_string(Overlap) ->
case Overlap of
overlap_true ->
<<"true"/utf8>>;
overlap_false ->
<<"false"/utf8>>;
scale ->
<<"scale"/utf8>>;
scale_x_y ->
<<"scalexy"/utf8>>;
prism ->
<<"prism"/utf8>>;
{custom_overlap, S} ->
S
end.
-file("src/yog/render/dot.gleam", 552).
?DOC(
" Converts a graph to DOT (Graphviz) syntax.\n"
"\n"
" Works with any node data type `n` and edge data type `e`. Use the options\n"
" to customize labels, styling, and to define subgraphs. Use\n"
" `default_dot_options()` or `default_dot_options_with_edge_formatter()` to\n"
" create appropriate options for your graph.\n"
"\n"
" **Time Complexity:** O(V + E + S) where S is the total number of nodes\n"
" across all subgraphs.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let graph =\n"
" model.new(Directed)\n"
" |> model.add_node(1, \"Start\")\n"
" |> model.add_node(2, \"Process\")\n"
" |> model.add_edge(from: 1, to: 2, with: \"5\")\n"
"\n"
" let diagram = dot.to_dot(graph, dot.default_dot_options())\n"
" // io.println(diagram)\n"
" ```\n"
"\n"
" ## Custom Styling Example\n"
"\n"
" ```gleam\n"
" let options = DotOptions(\n"
" ..dot.default_dot_options(),\n"
" node_attributes: fn(id, data) {\n"
" case id {\n"
" 1 -> [(\"fillcolor\", \"green\"), (\"shape\", \"diamond\")]\n"
" _ -> []\n"
" }\n"
" },\n"
" subgraphs: Some([\n"
" Subgraph(name: \"cluster_0\", label: Some(\"Group A\"), node_ids: [1, 2]),\n"
" ]),\n"
" )\n"
" ```\n"
"\n"
" This output can be processed by Graphviz tools (e.g., `dot -Tpng -o graph.png`):\n"
" ````dot\n"
" digraph G {\n"
" node [shape=ellipse];\n"
" 1 [label=\"Start\"];\n"
" 2 [label=\"Process\"];\n"
" subgraph cluster_0 {\n"
" label=\"Group A\";\n"
" 1; 2;\n"
" }\n"
" 1 -> 2 [label=\"5\"];\n"
" }\n"
" ````\n"
).
-spec to_dot(yog@model:graph(YYI, YYJ), dot_options(YYI, YYJ)) -> binary().
to_dot(Graph, Options) ->
Graph_type = case erlang:element(2, Graph) of
directed ->
<<<<"digraph "/utf8, (erlang:element(9, Options))/binary>>/binary,
" {\n"/utf8>>;
undirected ->
<<<<"graph "/utf8, (erlang:element(9, Options))/binary>>/binary,
" {\n"/utf8>>
end,
Graph_attrs = begin
_pipe = [gleam@option:map(
erlang:element(10, Options),
fun(V) -> <<"layout="/utf8, (layout_to_string(V))/binary>> end
),
gleam@option:map(
erlang:element(11, Options),
fun(V@1) ->
<<"rankdir="/utf8, (rankdir_to_string(V@1))/binary>>
end
),
gleam@option:map(
erlang:element(12, Options),
fun(V@2) ->
<<<<"bgcolor=\""/utf8, V@2/binary>>/binary, "\""/utf8>>
end
),
gleam@option:map(
erlang:element(13, Options),
fun(V@3) ->
<<"splines="/utf8, (splines_to_string(V@3))/binary>>
end
),
gleam@option:map(
erlang:element(14, Options),
fun(V@4) ->
<<"overlap="/utf8, (overlap_to_string(V@4))/binary>>
end
),
gleam@option:map(
erlang:element(15, Options),
fun(V@5) ->
<<"nodesep="/utf8,
(gleam_stdlib:float_to_string(V@5))/binary>>
end
),
gleam@option:map(
erlang:element(16, Options),
fun(V@6) ->
<<"ranksep="/utf8,
(gleam_stdlib:float_to_string(V@6))/binary>>
end
)],
gleam@list:filter_map(_pipe, fun(Opt) -> case Opt of
{some, Attr} ->
{ok, Attr};
none ->
{error, nil}
end end)
end,
Graph_attr_line = case Graph_attrs of
[] ->
<<""/utf8>>;
Attrs ->
<<<<" graph ["/utf8,
(gleam@string:join(Attrs, <<", "/utf8>>))/binary>>/binary,
"];\n"/utf8>>
end,
Node_attrs = [<<"shape="/utf8,
(node_shape_to_string(erlang:element(17, Options)))/binary>>,
<<"style="/utf8, (style_to_string(erlang:element(19, Options)))/binary>>,
<<<<"fillcolor=\""/utf8, (erlang:element(18, Options))/binary>>/binary,
"\""/utf8>>,
<<<<"fontname=\""/utf8, (erlang:element(20, Options))/binary>>/binary,
"\""/utf8>>,
<<"fontsize="/utf8,
(erlang:integer_to_binary(erlang:element(21, Options)))/binary>>,
<<<<"fontcolor=\""/utf8, (erlang:element(22, Options))/binary>>/binary,
"\""/utf8>>],
Base_node_style = <<<<" node ["/utf8,
(gleam@string:join(Node_attrs, <<", "/utf8>>))/binary>>/binary,
"];\n"/utf8>>,
Edge_attrs = [<<<<"color=\""/utf8, (erlang:element(23, Options))/binary>>/binary,
"\""/utf8>>,
<<"style="/utf8, (style_to_string(erlang:element(24, Options)))/binary>>,
<<<<"fontname=\""/utf8, (erlang:element(25, Options))/binary>>/binary,
"\""/utf8>>,
<<"fontsize="/utf8,
(erlang:integer_to_binary(erlang:element(26, Options)))/binary>>,
<<"penwidth="/utf8,
(gleam_stdlib:float_to_string(erlang:element(27, Options)))/binary>>],
Edge_attrs_with_arrows = case {erlang:element(28, Options),
erlang:element(29, Options)} of
{{some, Head}, {some, Tail}} ->
[<<"arrowhead="/utf8, (arrow_style_to_string(Head))/binary>>,
<<"arrowtail="/utf8, (arrow_style_to_string(Tail))/binary>> |
Edge_attrs];
{{some, Head@1}, none} ->
[<<"arrowhead="/utf8, (arrow_style_to_string(Head@1))/binary>> |
Edge_attrs];
{none, {some, Tail@1}} ->
[<<"arrowtail="/utf8, (arrow_style_to_string(Tail@1))/binary>> |
Edge_attrs];
{none, none} ->
Edge_attrs
end,
Base_edge_style = <<<<" edge ["/utf8,
(gleam@string:join(Edge_attrs_with_arrows, <<", "/utf8>>))/binary>>/binary,
"];\n"/utf8>>,
Nodes = begin
_pipe@1 = gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Label = (erlang:element(2, Options))(Id, Data),
Id_str = erlang:integer_to_binary(Id),
Attrs@1 = [{<<"label"/utf8>>, Label}],
Attrs@2 = case erlang:element(4, Options) of
{some, Highlighted} ->
case gleam@list:contains(Highlighted, Id) of
true ->
[{<<"fillcolor"/utf8>>,
erlang:element(30, Options)} |
Attrs@1];
false ->
Attrs@1
end;
none ->
Attrs@1
end,
Custom_attrs = (erlang:element(6, Options))(Id, Data),
Attrs@3 = merge_attributes_list(Attrs@2, Custom_attrs),
Attr_str = format_attributes_list(Attrs@3),
[<<<<<<<<" "/utf8, Id_str/binary>>/binary, " ["/utf8>>/binary,
Attr_str/binary>>/binary,
"];"/utf8>> |
Acc]
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Subgraphs_str = case erlang:element(8, Options) of
none ->
<<""/utf8>>;
{some, Subgraph_list} ->
_pipe@4 = gleam@list:map(
Subgraph_list,
fun(Sub) ->
Header = <<<<" subgraph "/utf8,
(erlang:element(2, Sub))/binary>>/binary,
" {\n"/utf8>>,
Label@1 = case erlang:element(3, Sub) of
{some, L} ->
<<<<" label=\""/utf8, L/binary>>/binary,
"\";\n"/utf8>>;
none ->
<<""/utf8>>
end,
Style = case erlang:element(5, Sub) of
{some, S} ->
<<<<" style="/utf8, (style_to_string(S))/binary>>/binary,
";\n"/utf8>>;
none ->
<<""/utf8>>
end,
Fillcolor = case erlang:element(6, Sub) of
{some, F} ->
<<<<" fillcolor=\""/utf8, F/binary>>/binary,
"\";\n"/utf8>>;
none ->
<<""/utf8>>
end,
Color = case erlang:element(7, Sub) of
{some, C} ->
<<<<" color=\""/utf8, C/binary>>/binary,
"\";\n"/utf8>>;
none ->
<<""/utf8>>
end,
Node_list = case erlang:element(4, Sub) of
[] ->
<<""/utf8>>;
Ids ->
<<(begin
_pipe@2 = Ids,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Id@1) ->
<<" "/utf8,
(erlang:integer_to_binary(Id@1))/binary>>
end
),
gleam@string:join(_pipe@3, <<";\n"/utf8>>)
end)/binary,
";\n"/utf8>>
end,
<<<<<<<<<<<<Header/binary, Label@1/binary>>/binary,
Style/binary>>/binary,
Fillcolor/binary>>/binary,
Color/binary>>/binary,
Node_list/binary>>/binary,
" }"/utf8>>
end
),
gleam@string:join(_pipe@4, <<"\n"/utf8>>)
end,
Edges = begin
_pipe@5 = gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc@1, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Weight) ->
Is_valid = case erlang:element(2, Graph) of
undirected ->
From_id =< To_id;
directed ->
true
end,
case Is_valid of
false ->
Inner_acc;
true ->
Connector = case erlang:element(2, Graph) of
directed ->
<<" -> "/utf8>>;
undirected ->
<<" -- "/utf8>>
end,
Label@2 = (erlang:element(3, Options))(Weight),
Attrs@4 = [{<<"label"/utf8>>, Label@2}],
Is_highlighted = case erlang:element(5, Options) of
{some, Highlighted@1} ->
gleam@list:contains(
Highlighted@1,
{From_id, To_id}
)
orelse gleam@list:contains(
Highlighted@1,
{To_id, From_id}
);
none ->
false
end,
Attrs@5 = case Is_highlighted of
true ->
[{<<"penwidth"/utf8>>,
gleam_stdlib:float_to_string(
erlang:element(31, Options)
)},
{<<"color"/utf8>>,
erlang:element(30, Options)} |
Attrs@4];
false ->
Attrs@4
end,
Custom_attrs@1 = (erlang:element(7, Options))(
From_id,
To_id,
Weight
),
Attrs@6 = merge_attributes_list(
Attrs@5,
Custom_attrs@1
),
Attr_str@1 = format_attributes_list(Attrs@6),
Edge_def = <<<<<<<<<<<<" "/utf8,
(erlang:integer_to_binary(
From_id
))/binary>>/binary,
Connector/binary>>/binary,
(erlang:integer_to_binary(To_id))/binary>>/binary,
" ["/utf8>>/binary,
Attr_str@1/binary>>/binary,
"];"/utf8>>,
[Edge_def | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc@1])
end
),
gleam@string:join(_pipe@5, <<"\n"/utf8>>)
end,
<<<<<<<<<<<<<<<<Graph_type/binary, Graph_attr_line/binary>>/binary,
Base_node_style/binary>>/binary,
Base_edge_style/binary>>/binary,
Nodes/binary>>/binary,
"\n"/utf8>>/binary,
(case Subgraphs_str of
<<""/utf8>> ->
<<""/utf8>>;
S@1 ->
<<S@1/binary, "\n"/utf8>>
end)/binary>>/binary,
Edges/binary>>/binary,
"\n}"/utf8>>.