Here I map characters of the LSystem grammar to human readable symbols. This I do by creating a hash. I then convert axiom strings to an array of symbols. The grammar is then "produced" as an array of symbols. The renderer the interprets the production as an array of symbols (it might use less memory than non-symbolic version but is it any faster???). I don't seem to be able use collect, so I need to create a new array of symbols when generating the grammar.
##
# A Sierpinski Triangle implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
###
require 'symbol'
class Sierpinski_Test < Processing::App
load_library :grammar
attr_reader :sierpinski
def setup
size 610, 600
@sierpinski = Sierpinski.new
sierpinski.create_grammar(8)
no_loop
end
def draw
background 0
sierpinski.render
end
end
############################
# sierpinski.rb
###########################
class Sierpinski
include Processing::Proxy
attr_accessor :axiom, :grammar, :start_length, :production, :draw_length, :xpos, :ypos, :symbol_map
XPOS = 0
YPOS = 1
ANGLE = 2
DELTA = (Math::PI/180) * 120 # convert degrees to radians
def initialize
@start_length = 300
setup_grammar
@xpos = width/60
@ypos = height*0.9
stroke 255
@draw_length = start_length
end
def setup_grammar
@axiom = "FX"
@symbol_map = {"X" => :X, "+" => :PLUS, "-" => :MINUS, "F" => :FORWARD}
@grammar = Grammar.new(axiom, symbol_map)
grammar.add_rule "F", "FF" # replace F rule see grammar library
grammar.add_rule "X", "-FXF+FXF+FXF-" # replace X rule see grammar library
@production = axiom
end
def render # NB not using affine transforms here
turtle = [xpos, ypos, 0.0]
production.each do |element|
case element
when :FORWARD
turtle = draw_line(turtle, draw_length)
when :PLUS
turtle[ANGLE] += DELTA # rotate by + theta if going the affine transform route
when :MINUS
turtle[ANGLE] -= DELTA # rotate by - theta if going the affine transform route
when :X # do nothing except recognize :X as a word in the L-system grammar
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.5**gen
@production = grammar.generate gen
end
private
######################################################
# draws line using current turtle and length parameters
# returns a turtle corresponding to the new position
######################################################
def draw_line(turtle, length)
new_xpos = turtle[XPOS] + length * Math.cos(turtle[ANGLE])
new_ypos = turtle[YPOS] + length * Math.sin(turtle[ANGLE])
line(turtle[XPOS], turtle[YPOS], new_xpos, new_ypos)
turtle = [new_xpos, new_ypos, turtle[ANGLE]]
end
end
############################
# grammar.rb
# Non-stochastic symbolic grammar
# with unique premise/rules
############################
class Grammar
attr_accessor :axiom, :rules, :symbol_map
def initialize(axiom, symbol_map)
@symbol_map = symbol_map
@axiom = string_to_symbol_array(axiom)
@rules = Hash.new
end
def string_to_symbol_array(a_string)
sym_array = []
a_string.scan(/./).each do |ch|
sym_array.push(symbol_map[ch])
end
return sym_array
end
def add_rule premise, rule
skey = symbol_map[premise]
svalue = string_to_symbol_array(rule)
rules.store(skey, svalue)
end
##########################################
# control the number of iterations
# default 0, returns the axiom
##########################################
def generate repeat = 0
prod = axiom
repeat.times do
prod = new_production(prod)
end
return prod
end
##########################################
# replace each pre symbol with un-splatted symbol array
# from rules lookup
##########################################
def new_production production
prod = []
production.each do |c|
(r = rules[c]) ? prod.push(*r) : prod.push(c)
end
return prod
end
end
No comments:
Post a Comment