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

Monday, 27 June 2011

CFDG Swatch revisited (Version 3.0 Syntax)

Get Version 3.0 here.

startshape swatch
shape swatch {   
  loop 19 [h 1 x 1.1] {
    loop 19 [ h 19 y 1.1] 
    SQUARE [sat 1 b 1]
  }
}

Sunday, 19 June 2011

A recursive spiral in pyprocessing

Get pyprocessing here.

   1 """
   2  spiral.py pyprocessing sketch
   3  features fairly extreme recursion fot
   4  python hence need to increase limit
   5 """
   6 
   7 from pyprocessing import *
   8 
   9 MAX_RECURSION = 3451
  10 
  11 def setup():
  12     """
  13     processing setup
  14     """
  15     size(400, 400) 
  16     translate(100, 330) 
  17     rotate(0.3)
  18     fill(255, 0, 0, 0)
  19     background(0)
  20     noStroke()   
  21     smooth()
  22     fill(255, 0, 0, 20)# transparency makes for almost '3d' look
  23     import sys         # increase recursion limit or sketch will fail 
  24     sys.setrecursionlimit(MAX_RECURSION)
  25     try:               # handle potential index error
  26         shell(-0.008, 1.5, 25)
  27     except IndexError:
  28         print "Sketch exceeds set recursion limit"
  29     else:
  30         pass # Sketch finishes before recursion limit is reached
  31   
  32 def shell(rot, disp, sz):
  33     """
  34     Recursive shell shape limited by sz
  35     """
  36     REDUCE = 0.999  
  37     MIN_SIZE = 0.8   # about right for processing
  38     if (sz > MIN_SIZE):
  39         sz *= REDUCE 
  40         disp *= REDUCE
  41         translate(disp, 0)
  42         rotate(rot)
  43         ellipse(disp, 0, sz, sz)
  44         return shell(rot, disp, sz) # recursive call
  45     else: return # break recursive loop on size  
  46  
  47 run()

Monday, 13 June 2011

Nautilus shape in pyprocessing (using takesnapshot.py)

Here is example another of my vanilla processing sketches translated to pyprocessing. In this case to save the image I used the takesnapshot.py script (included with pyprocessing) from jEdit. Get my jEdit macro and commando files here. See screenshot here. I've just found a simple way to do it by using pyglet calls directly in your sketch in the draw loop:-
    pyglet.image.get_buffer_manager().get_color_buffer().save('screenshot.png')
did the trick for me.

from pyprocessing import *
from math import sin, cos, tan, exp, pi  
  
A = 0.8
B = 1.4
pvect = []

def getX(theta):
    return A * cos(theta)*exp(theta/tan(B))

def getY(theta):
    return A * sin(theta)*exp(theta/tan(B))  

def setup():
    size(400, 400)
    background(255)
    translate(width/2, height/3)
    rotate(0.75 * pi)
    for i in range(0, 41):
        x = getX(i*A)
        y = getY(i*A)
        pvect.append(PVector(x, y))
    for z in range(8, len(pvect)):
        tmp0 = pvect[z]
        tmp1 = pvect[z - 1]
        tmp2 = pvect[z - 8]
        tmp3 = pvect[z - 7]
        x0 = tmp0.x
        y0 = tmp0.y
        x1 = tmp1.x # we would have negative increments otherwise
        y1 = tmp1.y   
        x2= tmp2.x
        y2 = tmp2.y
        x3= tmp3.x # we would have negative increments otherwise
        y3 = tmp3.y 
        curveTightness(-0.8)    
        curve( x0, y0, x1, y1, x2, y2, x3, y3) # draw the radial lines

    strokeWeight(4)
    stroke(255, 0, 0)
    noFill()
    curveTightness(0.0)   
    beginShape() # begin spiral 'shell' shape
    for v in range(0, len(pvect)):
        tmp = pvect[v]
        curveVertex(tmp.x, tmp.y) # successive calls to curveVertex a variation on curve?  
    endShape()        
        
