Recently I've been looking at Povray, pyprocessing, and cfdg (version 3.0) as tools for creating digital images. I have branched two separate blogs where I mainly explore jruby + processing and processing.py

Sunday, 30 January 2011

Using a module and stochastic l-systems for processing.py

Let the lawyers sort it out (preferably not real ones just Guido and clan) but here appears to be more than one processing.py out there (one is for audio stuff).
Here I've created a grammar module to simplify lsystem usage in processing.py (the module is a regular python module, and does not rely on processing). The module function can be used to parse stochastic rules (note a weighting must be provided, owing the pythonic way of creating a rule as a dict) and non-stochastic rules (examples are given in the module header).

   1 """
   2 stochastic_plant.py is a python script for use in processing.py. 
   3 Uses a lsystem grammar module, that can parse both stochastic and
   4 non-stochastic rules, or a mixture thereof
   5 """
   6 
   7 import math
   8 import grammar
   9 
  10 # some constants
  11 XPOS = 0
  12 YPOS = 1
  13 ANGLE = 2
  14 WEIGHT = 3
  15 DELTA = math.pi/8
  16 # A simple stochastic rule as a dict, with a dict of values (only one key)
  17 # and 3 weighted alternative substitutions.
  18 RULES = {  
  19       'F' : {'F[+F]F[-F]' : 10,
  20       'F[+F]F' : 45,
  21       'F[-F]F' : 45
  22       }
  23 }
  24 
  25 AXIOM = 'F'
  26    
  27 
  28 def render(production):       
  29     """
  30     Render evaluates the production string and calls draw_line
  31     """
  32     pen = [width/2, height*0.95, math.pi/2, 3]
  33     stack = []
  34     repeat = 1
  35     for val in production:
  36         if val == "F": 
  37             pen = draw_line(pen, 12)
  38         elif val == "+": 
  39             pen[ANGLE] += DELTA * repeat
  40         elif val == "-": 
  41             pen[ANGLE] -= DELTA * repeat
  42         elif val == "[": 
  43           temp = [pen[XPOS], pen[YPOS], pen[ANGLE],pen[WEIGHT] * 0.6]
  44           stack.append(temp)
  45         elif val == "]": 
  46             pen = stack.pop() 
  47             pen[WEIGHT] *= 1/0.6
  48         else: 
  49             pass
  50 
  51 
  52 def draw_line(pen, length):
  53     """
  54     Draw line utility uses processing 'line' function to draw lines
  55     """
  56     new_xpos = pen[XPOS] + length * math.cos(pen[ANGLE])
  57     new_ypos = pen[YPOS] - length * math.sin(pen[ANGLE])
  58     strokeWeight(pen[WEIGHT])
  59     line(pen[XPOS], pen[YPOS], new_xpos, new_ypos)
  60     return [new_xpos, new_ypos, pen[ANGLE],pen[WEIGHT]]     
  61 
  62 
  63 def setup():
  64     """
  65     The processing setup statement
  66     """
  67     size(500, 500)
  68     background(200, 200, 0)
  69     stroke(0, 100, 0)
  70     plant0 = grammar.repeat(4, AXIOM, RULES)
  71     plant1 = grammar.repeat(5, AXIOM, RULES)    
  72     plant2 = grammar.repeat(4, AXIOM, RULES)
  73     render(plant1)
  74     translate(-100, 10)
  75     render(plant0)
  76     translate(200, 0)
  77     render(plant2)  
  78 
  79 
  80 def draw():
  81     """
  82     An empty processing draw statement seems to be required to prevent premature exit()?
  83     """
  84     pass 

   1 """
   2 grammar.py module supports the parsing of both stochastic and non-stochastic rules
   3 axiom/rules are evaluated by the produce function, which uses the __weightedRule function
   4 to return any stochastic rule according to the input dict, the repeat function is used to repeatedly
   5 iterate the rules in a recursive fashion.
   6 Example Rules:
   7 
   8 Non-Stochastic = { "A": "A+F", "G": "GG", "X" :"G-G"}
   9 
  10 Stochastic = {"A" : {"BCD": 5, "C+C+C":10, "ACA": 40}, "B" : {"DE-F": 5, "CCC":10, "A[C]A": 40}}
  11 
  12 The Stochastic rule may contain non stochastic elements, in the above example there are two stochastic, elements,
  13 with keys "A" and "B". The stochastic rule is detected by checking the 'value' type is dict. The dict needs to of the
  14 form "string substitution" as key with the weighting as value.  A test function is included for the test conscious or
  15 skeptic.
  16 """
  17 
  18 
  19 import random
  20 
  21 def __weightedRule(rules):
  22     """
  23     A private method used to choose a substitution rule from a dict of rules, according to its
  24     weighted probality. 'rules' is expected to be a dict where the substition string is the 'key' 
  25     and the 'value' is the rule weight
  26     """
  27     rand = random.random()
  28     prob = 0
  29     tot = sum(rules.values())     # sum probabilities
  30     for rule in rules.keys():     # iterate over rule choices
  31         prob += rules.get(rule)   # add assigned probalities
  32         if ((rand * tot) < prob): # compare running total with scaled random value
  33             return(rule)
  34 
  35 def produce(axiom, rules):
  36     """
  37     The single rule substitution utility, that uses type to check for dict or str
  38     as key value, else declares 'Unrecognized grammar'. Does not throw exception!!!
  39     """
  40     output = ""
  41 
  42     for i in axiom:
  43         temp = rules.get(i, i)
  44         if (type(temp) is dict):
  45             output += __weightedRule(temp)
  46         elif (type(temp) is str):
  47             output += temp
  48         else:
  49             print("Unrecognized grammar")
  50     return output
  51     
  52  
  53 def repeat(rpx, axiom, rules):
  54     """
  55     Repeat rule substitution in a recursive fashion rpx times
  56     """ 
  57     production = axiom
  58     for i in range(0, rpx):
  59         production = produce(production, rules)
  60     return production
  61     
  62 def __testWeighting(rules, key, total):     
  63     """
  64     Private test function see module header for examples of rules format
  65     Takes a dict containing a stochastic rule with 'key' as key.
  66     Tests the weighted rule function a 'total' number of times.
  67     Frequency result is printed.
  68     """
  69     wordList = [] # create a big test list of replacement rules
  70     for z in range(0, total):    
  71         wordList.append(__weightedRule(rules.get(key)))
  72         
  73     # calculate each word frequency in generated list (NB: does not test the
  74     # randomness of order though)
  75     freqD2 = {}
  76     for word2 in wordList:
  77         freqD2[word2] = freqD2.get(word2, 0) + 1
  78     keyList = freqD2.keys()
  79     keyList.sort()
  80     
  81     print "Frequency of each substitution string in the word list (sorted):"
  82     for key2 in keyList:
  83         print "%-10s %d" % (key2, freqD2[key2])
  84         
  85 def toRuleString(axiom, rules):
  86     """
  87     Creates a string representing the pythonic rules in a more conventional
  88     manner
  89     """
  90     output = "Axiom:\t%s\n" % axiom 
  91     keys = rules.keys()
  92     for key in keys:
  93         temp = rules.get(key)
  94         if (type(temp) is dict):
  95             keys2 = temp.keys()
  96             for key2 in keys2:
  97                 output += "Rule:\t%s => %s\t%d\n" % (key, key2, temp.get(key2))
  98         elif (type(temp) is str):
  99             output += "Rule:\t%s => %s\n" % (key, temp)
 100         else:
 101             output += "Unrecognized grammar\n"
 102     return output        
 103         
 104 
 105    


7 comments:

  1. How to run this module??

    thanks
    email=puzmoetz@gmail.com

    ReplyDelete
  2. First you need to install processing.py, links here http://martinpblogformasswritingproject.blogspot.com/2011/01/and-now-for-something-completely.html.
    Jonathan has produced a bat script for windows. I've produced a bash script for linux (something similar should work on the Mac?

    ReplyDelete
  3. Wow
    Cool Project

    hmmm
    Can this module work in 3D form?
    How to build it??

    Thanks

    ReplyDelete
  4. If you haven't done so already you need to install git to clone a GitHub repository. I've published 3D L-System Example on my fork of processing.py, so if you cloned that instead of the original you could access those examples. Jonathan Feinberg the author of processing.py has include build instructions in the project (you should direct any build inquiries to him). NB you not need to fork..

    ReplyDelete
  5. To: puz the above should answer your question which was near enough identical to Lukmans
    I deleted your comment.....

    ReplyDelete
  6. I need example of 3D L-Systems/stochastic_plant.py

    Do u have it??

    Thanks

    ReplyDelete
  7. See http://martinpblogformasswritingproject.blogspot.com/2010/02/improved-stochastic-3d-tree-using.html
    Is an example I created for vanilla processing, you should be able translate it to processing.py if you follow my examples (you will need to give weighting to stochastic rules say 33 to each in processing.py).

    ReplyDelete

Followers

Blog Archive

About Me

My photo
Pembrokeshire, United Kingdom
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2