For the grammar library see my previously posted Cesàro fractal (plant grammar after Hung-Wen Chen). If you are using either Windows or Mac OS then try using opengl instead of P3D. There is an issue with linux such that you need to use fullscreen in combination with opengl, and that for me at least mucks up using the control panel.
########################################################
# A 3D plant implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
########################################################
require 'plant'
class Plant_Test < Processing::App
load_libraries :grammar, :control_panel
attr_reader :plant, :rot_z, :rot_y, :rot_x
def setup()
size(800, 800, P3D)
setup_panel
@plant = Plant.new
plant.create_grammar(5)
no_stroke()
@rot = 0
end
def setup_panel
control_panel do |c|
c.title = "Control:"
c.slider :rot_x, 43..63, 53.81
c.slider :rot_y, 18..38, 28.41
c.slider :rot_z, -20..20, -0.45
c.button :reset
end
end
def reset
@rot_x = 53.81
@rot_y = 28.41
@rot_z = -0.45
end
def draw()
background(0)
ambient_light(0, 255, 0)
directional_light(0, 255, 0, 0.0, -1.0, 0.0)
camera(rot_x, rot_y, rot_z, # eye_x, eye_y, eye_z
0.0, -50.0, 0.0, # centerX, centerY, centerZ
0.0, 1.0, 0.0)
plant.render()
end
end
############################
# plant.rb
###########################
class Plant
include Processing::Proxy
attr_reader :grammar, :axiom, :production, :premis, :rule,
:theta, :scale_factor, :distance, :phi
def initialize()
@axiom = "F"
@grammar = Grammar.new(axiom)
@production = axiom
@premis = "F"
@rule = "F[&+F]F[->F][->F][&F]"
@scale_factor = 0.8
@distance = 3
@theta = Math::PI/180 * 28
@phi = Math::PI/180 * 28
grammar.add_rule(premis, rule)
no_stroke()
end
def render()
production.each_char do |ch|
case(ch)
when "F"
fill(0, 200, 0)
translate(0, distance/-2, 0)
box(distance/4 , distance, distance/4)
translate(0, distance/-2, 0)
when "-"
rotateX(-theta)
when "+"
rotateX(theta)
when "&"
rotateY(-phi%(Math::PI*2))
when ">"
rotateZ(phi%(Math::PI*2))
when "<"
rotateZ(-phi)
when "["
push_matrix
@distance = distance * scale_factor
when "]"
pop_matrix
@distance = distance / scale_factor
else
puts("character '#{ch}' not in grammar")
end
end
end
##############################
# create grammar from axiom and
# rules (adjust scale)
##############################
def create_grammar(gen)
@production = @grammar.generate gen
end
end
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Wednesday, 13 January 2010
Wednesday, 6 January 2010
Another control panel example with odd turtle turns
##
# A Spiro fractal implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
###
require 'spiro'
class Spiro_Test < Processing::App
load_libraries :grammar, :control_panel
attr_reader :spiro, :repeat, :xpos, :ypos
def setup
size 600, 800
setup_panel
@xpos = width/3
@ypos = height/4
@repeat = 5
redraw
end
def setup_panel
control_panel do |c|
c.title = "Control:"
c.menu(:generation, ['5', '6', '7', '8'], '5') {|m| load_menu_item(m) }
c.slider(:xpos, 0..600, 200)
c.slider(:ypos, 0..800, 200)
c.button(:redraw)
end
end
def redraw
@spiro = Spiro.new(xpos, ypos)
spiro.create_grammar(repeat)
end
def load_menu_item(item)
@repeat = item.to_i
case repeat
when 7
@xpos = width/2
@ypos = height/2
when 5, 6
@xpos = width/3
@ypos = height/4
else
@xpos = width/2
@ypos = height/2
end
end
def draw
background 0
spiro.render
end
end
############################
# spiro.rb
###########################
class Spiro
include Processing::Proxy
attr_accessor :axiom, :grammar, :start_length, :theta, :production, :draw_length, :xpos, :ypos
DELTA = (Math::PI/180) * 251 # convert degrees to radians
GAMMA = (Math::PI/180) * 252 # convert degrees to radians
def initialize(xpos, ypos)
@axiom = "F"
@grammar = Grammar.new axiom
grammar.add_rule "F", "F-F+F+F-F" # replace F rule see grammar library
@start_length = 100
@theta = 0.0
@xpos = xpos
@ypos = ypos
stroke 255
@production = axiom
@draw_length = start_length
end
def render # NB using affine transforms here
translate(xpos, ypos)
production.each_char do |element|
case element
when 'F'
line(0, 0, draw_length, 0)
translate(draw_length, 0)
when '+'
rotate(DELTA) # rotate by +DELTA
when '-'
rotate(-GAMMA) # rotate by - GAMMA
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.6**gen
@production = grammar.generate gen
end
end
Labels:
Spirograph type fractal
Adding some controls to Sierpinski sketch
##
# A controllable Sierpinski Variant implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
###
require 'sierpinski'
class Sierpinski_Test < Processing::App
load_libraries :grammar, :control_panel
attr_reader :sierpinski, :fine, :coarse, :generation, :scale_factor, :xpos, :ypos
def setup
setup_panel
size(1000, 1000)
redraw_fractal
end
def setup_panel
control_panel do |c|
c.title = "Controller:"
c.slider :fine, 0.00..3.00, 0.00
c.slider :coarse, 0.00..3.00, 0.00
c.slider :generation, 5..8, 6
c.slider :scale_factor, 1..4, 1
c.slider :xpos, 0..1000, 100
c.slider :ypos, 0..1000, 700
c.button :redraw_fractal
end
end
def redraw_fractal
@sierpinski = Sierpinski.new(xpos, ypos)
sierpinski.gamma = fine/150 + coarse/15
sierpinski.factor = scale_factor
sierpinski.create_grammar(generation.to_int)
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, :gamma, :factor
attr_reader :xpos, :ypos
DELTA = (Math::PI/180) * 60.0
def initialize(xpos, ypos)
@xpos = xpos
@ypos = ypos
@axiom = "A"
@grammar = Grammar.new axiom
grammar.add_rule "A", "B+A+B"
grammar.add_rule "B", "A-B-A"
@start_length = 400
@factor = 1
stroke 255
@production = axiom
@draw_length = start_length
@gamma = 0
end
def render # NB using affine transforms here
translate(xpos, ypos)
scale(factor)
production.each_char do |element|
case element
when 'B', 'A'
line(0, 0, draw_length, 0)
translate(draw_length, 0)
when '+'
rotate(DELTA)
when '-'
rotate(-(DELTA+gamma))
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
end
Click on image to see full size, I have since added a coarse slider (and renamed delta as fine). The fine range here is fairly conservative, you should try that first. Before trying the coarse or use both controls in combination, to get some pretty funky fractals...
NB requires my custom grammar library see Cesàro fractal in a previous post for code.
Labels:
control panel,
l-systems,
ruby processing
Tuesday, 5 January 2010
A Sierpinski Variant using L-Systems and ruby processing
A Sierpinski (triangle) variant after Mark Meyers nodebox l-systems...
for the grammar library see a previous post Cesàro fractal
##
# A Sierpinski Variant implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
###
require 'sierpinski'
class Sierpinski_Test < Processing::App
load_libraries 'grammar'
attr_reader :sierpinski
def setup
size 500, 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, :theta, :production, :draw_length, :xpos, :ypos
DELTA = (Math::PI/180) * 60.0 # convert degrees to radians
GAMMA = (Math::PI/180) * 60.25
def initialize
@axiom = "A"
@grammar = Grammar.new axiom
grammar.add_rule "A", "B+A+B"
grammar.add_rule "B", "A-B-A"
@start_length = 450
@theta = 0.0
@xpos = width * 0.95
@ypos = height * 0.8
stroke 255
@production = axiom
@draw_length = start_length
end
def render # NB not using affine transforms here
repeats = 1
production.each_char do |element|
case element
when 'B', 'A'
line(xpos, ypos, (@xpos -= draw_length * Math.cos(theta)), (@ypos += draw_length * Math.sin(theta)))
when '+'
@theta += DELTA * repeats
repeats = 1
when '-'
@theta -= GAMMA * repeats
repeats = 1
when '2'
repeats = 2
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
end
for the grammar library see a previous post Cesàro fractal
##
# A Sierpinski Variant implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
###
require 'sierpinski'
class Sierpinski_Test < Processing::App
load_libraries 'grammar'
attr_reader :sierpinski
def setup
size 500, 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, :theta, :production, :draw_length, :xpos, :ypos
DELTA = (Math::PI/180) * 60.0 # convert degrees to radians
GAMMA = (Math::PI/180) * 60.25
def initialize
@axiom = "A"
@grammar = Grammar.new axiom
grammar.add_rule "A", "B+A+B"
grammar.add_rule "B", "A-B-A"
@start_length = 450
@theta = 0.0
@xpos = width * 0.95
@ypos = height * 0.8
stroke 255
@production = axiom
@draw_length = start_length
end
def render # NB not using affine transforms here
repeats = 1
production.each_char do |element|
case element
when 'B', 'A'
line(xpos, ypos, (@xpos -= draw_length * Math.cos(theta)), (@ypos += draw_length * Math.sin(theta)))
when '+'
@theta += DELTA * repeats
repeats = 1
when '-'
@theta -= GAMMA * repeats
repeats = 1
when '2'
repeats = 2
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
end
Monday, 4 January 2010
An interactive l-system sketch using the Control Panel
Requires my grammar library see Cesàro fractal, cycles through 2, 3 and 4 generation versions using the control panel selector (and redraw button).
#####################
# pentagonal_test.rb
# by Martin Prout
#####################
require 'pentagonal'
class Pentagonal_Test < Processing::App
load_library :control_panel
load_library :grammar
attr_reader :pentagonal, :repeat
def setup
size 600, 600
setup_panel
@pentagonal = Pentagonal.new
pentagonal.create_grammar(2)
end
def setup_panel
control_panel do |c|
c.title = "Control:"
c.menu(:generation, ['2', '3', '4', '5'], '2') {|m| load_menu_item(m) }
c.button(:redraw)
end
end
def draw
background(0)
stroke(255)
pentagonal.render
end
def redraw
@pentagonal = Pentagonal.new
pentagonal.create_grammar(repeat)
end
def load_menu_item(item)
@repeat = item.to_i
end
end
############################
# pentagonal.rb
# Pentagonal Fractal
###########################
class Pentagonal
include Processing::Proxy
SCALE = 0.44
DELTA = (Math::PI/180) * 36.0 # convert degrees to radians
XPOS = 0 # placeholders for turtle array
YPOS = 1
ANGLE = 2
attr_accessor :axiom, :grammar, :start_length, :theta, :production, :draw_length, :xpos, :ypos
def initialize
setup_grammar
@start_length = 100
@theta = 0.0
@xpos = width*0.5
@ypos = height*0.7
@draw_length = start_length
end
def setup_grammar
@axiom = "F--F--F--F--F"
@grammar = Grammar.new axiom
grammar.add_rule "F", "F+F+F-F-F-F+F+F+" # replace F with this string see iterate function
@production = axiom
end
def render
turtle = [xpos, ypos, 0.0] # simple array act as turtle
production.scan(/./).each do |element|
case element
when 'F' # NB NOT using affine transforms
turtle = draw_line(turtle, draw_length)
when '+'
turtle[ANGLE] += DELTA
when '-'
turtle[ANGLE] -= DELTA
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 *= SCALE**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
#####################
# pentagonal_test.rb
# by Martin Prout
#####################
require 'pentagonal'
class Pentagonal_Test < Processing::App
load_library :control_panel
load_library :grammar
attr_reader :pentagonal, :repeat
def setup
size 600, 600
setup_panel
@pentagonal = Pentagonal.new
pentagonal.create_grammar(2)
end
def setup_panel
control_panel do |c|
c.title = "Control:"
c.menu(:generation, ['2', '3', '4', '5'], '2') {|m| load_menu_item(m) }
c.button(:redraw)
end
end
def draw
background(0)
stroke(255)
pentagonal.render
end
def redraw
@pentagonal = Pentagonal.new
pentagonal.create_grammar(repeat)
end
def load_menu_item(item)
@repeat = item.to_i
end
end
############################
# pentagonal.rb
# Pentagonal Fractal
###########################
class Pentagonal
include Processing::Proxy
SCALE = 0.44
DELTA = (Math::PI/180) * 36.0 # convert degrees to radians
XPOS = 0 # placeholders for turtle array
YPOS = 1
ANGLE = 2
attr_accessor :axiom, :grammar, :start_length, :theta, :production, :draw_length, :xpos, :ypos
def initialize
setup_grammar
@start_length = 100
@theta = 0.0
@xpos = width*0.5
@ypos = height*0.7
@draw_length = start_length
end
def setup_grammar
@axiom = "F--F--F--F--F"
@grammar = Grammar.new axiom
grammar.add_rule "F", "F+F+F-F-F-F+F+F+" # replace F with this string see iterate function
@production = axiom
end
def render
turtle = [xpos, ypos, 0.0] # simple array act as turtle
production.scan(/./).each do |element|
case element
when 'F' # NB NOT using affine transforms
turtle = draw_line(turtle, draw_length)
when '+'
turtle[ANGLE] += DELTA
when '-'
turtle[ANGLE] -= DELTA
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 *= SCALE**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
Saturday, 2 January 2010
Checking the probabilities
After a bit of a faux pas, where I needed to change my code I thought I'd better check the randomness of the code (its so easy in ruby).
class ProbTest
attr_accessor :rules
def initialize
@rules = Hash.new
rules.store(["abc", 0], 0.1)
rules.store(["def", 0], 0.2)
rules.store(["ghi", 0], 0.3)
rules.store(["jkl", 0], 0.4)
end
def stochastic_rule(rules)
target = rand
rules.each do |item, weight|
return item unless target > weight
target -= weight
end
end
def increment(rules)
item = stochastic_rule(rules)
item[1] += 1
return item
end
end
test = ProbTest.new
10000.times do
test.increment(test.rules)
end
puts test.rules
# {["abc", 1002]=>0.1, ["def", 2043]=>0.2, ["ghi", 2980]=>0.3, ["jkl", 3975]=>0.4}
Such a simple check would not satisfy a statistician, but there good enough for me... to see it working in my December 10th post.
class ProbTest
attr_accessor :rules
def initialize
@rules = Hash.new
rules.store(["abc", 0], 0.1)
rules.store(["def", 0], 0.2)
rules.store(["ghi", 0], 0.3)
rules.store(["jkl", 0], 0.4)
end
def stochastic_rule(rules)
target = rand
rules.each do |item, weight|
return item unless target > weight
target -= weight
end
end
def increment(rules)
item = stochastic_rule(rules)
item[1] += 1
return item
end
end
test = ProbTest.new
10000.times do
test.increment(test.rules)
end
puts test.rules
# {["abc", 1002]=>0.1, ["def", 2043]=>0.2, ["ghi", 2980]=>0.3, ["jkl", 3975]=>0.4}
Such a simple check would not satisfy a statistician, but there good enough for me... to see it working in my December 10th post.
Labels:
testing the code
Monday, 28 December 2009
Peano curve fractal using Lindenmayer Systems
Peano curve using my custom library
######################################################
# peano_test.rb
#
# Lindenmayer System in ruby-processing by Martin Prout
######################################################
require 'grammar'
require 'peano'
class Peano_Test < Processing::App
attr_reader :peano
def setup
size 1000, 1000
@peano = Peano.new
peano.create_grammar 4
no_loop
end
def draw
background 0
peano.render
end
end
############################
# peano.rb
#
# Peano Fractal
###########################
class Peano
include Processing::Proxy
require 'grammar'
DELTA = (Math::PI/180) * 60.0 # convert degrees to radians
attr_accessor :axiom, :grammar, :start_length, :theta, :production, :xpos, :ypos, :draw_length
def initialize
@axiom = "XF"
@grammar = Grammar.new axiom
grammar.add_rule 'X', "X+YF++YF-FX--FXFX-YF+" ## replace X with this string see grammar library
grammar.add_rule 'Y', "-FX+YFYF++YF+FX--FX-Y" ## replace Y with this string see grammar library
@start_length = 120.0
@theta = 0.0
@xpos = width/3
@ypos = height/10
@production = axiom
@draw_length = start_length
end
def render()
@production.each_char do |element|
case element
when 'F'
line(@xpos, @ypos, (@xpos -= (draw_length * Math.cos(theta))), (@ypos += (draw_length * Math.sin(theta))))
when '+'
@theta += DELTA
when '-'
@theta -= DELTA
when 'X','Y' ## do nothing except recognize the grammar
else puts "Grammar not recognized"
end
end
end
def create_grammar(gen)
stroke 255
@draw_length *= 0.6**gen
@production = grammar.generate gen
end
end
######################################################
# peano_test.rb
#
# Lindenmayer System in ruby-processing by Martin Prout
######################################################
require 'grammar'
require 'peano'
class Peano_Test < Processing::App
attr_reader :peano
def setup
size 1000, 1000
@peano = Peano.new
peano.create_grammar 4
no_loop
end
def draw
background 0
peano.render
end
end
############################
# peano.rb
#
# Peano Fractal
###########################
class Peano
include Processing::Proxy
require 'grammar'
DELTA = (Math::PI/180) * 60.0 # convert degrees to radians
attr_accessor :axiom, :grammar, :start_length, :theta, :production, :xpos, :ypos, :draw_length
def initialize
@axiom = "XF"
@grammar = Grammar.new axiom
grammar.add_rule 'X', "X+YF++YF-FX--FXFX-YF+" ## replace X with this string see grammar library
grammar.add_rule 'Y', "-FX+YFYF++YF+FX--FX-Y" ## replace Y with this string see grammar library
@start_length = 120.0
@theta = 0.0
@xpos = width/3
@ypos = height/10
@production = axiom
@draw_length = start_length
end
def render()
@production.each_char do |element|
case element
when 'F'
line(@xpos, @ypos, (@xpos -= (draw_length * Math.cos(theta))), (@ypos += (draw_length * Math.sin(theta))))
when '+'
@theta += DELTA
when '-'
@theta -= DELTA
when 'X','Y' ## do nothing except recognize the grammar
else puts "Grammar not recognized"
end
end
end
def create_grammar(gen)
stroke 255
@draw_length *= 0.6**gen
@production = grammar.generate gen
end
end
Labels:
l-system,
Peano curve
Subscribe to:
Comments (Atom)
Followers
About Me
- monkstone
- I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2







