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