########################################################################### ## A simple LSystem to POV-Ray converter ## ## just supply an L-System Grammar ## and this script writes out an include file for POV-Ray ## 2D : object in x/z-plane, scaled to fit into [-0.5,0.5]^2 ## 3D : x/z/y coordinates ## LSys-turtle starts in x-direction, right hand coordinate system ## Grammar following Prusinkiewicz ## plus: you may supply an entry 'Plots' : 'FG..' (whatever you need) ## if more than F should draw a line. ## ## Tkinter visualisation: x goes up ## see sample run at end of program ## ## 03/2006 Wolfgang.Urban@schule.at ########################################################################### ## Koch Schneeflocke GRAMMAR1 = { 'Name' : "Snowflake", 'Axiom' : 'F++F++F++', 'Winkel' : 60, 'F' : 'F+F--F+F', } ## 2D Hilbertkurve GRAMMAR2 = { 'Name' : "Hilbert 2D var.2", 'Axiom' : 'X', 'Winkel' : 90, 'X' : '-YF+XFX+FY-', 'Y' : '+XF-YFY-FX+' } ## 2D Hilbertkurve GRAMMAR2a = { 'Name' : "Hilbert 2D var.1", 'Axiom' : '-L', 'Winkel' : 90, 'L' : 'LFLF+RFR+FLFL-FRF-LFL-FR+F+RF-LFL-FRFRFR+', 'R' : '-LFLFLF+RFR+FL-F-LF+RFR+FLF+RFRF-LFL-FRFR' } ## Hexagonal Gosper GRAMMAR3 = { 'Name' : "Hex Gosper", 'Axiom' : 'XF', 'Winkel' : 60, 'X' : 'X+YF++YF-FX--FXFX-YF+', 'Y' : '-FX+YFYF++YF+FX--FX-Y' } ## Busch GRAMMAR4 = { 'Name' : "bush", 'Axiom' : 'F', 'Winkel' : 22.5, 'F' : 'FF-[-F+F+F]+[+F-F-F]', } ## 3D Hilbertkurve GRAMMAR10 = { 'Axiom' : 'A', 'Winkel' : 90, 'A' : 'B-F+CFC+F-D&F^D-F+&&CFC+F+B//', 'B' : 'A&F^CFB^F^D^^-F-D^|F^B|FC^F^A//', 'C' : '|D^|F^B-F+C^F^A&&FA&F^C+F+B^F^D//', 'D' : '|CFB-F+B|FA&F^A&&FB-F+B|FC//' } ########################################################################### from math import * from Tkinter import * class Plotter: def __init__(self): self.root = None def compile(self,grammatik,tiefe): g = {} for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ[]+-^&/\\|$": g[c]=c g.update(grammatik) self.plots = "F" if g.has_key("Plots"): self.plots=g["Plots"] self.w = g["Winkel"] a = g['Axiom'] if tiefe<1: return a for i in range(tiefe): result = "" for c in a: result += g[c] a=result return result def parse(self,s): self.xmin=self.ymin=self.zmin = +1000 self.xmax=self.ymax=self.zmax = -1000 self.p = [0.0,0.0,0.0] # Position self.h,self.l,self.u = [1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0], self.step = 1.0 # Schrittweite self.stack = [] self.output = [] self.lastpoint = [] plots = self.plots for c in s: if c in plots: self.fd(True) elif c=="f": self.fd(False) elif c=="+": self.yaw(self.w) elif c=="-": self.yaw(-self.w) elif c=="^": self.pitch(-self.w) elif c=="&": self.pitch(self.w) elif c=="\\": self.roll(self.w) elif c=="/": self.roll(-self.w) elif c=="|": self.turn() elif c=="[": self.push() elif c=="]": self.restore() return self.output def push(self): self.stack.append([self.p[:],self.h[:],self.l[:],self.u[:], self.w,self.step]) def restore(self): if self.stack==[]: raise Exception,"turtle stack empty" ## return last element and remove it from list self.p,self.h,self.l,self.u,self.w,self.step = self.stack.pop() # rotiere um Winkel def rotate(self,v1,v2,winkel): w = winkel*pi/180.0 s,c = sin(w),cos(w) return [c*v1[0]+s*v2[0],c*v1[1]+s*v2[1],c*v1[2]+s*v2[2]] # around up def yaw(self,w): self.h,self.l = self.rotate(self.h,self.l,w),self.rotate(self.l,self.h,-w) # around left def pitch(self,w): self.h,self.u = self.rotate(self.h,self.u,w),self.rotate(self.u,self.h,-w) # around heading def roll(self,w): self.l,self.u = self.rotate(self.l,self.u,w),self.rotate(self.u,self.l,-w) # twist around def turn(self): self.yaw(180) # move or draw def fd(self,execute=False): pnew = [self.p[0]+self.step*self.h[0], self.p[1]+self.step*self.h[1], self.p[2]+self.step*self.h[2]] if execute: self.publish(self.p,pnew) self.p = pnew ########################################################################### # record walk-vector from v1 to v2 def publish(self,v1,v2): if v1 != self.lastpoint: if v1[0]self.xmax: self.xmax=v1[0] if v1[1]>self.ymax: self.ymax=v1[1] if v1[2]>self.zmax: self.zmax=v1[2] if v2[0]self.xmax: self.xmax=v2[0] if v2[1]>self.ymax: self.ymax=v2[1] if v2[2]>self.zmax: self.zmax=v2[2] if v1 != self.lastpoint: self.output.append(v1) self.output.append((v1,v2)) self.output.append(v2) self.lastpoint = v2 def save(self,name="lsystem.inc",linedata=[]): if linedata==[]: l=self.output else: l=linedata ntotal = len(l) scale = max(self.xmax-self.xmin,self.ymax-self.ymin,self.zmax-self.zmin) mx = (self.xmax+self.xmin)/2.0 my = (self.ymax+self.ymin)/2.0 mz = (self.zmax+self.zmin)/2.0 numlines = numlinks = 0 s = "// Lsystem Object containing "+str(len(l))+" parts\n\n" s += "#declare LSysObject = union {\n" for n in range(ntotal): if len(l[n])==2: # line segment (aa,bb) = l[n] ax,ay,az = (aa[0]-mx)/scale,(aa[1]-my)/scale,(aa[2]-mz)/scale bx,by,bz = (bb[0]-mx)/scale,(bb[1]-my)/scale,(bb[2]-mz)/scale s += " LineObject(<%f,%f,%f>,<%f,%f,%f>,%d)\n"%(ax,az,ay,bx,bz,by,numlines) numlines += 1 else: # joint aa = l[n] ax,ay,az = (aa[0]-mx)/scale,(aa[1]-my)/scale,(aa[2]-mz)/scale s += " JointObject(<%f,%f,%f>,%d)\n"%(ax,az,ay,numlinks) numlinks += 1 s += "}\n\n" s += "#declare NumOfLines = %d;\n"%numlines s += "#declare NumOfLinks = %d;\n\n"%numlinks ## write to file f = open(name,"w") f.write(s) f.close() ########################################################################### def show(self,data): if not self.root: self.root = Tk() self.c = Canvas(self.root,width=500+15,height=500+15) self.scale = 500 self.c.pack() self.root.update() self.c.delete("all") size = max(self.xmax-self.xmin,self.ymax-self.ymin) mx = (self.xmax+self.xmin)/2.0 my = (self.ymax+self.ymin)/2.0 s,M = self.scale/size, 257 for seg in data: if len(seg) != 2: continue v1x,v1y,v2x,v2y = seg[0][0]-mx,seg[0][1]-my,seg[1][0]-mx,seg[1][1]-my a,b,c,d = v1x*s,v1y*s,v2x*s,v2y*s self.c.create_line(M-b,M-a,M-d,M-c) self.c.update() def close(self): if self.root: self.root.destroy() self.root = None ########################################################################### p = Plotter() ## execute rules c = p.compile(GRAMMAR3,3) # fill in grammar and depth (!not too high!) ## build list of plotted lines l = p.parse(c) # check list of lines ## convert to POV-Ray Objects and save p.save("lsystem.inc",l) ## show a 2D sketch p.show(l)