布局函数¶
限定名称: manim.mobject.graph.LayoutFunction
- class LayoutFunction(*args, **kwargs)[来源]¶
基础:
Protocol一种用于自动布局功能的协议,该功能计算用于
change_layout()中的图形布局。注意
布局函数必须是一个纯函数,即它不能修改传递给它的图。
示例
这是一个将节点按排序顺序排列在n x m网格中的示例。
示例: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)manim 提供了几种自动布局,可以通过将它们的名称作为
layout参数传递给change_layout()来使用。 或者,可以将自定义布局函数作为layout参数传递给change_layout()。这样的函数必须遵守LayoutFunction协议。由manim提供的
LayoutFunction如下所示:圆形布局:将顶点放置在一个圆上
示例: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 布局:尝试将顶点放置得尊重它们之间的给定距离
示例: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)分区布局:将顶点放置到不同的分区中
示例: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)平面布局:放置顶点以使边不相交
示例:平面布局 ¶
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)随机布局:随机放置顶点
示例: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布局:将顶点放置在同心圆中
示例: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)谱布局:使用图的拉普拉斯矩阵的特征向量放置顶点(近似于比率割的节点聚类)
示例: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)螺旋布局:将顶点以螺旋模式排列
示例: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 布局:根据 Fruchterman-Reingold 力导向算法放置节点(尝试最小化边长度,同时最大化节点间距)
示例: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)树布局:将顶点放置到具有根节点和分支的树中(只能用于合法的树)
示例: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)方法