Python海龟绘图——分形、递归与递归算法(下)

    上篇(Python海龟绘图——分形、递归与递归算法(上))介绍了用分形理论和递归函数,用Python的turtle绘制分形树,尽管加了树干、树枝粗细变化,树仍因为分形的自相似性而完全对称,比较呆板。需要给树干、树枝长度加上随机数扰动,树枝夹角也加上随机数扰动,那就更像自然的树。

    在讲树干、树枝长度、树枝夹角加随机数扰动前先来画一棵雪松树(圣诞树)。

四、Python绘制向下分叉的分形树——雪松树(圣诞树)

    如果二叉树的分叉是120°树枝就向下垂,就像雪松树。最后树干用顶角为2°的等腰三角形绘制。

    设三角形的腰长为l,则底边长为d=2lsin(1°)≈2πl/180

    Python用turtle绘制雪松树(圣诞树)程序代码如下:

#############################################
# 设计 Zhang Ruilin   创建 2021-12-05 12:55 #
# 用分形理论+递归,turtle绘制雪松树(圣诞树) #
#############################################
import turtle as tl
import random

n=100.0

def tree(d, s): 				# 递归画树、树枝
    if d <=0: return 
    tl.fd(s) 					# 画树干
    tree(d-1, s*.8) 				# 在树干上画树枝
    tl.rt(120)
    tree(d-3,s*.5) 			 	# 向右下方在树干上画树枝
    tl.rt(120) 
    tree(d-3,s*.5) 			 	# 向左下方在树干上画树枝
    tl.rt(120) 
    tl.bk(s) 			 		# 返回上层分叉点
    
tl.speed('fastest')
tl.screensize(bg='lightyellow')
tl.ht()						# 隐藏海龟形状,可加快绘图速度
tl.lt(90)					# 方向向上
tl.fd(2.5 * n)
tl.color('orange', 'yellow')			# 画笔橘红色、填充黄颜色
tl.begin_fill()
tl.lt(126)
for i in range(5):				# 画橘红色边黄颜色五角星
    tl.fd(n/5) 
    tl.rt(144) 
    tl.fd(n/5) 
    tl.lt(72) 
tl.end_fill()
tl.rt(126)					# 方向向上
tl.color('dark green') 
tl.bk(n * 4.8) 

tree(15,n)					# 画树
tl.bk(n/2)
tl.begin_fill()					# 画树干(顶角2°的等腰三角形)
tl.fd(n * 5.3)
tl.lt(179)
tl.fd(n * 5.3)
tl.lt(91)
tl.fd(2 * n * 5.3 * 3.141592654 / 180)		# 角度较小时的sin近似算法
tl.lt(91)
tl.fd(n * 5.3)
tl.end_fill()
tl.up()
tl.lt(180)
tl.fd(n * 5.3)
tl.rt(91)
tl.fd(4 * n * 5.3 * 3.141592654 / 180)
tl.rt(91)
tl.fd(n * 5.3)
tl.rt(179)
tl.fd(n * 5.3)

a = 200
b = 40
for i in range(200):				# 画小礼物
    x = a - 2 * a * random.random() 
    y = b - 2 * b * random.random()
    if x*x/a/a+y*y/b/b <= 1:			# 2a、2b的sbe椭圆范围内
        tl.up()
        tl.fd(y) 
        tl.lt(90) 
        tl.fd(x) 
        tl.pd()
        if random.randint(0, 1) == 0:
            tl.color( 'violet' )
        else:
            tl.color( 'orange' )
        tl.dot(8)
        tl.up()
        tl.bk(x) 
        tl.rt(90) 
        tl.bk(y) 

tl.done()

    运行结果如图1所示。

 图1 雪松树(圣诞树)

五、Python绘制已发芽并开着桃花的桃树

    本篇在上篇的基础上,对树枝长度、分叉角增加了随机数扰动,为了避免树枝过长,用树枝长度和树枝粗细共同控制递归深度(结束递归的条件)。

    在Python中可以用random模块的random()函数产生[0, 1)的随机数,包含0但不含1。长度每次递归长度缩短0~30%(length=length*(1-0.3*random())),分叉角在0~120%(ang= ang *1.2*random())间变化,分叉角为0即不分叉。randint(a, b)函数产生[a, b]的随机整数,包含a也包含b。

    桃树是先开花后长叶,在开花时叶子还只是1、2厘米左右的叶芽。

    Python用turtle绘制已发芽并开着桃花的桃树程序代码如下:

