Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Thursday 14 January 2010

Refactoring my Penrose Snowflake

Dan Mayer over at devver.net has kindly analysed and re-factored my penrose_snowflake with caliper (something I've never heard of but then I'm not really in the community, just ploughing my own furrow). He also extended my code in an interesting way to produce a "snowflake screensaver" which labours a bit.
Well I tried out Dans refactoring on my own code (which I had already changed to have a separate grammar library).  Here is my revised penrose_snowflake.rb (requires grammar.rb and runs with penrose.rb) incorporating some additional refactoring, which you may or may not like depending on your view of the ternary operator (my multiplier has fewer lines of code and is 4% faster according to my benchmark test). However I have since come up with a different version which I have now used to replace my original posting!!! I think the new version will come into its own for my bracketed L-Systems and my Island fractal which is next on the list for refactoring.



class PenroseSnowflake
  include Processing::Proxy

  attr_accessor :axiom, :grammar, :start_length, :theta, :production, :draw_length,
    :repeats, :xpos, :ypos
  DELTA = (Math::PI/180) * 18.0 # convert degrees to radians

  def initialize
    @axiom = "F3-F3-F3-F3-F"
    @grammar = Grammar.new axiom
    grammar.add_rule "F", "F3-F3-F45-F++F3-F"
    @start_length = 450.0
    @theta = 0
    @xpos = width * 0.8
    @ypos = height * 0.95
    @production = axiom
    @draw_length = start_length
  end

  ##############################################################################
  # Not strictly in the spirit of either processing or L-Systems in my iterate
  # function I have ignored the processing translate/rotate functions in favour
  # of the direct calculation of the new x and y positions, thus avoiding such
  # affine transformations.
  ##############################################################################

  def render()
    repeats = 1
    production.each_char do |element|
      case element
      when 'F'
        line(xpos, ypos, (@xpos -= multiplier(repeats, :cos)), (@ypos += multiplier(repeats, :sin)))
        repeats = 1
      when '+'
        @theta += DELTA * repeats
        repeats = 1
      when '-'
        @theta -= DELTA * repeats
        repeats = 1
      when '3', '4', '5'
        repeats += Integer(element)
      else
        puts "Character '#{element}' is not in grammar"
      end
    end
  end

  ##############################
  # create grammar from axiom and
  # rules (adjust scale)
  ##############################

  def create_grammar(gen)
    @draw_length *= 0.4**gen
    @production = grammar.generate gen
  end

  def multiplier(repeats, type)
    value = draw_length * repeats
    (type == :cos)?  value * Math.cos(theta) : value *  Math.sin(theta)
  end
end

No comments:

Post a Comment

Followers

Blog Archive

About Me

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