Yog.Layout (YogEx v0.99.0)

Copy Markdown View Source

Algorithms for calculating 2D coordinates for graph nodes.

Calculates coordinates mapping node IDs to {x, y} float coordinate tuples. These coordinates can be used for rendering graphs visually via custom SVG elements, exporting data for web dashboards (using Cytoscape.js or D3.js), or generating layouts.

Overview

AlgorithmFunctionMathematical ModelBest ForTime Complexity
Circularcircular/2Uniform spacing on unit circleSymmetric/small graphs, cycles$O(V)$
Randomrandom/2Uniform distribution in bounding boxInitial states, baseline checks$O(V)$
Springspring/2Fruchterman-Reingold force modelSocial networks, general graphs$O(I \cdot (V^2 + E))$
Tuttetutte/3Gauss-Seidel barycentric relaxationPlanar graphs, routing visual flow$O(I \cdot (V + E))$
Shellshell/3Concentric circles placementHierarchies, core-periphery structures$O(V)$
Multipartitemultipartite/3Parallel rows/columns alignmentBipartite graphs, neural nets, flow nets$O(V)$

Graph Layout Visualization (Spring vs. Circular)

The layout determines the structural aesthetic. Spring layout cluster connected nodes together, whereas Circular layout focuses purely on ordering.

graph LayoutComparison { bgcolor="transparent"; node [shape=circle, fontname="inherit"]; subgraph cluster_spring { label="Spring (Force-Directed)"; color="#10b981"; s1 -- s2; s2 -- s3; s3 -- s1; s1 -- s4; s4 -- s5; s5 -- s1; } subgraph cluster_circular { label="Circular"; color="#3b82f6"; c1 -- c2 -- c3 -- c4 -- c5 -- c1; } }

Usage Example

Below is an example showing how layout coordinates can be mapped directly to generate a visual representation:

iex> graph = Yog.from_unweighted_edges(:undirected, [{1, 2}, {2, 3}])
iex> pos = Yog.Layout.circular(graph, radius: 10.0)
iex> Map.keys(pos) |> Enum.sort()
[1, 2, 3]

Summary

Functions

Positions nodes uniformly spaced on a circle.

Positions nodes in parallel layers (columns or rows).

Positions nodes randomly within a specified bounding box.

Positions nodes in concentric circles (shells).

Positions nodes using a spring/force-directed model (Fruchterman-Reingold).

Positions nodes using Tutte's barycentric embedding.

Functions

circular(graph, opts \\ [])

@spec circular(
  Yog.Graph.t(),
  keyword()
) :: %{required(Yog.Graph.node_id()) => {float(), float()}}

Positions nodes uniformly spaced on a circle.

Delegates to Yog.Layout.Circular.layout/2.

multipartite(graph, layers, opts \\ [])

@spec multipartite(Yog.Graph.t(), [[Yog.Graph.node_id()]], keyword()) :: %{
  required(Yog.Graph.node_id()) => {float(), float()}
}

Positions nodes in parallel layers (columns or rows).

Delegates to Yog.Layout.Multipartite.layout/3.

random(graph, opts \\ [])

@spec random(
  Yog.Graph.t(),
  keyword()
) :: %{required(Yog.Graph.node_id()) => {float(), float()}}

Positions nodes randomly within a specified bounding box.

Delegates to Yog.Layout.Random.layout/2.

shell(graph, shells, opts \\ [])

@spec shell(Yog.Graph.t(), [[Yog.Graph.node_id()]], keyword()) :: %{
  required(Yog.Graph.node_id()) => {float(), float()}
}

Positions nodes in concentric circles (shells).

Delegates to Yog.Layout.Shell.layout/3.

spring(graph, opts \\ [])

@spec spring(
  Yog.Graph.t(),
  keyword()
) :: %{required(Yog.Graph.node_id()) => {float(), float()}}

Positions nodes using a spring/force-directed model (Fruchterman-Reingold).

Delegates to Yog.Layout.Spring.layout/2.

tutte(graph, boundary_nodes, opts \\ [])

@spec tutte(Yog.Graph.t(), [Yog.Graph.node_id()], keyword()) :: %{
  required(Yog.Graph.node_id()) => {float(), float()}
}

Positions nodes using Tutte's barycentric embedding.

Delegates to Yog.Layout.Tutte.layout/3.