画一棵树
从一个树枝开始,分叉向两端(或者更多端),然后继续从新的树枝进行分叉,…
while True:
树枝 = 树枝.分叉
如果不限制,树可以一直这么长下去…
最后会长成这样:(限制了分叉层数)
对颜色等参数进行一些改变,就可以得到不同的树:
好了,我们已经知道树的生长原理了,现在让我们把这个过程画出来。
turtle 海龟绘图
turtle是Python内置的一个画图库,使用tkinter实现基本图形界面。
它的方法就是用一只海龟作为画笔在屏幕绘图。
更多方法参考标准库中turtle一节。
这里用一个画正方形的例子演示turtle用法:
import turtle
p = turtle.Turtle()
p.speed(1)
for i in range(4):
p.forward(300)
p.right(90)
turtle.mainloop()
(注:画笔初始方向朝右)
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)
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()
forest
文章开始画的树示是例中的forest .
也使用了广度优先。代码比较长,就不全放了。只看关键的树的生成:
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 )
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
原始的代码比较难懂,因为他用了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)
tree(lst, size*sizefactor, level-1, widthfactor, brs,
angledist, sizedist)
微调:
我们对原始代码的pensize, pencolor中的参数进行调整,就可以改变树的颜色和粗细。
RGB颜色对照表 (oschina.net)
用下面方法可以创建一棵树。可以调整参数,创建n棵数。
pen.hideturtle()
start(pen, 20, -208)
t = tree( [pen], 80, level, 0.1, [[ (45,0.69), (0,0.65), (-45,0.71) ]] )
附录:
完整代码: 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)
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()
forest.py
""" 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 )
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
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()
修改后的forest,去掉了yield
""" 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)
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
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()
|