简单的D0L-系统
描述
考虑由a、b组成的字符串,每一个字母代表一个改写规则,例如,规定a->ab,b->a,分别表示a可被改写为ab,b被改写为a。改写过程从一个被称为 公理 的字符串开始。
例如:
公理omega:b
改写规则:a->ab; b->a
第1次改写:a
第2次改写:ab
第3次改写:aba
第4次改写:abaab
……
这就是简单的D0L-系统
字符串的海龟解释
假设有一只海龟在行动,我们假定海龟遇到字符 f
则向前走一个步长,遇到字符 +
就向左转弯一定角度,遇到字符 -
就向右转弯一定角度。具体走动情况就不显示了,我们直接进入主题就行。
Python自带海龟(turtle)模块,但是效率不够,因此我们改写pygame模块以适应我们的需求
""" 简单D0L系统 """ from math import sin, cos, pi import pygame def left(screen, st, angle, d): # st: 起点座标 # angle: 向左偏转的度数 # d: 距离 angle = pi * angle / 180 return [st[0] + d * cos(angle), st[1] - d * sin(angle)] class Pen: def __init__(self, size, title=""): # size 画布的宽高 [width, hight] # title 画布标题 pygame.init() self.screen = pygame.display.set_mode(size) pygame.display.set_caption(title) self.screen.fill([255, 255, 255]) self.setPoint([size[0]/2, size[1]/2]) pygame.display.flip() self.pos = [0, 0] # 当前头的位置 self.angle = 0 # 当前角度 self.color = [0, 0, 0] # 画笔颜色 self.width = 2 def setPoint(self, pos): # 设置笔的位置 self.pos = pos def setWidth(self, width): # 设置线宽 self.width = width def setColor(self, color): # 设置颜色 self.color = color def left(self, angle): # 向左转angle度 self.angle = self.angle + angle def right(self, angle): # 向右转angle度 self.angle = self.angle - angle def forward(self, d): # 向前走d步长 np = left(self.screen, self.pos, self.angle, d) pygame.draw.line(self.screen, self.color, self.pos, np, self.width) pygame.display.flip() self.pos = np def doD0L(self, omega, P, delta, times, length, rate, delta0 = 0): # omega: 公理(初始字符串) # P: 产生式(映射规则) # delta0: 初始偏移量 # delta: 角度增量 # times: 迭代次数 # length: 初始线长 # rate: 每次迭代后缩小的倍数 length /= (rate**times) for i in range(times): for key in P: omega = omega.replace(key, P[key]) self.left(delta0) for j in omega: if j == '+': self.left(delta) elif j == '-': self.right(delta) else: self.forward(length) def save(self, title): # 保存图片,title为文件名 pygame.image.save(self.screen, title) def wait(self): # 绘制结束等用户关闭程序 while 1: for event in pygame.event.get(): if event.type == pygame.QUIT: exit()
由于我已经将其放到pypi上,因此,如果安装的话只需要执行:
pip install fractal
就可以了。
1.Koch曲线
**omega: ** f
P: f–>f+f–f+f
说明: 初始角度 0, 角度增量 60 度, 步长d(本次取步长为490px)在相邻两次迭代间缩短了3倍,这里我们显示迭代4次后的图像
代码:
# 科赫曲线 from fractal import Pen p = Pen([500, 300], title="Koch") p.setPoint([5, 190]) p.doD0L(omega="f", P={"f": "f+f--f+f"}, delta=60, times=4, length=490, rate=3) p.wait()
结果:
2.Koch 雪花
omega: f–f–f
P: f–>f+f–f+f
说明: 初始角度:0、角度增量:60°、每一次迭代步长缩小3倍
代码:
# 科赫雪花 from fractal import Pen p = Pen([500,500],title = "Koch Snow") p.setPoint([30,130]) p.doD0L(omega = "f--f--f", P = {"f": "f+f--f+f"}, delta = 60, times = 5, length = 400, rate = 3) p.wait()
结果:
3.Sierpinski 三角
omega: f–f–f
P: g–>gg; f–>f–f–f–gg
说明: 初始角度0,角度增量60°,每次迭代三角形边长就缩短两倍,g的含义与f一样
代码:
# 谢尔宾斯基三角 from fractal import Pen p = Pen([500, 460], title="Sierpinski") p.setPoint([5, 5]) p.doD0L(omega="f--f--f", P={"g": "gg", "f": "f--f--f--gg"}, delta=60, times=5, length=490, rate=2) p.wait()
结果:
4.二次 Koch 岛
**omega: ** f-f-f-f
**P: ** f–>f+f-f-ff+f+f-f
说明: 初始角度0,角度增量90°,两次迭代间缩小4倍
代码:
# 二次Koch岛 from fractal import Pen p = Pen([500, 500], title="Koch island") p.setPoint([100, 100]) p.doD0L(omega="f-f-f-f", P={"f": "f+f-f-ff+f+f-f"}, delta=90, times=4, length=300, rate=4) p.wait()
结果:
5.窗花
omega: f+f+f+f
P: f–>ff+f–f+f
说明: 初始角度90°,角度增量90°,缩小率3
代码:
# 窗花 from fractal import Pen p = Pen([500, 500], title="Window") p.setPoint([495, 495]) p.doD0L(omega="f+f+f+f", P={"f": "ff+f--f+f"}, delta=90, times=5, length=490, rate=3, delta0 = 90) p.wait()
结果:
当然了,还有很多有趣的图案,按简单D0L-系统都是可以生成的,目前 fractal
模块还很不成熟,日后会慢慢改进,有兴趣的小伙伴欢迎加入一起做,项目地址:fractal。