简单的D0L-系统生成分形

简单的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()

结果:

《简单的D0L-系统生成分形》

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()

结果:

《简单的D0L-系统生成分形》

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()

结果:

《简单的D0L-系统生成分形》

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()

结果:

《简单的D0L-系统生成分形》

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-系统生成分形》

当然了,还有很多有趣的图案,按简单D0L-系统都是可以生成的,目前 fractal 模块还很不成熟,日后会慢慢改进,有兴趣的小伙伴欢迎加入一起做,项目地址:fractal

点赞