简单的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。