LayoutFunction¶
Qualified name: manim.mobject.graph.LayoutFunction
- class LayoutFunction(*args, **kwargs)[source]¶
Bases:
Protocol
A protocol for automatic layout functions that compute a layout for a graph to be used in
change_layout()
.Note
The layout function must be a pure function, i.e., it must not modify the graph passed to it.
Examples
Here is an example that arranges nodes in an n x m grid in sorted order.
Example: CustomLayoutExample ¶
from manim import * class CustomLayoutExample(Scene): def construct(self): import numpy as np import networkx as nx # create custom layout def custom_layout( graph: nx.Graph, scale: float | tuple[float, float, float] = 2, n: int | None = None, *args: Any, **kwargs: Any, ): nodes = sorted(list(graph)) height = len(nodes) // n return { node: (scale * np.array([ (i % n) - (n-1)/2, -(i // n) + height/2, 0 ])) for i, node in enumerate(graph) } # draw graph n = 4 graph = Graph( [i for i in range(4 * 2 - 1)], [(0, 1), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (4, 5), (5, 6)], labels=True, layout=custom_layout, layout_config={'n': n} ) self.add(graph)
class CustomLayoutExample(Scene): def construct(self): import numpy as np import networkx as nx # create custom layout def custom_layout( graph: nx.Graph, scale: float | tuple[float, float, float] = 2, n: int | None = None, *args: Any, **kwargs: Any, ): nodes = sorted(list(graph)) height = len(nodes) // n return { node: (scale * np.array([ (i % n) - (n-1)/2, -(i // n) + height/2, 0 ])) for i, node in enumerate(graph) } # draw graph n = 4 graph = Graph( [i for i in range(4 * 2 - 1)], [(0, 1), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (4, 5), (5, 6)], labels=True, layout=custom_layout, layout_config={'n': n} ) self.add(graph)
Several automatic layouts are provided by manim, and can be used by passing their name as the
layout
parameter tochange_layout()
. Alternatively, a custom layout function can be passed tochange_layout()
as thelayout
parameter. Such a function must adhere to theLayoutFunction
protocol.The
LayoutFunction
s provided by manim are illustrated below:Circular Layout: places the vertices on a circle
Example: CircularLayout ¶
from manim import * class CircularLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="circular", labels=True ) self.add(graph)
class CircularLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="circular", labels=True ) self.add(graph)
Kamada Kawai Layout: tries to place the vertices such that the given distances between them are respected
Example: KamadaKawaiLayout ¶
from manim import * class KamadaKawaiLayout(Scene): def construct(self): from collections import defaultdict distances: dict[int, dict[int, float]] = defaultdict(dict) # set desired distances distances[1][2] = 1 # distance between vertices 1 and 2 is 1 distances[2][3] = 1 # distance between vertices 2 and 3 is 1 distances[3][4] = 2 # etc distances[4][5] = 3 distances[5][6] = 5 distances[6][1] = 8 graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1)], layout="kamada_kawai", layout_config={"dist": distances}, layout_scale=4, labels=True ) self.add(graph)
class KamadaKawaiLayout(Scene): def construct(self): from collections import defaultdict distances: dict[int, dict[int, float]] = defaultdict(dict) # set desired distances distances[1][2] = 1 # distance between vertices 1 and 2 is 1 distances[2][3] = 1 # distance between vertices 2 and 3 is 1 distances[3][4] = 2 # etc distances[4][5] = 3 distances[5][6] = 5 distances[6][1] = 8 graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1)], layout="kamada_kawai", layout_config={"dist": distances}, layout_scale=4, labels=True ) self.add(graph)
Partite Layout: places vertices into distinct partitions
Example: PartiteLayout ¶
from manim import * class PartiteLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="partite", layout_config={"partitions": [[1,2],[3,4],[5,6]]}, labels=True ) self.add(graph)
class PartiteLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="partite", layout_config={"partitions": [[1,2],[3,4],[5,6]]}, labels=True ) self.add(graph)
Planar Layout: places vertices such that edges do not cross
Example: PlanarLayout ¶
from manim import * class PlanarLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="planar", layout_scale=4, labels=True ) self.add(graph)
class PlanarLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="planar", layout_scale=4, labels=True ) self.add(graph)
Random Layout: randomly places vertices
Example: RandomLayout ¶
from manim import * class RandomLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="random", labels=True ) self.add(graph)
class RandomLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="random", labels=True ) self.add(graph)
Shell Layout: places vertices in concentric circles
Example: ShellLayout ¶
from manim import * class ShellLayout(Scene): def construct(self): nlist = [[1, 2, 3], [4, 5, 6, 7, 8, 9]] graph = Graph( [1, 2, 3, 4, 5, 6, 7, 8, 9], [(1, 2), (2, 3), (3, 1), (4, 1), (4, 2), (5, 2), (6, 2), (6, 3), (7, 3), (8, 3), (8, 1), (9, 1)], layout="shell", layout_config={"nlist": nlist}, labels=True ) self.add(graph)
class ShellLayout(Scene): def construct(self): nlist = [[1, 2, 3], [4, 5, 6, 7, 8, 9]] graph = Graph( [1, 2, 3, 4, 5, 6, 7, 8, 9], [(1, 2), (2, 3), (3, 1), (4, 1), (4, 2), (5, 2), (6, 2), (6, 3), (7, 3), (8, 3), (8, 1), (9, 1)], layout="shell", layout_config={"nlist": nlist}, labels=True ) self.add(graph)
Spectral Layout: places vertices using the eigenvectors of the graph Laplacian (clusters nodes which are an approximation of the ratio cut)
Example: SpectralLayout ¶
from manim import * class SpectralLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spectral", labels=True ) self.add(graph)
class SpectralLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spectral", labels=True ) self.add(graph)
Sprial Layout: places vertices in a spiraling pattern
Example: SpiralLayout ¶
from manim import * class SpiralLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spiral", labels=True ) self.add(graph)
class SpiralLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spiral", labels=True ) self.add(graph)
Spring Layout: places nodes according to the Fruchterman-Reingold force-directed algorithm (attempts to minimize edge length while maximizing node separation)
Example: SpringLayout ¶
from manim import * class SpringLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spring", labels=True ) self.add(graph)
class SpringLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6], [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)], layout="spring", labels=True ) self.add(graph)
Tree Layout: places vertices into a tree with a root node and branches (can only be used with legal trees)
Example: TreeLayout ¶
from manim import * class TreeLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6, 7], [(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7)], layout="tree", layout_config={"root_vertex": 1}, labels=True ) self.add(graph)
class TreeLayout(Scene): def construct(self): graph = Graph( [1, 2, 3, 4, 5, 6, 7], [(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7)], layout="tree", layout_config={"root_vertex": 1}, labels=True ) self.add(graph)
Methods