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

Saturday, 10 November 2012

Testing processing.py with processing-2.0 libraries

Not quite there yet but progress is being made towards translating processing.py to use processing-2.0 java libraries, here is a sketch featuring use of a python lsystems grammar library and the regular PeasyCam library.

The python sketch showing importing peasycam library (placed in processing.py library, as not as cool as ruby-processing that picks up libraries from regular processing).

   1 """
   2 hilbert.py by Martin Prout based on a Hilbert curve from "Algorithmic Beauty of Plants"
   3 by Przemyslaw Prusinkiewicz & Aristid Lindenmayer
   4 Uses a java peasycam library (Jonathan Feinberg), and a python grammar module.
   5 Features processing affine transforms.
   6 """
   7 from math import pi
   8 import processing.opengl
   9 import peasycam
  10 import peasy.PeasyCam as PeasyCam
  11 
  12 from grammar import grammar
  13 
  14 # some lsystem constants
  15 XPOS = 0
  16 YPOS = 1
  17 ANGLE = 2
  18 BEN = pi/360   # just a bit of fun set BEN to zero for a regular Hilbert
  19 THETA = pi/2 + BEN
  20 PHI = pi/2 - BEN
  21 ADJUST = [0, 0.5, -1.5, 3.5, -7.5, 15]
  22 distance = 200
  23 depth = 3
  24 
  25 RULES = {
  26     'A': "B>F<CFC<F>D+F-D>F<1+CFC<F<B1^",
  27     'B': "A+F-CFB-F-D1->F>D-1>F-B1>FC-F-A1^",
  28     'C': "1>D-1>F-B>F<C-F-A1+FA+F-C<F<B-F-D1^",
  29     'D': "1>CFB>F<B1>FA+F-A1+FB>F<B1>FC1^"
  30 }
  31 
  32 AXIOM = 'A'
  33 
  34 def render(production):       
  35     """
  36     Render evaluates the production string and calls box primitive
  37     uses processing affine transforms (translate/rotate)
  38     """
  39     global distance, depth
  40    
  41     translate(-distance * ADJUST[depth], -distance * ADJUST[depth], -distance * ADJUST[depth])
  42     lightSpecular(204, 204, 204) 
  43     specular(255, 255, 255) 
  44     shininess(1.0)    
  45     repeat = 3
  46     for val in production:
  47         if val == "F":
  48             translate(00-distance / 2 )
  49             box(distance/ 6, distance/ 6, distance* 5/ 6)
  50             translate(00-distance / 2 )
  51             box(distance / 6, distance / 6, distance / 6);
  52         elif val == '+': 
  53             rotateX(THETA * repeat)
  54             repeat = 1
  55         elif val == '-': 
  56             rotateX(-THETA * repeat)
  57             repeat = 1
  58         elif val == '>': 
  59             rotateY(THETA * repeat)
  60             repeat = 1
  61         elif val == '<': 
  62             rotateY(-THETA * repeat)
  63         elif val == '^': 
  64             rotateZ(PHI * repeat)
  65             repeat = 1
  66         elif (val == '1') :
  67             repeat += 1           
  68         elif (val == 'A' or val == 'B' or val == 'C' or val == 'D'):            
  69             pass  # assert as valid grammar and do nothing
  70         else: 
  71             print("Unknown grammar %s" % val)
  72     
  73 def setup():
  74     """
  75     The processing setup statement
  76     """
  77     size(500, 500, P3D)
  78     cam = PeasyCam(this, 200)
  79     cam.setMinimumDistance(100)
  80     cam.setMaximumDistance(500)
  81     smooth(16)
  82     global production, depth, distance
  83     production = grammar.repeat(depth, AXIOM, RULES)
  84     distance *= 1/(pow(2, depth) - 1)
  85     noStroke()
  86     fill(200, 0, 180) 
  87    
  88 
  89    
  90     
  91 def draw():
  92     """
  93     Render a 3D Hilbert/Ben Tilbert, somewhat centered
  94     """
  95     lights()
  96     background(255)
  97     global production
  98     render(production)
  99 

Here is the python lsystems library, which can cope with stochastic grammar (weighted rules) as well a simple rules used here:-

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

No comments:

Post a Comment

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