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

Thursday, 17 February 2011

Alhambra Tiling in processing.py (Version 3)

PVector is quite fattening really (and Jonathan has had to do some fancy monkey patching stuff to get the class methods to work) so I've created a custom TPoint to store the point data, and given it a custom method to return a new mid_point instance of TPoint. In this revised version I've ditched camel case, except where required to access processing methods/functions/attributes (Jeremy Ashkenas had a neat trick that enables use of non-camelcase in ruby-processing, but of course that can cause namespace space issues, possibly more so in python?). In the interest of being more pythonic, and to reduce the overhead of function calls (pythonistas seem to worry about this) I now access the TPoint instance variables x and y directly.
Returning to python from ruby/java, it is the ruby features that I miss; it is so easy to use attr_accessor & attr_reader to define the access to variables in ruby. Further you really know where you stand regarding access to an objects variables in ruby (private unless exposed) I find the python global access to be both a bit inelegant and awkward.
Take processing sketches for example, where if you want to set something up (say a new instance of a class for example) in the setup() function, and then access it in the draw() loop you need to declare that variable as a global (in setup). For ruby, I generally favour just using attr_reader, then you know if you need to assign the variable you must use the sigil version of the variable.

Here is the revised sketch:-

   1 """
   2 alhambra.py by Martin Prout
   3 A third stab at converting my Alhambra tiling sketch to processing.py (overwites 2nd)
   4 Utilizes a custom lightweight TPoint (to replace the more fattening PVector)
   5 """
   6 import math
   7 from tpoint import TPoint
   8 
   9 xValues = [100, 300, 500, 700]
  10 yValues = [50 * math.sqrt(3), 150 * math.sqrt(3), 250 * math.sqrt(3), 350 * math.sqrt(3)]
  11 
  12 
  13 def setup():
  14     """
  15     processing setup
  16     """
  17     size(700, 650)
  18     background(21, 15, 72)
  19     smooth()
  20     render()
  21     saveFrame("alhambra.png")
  22     
  23     
  24 def draw_hexagon(xpos, ypos, sz, theta):
  25     """
  26     hexagon draw function
  27     """
  28     beginShape()
  29     for i in range(0, 6):
  30         vertex(xpos + sz*math.cos((math.pi/3 * i) + theta), ypos + sz*math.sin((math.pi/3 * i) +theta));
  31     endShape(CLOSE)
  32         
  33         
  34 def draw_triangle(x0, y0, sz, coluer, disp):
  35     """
  36     Wavy triangle draw function
  37     """
  38     # Set the three initial triangle points, thereafter calculate mid points, and  
  39     # quarter points. Then adjust the bezier curve control points.
  40     pts = []
  41     pts.append(TPoint(x0, y0 - sz/math.sqrt(3)))               # A (A, B and C are the triangle points)
  42     pts.append(TPoint(x0 - 0.5 * sz, y0 + (math.sqrt(3)*sz)/6))# B
  43     pts.append(TPoint(x0 + 0.5 * sz, y0 + (math.sqrt(3)*sz)/6))# C
  44     pts.append(pts[0].mid_point(pts[1]))                        # Ab (Ab, Bc and Ca are the triangle mid points)
  45     pts.append(pts[1].mid_point(pts[2]))                        # Bc
  46     pts.append(pts[0].mid_point(pts[2]))                        # Ca
  47     pts.append(pts[0].mid_point(pts[3]))                        # Aba (Aba ... are the triangle quarter points)
  48     adjust_bezier(pts[6], math.pi/3, disp*sz)                   # Aba
  49     pts.append(pts[3].mid_point(pts[1]))                        # Abb
  50     adjust_bezier(pts[7], math.pi/3, -disp*sz)                  # Abb
  51     pts.append(pts[1].mid_point(pts[4]))
  52     adjust_bezier(pts[8], math.pi/2, -disp*sz)
  53     pts.append(pts[4].mid_point(pts[2]))
  54     adjust_bezier(pts[9], math.pi/2, disp*sz)
  55     pts.append(pts[2].mid_point(pts[5]))
  56     adjust_bezier(pts[10], -math.pi/3, -disp*sz)
  57     pts.append(pts[5].mid_point(pts[0]))
  58     adjust_bezier(pts[11], -math.pi/3, disp*sz)
  59     # render triangle
  60     fill(coluer)
  61     beginShape()
  62     vertex(pts[0].x, pts[0].y)
  63     bezierVertex(pts[0].x, pts[0].y, pts[6].x, pts[6].y, pts[3].x, pts[3].y)
  64     bezierVertex(pts[3].x, pts[3].y, pts[7].x, pts[7].y, pts[1].x, pts[1].y)
  65     bezierVertex(pts[1].x, pts[1].y, pts[8].x, pts[8].y, pts[4].x, pts[4].y)
  66     bezierVertex(pts[4].x, pts[4].y, pts[9].x, pts[9].y, pts[2].x, pts[2].y)
  67     bezierVertex(pts[2].x, pts[2].y, pts[10].x, pts[10].y, pts[5].x, pts[5].y)
  68     bezierVertex(pts[5].x, pts[5].y, pts[11].x, pts[11].y, pts[0].x, pts[0].y)
  69     endShape(CLOSE)
  70     # set color and render a small hexagon
  71     fill(255)
  72     draw_hexagon(x0 + 4, y0, sz * 0.214, 0)
  73     
  74     
  75 def adjust_bezier(bzpoint, theta, disp):
  76     """
  77     Adjust the Bezier control point
  78     """
  79     bzpoint.add(TPoint(math.cos(theta)*disp, math.sin(theta)*disp))
  80     
  81 def render():
  82     """
  83     Tesselate the wavy triangles
  84     """
  85     for column in range(0,len(xValues)):
  86         for row in range(0,len(yValues)):
  87             if (row % 2 == 0):
  88                 if (column % 3 ==0):
  89                     draw_triangle(xValues[column], yValues[row], 200, color(255, 0, 0), 0.32)
  90                 else:
  91                     draw_triangle(xValues[column], yValues[row], 200, color(255, 0, 255), 0.32)
  92             elif ((column - 2) % 3 ==0):
  93                 draw_triangle(xValues[column] - 100, yValues[row], 200, color(255, 0, 0), 0.32)
  94             else:
  95                 draw_triangle(xValues[column] - 100, yValues[row], 200, color(255, 0, 255), 0.32)
  96                 
  97 def draw():
  98     """
  99     Is the processing draw loop
 100     """
 101     pass

Here is the revised TPoint class (no accessors, public attributes)

   1 """
   2 tpoint.py is module that contains my lighweight TPoint class
   3 avoids accessors by having public attributes (as python default)
   4 """
   5 
   6 class TPoint(object):
   7     """
   8     A lightweight convenience class to store triangle point data
   9     With a function to return new instance a the mid point between 
  10     two TPoint objects
  11     """
  12     
  13     def __init__(self, x = 0, y = 0):
  14         """
  15         Initialise the new TPoint object
  16         """
  17         self.x = x
  18         self.y = y 
  19        
  20         
  21     def mid_point(self, tvector):
  22         """
  23         Calculate the mid point between this and another tvector
  24         returns a new TVector representing the mid point
  25         """
  26         return TPoint((self.x + tvector.x)/2, (self.y + tvector.y)/2)
  27         
  28     def add(self, tvector):
  29         """
  30         Adds tvector TPoints to self
  31         """
  32         self.x += tvector.x
  33         self.y += tvector.y

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