Mind-map builder on top of Yog.
Choreo.MindMap models hierarchical concept maps where a central idea
radiates into topics, sub-topics, and notes. Cross-links (associations)
between branches are supported for non-hierarchical relationships.
When to use
Use Choreo.MindMap when brainstorming, documenting knowledge domains,
planning talks, or structuring documentation. It enforces a single-root
invariant and detects orphaned concepts automatically.
Node types
:root— the central concept (exactly one per map):topic— main branch radiating from the root:subtopic— nested idea under a topic:note— annotation or detail node
Edge types
:branch— hierarchical parent→child relationship:associates— cross-link between any two nodes
Further reading
Quick Start
map =
Choreo.MindMap.new()
|> Choreo.MindMap.set_root(:elixir, label: "Elixir")
|> Choreo.MindMap.add_topic(:concurrency, label: "Concurrency")
|> Choreo.MindMap.add_topic(:ecosystem, label: "Ecosystem")
|> Choreo.MindMap.add_subtopic(:processes, label: "Processes")
|> Choreo.MindMap.add_note(:beam, label: "BEAM VM")
|> Choreo.MindMap.branch(:elixir, :concurrency)
|> Choreo.MindMap.branch(:elixir, :ecosystem)
|> Choreo.MindMap.branch(:concurrency, :processes)
|> Choreo.MindMap.branch(:ecosystem, :beam)
|> Choreo.MindMap.associate(:processes, :beam, label: "runs on")
dot = Choreo.MindMap.to_dot(map)Diagram
Analysis
# Detect orphaned ideas not reachable from the root
Choreo.MindMap.Analysis.orphan_nodes(map)
# Measure map complexity
Choreo.MindMap.Analysis.depth(map)
Choreo.MindMap.Analysis.breadth(map)
# Validate structural soundness
Choreo.MindMap.Analysis.validate(map)
Summary
Functions
Adds a note node (annotation or detail).
Adds a subtopic node (nested idea under a topic).
Adds a topic node (main branch).
Creates an associative (cross-link) edge between two nodes.
Creates a hierarchical branch from parent to child.
Returns all edges as {from, to, weight} tuples.
Creates a new empty mind map.
Returns all node IDs in the mind map.
Returns all note node IDs.
Returns the root node id, or nil if not set.
Sets the root concept node of the mind map.
Returns all subtopic node IDs.
Returns a theme for Choreo.MindMap.
Renders the mind map to DOT format.
Returns the raw Yog.Graph struct underpinning the mind map.
Returns all topic node IDs.
Types
@type t() :: %Choreo.MindMap{ edge_meta: %{optional({Yog.node_id(), Yog.node_id()}) => map()}, graph: Yog.graph(), root: Yog.node_id() | nil }
Functions
@spec add_note(t(), Yog.node_id(), keyword()) :: t()
Adds a note node (annotation or detail).
Options
:label(String.t/0):description(String.t/0):shape(atom/0):fillcolor(String.t/0):fontcolor(String.t/0):style(String.t/0):penwidth:image(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = Choreo.MindMap.add_note(map, :ref, label: "See RFC 7231")
iex> :ref in Choreo.MindMap.nodes(map)
true
iex> Yog.node(map.graph, :ref).node_type
:noteDiagram
@spec add_subtopic(t(), Yog.node_id(), keyword()) :: t()
Adds a subtopic node (nested idea under a topic).
Options
:label(String.t/0):description(String.t/0):shape(atom/0):fillcolor(String.t/0):fontcolor(String.t/0):style(String.t/0):penwidth:image(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = Choreo.MindMap.add_subtopic(map, :patterns, label: "Patterns")
iex> :patterns in Choreo.MindMap.nodes(map)
true
iex> Yog.node(map.graph, :patterns).node_type
:subtopicDiagram
@spec add_topic(t(), Yog.node_id(), keyword()) :: t()
Adds a topic node (main branch).
Options
:label(String.t/0):description(String.t/0):shape(atom/0):fillcolor(String.t/0):fontcolor(String.t/0):style(String.t/0):penwidth:image(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = Choreo.MindMap.add_topic(map, :design, label: "Design")
iex> :design in Choreo.MindMap.nodes(map)
true
iex> Yog.node(map.graph, :design).node_type
:topicDiagram
@spec associate(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
Creates an associative (cross-link) edge between two nodes.
Associative edges are rendered as dashed lines without arrowheads, representing non-hierarchical relationships.
Options
:label(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "A")
...> |> Choreo.MindMap.add_topic(:b, label: "B")
...> |> Choreo.MindMap.add_topic(:c, label: "C")
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:a, :c)
...> |> Choreo.MindMap.associate(:b, :c, label: "related")
iex> map.edge_meta[{:b, :c}].edge_type
:associates
iex> map.edge_meta[{:b, :c}].label
"related"
@spec branch(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
Creates a hierarchical branch from parent to child.
Options
:label(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "A")
...> |> Choreo.MindMap.add_topic(:b, label: "B")
...> |> Choreo.MindMap.branch(:a, :b)
iex> Yog.has_edge?(map.graph, :a, :b)
true
iex> map.edge_meta[{:a, :b}].edge_type
:branch
@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), number()}]
Returns all edges as {from, to, weight} tuples.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "A")
...> |> Choreo.MindMap.add_topic(:b, label: "B")
...> |> Choreo.MindMap.branch(:a, :b)
iex> Choreo.MindMap.edges(map)
[{:a, :b, 1}]
@spec new() :: t()
Creates a new empty mind map.
Examples
iex> map = Choreo.MindMap.new()
iex> Choreo.MindMap.nodes(map)
[]
iex> Choreo.MindMap.root(map)
nil
@spec nodes(t()) :: [Yog.node_id()]
Returns all node IDs in the mind map.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "A")
...> |> Choreo.MindMap.add_topic(:b, label: "B")
iex> Enum.sort(Choreo.MindMap.nodes(map))
[:a, :b]
@spec notes(t()) :: [Yog.node_id()]
Returns all note node IDs.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_note(:n, label: "Note")
iex> Choreo.MindMap.notes(map)
[:n]
@spec root(t()) :: Yog.node_id() | nil
Returns the root node id, or nil if not set.
Examples
iex> map = Choreo.MindMap.new()
iex> Choreo.MindMap.root(map)
nil
iex> map = Choreo.MindMap.set_root(map, :idea, label: "Idea")
iex> Choreo.MindMap.root(map)
:idea
@spec set_root(t(), Yog.node_id(), keyword()) :: t()
Sets the root concept node of the mind map.
Can only be called once. Raises ArgumentError if the map already has a root.
Options
:label(String.t/0):description(String.t/0):shape(atom/0):fillcolor(String.t/0):fontcolor(String.t/0):style(String.t/0):penwidth:image(String.t/0)
Examples
iex> map = Choreo.MindMap.new()
iex> map = Choreo.MindMap.set_root(map, :idea, label: "Big Idea")
iex> Choreo.MindMap.root(map)
:idea
iex> Yog.node(map.graph, :idea).node_type
:root
iex> Yog.node(map.graph, :idea).label
"Big Idea"Diagram
iex> map = Choreo.MindMap.new() |> Choreo.MindMap.set_root(:a, label: "A")
iex> Choreo.MindMap.set_root(map, :b, label: "B")
** (ArgumentError) Mind map already has a root
@spec subtopics(t()) :: [Yog.node_id()]
Returns all subtopic node IDs.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_subtopic(:c)
iex> Choreo.MindMap.subtopics(map)
[:c]
@spec theme( atom(), keyword() ) :: Choreo.Theme.t()
Returns a theme for Choreo.MindMap.
Examples
iex> theme = Choreo.MindMap.theme(:default, graph_rankdir: :lr)
iex> theme.graph_rankdir
:lr
Renders the mind map to DOT format.
Options
:theme—:default,:dark, or aChoreo.Themestruct
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "A")
...> |> Choreo.MindMap.add_topic(:b, label: "B")
...> |> Choreo.MindMap.branch(:a, :b)
iex> dot = Choreo.MindMap.to_dot(map)
iex> String.contains?(dot, "digraph")
true
iex> String.contains?(dot, "A")
true
iex> String.contains?(dot, "B")
true
Returns the raw Yog.Graph struct underpinning the mind map.
Examples
iex> map = Choreo.MindMap.new()
iex> graph = Choreo.MindMap.to_graph(map)
iex> graph.kind
:directed
@spec topics(t()) :: [Yog.node_id()]
Returns all topic node IDs.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_subtopic(:c)
iex> Choreo.MindMap.topics(map)
[:b]