Manim教程:第六章 动画对象的表达 ——【下】

6.24生成树形结构

 完整代码:

from manim import *  

class LargeTreeGeneration01(MovingCameraScene):  
    DEPTH = 3  
    CHILDREN_PER_VERTEX = 2  
    LAYOUT_CONFIG = {"vertex_spacing": (0.8, 1)}  
    VERTEX_CONF = {"radius": 0.12, "color": BLUE_B, "fill_opacity": 1}  

    def expand_vertex(self, g, vertex_id: str, depth: int):  
        new_vertices = [f"{vertex_id}/{i}" for i in range(self.CHILDREN_PER_VERTEX)]  
        new_edges = [(vertex_id, child_id) for child_id in new_vertices]  
        
        g.add_edges(  
            *new_edges,  
            vertex_config=self.VERTEX_CONF,  
            positions={  
                k: g.vertices[vertex_id].get_center() + 0.1 * DOWN for k in new_vertices  
            },  
        )  
        
        if depth < self.DEPTH:  
            for child_id in new_vertices:  
                self.expand_vertex(g, child_id, depth + 1)  

        return g  

    def construct(self):  
        # 创建一个空的图,只有一个根节点  
        g = Graph(["ROOT"], [], vertex_config=self.VERTEX_CONF)  
        
        # 扩展树结构  
        g = self.expand_vertex(g, "ROOT", 1)  
        self.add(g)  

        # 改变布局,并添加动画  
        self.play(  
            g.animate.change_layout(  
                "tree",  
                root_vertex="ROOT",  
                layout_config=self.LAYOUT_CONFIG,  
            )  
        )  
        
        # 自动缩放相机以适应图形  
        self.play(self.camera.auto_zoom(g, margin=1), run_time=0.5)  

运行结果:https://download.youkuaiyun.com/download/qq_45449625/89591393https://download.youkuaiyun.com/download/qq_45449625/89591393

接下来解释一下代码吧:

 

1. 导入所需库

from manim import * 

这一行导入了 Manim 库的所有内容。Manim 是一个用于制作数学动画的 Python 库。

2. 创建类

class LargeTreeGeneration(MovingCameraScene): 

这个类 LargeTreeGeneration 继承自 MovingCameraSceneMovingCameraScene 提供了相应的方法来控制相机的移动和缩放,适合用于制作动态场景。

3. 定义类属性

DEPTH = 4 CHILDREN_PER_VERTEX = 3 LAYOUT_CONFIG = {"vertex_spacing": (0.5, 1)} VERTEX_CONF = {"radius": 0.12, "color": BLUE_B, "fill_opacity": 1} 
  • DEPTH:指树的深度(最深的层级)。
  • CHILDREN_PER_VERTEX:每个节点的子节点数量。
  • LAYOUT_CONFIG:布局配置,控制节点间的垂直和水平间距。
  • VERTEX_CONF:节点的属性配置,包括半径、颜色和填充透明度。

4. 扩展树的节点

def expand_vertex(self, g, vertex_id: str, depth: int): 

这个方法用于递归地生成树的节点和连接。

  • g:当前的图形对象。
  • vertex_id:当前节点的 ID。
  • depth:当前的深度,始于1。

方法内的逻辑:

new_vertices = [f"{vertex_id}/{i}" for i in range(self.CHILDREN_PER_VERTEX)] 

根据当前节点 ID 生成子节点的 ID。

new_edges = [(vertex_id, child_id) for child_id in new_vertices] 

创建从当前节点到所有子节点的边。

g.add_edges( *new_edges, vertex_config=self.VERTEX_CONF, positions={ k: g.vertices[vertex_id].get_center() + 0.1 * DOWN for k in new_vertices }, ) 

将这些边添加到图形对象中,同时为新节点设置位置和配置。

if depth < self.DEPTH: for child_id in new_vertices: self.expand_vertex(g, child_id, depth + 1) 

如果当前深度小于指定的深度,则递归调用自己,为每个新子节点继续生成其子节点。