##############################################
# 设计 Zhang Ruilin    创建 2021-12-04 20:45 #
# 用分形理论+递归,turtle 绘制开着桃花的桃树 #
##############################################
import turtle as tl
import random

tl.setup(600, 600)					# 定义窗体大小
tl.speed(0)
tl.ht()							# 隐藏画笔形状

def draw_branches(tree_length, tree_size, tree_angle):	# 画桃树的躯干枝叶和花
    if tree_length > 3 and tree_size > 1:
        if 8 <= tree_length <= 12:
            k = random.randint(0, 4)
            if k == 0:					# 20%画浅色桃花
                tl.color('mistyrose')			# 薄雾玫瑰色
            elif k == 1 or  k == 2:			# 40%画树叶
                tl.color('green')			# 绿色
            else:					# 40%画深色桃花
                tl.color('pink')			# 粉色(桃红)
            tl.pensize(tree_length / 3)
        elif tree_length < 8:
            if random.randint(0, 1) == 0:
                tl.color('mistyrose')			# 薄雾玫瑰色
            else:
                tl.color('pink')			# 粉色(桃红)
            tl.pensize(tree_length / 2)
        else:
            tl.color('brown')				# 综色
            tl.pensize(tree_size)
        if tl.color()[0] == 'green':			# 画叶子
            if tree_length >=5:
                for _ in range(int(tree_length+0.5)):
                    tl.pensize(tree_length/2 -_*(tree_length/2-1)/tree_length)
                    tl.fd(1)
            else:
                tl.pensize(3)
                tl.fd(int(tree_length/2))
                tl.pensize(1)
                tl.fd(tree_length-int(tree_length/2))
        else:						# 画树枝或花朵
            tl.fd(tree_length)
        a = 1.2 * random.random()
        b = random.random()
        tl.rt(tree_angle * a)
        draw_branches(tree_length * (1-0.3*b), tree_size * 0.85, tree_angle)
        tl.lt(2 * tree_angle * a)
        draw_branches(tree_length * (1-0.3*b), tree_size * 0.85, tree_angle)
        tl.rt(tree_angle * a)
        tl.up()
        tl.bk(tree_length)
        tl.pd()

if __name__ == '__main__':
    tl.getscreen().tracer(5, 0)				# 设置海龟动画更新及延迟,加快绘图
    tl.screensize(bg='honeydew')			# 蜜瓜色(白淡绿色)
    tl.lt(90)						# 方向向上(12点方向)
    tl.up()
    tl.bk(200)						# 移动到树干起始位置
    tree_length = 70					# 设置的树干树枝初始长度
    tree_size = 6					# 设置的树干树枝初始粗细
    tree_angle = 25					# 设置基准树枝分叉角度
    tl.pd()
    tl.color('brown')					# 树干部分为棕色
    draw_branches(tree_length, tree_size, tree_angle)	# 启动绘图
    tl.exitonclick()					# 单击鼠标左键退出
    tl.done()						# 结束时保持图形窗体

    运行结果如图2所示。

 图2 已发芽并开着桃花的桃树

    在程序中为了避免树枝过长,用树枝长度和树枝粗细共同控制递归深度:

    tree_length> 3 and tree_size > 1

    因此有一部分树枝就没有达到绘制树叶或桃花的条件,故没有绘制树叶或桃花,给人以桃花凋谢的感觉。

    另外用randint(0, 4)产生[0, 4]五个整数的随机数,每个数出现的概率是20%,当枝条不是太细时20%画浅色桃花、40%画叶芽和40%画深色桃花,当枝条比较细时用各50%的概率画浅色桃花和深色桃花。由于叶芽很小,就用从粗到细的直线(长三角形)模拟。

    那凋谢的花呢? 应该是落地上了。

六、Python绘制有掉落花瓣并开着桃花的桃树

    桃树树冠从顶上看大致是个圆形,落花也基本上落在一个圆形区域,侧面看大致是个椭圆形区域。椭圆方程为x2/a2+y2/b2=1,椭圆内的条件是x2/a2+y2/b2<=1。所以x[-aa]y[-bb]矩形区域内满足:x2/a2+y2/b2<=1,即在椭圆范围之内。

    由于绘制落下的花瓣会绘制到树干上(可参考图1),所以需要先画掉落的花瓣,再画桃树,这样可保证在树干上没有落下的花瓣。掉落的花瓣只在椭圆形范围内绘制。

    Python用turtle绘制有掉落花瓣并开着桃花的桃树程序代码如下:

