# `Hakushi.Graph`
[🔗](https://github.com/hattoriai/hakushi/blob/v0.1.0/lib/hakushi/graph.ex#L1)

The nodes and edges of a canvas.

    graph =
      Hakushi.Graph.new(
        nodes: [
          %{id: "users", type: "table", x: 0, y: 0, data: %{name: "users"}},
          %{id: "orders", type: "table", x: 320, y: 40, data: %{name: "orders"}}
        ],
        edges: [%{source: "orders", source_handle: "user_id", target: "users", target_handle: "id"}]
      )

A graph keeps the order of its nodes, which is the order in which they
are drawn: a node drawn later is over the ones before it. New nodes go
last.

`apply/2` applies a `Hakushi.Change` and gives back the change that
undoes it; the canvas uses it for every change, and so can the app, to
keep its own copy of the graph or to save it.

# `reason`

```elixir
@type reason() :: :missing | :exists | :locked | :dangling | :nested
```

Why a change does not apply.

# `t`

```elixir
@type t() :: %Hakushi.Graph{
  edge_order: [Hakushi.Edge.id()],
  edges: %{required(Hakushi.Edge.id()) =&gt; Hakushi.Edge.t()},
  node_order: [Hakushi.Node.id()],
  nodes: %{required(Hakushi.Node.id()) =&gt; Hakushi.Node.t()}
}
```

# `apply`

```elixir
@spec apply(t(), Hakushi.Change.t()) ::
  {:ok, t(), Hakushi.Change.t(), Hakushi.Change.t()}
  | {:error, reason(), String.t()}
```

Applies a change. Gives back the graph, the change as it applied, and
the change that undoes it:

  * a move of a node moves the nodes in it (`parent`) too, unless the
    change places them itself, and a move may put a node in a group or
    take it out (`:parent`); the change lists every node it moved;
  * a delete lists the nodes in its nodes, and the edges of all of
    them, which go with them;
  * the change that undoes a delete adds the nodes back, with their
    edges (over the other nodes, as new nodes are).

A change does not apply, and nothing changes, when it names a node or an
edge that the graph does not have (`:missing`), adds one it has
(`:exists`), moves, resizes or deletes a locked node (`:locked`), or
adds an edge to a node it does not have, or a node to a parent it does
not have (`:dangling`), or puts a node in itself or in a node inside it
(`:nested`). The error has the id.

# `bounds`

```elixir
@spec bounds(t()) :: {number(), number(), number(), number()} | nil
```

The box around the nodes, `{x, y, width, height}`, or `nil` for a graph
without nodes. A node without a size counts as a point.

# `children`

```elixir
@spec children(t(), Hakushi.Node.id()) :: [Hakushi.Node.id()]
```

The ids of the nodes in a node (its children), in the order they are drawn.

# `descendants`

```elixir
@spec descendants(t(), Hakushi.Node.id()) :: [Hakushi.Node.id()]
```

The ids of the nodes in a node, and in those, all the way down, in the
order they are drawn.

# `diff`

```elixir
@spec diff(t(), t()) :: %{
  nodes: [Hakushi.Node.t()],
  edges: [Hakushi.Edge.t()],
  deleted_nodes: [Hakushi.Node.id()],
  deleted_edges: [Hakushi.Edge.id()]
}
```

What changed from one graph to another: the nodes and edges that are
new or not the same, and the ids of those that went away.

# `dump`

```elixir
@spec dump(t()) :: map()
```

The graph as JSON-able maps, with string keys: for a column of the
app's database (see `Hakushi.Graph.Type`) or a file. `data` goes as it
is, so it must be JSON-able itself.

# `edge`

```elixir
@spec edge(t(), Hakushi.Edge.id()) :: Hakushi.Edge.t() | nil
```

The edge of an id, or `nil`.

# `edges`

```elixir
@spec edges(t()) :: [Hakushi.Edge.t()]
```

The edges, in the order they were made.

# `edges_of`

```elixir
@spec edges_of(t(), Hakushi.Node.id()) :: [Hakushi.Edge.t()]
```

The edges of a node: those that start or end at it.

# `load`

```elixir
@spec load(map()) :: t()
```

The graph of `dump/1`'s maps.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Makes a graph of `nodes` and `edges`: `Hakushi.Node`s and
`Hakushi.Edge`s, or maps and keyword lists for `Hakushi.Node.new/1` and
`Hakushi.Edge.new/1`. Raises on two nodes or two edges with one id, and
on an edge to a node the graph does not have.

# `node`

```elixir
@spec node(t(), Hakushi.Node.id()) :: Hakushi.Node.t() | nil
```

The node of an id, or `nil`.

# `nodes`

```elixir
@spec nodes(t()) :: [Hakushi.Node.t()]
```

The nodes, in the order they are drawn.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