5. 构建场景

def construct(self): 

这是 Manim 中用来生成场景的主要方法。

g = Graph(["ROOT"], [], vertex_config=self.VERTEX_CONF) 

创建一个图形,从一个根节点("ROOT")开始,且没有初始边。

g = self.expand_vertex(g, "ROOT", 1) 

调用 expand_vertex 方法,扩展整个图形。

self.add(g) 

将图形添加到当前场景中。

6. 动画效果

self.play( g.animate.change_layout( "tree", root_vertex="ROOT", layout_config=self.LAYOUT_CONFIG, ) ) 

使用 g.animate.change_layout 方法将图形转换为树形布局,并在动画中展示。

self.play(self.camera.auto_zoom(g, margin=1), run_time=0.5) 

将相机自动缩放,以适应整个图形,并在0.5秒内完成该动作。

7. 运行代码

最后一行

%manim -qm -v WARNING LargeTreeGeneration 

是用来在 Jupyter Notebook 中运行这个场景的命令,它会编译并展示动画。

总结

整体来看,这段代码使用了递归方法生成功能强大的树形结构,同时通过相机动画使得最终的展示更加生动。设计上颇具灵活性,可通过调整 DEPTH 和 CHILDREN_PER_VERTEX 属性轻松实现不同规模的树。

 

 6.25探索有向图

        在manim中有Circular Layou,Kamada Kawai Layout,Partite Layout,Planar Layout,Random Layout,Shell Layout,Spectral Layout,Sprial Layout,Spring Layout,Tree Layout 函数,它们都有自己的特点。下面是Manim中不同布局函数的简要解释,包括每个布局的用途、特点以及适用场景的简要描述。

一,介绍

1.Circular Layou

 

        Circular Layout 是一种图形布局方法,用于在一个圆形范围内均匀分布图形中的节点(或顶点)。在这种布局中,节点按照圆周均匀排列,形成一个圆,使得节点之间的距离尽可能相等。这种布局特别适合展示网络结构、群体关系或层次结构,因为它能够有效地利用空间并提供良好的可视化效果。

        主要优点: 均匀分布:节点在圆周上均匀分布,视觉上很平衡。 增强可读性:通过减少节点之间的重叠,确保节点的标签和链接更加清晰。 适用性广:适用于各种类型的图(如社交网络、树形结构等),特别是当关系相对对称或没有明显方向时。 视觉吸引力:圆形布局通常更具吸引力,并能够引导观众的视线。 使用场景: 社交网络分析:可以用来展示用户之间的连接和交互。 数据可视化:适合显示类别之间的关系,例如产品特性和相互作用。 生物信息学:在基因组学或生态学中,展示物种或基因之间的关系。

2. Kamada Kaway Layout

  • 用途: 常用于绘制图结构,特别是在网络图或连接图中。
  • 解释: 基于力导向算法,Kamada-Kaway布局试图最小化节点之间的距离,使图形结构在视觉上看起来更为平衡。
  • 特点: 可以很好地展示复杂的连接关系,优先考虑局部连接。

3. Partite Layout

  • 用途: 用于可视化分层或分组关系的图。
  • 解释: 将节点分为不同的组,并在图中以不同的水平显示这些组,有助于清晰地展示节点之间的连接关系。
  • 特点: 非常适合展示二分图或多分图结构,保持组与组之间的距离。

4. Planar Layout

  • 用途: 专门用于展示平面图。
  • 解释: 以一种不相交的方式将节点排列,并确保没有边线交叉,有助于清晰地可视化图形。
  • 特点: 适合于平面图的视觉化应用,能够保持视觉的整洁性和可读性。

5. Random Layout

  • 用途: 随机展示节点的布局。
  • 解释: 节点的排列没有特定的模式,通常用于快速实验或初步可视化,没有明确的线性或层次结构。
  • 特点: 虽然没有结构性,但适合于快速生成可视化,缺乏可读性和美观。

