Вот пример осенне-фрактального древа:

Далее ещё пара картинок и, собственно, сами скрипты.
Кривая Пеано-Госпера:

кривая Гильберта:

Прежде всего для работы с png необходим PNGCanvas.
Данная программа разбита на два модуля - LSystem.py и Turtle.py.
Первый отвечает за генерацию инструкций по L-системе для второго модуля, который, как видно по названию, является черепахой и просто рисует на основе инструкций.
Работа скрипта занимает вдвое больше времени чем надо только для отрисовки. Это связанно с тем что черепаха (предсказывает будущее) проходит по интсрукциям дважды, сперва вычисляя размеры холста, а затем уже рисует.
L-системы задаются в data-файлах
Пример файла для кривой Гильберта
---------------------------
L
4
6
2
L=+ R F - L F L - F R +
R=- L F + R F R + F L -
---------------------------
L #это аксиома, необходимо разделять слова пробелами.
4 #шаг изменения угла операциями + и - равен 360/4
6 #длинна штриха оставляемого черепахой по команде F
2 #начальный угол черепахи =360/2 (пожалуйста, не ставьте там ноль =) )
L=+ R F - L F L - F R + #правило, необходимо разделять слова пробелами, и писать слитно со знаком равно
Скачать все скрипты и PNGCanvas в одном архиве.
LSystem.py
- #!/usr/bin/python
- # -*- encoding: utf-8 -*-
- """L-System parser"""
- __version__="0.3"
- __author__ = "Anton Valter (avalter.blogspot.com)"
- __copyright__ = "CC Attribution Anton Valter"
-
-
-
- class LSystem(object):
- def __init__(self,funcs,deep=3):
- self.x=0
- self.y=0
- self.leftB=0
- self.rightB=0
- self.topB=0
- self.bottomB=0
- self.rules = dict()
- self.deep = deep
- self.funcs=funcs
- print self.funcs
-
- def setAxiom(self,axiom):
- self.axiom = axiom.split()
-
- def registerRule(self,name,rule):
- self.rules[name]=rule.split()
-
- def setDeep(self,deep):
- self.deep = deep
-
- def interSteps(self,string,deep):
- for c in string:
- if c in self.rules:
- if deep<self.deep:
- for ch in self.interSteps(self.rules[c],deep+1):
- yield ch
- elif c in self.funcs:
- yield c
- else:
- yield c
-
- def steps(self):
- return self.interSteps(self.axiom,1)
-
- # vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab:
* This source code was highlighted with Source Code Highlighter.Turtle.py:
#!/usr/bin/python
# -*- encoding: utf-8 -*-
"""2D Turtle for L-Systems"""
__version__ = "0.8"
__author__ = "Anton Valter (avalter.blogspot.com)"
__copyright__ = "CC Attribution Anton Valter"
from PNGCanvas import PNGCanvas
import math
import re
from LSystem import LSystem
maxw = 4000 #максимальные размеры изображения
maxh = 4000 #при привышении - прерывание
class Turtle(object):
def __init__(self):
self.x=float(0)
self.y=float(0)
self.leftB=0
self.rightB=0
self.topB=0
self.bottomB=0
self.stack = list()
self.p=0
self.alpha=0
self.step=50
self.backcolor=[0,0,0,0]
self.basedrawcolor=(0,0,0,0xff)
self.drawcolor=self.basedrawcolor
self.stepcount=0
self.baseAlpha=0
self.colorstep=50
self.basestep=10
def setBorders(self,left,right,top,bottom):
self.leftB=left
self.rightB=right
self.topB=top
self.bottomB=bottom
def prepareCanvas(self,width,height):
if width<2:
width=5
if height<2:
height=5
self.c = PNGCanvas(width,height)
self.c.color = self.backcolor
# self.c.filledRectangle(0,0,width-1,height-1) // раскоменть если надо фон не прозрачный
if width<2:
width=5
if height<2:
height=5
self.c.blendRect(1,1,width-1,height-1,width,0,self.c)
self.c.color = self.drawcolor
def produceSteps(self,drawFunction,t):
self.alpha=self.baseAlpha
self.drawcolor = self.basedrawcolor
self.step = self.basestep
for action in t.steps():
if action=='+':
self.alpha=self.alpha+self.p
elif action=='-':
self.alpha=self.alpha-self.p
elif action=='[' or action=='{':
self.stack.append((self.x,self.y,self.alpha,self.drawcolor,self.step))
elif action==']' or action=='}':
pos = self.stack.pop()
self.x=pos[0]
self.y=pos[1]
self.alpha=pos[2]
if action=='}':
self.drawcolor = pos[3]
self.step = pos[4]
elif action=='F':
newPosx=(self.x+math.cos(self.alpha)*self.step)
newPosy=(self.y+math.sin(self.alpha)*self.step)
drawFunction(newPosx,newPosy)
self.x=newPosx
self.y=newPosy
elif action=='f':
newPosx=(self.x+math.cos(self.alpha)*self.step)
newPosy=(self.y+math.sin(self.alpha)*self.step)
self.calcSize(newPosx,newPosy)
self.x=newPosx
self.y=newPosy
elif re.match('\(([-+])?,([-+])?,([-+])?,([-+])?\)',action):
m = re.match('\(([-+])?,([-+])?,([-+])?,([-+])?\)',action)
r,g,b,a = m.group(1),m.group(2),m.group(3),m.group(4)
rv,gv,bv,av=self.drawcolor
def modifyColor(color,step,value):
if color=='+':
return min(value+step,0xff)
if color=='-':
return max(value-step,0x00)
return value
rv = modifyColor(r,self.colorstep,rv)
gv = modifyColor(g,self.colorstep,gv)
bv = modifyColor(b,self.colorstep,bv)
av = modifyColor(a,self.colorstep,av)
self.drawcolor=(rv,gv,bv,av)
elif re.match('\((\d+)?,(\d+)?,(\d+)?,(\d+)?\)',action):
m = re.match('\((\d+)?,(\d+)?,(\d+)?,(\d+)?\)',action)
r,g,b,a = m.group(1),m.group(2),m.group(3),m.group(4)
rv,gv,bv,av=self.drawcolor
def setColor(color,value):
if color:
return min(max(int(color),0x00),0xff)
return value
rv = setColor(r,rv)
gv = setColor(g,gv)
bv = setColor(b,bv)
av = setColor(a,av)
self.drawcolor = rv,gv,bv,av
elif re.match('\@(I)?(Q)?(\d+(?:\.\d+)?)',action):
m = re.match('\@(I)?(Q)?(\d+(?:\.\d+)?)',action)
i,q,n=m.group(1),m.group(2),m.group(3)
num = float(n)
if q:
num = math.sqrt(num)
if i:
if num > 0.00001:
num = 1.0/num
self.step=self.step*num
def draw(self,newposx,newposy):
self.c.color=self.drawcolor
self.stepProduced+=1
strStep = str(self.stepProduced)+'/'+str(self.stepcount-1)
print strStep,self.c.color,self.step
self.c.line(self.x,self.y,newposx,newposy)
def calcSize(self,newposx,newposy):
self.stepcount+=1
if newposx > self.rightB:
self.rightB=newposx
if newposx < self.leftB:
self.leftB=newposx
if newposy > self.topB:
self.topB=newposy
if newposy < self.bottomB:
self.bottomB=newposy
def start(self,pngFileName,rulesFileName,deep):
rules = open(rulesFileName,'r')
t = LSystem(['F','-','+','f','[',']','(',')','{','}'],deep)
t.setAxiom(rules.readline())
self.p = 2*math.pi/float(rules.readline())
self.basestep = float(rules.readline())
self.baseAlpha = 2*math.pi/float(rules.readline())
while 1:
str = rules.readline()
print str
if not str: break
rule=str.split('=')
t.registerRule(rule[0],rule[1])
rules.close()
self.strlen=0
self.calcSize(self.x,self.y)
self.stepProduced=0
self.produceSteps(self.calcSize,t)
width=self.rightB-self.leftB
height=self.topB-self.bottomB
if height > maxh or width > maxw:
return
self.x=abs(self.leftB)
self.y=abs(self.bottomB)
self.prepareCanvas(int(width+1),int(height+1))
self.produceSteps(self.draw,t)
file = open(pngFileName,'wb')
file.write(self.c.dump())
file.close()
if __name__=='__main__':
png = raw_input('outputfile:')
# png = 'test.png'
data = raw_input('datafile:')
deep = int(raw_input('deep:'))
t = Turtle()
t.start(png,data,deep)
# vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab:
* This source code was highlighted with Source Code Highlighter.
Ждём 3D L-систем.
ОтветитьУдалитьМатериал интересный, а кода как-то многовато для такой простенькой задачки. У меня функция рисования при помощи PIL занимает 50 строчек с отступами + пару десятков строк для обвязок и рисования некоторых графов.
ОтветитьУдалить