#################################################
# 设计 Zhang Ruilin       创建 2021-12-05 07:35 #
# 用分形+递归,turtle绘制洒落花瓣开着桃花的桃树 #
#################################################
import turtle as tl
import random

tl.setup(600, 600)					# 定义窗体大小
tl.speed(0)
tl.ht()							# 隐藏画笔形状

def draw_branches(tree_length, tree_size, tree_angle):	# 画桃树的躯干枝叶和花
    if tree_length > 3 and tree_size > 1:
        if 8 <= tree_length <= 12:
            k = random.randint(0, 4)
            if k == 0:					# 20%画浅色桃花
                tl.color('mistyrose')			# 薄雾玫瑰色
            elif k == 1 or  k == 2:			# 40%画树叶
                tl.color('green')			# 绿色
            else:					# 40%画深色桃花
                tl.color('pink')			# 粉色(桃红)
            tl.pensize(tree_length / 3)
        elif tree_length < 8:
            if random.randint(0, 1) == 0:
                tl.color('mistyrose')			# 薄雾玫瑰色
            else:
                tl.color('pink')			# 粉色(桃红)
            tl.pensize(tree_length / 2)
        else:
            tl.color('brown')				# 综色
            tl.pensize(tree_size)
        if tl.color()[0] == 'green':			# 画叶子
            if tree_length >=5:
                for _ in range(int(tree_length+0.5)):
                    tl.pensize(tree_length/2 -_*(tree_length/2-1)/tree_length)
                    tl.fd(1)
            else:
                tl.pensize(3)
                tl.fd(int(tree_length/2))
                tl.pensize(1)
                tl.fd(tree_length-int(tree_length/2))
        else:						# 画树枝或花朵
            tl.fd(tree_length)
        a = 1.2 * random.random()
        b = random.random()
        tl.rt(tree_angle * a)
        draw_branches(tree_length * (1-0.3*b), tree_size * 0.85, tree_angle)
        tl.lt(2 * tree_angle * a)
        draw_branches(tree_length * (1-0.3*b), tree_size * 0.85, tree_angle)
        tl.rt(tree_angle * a)
        tl.up()
        tl.bk(tree_length)
        tl.pd()

def petals(m):						# 画掉落的花瓣
    a0 = 225
    b0 = 20
    for i in range(m):
        x = a0 - 2 * a0 * random.random()
        y = b0 - 2 * b0 * random.random()
        if x * x /a0 / a0 + y * y / b0 / b0 <= 1:	# 只画在2a、2b的椭圆中
            tl.up()
            tl.fd(y)
            tl.lt(90)
            tl.fd(x)
            tl.pd()
            tl.color('pink')				# 粉色(桃红)
            tl.dot(3)
            tl.up()
            tl.bk(x)
            tl.rt(90)
            tl.bk(y)

if __name__ == '__main__':
    tl.getscreen().tracer(5, 0)				# 设置海龟动画更新及延迟,加快绘图
    tl.screensize(bg='honeydew')			# 蜜瓜色(白淡绿色)
    tl.lt(90)						# 方向向上(12点方向)
    tl.up()
    tl.bk(200)						# 移动到树干起始位置
    tl.pd()
    petals(400)						# 以树干起始为中心画落花
    tree_length = 70					# 设置的树干树枝初始长度
    tree_size = 6					# 设置的树干树枝初始粗细
    tree_angle = 25					# 设置基准树枝分叉角度
    tl.pd()
    tl.color('brown')					# 树干部分为棕色
    draw_branches(tree_length, tree_size, tree_angle)	# 启动绘图
    tl.exitonclick()					# 单击鼠标左键退出
    tl.done()						# 结束时保持图形窗体

    运行结果如图3所示。

 图3 有掉落花瓣并开着桃花的桃树

    由于用了随机数,所以每次运行绘制的桃树都会有差异,请看图4是另一幅不同时间绘制的开着桃花的桃树。有的可能画得不满意,可选择满意的保存。

 图4 不同时间运行生成的有掉落花瓣并开着桃花的桃树

猜你喜欢

转载自blog.csdn.net/hz_zhangrl/article/details/131017244