run()        

Friday, 10 June 2011

A 3D plant in pyprocessing with context sensitive lsystem rules

Here I have used my snapshot.bsh macro and snapshot.xml commando file to call takesnapshot.py from jEdit, however I did do bit of cropping with the gimp, haven't quite got my serial snapshot working (and hence movie maker, mencoder is old hat I'm now using ffmpeg).

"""
cplant.py pythonic exploration of context sensitive lsystems grammar
context sensitive plant (includes cs grammar generator)
code looks super messy 'twas easier in ruby
"""
from pyprocessing import *
from math import pi, sin
from time import time

AXIOM = 'F'
IGNORE = '[]+-^&3'
RULES = {
  'F': 'F[-EF[3&A]]E[+F[3^A]]',
  'F<E': 'F[&F[3+A]][^F[3-A]]'
}

# string walk constants
LEFT = -1
RIGHT = 1

THETA = (5 * pi)/36 # 25 degrees in radians
production = None   # needs exposure at module level

def speedVector(speed):
    """
    Adapted from lazydogs 3D Sierpinski sketch.
    """
    mills = time() * 0.03 
    y = 0.5 * sin(mills * speed) + 0.5
    return y           
        
def speedRotation(speed):
    """
    Adapted from lazydogs 3D Sierpinski sketch. Rotate the current coordinate system.
    Uses speedVector() to speedly animate the trre rotation.
    """
    r1 = speedVector(speed) 
    rotateY(2.0 * pi * r1)

def render(production):       
    """
    Render evaluates the production string and calls box primitive
    uses processing affine transforms (translate/rotate)
    """
    lightSpecular(204, 204, 204) 
    specular(255, 255, 255) 
    shininess(1.0) 
    distance = 80
    repeat = 1
    for val in production:
        if val == "F":
            translate(0, distance/-2, 0)
            box(distance/9, distance, distance/9)
            translate(0, distance/-2, 0)
        elif val == '[': 
            pushMatrix() 
        elif val == ']': 
            popMatrix()              
        elif val == '+': 
            rotateX(-THETA * repeat)
            repeat = 1
        elif val == '-': 
            rotateX(THETA * repeat)
            repeat = 1
        elif val == '^': 
            rotateZ(THETA * repeat)            
            repeat = 1
        elif val == '&': 
            rotateZ(-THETA * repeat)
            repeat = 1
        elif (val == '3') :
            repeat = 3      
        elif (val == 'A' or val == 'E'):            
            pass  # assert as valid grammar and do nothing
        else: 
            print("Unknown grammar %s" % val)

def __context(a):
    """
    Private helper function returns a tuple of value, index and context from key 'a'
    Valid direction setters are '>' or '<' else no direction is set
    """
    index = 0
    before =  a[1] == '<' if len(a) == 3 else False # python ternary operator
    after =  a[1] == '>' if len(a) == 3 else False # python ternary operator
    cont = a[0] if (before or after) else None
    if (before): 
        index += LEFT
    if (after): 
        index += RIGHT
    value = a[2] if len(a) == 3 else a[0]    
    return (value, index, cont)

def __getContext(path, index, direction, ignore):
    """
    Private helper returns context character from path[index + direction], 
    skipping any ignore characters
    """
    skip = 0
    while path[direction + index + skip] in ignore:
        skip += direction
    return path[direction + index + skip] 
    
        
def hasContext(a, b, index):
    """
    from a given key, axiom/production string and index returns
    has context boolean (ignoring characters in IGNORED)
    """
    cont = False
    if __context(a)[0] == b[index] and __context(a)[1] != 0:
        if __context(a)[1] == LEFT and index > 0:     # guard negative index
            cont = __context(a)[2] == __getContext(b, index, LEFT, IGNORE)
        elif __context(a)[1] == RIGHT and index < len(b) - 1: # guard out of range index
            cont = __context(a)[2] == __getContext(b, index, RIGHT, IGNORE)  
    return cont

