Current section
Files
Jump to
Current section
Files
lib/graphic.ex
defmodule Graphic do
@moduledoc """
Build directed graphs, query them,
and compile into common diagramming languages.
iex> alias Graphic, as: G
...> G.new()
...> |> G.bridge("a", "b")
...> |> G.edges()
[
{[:"$e" | 0], "a", "b", []}
]
iex> alias Graphic, as: G
...> G.new()
...> |> G.bridge("a", "b", "broken!")
...> |> G.edges()
[
{[:"$e" | 0], "a", "b", "broken!"}
]
"""
@moduledoc since: "0.1.2"
@dg :digraph
def new(), do: :digraph.new()
@doc "add a plain edge, and any missing nodes."
def bridge(g, a, b) do
if !(g|>@dg.vertex(a)), do: g|>@dg.add_vertex(a)
if !(g|>@dg.vertex(b)), do: g|>@dg.add_vertex(b)
g|>@dg.add_edge(a, b)
g
end
@doc "add a labeled edge, and any missing nodes."
def bridge(g, a, b, label) do
if !(g|>@dg.vertex(a)), do: g|>@dg.add_vertex(a)
if !(g|>@dg.vertex(b)), do: g|>@dg.add_vertex(b)
g|>@dg.add_edge(a, b, label)
g
end
@doc "query all edges in graph."
def edges(g), do: g|>@dg.edges()|>Enum.map(& g|>@dg.edge(&1))
# choose a logical order, used in rendering.
defp order(g), do: g|>:digraph_utils.topsort()
@doc ~S"""
render graph as a d2 diagram.
iex>alias Graphic, as: G
iex> G.new()
...> |>G.bridge("a", "b")
...> |>G.bridge("a", "c")
...> |>G.bridge("c", "d", "broken!")
...> |>G.bridge("b", "d")
...> |>G.as_d2()
\"""
a -> b
a -> c
c -> d: 'broken!'
b -> d
\"""
"""
@doc since: "0.1.2"
def as_d2(g) do
g|>order()|>Enum.map(fn n ->
g
|>@dg.out_edges(n)
|>Enum.map(& g|>@dg.edge(&1))
|>Enum.map(fn {_, a, b, label} ->
"#{a} -> #{b}#{if (label != []), do: ": '#{label}'"}"
end)
|> Enum.sort()
# g|>neighbors(&1)|>Enum.map(fn n ->
# {_, ^&1, ^n, edge} = g|>edge(&1, n)
# "#{&1} -> #{n}#{if edge, do: ": '#{edge}'"}"
# end)
end)
|>List.flatten()
|>Enum.concat([""])
|>Enum.join("\n")
end
@doc "describe neighboring nodes."
def neighbors(g, n), do: g|>@dg.out_neighbours(n)
end