Hakushi.Graph (Hakushi v0.6.0)

Copy Markdown View Source

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.

paper is the canvas's paper when it has one of its own (people chose it with the canvas's picker, or the app did): a Hakushi.Paper, or nil for the canvas's background and grid.

Summary

Types

Why a change does not apply.

t()

Functions

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

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.

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

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

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

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.

The edge of an id, or nil.

The edges, in the order they were made.

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

The graph of dump/1's maps.

Makes a graph of nodes and edges: Hakushi.Nodes and Hakushi.Edges, or maps and keyword lists for Hakushi.Node.new/1 and Hakushi.Edge.new/1, and its paper (see Hakushi.Paper.new/1). Raises on two nodes or two edges with one id, and on an edge to a node the graph does not have.

The node of an id, or nil.

The nodes, in the order they are drawn.

Types

reason()

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

Why a change does not apply.

t()

@type t() :: %Hakushi.Graph{
  edge_order: [Hakushi.Edge.id()],
  edges: %{required(Hakushi.Edge.id()) => Hakushi.Edge.t()},
  node_order: [Hakushi.Node.id()],
  nodes: %{required(Hakushi.Node.id()) => Hakushi.Node.t()},
  paper: Hakushi.Paper.t() | nil
}

Functions

apply(graph, change)

@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(graph)

@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(graph, id)

@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(graph, id)

@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(old, new)

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

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

dump(graph)

@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(graph, id)

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

The edge of an id, or nil.

edges(graph)

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

The edges, in the order they were made.

edges_of(graph, id)

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

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

load(map)

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

The graph of dump/1's maps.

new(opts \\ [])

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

Makes a graph of nodes and edges: Hakushi.Nodes and Hakushi.Edges, or maps and keyword lists for Hakushi.Node.new/1 and Hakushi.Edge.new/1, and its paper (see Hakushi.Paper.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(graph, id)

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

The node of an id, or nil.

nodes(graph)

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

The nodes, in the order they are drawn.