• 如何画一棵树


    画一棵树

    从一个树枝开始,分叉向两端(或者更多端),然后继续从新的树枝进行分叉,…

    while True:
        树枝 = 树枝.分叉
    
    • 1
    • 2

    如果不限制,树可以一直这么长下去…

    请添加图片描述

    最后会长成这样:(限制了分叉层数)

    请添加图片描述

    对颜色等参数进行一些改变,就可以得到不同的树:

    请添加图片描述
    请添加图片描述

    好了,我们已经知道树的生长原理了,现在让我们把这个过程画出来。

    turtle 海龟绘图

    turtle是Python内置的一个画图库,使用tkinter实现基本图形界面。

    它的方法就是用一只海龟作为画笔在屏幕绘图。

    更多方法参考标准库中turtle一节。

    这里用一个画正方形的例子演示turtle用法:

    import turtle       # 首先导入turtle库
    
    p = turtle.Turtle() # 初始化画笔p
    p.speed(1)          # 设置速度为1,最慢。
    for i in range(4):  
        p.forward(300)  # 画笔向前移动300像素点
        p.right(90)     # 画笔角度右转90度。
    
    turtle.mainloop()   #启动绘图。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (注:画笔初始方向朝右)

    turtle自带了一些示例,我们可以在命令行输入python -m turtledemo 查看
    示例包括特殊图形、噪声、时钟、混色器、森林、分形曲线、nim游戏、画笔、彩虹、penrose、星球、球、三角、排序模拟、树、上下画布、太极图。

    请添加图片描述里面有很多例子,可以选择。左边可以查看代码,右边点击START运行。

    tree

    tree为例,

    下面tree的生成使用了广度优先。把当前层的树枝画完,并且把下一层的放入列表,然后递归处理下一层。

    from turtle import Turtle, mainloop
    from time import perf_counter as clock
    
    def tree(plist, l, a, f):
        """ plist 笔的列表
        l 树枝长度
        a 树枝转弯角度
        f 树枝长度衰减因子
        from level to level."""
        if l > 3:
            lst = []
            for p in plist:
                p.forward(l)
                q = p.clone()
                p.left(a) #左转
                q.right(a) #右转
                lst.append(p)
                lst.append(q)
            tree(lst, l*f, a, f)
    
    
    def maketree():
        p = Turtle()
        p.setundobuffer(None)
        p.hideturtle()
        p.speed(1)
        p.getscreen().tracer(10,0)
        p.left(90) #初始朝右,左转90度朝上
        p.penup()
        p.forward(-210)
        p.pendown()
        tree([p], 200, 65, 0.635)
    
    
    def main():
        a=clock()
        maketree()
        b=clock()
        return "done: %.2f sec." % (b-a)
    
    if __name__ == "__main__":
        msg = main()
        print(msg)
        mainloop()
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    forest

    文章开始画的树示是例中的forest.

    也使用了广度优先。代码比较长,就不全放了。只看关键的树的生成:

    def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5):
        # benutzt Liste von turtles und Liste von Zweiglisten,
        # fuer jede turtle eine!
        if level > 0:
            lst = []
            brs = []
            for t, branchlist in list(zip(tlist,branchlists)):
                t.pensize( size * widthfactor )
                t.pencolor( 255 - (180 - 11 * level + symRandom(15)),
                            180 - 11 * level + symRandom(15),
                            0 )
                t.pendown()
                randomfd(t, size, level, angledist )
                yield 1
                for angle, sizefactor in branchlist:
                    t.left(angle)
                    lst.append(t.clone())
                    brs.append(randomize(branchlist, angledist, sizedist))
                    t.right(angle)
            for x in tree(lst, size*sizefactor, level-1, widthfactor, brs,
                          angledist, sizedist):
                yield None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    原始的代码比较难懂,因为他用了yield来方便后面的交替画树。(带有yield的函数实际上是一个生成器。后面再说生成器。)

    我们把yield去掉,改成常规的递归调用。这就是一个典型的广度优先遍历。和上面的tree基本一样。

    def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5):
        if level > 0:
            lst = []
            brs = []
            for t, branchlist in list(zip(tlist,branchlists)):
                t.pensize( size * widthfactor )
                t.pencolor( 255 - (180 - 11 * level + symRandom(15)),
                            180 - 11 * level + symRandom(15),
                            0 )
                t.pendown()
                #树枝进行生长
                randomfd(t, size, level, angledist)
                for angle, sizefactor in branchlist:
                    t.left(angle)
                    lst.append(t.clone()) #做出选择,并保存
                    brs.append(randomize(branchlist, angledist, sizedist))
                    t.right(angle)  #back 回撤选择
            #递归处理下一层
            tree(lst, size*sizefactor, level-1, widthfactor, brs,
                          angledist, sizedist)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    微调:

    我们对原始代码的pensize, pencolor中的参数进行调整,就可以改变树的颜色和粗细。

    RGB颜色对照表 (oschina.net)

    用下面方法可以创建一棵树。可以调整参数,创建n棵数。

    pen.hideturtle()
    start(pen, 20, -208)
    #参数: 笔(Turtle对象),线条宽度,树的层数,宽度因子,[[(树枝改变角度,宽度衰减因子)]]
    t = tree( [pen], 80, level, 0.1, [[ (45,0.69), (0,0.65), (-45,0.71) ]] )
    
    • 1
    • 2
    • 3
    • 4

    附录:

    完整代码:
    tree.py

    from turtle import Turtle, mainloop
    from time import perf_counter as clock
    
    def tree(plist, l, a, f):
        """ plist 笔的列表
        l 树枝长度
        a 树枝转弯角度
        f 树枝长度衰减因子
        from level to level."""
        if l > 3:
            lst = []
            for p in plist:
                p.forward(l)
                q = p.clone()
                p.left(a) #左转
                q.right(a) #右转
                lst.append(p)
                lst.append(q)
            tree(lst, l*f, a, f)
    
    
    def maketree():
        p = Turtle()
        p.setundobuffer(None)
        p.hideturtle()
        p.speed(1)
        p.getscreen().tracer(10,0)
        p.left(90) #初始朝右,左转90度朝上
        p.penup()
        p.forward(-210)
        p.pendown()
        tree([p], 200, 65, 0.635)
    
    
    def main():
        a=clock()
        maketree()
        b=clock()
        return "done: %.2f sec." % (b-a)
    
    if __name__ == "__main__":
        msg = main()
        print(msg)
        mainloop()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    forest.py

    #!/usr/bin/env python3
    """     turtlegraphics-example-suite:
    
                 tdemo_forest.py
    
    Displays a 'forest' of 3 breadth-first-trees
    similar to the one in tree.
    For further remarks see tree.py
    
    This example is a 'breadth-first'-rewrite of
    a Logo program written by Erich Neuwirth. See
    http://homepage.univie.ac.at/erich.neuwirth/
    """
    from turtle import Turtle, colormode, tracer, mainloop
    from random import randrange
    from time import perf_counter as clock
    
    def symRandom(n):
        return randrange(-n,n+1)
    
    def randomize( branchlist, angledist, sizedist ):
        return [ (angle+symRandom(angledist),
                  sizefactor*1.01**symRandom(sizedist))
                         for angle, sizefactor in branchlist ]
    
    def randomfd( t, distance, parts, angledist ):
        for i in range(parts):
            t.left(symRandom(angledist))
            t.forward( (1.0 * distance)/parts )
    
    def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5):
        # benutzt Liste von turtles und Liste von Zweiglisten,
        # fuer jede turtle eine!
        if level > 0:
            lst = []
            brs = []
            for t, branchlist in list(zip(tlist,branchlists)):
                t.pensize( size * widthfactor )
                t.pencolor( 255 - (180 - 11 * level + symRandom(15)),
                            180 - 11 * level + symRandom(15),
                            0 )
                t.pendown()
                randomfd(t, size, level, angledist )
                yield 1
                for angle, sizefactor in branchlist:
                    t.left(angle)
                    lst.append(t.clone())
                    brs.append(randomize(branchlist, angledist, sizedist))
                    t.right(angle)
            for x in tree(lst, size*sizefactor, level-1, widthfactor, brs,
                          angledist, sizedist):
                yield None
    
    
    def start(t,x,y):
        colormode(255)
        t.reset()
        t.speed(0)
        t.hideturtle()
        t.left(90)
        t.penup()
        t.setpos(x,y)
        t.pendown()
    
    def doit1(level, pen):
        pen.hideturtle()
        start(pen, 20, -208)
        t = tree( [pen], 80, level, 0.1, [[ (45,0.69), (0,0.65), (-45,0.71) ]] )
        return t
    
    def doit2(level, pen):
        pen.hideturtle()
        start(pen, -135, -130)
        t = tree( [pen], 120, level, 0.1, [[ (45,0.69), (-45,0.71) ]] )
        return t
    
    def doit3(level, pen):
        pen.hideturtle()
        start(pen, 190, -90)
        t = tree( [pen], 100, level, 0.1, [[ (45,0.7), (0,0.72), (-45,0.65) ]] )
        return t
    
    # Hier 3 Baumgeneratoren:
    def main():
        p = Turtle()
        p.ht()
        tracer(75,0)
        u = doit1(6, Turtle(undobuffersize=1))
        s = doit2(7, Turtle(undobuffersize=1))
        t = doit3(5, Turtle(undobuffersize=1))
        a = clock()
        while True:
            done = 0
            for b in u,s,t:
                try:
                    b.__next__()
                except:
                    done += 1
            if done == 3:
                break
    
        tracer(1,10)
        b = clock()
        return "runtime: %.2f sec." % (b-a)
    
    if __name__ == '__main__':
        run_time = main()
        print(run_time)
        mainloop()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    修改后的forest,去掉了yield

    #!/usr/bin/env python3
    """     turtlegraphics-example-suite:
    
                 tdemo_forest.py
    
    Displays a 'forest' of 3 breadth-first-trees
    similar to the one in tree.
    For further remarks see tree.py
    
    This example is a 'breadth-first'-rewrite of
    a Logo program written by Erich Neuwirth. See
    http://homepage.univie.ac.at/erich.neuwirth/
    """
    from turtle import Turtle, colormode, tracer, mainloop
    from random import randrange
    from time import perf_counter as clock
    
    
    def symRandom(n):
        return randrange(-n, n + 1)
    
    
    def randomize(branchlist, angledist, sizedist):
        return [(angle + symRandom(angledist),
                 sizefactor * 1.01 ** symRandom(sizedist))
                for angle, sizefactor in branchlist]
    
    
    def randomfd(t, distance, parts, angledist):
        for i in range(parts):
            t.left(symRandom(angledist))
            t.forward((1.0 * distance) / parts)
    
    
    def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5):
        if level > 0:
            lst = []
            brs = []
            for t, branchlist in list(zip(tlist, branchlists)):
                t.pensize(size * widthfactor)
                t.pencolor(255 - (180 - 11 * level + symRandom(15)),
                           180 - 11 * level + symRandom(15),
                           0)
                t.pendown()
                # 树枝进行生长
                randomfd(t, size, level, angledist)
                for angle, sizefactor in branchlist:
                    t.left(angle)
                    lst.append(t.clone())  # 做出选择,并保存
                    brs.append(randomize(branchlist, angledist, sizedist))
                    t.right(angle)  # back 回撤选择
            # 递归处理下一层
            tree(lst, size * sizefactor, level - 1, widthfactor, brs,
                 angledist, sizedist)
    
    
    def start(t, x, y):
        colormode(255)
        t.reset()
        t.speed(0)
        t.hideturtle()
        t.left(90)
        t.penup()
        t.setpos(x, y)
        t.pendown()
    
    
    def doit1(level, pen):
        pen.hideturtle()
        start(pen, 20, -208)
        t = tree([pen], 80, level, 0.1, [[(45, 0.69), (0, 0.65), (-45, 0.71)]])
        return t
    
    
    def doit2(level, pen):
        pen.hideturtle()
        start(pen, -135, -130)
        t = tree([pen], 120, level, 0.1, [[(45, 0.69), (-45, 0.71)]])
        return t
    
    
    def doit3(level, pen):
        pen.hideturtle()
        start(pen, 190, -90)
        t = tree([pen], 100, level, 0.1, [[(45, 0.7), (0, 0.72), (-45, 0.65)]])
        return t
    
    
    # Hier 3 Baumgeneratoren:
    def main():
        a = clock()
        p = Turtle()
        p.ht()
        tracer(75, 0)
        u = doit1(6, Turtle(undobuffersize=1))
        s = doit2(7, Turtle(undobuffersize=1))
        t = doit3(5, Turtle(undobuffersize=1))
        b = clock()
        return "runtime: %.2f sec." % (b - a)
    
    
    if __name__ == '__main__':
        run_time = main()
        print(run_time)
        mainloop()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
  • 相关阅读:
    再次认识常量与局部量
    2023天津工业大学计算机考研信息汇总
    乳制品行业如何通过APS智能排产进行生产管理的优化?
    TSINGSEE青犀老旧小区升级改造AI+视频监控方案
    双 11,盘点一下 AI 边缘计算平台
    SwiftUI 为不同视图限制不同的屏幕旋转方向
    非最小相位系统;频率特性的对称性
    jmeter文件下载接口处理
    Pandas数据类型自行变换及数据类型转换失败情况分析与解决方法
    郑州分销系统开发|如何实现快速分销裂变?
  • 原文地址:https://blog.csdn.net/qq_41068877/article/details/124880135