初次接触分形是由于动漫《分形世界》,当时就觉得好高大上,然后就想知道这到底是是怎么回事。于是就在网上找了一些资料,简单地研究了一下。
递归画法
最简单的分形图形应该是Koch曲线了吧,曲线的绘制我是参考大佬的博客的
https://blog.csdn.net/ryan_jianjian/article/details/53678062
我们来看一下绘制的过程
这是用递归实现的,下面来看代码。
import turtle
import math
#初始化画布
def init():
turtle.screensize(800, 600, "white")
turtle.pensize(2)
turtle.pencolor("orange")
turtle.speed(1)
#画连接两点的直线
def draw(x1, y1, x2, y2):
turtle.penup() #提起笔
turtle.setposition(x1, y1)
turtle.pendown() #放下笔
turtle.goto(x2, y2)
#画koch曲线程序,递归方法
def koch(x0, y0, x1, y1, k):
#算法这里就不赘述了
x2 = x0*2.0/3 + x1*1.0/3
y2 = y0*2.0/3 + y1*1.0/3
x3 = x0*1.0/3 + x1*2.0/3
y3 = y0*1.0/3 + y1*2.0/3
x4 = x2 + ((x1-x0) - (y1-y0)*math.sqrt(3))/6
y4 = y2 + ((x1-x0)*math.sqrt(3) + (y1-y0))/6
if(k > 1): #递归
koch(x0, y0, x2, y2, k-1)
koch(x2, y2, x4, y4, k-1)
koch(x4, y4, x3, y3, k-1)
koch(x3, y3, x1, y1, k-1)
else:
draw(x0, y0, x2, y2)
draw(x2, y2, x4, y4)
draw(x4, y4, x3, y3)
draw(x3, y3, x1, y1)
#主函数
if __name__ == "__main__":
init()
koch(-400, 0 , 400, 0, 5)
turtle.done()
这里关于点的计算是难点。首先我们选定了一条直线的首位两个点作为原点,他们是(x0, y0)和(x1, y1)然后在第一次迭代之后中间的三分之一就会由(x2, y2)和(x3, y3)组成,等边三角形的另一个定点就是(x4, y4)。其中(x2, y2)和(x3,y3)的计算并不困难,拿(x2, y2)距离。
x2 = 1/3*(x1-x0) + x0 = 2/3*x0 + 1/3*x1
y2 = 1/3*(y1-y0) + y0 = 2/3*y0 + 1/3*y1
(x4, y4)可以看成中间的线段旋转60°得到的点,所以可以使用旋转坐标公式
x0= (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0
y0= (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0
其中(rx0,ry0)是旋转中心,(x,y)待旋转点,(x0,y0)旋转后的新坐标。
计算得
x5 = x3 + ((x2 - x1) - (y2 - y1) * Math.sqrt(3)) / 6;
y5 = y3 + ((x2 - x1) * Math.sqrt(3) + (y2 - y1)) / 6;
L系统
这部分我是从灯神的视频里学的
https://www.bilibili.com/video/av28637396?t=1133
L系统有三个要素:变量集合,原始公式和递推式
每次迭代,所有原始公式中可以递推的元素都要进行替换。
扫描二维码关注公众号,回复:
9828014 查看本文章
我们先来看一个例子
import turtle
length = 20
angle = 90
path = "F-F-F-F" #最原始的规则
#画图函数
def draw_path(path):
for symbol in path:
if symbol == "F":
turtle.forward(length)
elif symbol == "-":
turtle.left(angle)
elif symbol == "+":
turtle.right(angle)
#将路径迭代
def apply_rule(path):
rule = "F-F+F+FF-F+F" #这个规则可以随意改,画出来的图形都很好看
path = path.replace("F", rule) #将所有F换成rule
return path
turtle.speed(5)
turtle.pensize(2)
turtle.pencolor("orange")
for i in range(2):
path = apply_rule(path)
draw_path(path)
turtle.done()
这个例子中,变量集合有{F, + , -};初始公式为“F-F-F-F”;递推式为“F->F-F+F+FF-F+F”
我们再来看一个稍微复杂一点的例子
在这个例子中,递推式变成了两个
变量集合{Fl, Fr, + ,-};初始公式为Fl;
递推式为“Fl->Fl+Fr++Fr-Fl--FlFl-Fr+” ; "Fr->-Fl+FrFr++Fr+Fl--Fl-Fr"
import turtle
length = 10
angle = 60
rules = {
"Fl" : "Fl+Fr++Fr-Fl--FlFl-Fr+",
"Fr" : "-Fl+FrFr++Fr+Fl--Fl-Fr"
}
path = "Fl"
def split_path(path):
i = 0
lst = []
while i < len(path):
if path[i] == "F":
lst.append(path[i:i+2]) #组合成一个元素
i = i+2
else:
lst.append(path[i])
i = i+1
return lst
def apply_rule(path, rules):
lst = split_path(path)
for i in range(len(lst)):
symbol = lst[i]
if symbol in rules:
lst[i] = rules[symbol]
path = "".join(symbol for symbol in lst) #将lst所有元素连接成字符串
return path
def draw_path(path):
lst = split_path(path)
for symbol in lst:
if symbol == "Fl" or symbol == "Fr":
turtle.forward(length)
elif symbol == "-":
turtle.left(angle)
elif symbol == "+":
turtle.right(angle)
turtle.speed(0)
turtle.pensize(2)
turtle.pencolor("orange")
turtle.penup()
turtle.setposition(100, 200)
turtle.pendown()
for i in range(4):
path = apply_rule(path, rules)
draw_path(path)
turtle.done()