6. Shell Layout

  • 用途: 用于环形结构的展示。
  • 解释: 节点被放置在同心圆或者环形中,可以展现节点之间的层次或群体。
  • 特点: 适合展示具有环形结构的图,特别是当节点具有层次性时,视觉效果较好。

7. Spectral Layout

  • 用途: 基于图的谱特征进行节点位置的布局,适用于复杂图形的展示。
  • 解释: 通过分析图的拉普拉斯矩阵,利用特征值分解来确定节点的位置,能够反映节点之间的关系。
  • 特点: 能够有效地减少图的边的交叉,适合于复杂图的数据表示。

8. Spiral Layout

  • 用途: 用于螺旋结构的节点排列。
  • 解释: 节点沿着一个螺旋形的路径排列,通常用于展示时间序列数据或具有顺序性质的结构。
  • 特点: 适合展示时间演变或是逐步发展关系的图,视觉上独特。

9. Spring Layout

  • 用途: 基于物理模拟进行节点布置,常用于社交网络或图形结构。
  • 解释: 使用力导向算法,模拟节点之间的相互作用(如弹簧吸引或推斥)来获得节点的位置,从而使图形结构尽可能平衡。
  • 特点: 能够直观地反映节点间的连接强度,适合展示复杂的网络关系。

10. Tree Layout

  • 用途: 可视化层次性的数据结构,如树、家谱图或组织结构图。
  • 解释: 节点根据层次结构排列,根节点位于顶部或中间,子节点分布在下方或侧面,清楚地呈现父子关系。
  • 特点: 强调层级关系,提供一种清晰且直观的图形表示。

这些布局功能在Manim中为用户提供了多样化的图形可视化工具,方便展示不同类型和结构的数据,非常适合用于教育、演示和科学研究等场景。

二,应用

1.Circular Layout

  • 圆形布局:将顶点放在一个圆上

  • 示例1:

    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)

    运行结果:

 示例2:

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,
            vertex_config={1: {"fill_color": BLUE}},  
            edge_config={(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1): {"stroke_color": RED}}  # Fixed line  
        )
        self.add(graph)

 运行结果:

 示例3:

from manim import *  

class CircularLayout04(Scene):  
    def construct(self):  
        points=[1, 2, 3, 4, 5, 6]
        edges = [  
            (1, 2), (2, 3), (3, 4), (4, 5),  
            (5, 6), (6, 1), (5, 1), (1, 3), (3, 5)  
        ]  
        
        # 定义外环边  
        outer_edges = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1)]  
        in_edges = [(5, 1), (1, 3), (3, 5)]  
        
        # 使用字典生成边配置,将外环边设置为红色,内部边设置为黄色  
        edge_config = {edge: {"stroke_color": RED} for edge in outer_edges}  
        edge_config.update({edge: {"stroke_color": YELLOW_E} for edge in in_edges})  
        
        graph = Graph(  
            points,  
            edges,  
            layout="circular",  
            labels=True,  
            vertex_config={vertex: {"fill_color": BLUE}for vertex in points},  # 配置顶点  
            edge_config=edge_config  # 使用合并后的边配置字典  
        )  

        self.add(graph)

运行结果: 

2. Kamada Kaway Layout

  • Kamada Kawai Layout:尝试放置顶点,以便尊重它们之间的给定距离

  • 示例1:

    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)

    运行结果:

示例2:

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] = 1  # etc
        distances[4][5] = 1
        distances[5][6] = 1
        distances[6][1] = 1
        distances[2][5] = 2
        distances[2][4] = 2.5

        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,
            vertex_config={2: {"fill_color": BLUE}} # 配置顶点 
        )
        self.add(graph)

 运行结果:

3. Partite Layout

  • Partite Layout:将顶点放置在不同的分区中

  • 示例1:

    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)

    运行结果:

示例2:

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,5],[3,6],[5,6]]},
            labels=True
        )
        self.add(graph)

 运行结果:

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yasen.M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值