Skip to content

Instantly share code, notes, and snippets.

@monkstone
Created December 31, 2009 08:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monkstone/266673 to your computer and use it in GitHub Desktop.
Save monkstone/266673 to your computer and use it in GitHub Desktop.
Stochastic LSystem Plants in ruby processing
########################################################
# stochastic_test.rb
#
# Lindenmayer System in ruby-processing by Martin Prout
########################################################
require 'stochastic_plant'
class Stochastic_Test < Processing::App
attr_reader :stochastic, :stochastic1, :stochastic2
def setup
size 1000, 800
@stochastic = StochasticPlant.new 200, 700
@stochastic1 = StochasticPlant.new 500, 700
@stochastic2 = StochasticPlant.new 700, 700
stochastic.create_grammar 5
stochastic1.create_grammar 4 # simpler plant
stochastic2.create_grammar 5
no_loop
end
def draw
background 0
stroke(0, 255, 0)
stroke_width(3)
stochastic.render
stochastic1.render
stochastic2.render
end
end
############################
# stochastic_plant.rb
# includes option for no pen
# move forward, unused here
###########################
require 'stochastic_grammar'
class StochasticPlant
include Processing::Proxy
attr_reader :grammar, :axiom, :draw_length, :theta, :xpos, :ypos, :production, :pen_down
XPOS = 0 # placeholders for turtle array
YPOS = 1
ANGLE = 2
DELTA = PI/8
def initialize xpos_, ypos_
@draw_length = 350
@xpos = xpos_
@ypos = ypos_
@theta = PI/2 # this way is up?
setup_grammar
end
def setup_grammar
@axiom = "F"
@grammar = StochasticGrammar.new(axiom)
grammar.add_rule("F", "F[+F]F[-F]F", 0.1)
grammar.add_rule("F", "F[+F]F", 0.45)
grammar.add_rule("F", "F[-F]F", 0.45)
@production = axiom
@pen_down = false
end
def render
stack = Array.new # ruby array as the turtle stack
turtle = [xpos, ypos, theta] # ruby array as a turtle
production.scan(/./).each do |element|
case element
when 'F' # NB NOT using affine transforms
@pen_down = true
turtle = draw_line(turtle, draw_length)
when '+'
turtle[ANGLE] += DELTA
when '-'
turtle[ANGLE] -= DELTA
when '['
stack.push(turtle.dup) # push a copy of the current turtle to stack
when ']'
turtle = stack.pop # assign current turtle to an instance popped from the stack
else
puts "Character '#{element}' is not in grammar"
end
end
end
def create_grammar gen
@draw_length *= 0.5**gen
@production = grammar.generate gen
end
private
######################################################
# draws a line using current turtle and length parameters
# unless pen_down = false (then only move forward)
# 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) if pen_down
@pen_down = false
turtle = [new_xpos, new_ypos, turtle[ANGLE]]
end
end
########################
# stochastic_grammar.rb
# unweighted rules accepted
# with default weight = 1
# complex stochastic rule
########################
class StochasticGrammar
PROB = 1
attr_accessor :axiom, :srules
def initialize axiom
@axiom = axiom
@srules = Hash.new # rules dictionary (a hash of hashes)
end
######################################################
# randomly selects a rule (with a weighted probability)
#####################################################
def stochastic_rule(rules)
total = rules.inject(0) do |total, rule_and_weight|
total += rule_and_weight[PROB]
end
srand
chance = rand * total
rules.each do |item, weight|
return item unless chance > weight
chance -= weight
end
return rule
end
def has_rule?(pre)
@srules.has_key?(pre)
end
def add_rule(pre, rule, weight = 1) # default weighting 1 (can handle non-stochastic rules)
if (has_rule?(pre)) then # add to existing hash
srules[pre].store rule, weight
else
temp = Hash.new # create a new hash for rule/weights
temp.store rule, weight # add to new hash
srules.store pre, temp # store new hash with pre key
end
end
def new_production(prod)
prod.gsub!(/./) do |ch|
(has_rule?(ch)) ? stochastic_rule(srules[ch]) : ch
end
end
def generate(repeat = 0)
prod = axiom
repeat.times do
prod = new_production(prod)
end
return prod
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment