Got an iPhone or iPad? We have a brand new Pastebin App for both devices, and it's totally free! Click here to download the new Pastebin App for iOS.
Guest

iamsebcom

By: a guest on Nov 15th, 2009  |  syntax: Python  |  size: 5.18 KB  |  hits: 225  |  expires: Never
download  |  raw  |  embed  |  report abuse
Copied
  1. # this code is an improved version of the l-system
  2. # sample code originally distributed with pycairo.
  3. # originally Copyright 2003 Jesse Andrews (jdandr2 at uky.edu)
  4. # this version Copyright 2009 Seb Potter (iamseb at iamseb.com)
  5. # licensed under GPL
  6.  
  7. import logging
  8. import cairo
  9.  
  10. # setup logging to just print to stdout.
  11. # python's logging module is significantly
  12. # better than using print for debugging
  13. LOG_FILENAME = '/dev/stdout'
  14. logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
  15.  
  16. class Lindenmayer:
  17.     """
  18.    A class to represent L-Systems and render them using cairo
  19.    """
  20.     def __init__( self ):
  21.         self.width = self.height = 500 # use an 800 pixel square image
  22.         self.prod = {'[':'[','F':'F',']':']','+':'+','-':'-'} # the identity products
  23.         self.start_pos = (self.width*0.5, self.height) # we translate to the middle bottom of the image to start
  24.         self.start_angle = 180 # we rotate through 180 degrees to draw upwards - cairo's origin is top-left
  25.         self.theta = 90 # the rotation angle in degrees
  26.         self.stack = [] # this will be the stack for storing translation and orientation tuples
  27.         self.str = 'f' # the starting string, or 'axiom'
  28.  
  29.         self.line_length = 5 # how far we move forward on each step
  30.         self.line_width = 2 # just controls the rendered width of the line
  31.         self.logger = logging.getLogger("flower.draw.lindenmayer") # use logging to print debugging
  32.         self.logger.setLevel(logging.DEBUG)
  33.  
  34.     def addProd(self, let, prod):
  35.         """
  36.        Add a production to the ordered list of productions to apply to the current string.
  37.        """
  38.         self.prod[let]=prod
  39.  
  40.     def iterate(self, iterations=1):
  41.         """
  42.        Iterate over the list of productions and apply them to the string, in a loop.
  43.        The end result is the final transformed string.
  44.        """
  45.         for i in xrange(iterations):
  46.             self.str = ''.join([ self.prod[l] for l in self.str])
  47.  
  48.         self.logger.info("String is: %s" % self.str)
  49.  
  50.     def line(self, ctx, len):
  51.         ctx.rel_line_to( 0, len )
  52.  
  53.     def rotate(self, ctx, deg):
  54.         ctx.rotate( 2*3.141592653589793*deg/360.0  )
  55.  
  56.     def draw(self, colour):
  57.         """Render the string representation of the L-System"""
  58.         self.logger.info(colour)
  59.  
  60.         # create a cairo drawing context from the provided surface.
  61.         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
  62.         ctx = cairo.Context(surface)
  63.  
  64.         # first we'll create a nice white rectangle as our background
  65.         ctx.rectangle(0, 0, self.width, self.height)
  66.         ctx.set_source_rgb(1, 1, 1)
  67.         ctx.fill()
  68.  
  69.         # set the line colour, width, and make sure that cairo knows how close
  70.         # lines should be to join them
  71.         ctx.set_source_rgb(*colour)
  72.         ctx.set_line_width(self.line_width)
  73.         ctx.set_tolerance(0.1)
  74.         ctx.set_line_join(cairo.LINE_JOIN_BEVEL)
  75.  
  76.         # start drawing a path, move to our start point, and rotate to
  77.         # the starting angle
  78.         ctx.new_path()
  79.         ctx.move_to(*self.start_pos)
  80.         self.logger.debug("Initial position: %s, %s" % ctx.get_current_point())
  81.         self.rotate(ctx, self.start_angle)
  82.  
  83.         # this is the very simple way we iterate over the final string
  84.         # and perform a drawing operation for each symbol in the string
  85.         for c in self.str:
  86.             if c == 'F':
  87.                 # move forward
  88.                 self.logger.debug("f: draw a line of %s" % self.line_length)
  89.                 self.line(ctx, self.line_length)
  90.             if c == '+':
  91.                 # rotate clockwise
  92.                 self.logger.debug("+: rotate %s" % self.theta)
  93.                 self.rotate(ctx, self.theta)
  94.             if c == '-':
  95.                 # rotate anti-clockwise
  96.                 self.logger.debug("-: rotate -%s" % self.theta)
  97.                 self.rotate(ctx, -self.theta)
  98.             if c == '[':
  99.                 # push the transform and orientation onto the stack
  100.                 m = ctx.get_matrix()
  101.                 p = ctx.get_current_point()
  102.                 self.logger.debug("[: push the matrix %s onto the stack" % m)
  103.                 self.stack.append((p, m))
  104.             if c == ']':
  105.                 # restore the transform and orientation from the stack
  106.                 p, m = self.stack.pop()
  107.                 self.logger.debug("]: pop the matrix %s off the stack" % m)
  108.                 ctx.set_matrix(m)
  109.                 ctx.move_to(*p)
  110.  
  111.         # now draw the path created as a stroke on the context
  112.         ctx.stroke()
  113.  
  114.         # write this to a png
  115.         surface.write_to_png("test.png")
  116.         self.logger.info("Wrote test.png")
  117.  
  118. def main():
  119.  
  120.     colour = (0, 0.3, 0)
  121.  
  122.     # setup the initial parameters for this l-system
  123.     lin = Lindenmayer()
  124.     lin.start_angle = 205
  125.     lin.start_pos = (lin.width*0.25, lin.height)
  126.     lin.str = 'X'
  127.     lin.addProd('X', 'F[+X]F[-X]+X')
  128.     lin.addProd('F', 'FF')
  129.     lin.theta = 20
  130.  
  131.     # generate the final string
  132.     lin.iterate(iterations=5)
  133.  
  134.     # render the string using pycairo
  135.     lin.draw(colour)
  136.  
  137. if __name__ == '__main__':
  138.     main()