L-system.frink

Download or view L-system.frink in plain text format

// General description:
// This code creates Lindenmayer rules via string manipulation
// It can generate many of the examples from the Wikipedia page
// discussing L-system fractals: http://en.wikipedia.org/wiki/L-system
//
// It does not support stochastic, context sensitive or parametric grammars
//
// It supports four special rules, and any number of variables in rules
// f = move forward one unit
// - = turn left one turn
// + = turn right one turn
// [ = save angle and position on a stack
// ] = restore angle and position from the stack

class LSystem
{
   var name
   var turn
   var start
   var rules

   // Array of LSystem
   class var systems = new array

   new[name, turn, start, rules] :=
   {
      this.name = name
      this.turn = turn
      this.start = start
      this.rules = rules
      systems.push[this]
   }
}

// The turn is how far each + or - in the final rule turns to either side
turn = 90 degrees
// This is how many times the rules get applied before we draw the result
times = 6
// This is our starting string
start = "++a"
// These are the rules we apply
rules = [["f","f"],["a","-bf+afa+fb-"], ["b","+af-bfb-fa+"]]

// L-System rules pulled from Wikipedia
// Dragon
new LSystem["Dragon", 90 degrees, "fx", [["f","f"],["x","x+yf"],["y","fx-y"]]]

// TerDragon
new LSystem["TerDragon", 120 degrees, "f", [["f","f+f-f"]]]

// Koch curve
// use "++f" as the start to flip it over
new LSystem["Koch", 90 degrees, "f", [["f","f+f-f-f+f"]]]

// Sierpinski Triangle
new LSystem["Sierpinski triangle", 60 degrees, "bf", [["f","f"],["a","bf-af-b"],["b","af+bf+a"]]]

// Plant
new LSystem["Plant", 25 degrees, "--x", [["f","ff"],["x","f-[[x]+x]+f[+fx]-x"]]]

// Hilbert space filling curve
new LSystem["Hilbert space-filling", 90 degrees, "++a", [["f","f"],["a","-bf+afa+fb-"], ["b","+af-bfb-fa+"]]]

// Peano-Gosper curve
new LSystem["Peano-Gosper", 60 degrees, "x", [["f","f"],["x","x+yf++yf-fx--fxfx-yf+"], ["y","-fx+yfyf++yf+fx--fx-y"]]]

// Lévy C curve
new LSystem["Lévy", 45 degrees, "f", [["f","+f--f+"]]]

// Kochawave curve,
// https://arxiv.org/abs/2210.17320
new LSystem["Kochawave", 30 degrees, "f", [["f", "f-f+++++f----f"]]]

prompt = ""
for i = rangeOf[LSystem.systems]
{
   lsys = LSystem.systems@i
   prompt=prompt+ "$i: " + lsys.name + "\n"
}
prompt = prompt + "\nChoose a system number:"

i = eval[input[prompt, 5]]
lsys = LSystem.systems@i
turn = lsys.turn
start = lsys.start
rules = lsys.rules

times = eval[input["Number of times:", 6]]

// This function will apply our rule once, using string substitutions based
// on the rules we pass it
// It does this in two passes to avoid problems with pairs of mutually referencing
// rules such as in the Sierpinski Triangle
// rules@k@1 could replace toString[k] and the entire second loop could
// vanish without adversely affecting the Dragon or Koch curves.

apply_rules[rules, current] :=
{
   n = current
   for k = 0 to length[rules]-1
   {
      rep = subst[rules@k@0,toString[k],"g"]
      n =~ rep
   }
   for k = 0 to length[rules]-1
   {
      rep = subst[toString[k],rules@k@1,"g"]
      n =~ rep
   }
   return n
}

// Here we will actually apply our rules the number of times specified
current = start
for i = 0 to times - 1
{
   current = apply_rules[rules, current]
   // Uncomment this line to see the string that is being produced at each stage
   // println[current]
}

// Go ahead and plot the image now that we've worked it out
g = new graphics
//g.antialiased[false]   // Comment this out for non-square rules. It looks better
theta = 0 degrees
x = 0
y = 0
stack = new array
for i = 0 to length[current]-1
{
   // This produces a nice sort of rainbow effect where most colors appear
   // comment it out for a plain black fractal
   // g.color[abs[sin[i degrees]],abs[cos[i*2 degrees]],abs[sin[i*4 degrees]]]

   cur = substrLen[current,i,1]
   if cur == "-"
      theta = theta - (turn)
   if cur == "+"
      theta = theta + (turn)
   if cur == "f" or cur == "F"
   {
      g.line[x,y,x + cos[theta],y + sin[theta]]
      x = x + cos[theta]
      y = y + sin[theta]
   }
   if cur == "[" 
      stack.push[[theta,x,y]]
   if cur == "]" 
      [theta,x,y] = stack.pop[]
}

g.show[]
g.write["hilbert.png",512,undef]


Download or view L-system.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 19973 days, 17 hours, 54 minutes ago.