Visualize.Layout.Force (Visualize v0.2.35)

Copy Markdown View Source

Force-directed graph layout.

Provides a physics-based simulation for positioning nodes in a graph. The simulation runs as a GenServer, making it suitable for real-time updates in LiveView applications.

Basic Usage

# Define nodes and links
nodes = [
  %{id: "a"},
  %{id: "b"},
  %{id: "c"}
]

links = [
  %{source: "a", target: "b"},
  %{source: "b", target: "c"}
]

# Start simulation; a subscriber given here receives every tick from the first
{:ok, sim} = Visualize.Layout.Force.start_link(
  nodes: nodes,
  links: links,
  subscribers: [self()]
)

# A process that joins a running simulation later subscribes
Visualize.Layout.Force.subscribe(sim, other_pid)

LiveView Integration

def mount(_params, _session, socket) do
  {:ok, sim} = Visualize.Layout.Force.start_link(
    nodes: @nodes,
    links: @links,
    forces: [
      {:center, x: 300, y: 200},
      {:many_body, strength: -100},
      {:link, distance: 50}
    ],
    subscribers: [self()]
  )

  {:ok, assign(socket, sim: sim, nodes: [], links: [])}
end

def handle_info({:force_tick, %{nodes: nodes, links: links}}, socket) do
  {:noreply, assign(socket, nodes: nodes, links: links)}
end

Available Forces

  • :center - Keeps the graph centered

    • x, y: center coordinates
    • strength: how strongly to center (default: 1)
  • :many_body - Repulsion/attraction between all nodes

    • strength: negative for repulsion, positive for attraction (default: -30)
    • theta: Barnes-Hut approximation threshold (default: 0.9)
    • distance_min, distance_max: distance bounds
  • :link - Spring forces between connected nodes

    • distance: target distance (default: 30)
    • strength: spring strength (default: 1)
    • iterations: constraint iterations per tick (default: 1)
  • :collision - Prevents node overlap

    • radius: node radius or function (default: 10)
    • strength: collision strength (default: 1)
  • :x - Push nodes toward an x position

    • x: target x or function
    • strength: force strength (default: 0.1)
  • :y - Push nodes toward a y position

    • y: target y or function
    • strength: force strength (default: 0.1)
  • :radial - Push nodes toward a circle

    • radius: target radius or function
    • x, y: circle center
    • strength: force strength (default: 0.1)

Summary

Functions

alpha(sim)

See Visualize.Layout.Force.Simulation.alpha/1.

fix_node(sim, node_id, x, y)

See Visualize.Layout.Force.Simulation.fix_node/4.

links(sim)

See Visualize.Layout.Force.Simulation.links/1.

nodes(sim)

See Visualize.Layout.Force.Simulation.nodes/1.

restart(sim)

See Visualize.Layout.Force.Simulation.restart/1.

run(opts)

@spec run(keyword()) :: %{nodes: [map()], links: [map()]}

Runs a simulation synchronously until it reaches equilibrium.

Useful for generating static layouts without a GenServer.

Options

  • :nodes - list of nodes (required)
  • :links - list of links (optional)
  • :forces - force configuration (optional)
  • :iterations - maximum iterations (default: 300)
  • :alpha - the starting temperature (default: 1.0); below 1 a reheat, d3's alpha(a).restart(), for nodes that already carry their positions
  • :alpha_decay - the fraction of alpha lost per iteration (default: 1 - 0.001 ^ (1 / iterations)); d3's own is 1 - 0.001 ^ (1 / 300)

A node given with :x and :y starts there, and with :vx and :vy keeps that velocity, so passing a previous run's nodes with a lower :alpha and a few :iterations continues that layout rather than starting a new one (spec/06 §8.3, D-132). With every option at its default the layout starts from the spiral.

Returns

A map with :nodes and :links containing final positions, each node carrying its final :x, :y, :vx and :vy.

Examples

result = Visualize.Layout.Force.run(
  nodes: nodes,
  links: links,
  forces: [{:center, x: 200, y: 200}]
)

positioned_nodes = result.nodes

set_alpha(sim, alpha)

See Visualize.Layout.Force.Simulation.set_alpha/2.

set_alpha_target(sim, target)

See Visualize.Layout.Force.Simulation.set_alpha_target/2.

set_links(sim, links)

See Visualize.Layout.Force.Simulation.set_links/2.

set_nodes(sim, nodes)

See Visualize.Layout.Force.Simulation.set_nodes/2.

start(sim)

See Visualize.Layout.Force.Simulation.start/1.

start_link(opts)

See Visualize.Layout.Force.Simulation.start_link/1.

stop(sim)

See Visualize.Layout.Force.Simulation.stop/1.

subscribe(sim, pid)

See Visualize.Layout.Force.Simulation.subscribe/2.

tick(sim)

See Visualize.Layout.Force.Simulation.tick/1.

unfix_node(sim, node_id)

See Visualize.Layout.Force.Simulation.unfix_node/2.

unsubscribe(sim, pid)

See Visualize.Layout.Force.Simulation.unsubscribe/2.