簡單的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。