defmodule Graphic do @moduledoc """ Build directed graphs, query them, and compile into common diagramming languages. iex> alias Graphic, as: G ...> g = G.new() ...> g |> G.bridge("a", "b") ...> g |> G.edges() [ {[:"$e" | 0], "a", "b", []} ] """ @moduledoc since: "0.1.1" @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) 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) 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 "render graph as a d2 diagram." def as_d2(g), do: g|>order()|>Enum.map(& g|>neighbors(&1)|>Enum.map(fn n -> "#{&1} -> #{n}" end) )|>List.flatten()|>Enum.join("\n") @doc "describe neighboring nodes." def neighbors(g, n), do: g|>@dg.out_neighbours(n) end