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

Monday, 30 November 2009

Towards a forest, or too many matrix calls

If you wanted to create a 'forest' using the Lindenmayer system, you might want to avoid too much pushing and popping of the picture matrix. This is a simple example; where you will find that you need to preserve the original context if you want to draw a second 'tree', otherwise you will not be quite sure where it will get placed, and it will almost certainly be canted over to some extent. All this pushing and popping has a memory load, and creates a lot of 'garbage' for the jvm to cleanup.


########################################################
# Toward a forest implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
########################################################

require 'fplant'

class Forest_Test < Processing::App
  attr_reader :fplant, :fplant2
  def setup
    size 600, 600
    @fplant = FPlant.new
    @fplant2 = FPlant.new
    fplant.simulate 6
    fplant2.simulate 6
    no_loop
  end
  def draw
    background 0
    push_matrix   # save original context
    translate 200, 550
    fplant.render
    pop_matrix    # restore original context
    translate 350, 570
    fplant2.render
    save_frame "forest.png"
  end
end

############################
# fplant.rb
###########################

require 'jcode'  ## required until jruby supports ruby 1.9

class FPlant
  include Processing::Proxy

  DELTA = (Math::PI/180) * 22.5

  attr_accessor :axiom, :rule, :rule1, :start_length, :production, :draw_length

  def initialize
    @axiom = "X"
    @rule = "FF"
    @rule1 = "F-[[X]+X]+F[+FX]-F"   # note nested matrices
    @start_length = 200
    stroke 0, 255, 0                # ghastly green
    stroke_weight 3
    reset
  end

  def reset                         # initialize or reset variables
    @production = axiom
    @draw_length = start_length
  end

  def iterate prod_, rule_, rule1_
    @draw_length *=  0.5
    new_production = prod_    
    new_production.gsub!('F', rule_)
    new_production.gsub!('X', rule1_)
  end

  def render
    production.each_char do |element|
      case element
      when 'F'                     # NB using affine transforms
        line(0, 0, 0, -draw_length)
        translate(0, -draw_length)
      when '+'
        rotate(+DELTA)  
      when '-'
        rotate(-DELTA)    
      when '['
        push_matrix
      when ']'
        pop_matrix
      when 'X'
      else puts "Grammar not recognized"
      end
    end
  end

  def simulate gen
    gen.times do
      @production = iterate(production, rule, rule1)
    end
  end
end



 


















Here is a possible solution, implement your own stack, and only store what you need to, it must be more efficient (see later post, my ripped square fractal for an even lighter weight implementation)!!!


########################################################
# Toward a forest implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
########################################################

require 'fplant'

class Forest_Test < Processing::App
  attr_reader :fplant, :fplant2
  def setup
    size 600, 600
    @fplant = FPlant.new 200, 550, Math::PI/2
    @fplant2 = FPlant.new 350, 570, Math::PI/2
    fplant.simulate 6
    fplant2.simulate 6
    no_loop
  end

  def draw
    background 0
    fplant.render
    fplant2.render
    save_frame "forest.png"
  end
end

############################
# fplant.rb
###########################

require 'jcode'  ## required until jruby supports ruby 1.9
require 'stack'

class FPlant
  include Processing::Proxy

  DELTA = (Math::PI/180) * 22.5

  attr_accessor :axiom, :rule, :rule1, :start_length, :production, :draw_length, :stack, :xpos, :ypos, :theta

  def initialize x0, y0, angle = 0
    @stack = Stack.new
    @xpos = x0
    @ypos = y0
    @theta = angle
    @axiom = "X"
    @rule = "FF"
    @rule1 = "F-[[X]+X]+F[+FX]-F"   # note nested matrices
    @start_length = 200
    stroke 0, 255, 0                # ghastly green
    stroke_weight 3
    reset
  end

  def reset                         # initialize or reset variables
    @production = axiom
    @draw_length = start_length
  end

  def iterate prod_, rule_, rule1_
    @draw_length *=  0.5
    new_production = prod_     
    new_production.gsub!('F', rule_)
    new_production.gsub!('X', rule1_)
  end

  def render
    production.each_char do |element|
      case element
      when 'F'                     # NB NOT using affine transforms
        line(xpos, ypos, (@xpos += -draw_length * Math.cos(theta)), (@ypos += -draw_length * Math.sin(theta)))     
      when '+'
        @theta += DELTA   
      when '-'
        @theta -= DELTA     
      when '['
        stack.push xpos, ypos, theta # using a simple stack to store individual turtle values
      when ']'
        @xpos, @ypos, @theta = stack.pop
      when 'X'
      else puts "Grammar not recognized"
      end
    end
  end

  def simulate gen
    gen.times do
      @production = iterate(production, rule, rule1)
    end
  end
end

############################
# stack.rb  a lightweight stack utility, tuned for turtles
###########################

class Stack  # a simple turtle stack

  def initialize
   @the_stack = []
  end

  def push(xpos, ypos, angle)
    turtle = [xpos, ypos, angle]
    @the_stack.push turtle
  end

  def pop
    @the_stack.pop
  end

  def count
    @the_stack.length
  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