def produce(ax, rules):
    """
    generate production from axiom and rules
    """
    str_buf = []   # initialize string buffer
    csrule = {}    # initialize context sensitive dict
    for key in rules.keys():
        if len(key) == 3:
            csrule[key[2]] = key
    for i, a in enumerate(ax):
        r = csrule.get(a, a)
        if (r == a):  # handle as a cf rule
            str_buf.append(rules.get(a, a))
        else:         # handle as a cs rule
            if hasContext(r, ax, i):
                str_buf.append(rules.get(r))
            else:
                str_buf.append(r[2])
    return ''.join(str_buf) # join str_buf list as a single string

def repeat(rpx, axiom, rules):
    """
    Repeat rule substitution in a recursive fashion rpx times
    """ 
    production = axiom
    from itertools import repeat
    for _ in repeat(None, rpx):
        production = produce(production, rules)
    return production
    
def setup():
    """
    processing setup
    """
    size(800, 600)
    global production
    production = repeat(5, AXIOM, RULES)
    rotateY(pi/2)
    fill(0, 200, 0)
    noStroke()
    

def draw():
    """
    Animate a 3D context free plant in processing/pyglet draw loop
    """
    background(20, 20, 180)
    lights()  
    camera(250, 250, 800, 0, -300, 0, 0, 1, 0)     
    speedRotation(4.5)
    pushMatrix()
    render(production)
    popMatrix()
run()        

Exploring context sensitive lsystems grammar in python

The parser in this example only supports the simplest of context sensitive grammars, sometimes referred to as a 1-L system. However the combination cs and non cs rules is supported.



Results:

baa[a]aaaa
aba[a]aaaa
aab[a]aaaa
aaa[b]aaaa
aaa[a]baaa
aaa[a]abaa
aaa[a]aaba
aaa[a]aaab

Wednesday, 8 June 2011

Taking Snapshots in pyprocessing (from jEdit)

There exists a utility script takesnapshop.py in the tools directory distributed with pyprocessing. This allows you to either take a single snapshot, or to take interactive snapshots by pressing the "F1" key. However this is all fiddly command line stuff, so I have created some tools to work on the sketch from jEdit. You can download my commando file and bsh macro here. I recommend taking a look at the takesnapshot.py script, it is quite interesting how it works (it essentially rewrites the input script to include the pyglet save commands), and could provide the basis for you to create a simple movie, from your running script.


Tuesday, 7 June 2011

PVector in pyprocessing

The class (static) methods of PVector are not supported in pyprocessing, in the following sketch PVector.add and PVector.mult have been replaced by using simple + and * operators, as suggested by Claudio Esperan├ža Warning you will need to to wait quite a bit for this sketch to render the first time you run it. The vanilla processing version renders much quicker by comparison. Attempts to optimize using the option -OO didn't do anything in my hands ie no *.pyo was created.

Update 18th of June 2011
Since version r89 (svn update) PVector class methods are supported, and the sketch seemed to load much quicker, but it may have been my imagination.

Get pyprocessing as a download here.
Or checkout the latest svn version here.



Sunday, 5 June 2011

3D Hilbert fractal in pyprocessing (using lsystems module)

This sketch uses grammar.py stored as a python module (lsystems), to produce the production grammar. This sketch runs in opengl using the pyglet libraries (actually as do all pyprocessing sketches). So there is no importing of opengl libraries, that is all provided by pyglet and then made available via the pyprocessing import. Understandably in the python world the wild card import of all modules from pyprocessing is discouraged, it also makes checking of code using pylint all but impossible (except using the pylint -d W option).
Get pyprocessing here

"""
Copyright (c) 2011 Martin Prout
 
This demo is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
http://creativecommons.org/licenses/LGPL/2.1/

hilbert.py by Martin Prout based on a Hilbert curve from "Algorithmic Beauty
of Plants" by Przemyslaw Prusinkiewicz & Aristid Lindenmayer
and a python lsystem module to provide grammar module.
Features processing affine transforms.
"""
from pyprocessing import *
from math import pi
import time
from lsystems import grammar

# some lsystem constants
XPOS = 0
YPOS = 1
ANGLE = 2
BEN = pi/480   # using BEN to create a bent Hilbert
THETA = pi/2 + BEN
PHI = pi/2 - BEN
distance = 40
RULES = {
    'A': "B>F<CFC<F>D+F-D>F<1+CFC<F<B1^",
    'B': "A+F-CFB-F-D1->F>D-1>F-B1>FC-F-A1^",
    'C': "1>D-1>F-B>F<C-F-A1+FA+F-C<F<B-F-D1^",
    'D': "1>CFB>F<B1>FA+F-A1+FB>F<B1>FC1^"
}

AXIOM = 'A'

production = None   # need exposure at module level

def render(production):       
    """
    Render evaluates the production string and calls box primitive
    uses processing affine transforms (translate/rotate)
    """
    lightSpecular(204, 204, 204) 
    specular(255, 255, 255) 
    shininess(1.0) 
    global distance 
    repeat = 1
    for val in production:
        if val == "F":
            translate(0, 0, -distance / 2.0)
            box(7, 7, -distance + 3.5)
            translate(0, 0, -distance / 2.0)
            box(7, 7, 7)
        elif val == '+': 
            rotateX(THETA * repeat)
            repeat = 1
        elif val == '-': 
            rotateX(-THETA * repeat)
            repeat = 1
        elif val == '>': 
            rotateY(THETA * repeat)
            repeat = 1
        elif val == '<': 
            rotateY(-THETA * repeat)
        elif val == '^': 
            rotateZ(PHI * repeat)
            repeat = 1
        elif (val == '1') :
            repeat = 2         
        elif (val == 'A' or val == 'B' or val == 'C' or val == 'D'):            
            pass  # assert as valid grammar and do nothing
        else: 
            print("Unknown grammar %s" % val)
            
def smoothVector(s1, s2, s3):
    """
    Stolen from lazydogs 3D Sierpinski sketch.
    Generate a vector whose components change smoothly over time in the range [ 0, 1 ].
    Each component uses a sin() function to map the current time in milliseconds 
    somewhere in the range [ 0, 1 ].A 'speed' factor is specified for each component.
    """
    mills = time.time() * 0.03 
    x = 0.5 * sin(mills * s1) + 0.5
    y = 0.5 * sin(mills * s2) + 0.5
    z = 0.5 * sin(mills * s3) + 0.5
    return [x, y, z]           
        
def smoothRotation(s1, s2, s3):
    """
    Stolen from lazydogs 3D Sierpinski sketch. Rotate the current coordinate system.
    Uses smoothVector() to smoothly animate the rotation.
    """
    r1 = smoothVector(s1, s2, s3) 
    rotateX(2.0 * pi * r1[0])
    rotateY(2.0 * pi * r1[1])
    rotateX(2.0 * pi * r1[2])   


    
def setup():
    """
    The processing setup statement
    """
    size(500, 500)
    global production
    camera(width/2.0, height/2.0, 600, 0, 0, 0, 0, -1, 0) 
    production = grammar.repeat(3, AXIOM, RULES)    
    noStroke()            
    fill(200, 0, 180)   
   
    
def draw():
    """
    Render a 3D Hilbert/Ben Tilbert, somewhat centered
    """
    background(255)
    lights()  
    
    translate (width/2,  height/2,  0) 
    smoothRotation(4.5, 3.7, 7.3)
    pushMatrix()
    translate( distance * 3.5, -distance * 3.5, distance * 3.5)
    render(production)
    popMatrix()
run()

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