It was very constructive to see if I could translate the EisenScript to my ruby processing DSL, I immediately learnt something about the EisenScript (recursion seems to be implicit, something I had not understood before). What I learnt about my library, was I was losing some context by directly altering the attitude (the image expected from the EisenScript appeared briefly but quickly degraded). I have now re-factored the sketch to only adjust the initial angle, the x rotation seems to affect the amplitude of deformation from the plane of the spirals. The y and z rotation give control of the attitude, and need to be used in combination to fully explore the 3 dimensional shape, which is now somewhat like that produced by StrucureSynth.
Here is my revised 3d context free DSL (test_free.rb):-
# A Context-Free library for Ruby-Processing, inspired by
# contextfreeart.org and StructureSynth
# Built on Jeremy Ashkenas context free DSL script
module Processing
class ContextFree
include Processing::Proxy
attr_accessor :rules, :app, :xr, :yr, :zr
AVAILABLE_OPTIONS = [:x, :y, :z, :rx, :ry, :rz, :size, :color, :hue, :saturation, :brightness, :alpha]
HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
# Define a context-free system. Use this method to create a ContextFree
# object. Call render() on it to make it draw.
def self.define(&block)
cf = ContextFree.new
cf.instance_eval &block
cf
end
# Initialize a bare ContextFree object with empty recursion stacks.
def initialize
@app = $app
@graphics = $app.g
@finished = false
@rules = {}
@rewind_stack = []
@matrix_stack = []
@xr = 0
@yr = 0
@zr = 0
end
# Create an accessor for the current value of every option. We use a values
# object so that all the state can be saved and restored as a unit.
AVAILABLE_OPTIONS.each do |option_name|
define_method option_name do
@values[option_name]
end
end
# Here's the first serious method: A Rule has an
# identifying name, a probability, and is associated with
# a block of code. These code blocks are saved, and indexed
# by name in a hash, to be run later, when needed.
# The method then dynamically defines a method of the same
# name here, in order to determine which rule to run.
def rule(rule_name, prob=1, &proc)
@rules[rule_name] ||= {:procs => [], :total => 0}
total = @rules[rule_name][:total]
@rules[rule_name][:procs] << [(total...(prob+total)), proc]
@rules[rule_name][:total] += prob
unless ContextFree.method_defined? rule_name
self.class.class_eval do
eval <<-METH
def #{rule_name}(options)
merge_options(@values, options)
pick = determine_rule(#{rule_name.inspect})
@finished = true if @values[:size] < @values[:stop_size]
unless @finished
get_ready_to_draw
pick[1].call(options)
end
end
METH
end
end
end
# Rule choice is random, based on the assigned probabilities.
def determine_rule(rule_name)
rule = @rules[rule_name]
chance = rand * rule[:total]
pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
return pick.flatten
end
# At each step of the way, any of the options may change, slightly.
# Many of them have different strategies for being merged.
def merge_options(old_ops, new_ops)
return unless new_ops
# Do size first
old_ops[:size] *= new_ops[:size] if new_ops[:size]
new_ops.each do |key, value|
case key
when :size
when :x, :y, :z
old_ops[key] = value * old_ops[:size]
when :rz, :ry, :rx
old_ops[key] = value * (Math::PI / 180.0)
when :hue, :saturation, :brightness, :alpha
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key]] *= value
old_ops[:color] = adjusted
when :width, :height
old_ops[key] *= value
when :color
old_ops[key] = value
else # Used a key that we don't know about or trying to set
merge_unknown_key(key, value, old_ops)
end
end
end
# Using an unknown key let's you set arbitrary values,
# to keep track of for your own ends.
def merge_unknown_key(key, value, old_ops)
key_s = key.to_s
if key_s.match(/^set/)
key_sym = key_s.sub('set_', '').to_sym
if key_s.match(/(brightness|hue|saturation|alpha)/)
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key_sym]] = value
old_ops[:color] = adjusted
else
old_ops[key_sym] = value
end
end
end
# Doing a 'split' saves the context, and proceeds from there,
# allowing you to rewind to where you split from at any moment.
def split(options=nil, &block)
save_context
merge_options(@values, options) if options
yield
restore_context
end
# Saving the context means the values plus the coordinate matrix.
def save_context
@rewind_stack.push @values.dup
@matrix_stack << @graphics.get_matrix
end
# Restore the values and the coordinate matrix as the recursion unwinds.
def restore_context
@values = @rewind_stack.pop
@graphics.set_matrix @matrix_stack.pop
end
# Rewinding goes back one step.
def rewind
@finished = false
restore_context
save_context
end
# Render the is method that kicks it all off, initializing the options
# and calling the first rule.
def render(rule_name, starting_values={})
@values = {:x => 0, :y => 0, :z => 0,
:rz => 0, :ry => 0, :rx => 0,
:size => 1, :width => 1, :height => 1,
:start_x => width/2, :start_y => height/2, :start_z => 0,
:color => [0.5, 0.5, 0.5, 1],
:stop_size => 1.5}
@values.merge!(starting_values)
@finished = false
@app.reset_matrix
@app.no_stroke
@app.color_mode HSB, 1.0
@app.translate @values[:start_x], @values[:start_y], @values[:start_z]
self.send(rule_name, {})
end
def rotate_x rt
@xr = rt
end
def rotate_y rt
@yr = rt
end
def rotate_z rt
@zr = rt
end
# Before actually drawing the next step, we need to move to the appropriate
# location.
def get_ready_to_draw
@app.translate(@values[:x], @values[:y], @values[:z])
@app.rotate_x(@values[:rx] + xr)
@app.rotate_y(@values[:ry] + yr)
@app.rotate_z(@values[:rz] + zr)
end
# Compute the rendering parameters for drawing a shape.
def get_shape_values(some_options)
old_ops = @values.dup
merge_options(old_ops, some_options) if some_options
@app.fill *old_ops[:color]
return old_ops[:size], old_ops
end
# Sphere, cube are the primitive drawing
def cube(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.box(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
def sphere(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.sphere_detail 10
@app.sphere(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
end
end
My Translation of default.es to ruby processing
# default.rb
# virtually a direct translation of default.es
# the StructureSynth default script
load_libraries :test_free, :control_panel
attr_reader :amplitude, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :default do
split do
R1 :brightness => 1
rewind
R2 :brightness => 1
end
end
rule :R1 do
sphere :brightness => 1
R1 :size => 0.99, :x => 0.25, :rz => 6, :ry => 6
end
rule :R2 do
sphere :brightness => 1
R2 :size => 0.99, :x => -0.25, :rz => 6, :ry => 6
end
end
end
def configure_control # setup control panel gui
control_panel do |c|
c.title = "Amplitude+Attitude"
c.slider :amplitude, -0.025..0.015, 0.0
c.slider :yrot, -6.3..6.3, 0.005
c.slider :zrot, -6.3..6.3, 0.005
end
end
def setup
size 800, 800, P3D
configure_control
smooth
setup_the_spiral
end
def draw
background 0
lights
@spiral.render :default, :start_x => 0, :start_y => 0, :start_z => -40, :size => height/400,
:stop_size => 0.2, :color => [0, 0.8, 0.8], :rx => amplitude, :ry => yrot, :rz => zrot
end
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Thursday, 9 September 2010
Testing the cube primitive in my context free DSL
Another context free DSL example, better annotated, control panel and library as previous post (not shown):-
# cube.rb
load_libraries :test_free, :control_panel
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do # define the cf rules
rule :cubeform do
5.times do |i|
split do # record the context
cuby :ry => 72 * i
rewind # restore the context
end
end
end
rule :beam do
6.times do |i|
split do # record the context
cube :y => 0.2 * i
rewind # restore the context
end
end
end
rule :cuby do
beam :brightness => 1
cuby :size => 0.98, :y => -0.24 # recursive call ends when size < 1
end
end
end
def setup # static setup
size 800, 800, P3D # I can only use P3D (unless full_screen) on linux opengl might work on mac/windows
configure_control
smooth
setup_the_spiral
end
def configure_control # setup control panel gui
control_panel do |c|
c.title = "Attitude Control"
c.slider :xrot, -3.1..3.1, 0.5
c.slider :yrot, -3.1..3.1, 0.5
c.slider :zrot, -3.1..3.1, 1.0
end
end
def draw # animation loop
background 0.8
lights
@spiral.render :cubeform, :start_x => 0, :start_y => 10, :start_z => -30, :size => height/300, :stop_size => 1, :color => [0, 0.8, 0.8]
@spiral.rotate_x xrot
@spiral.rotate_y yrot
@spiral.rotate_z zrot
end
Within the sketch :hue, :saturation and :brightness can be individually set. If you want to use :alpha, initialize :color with a 4 dimension array. Note, the color mode in the ruleset is hsb with range 0 .. 1.0. Increment and decrement is linear cf size where it is multiplicative.
# cube.rb
load_libraries :test_free, :control_panel
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do # define the cf rules
rule :cubeform do
5.times do |i|
split do # record the context
cuby :ry => 72 * i
rewind # restore the context
end
end
end
rule :beam do
6.times do |i|
split do # record the context
cube :y => 0.2 * i
rewind # restore the context
end
end
end
rule :cuby do
beam :brightness => 1
cuby :size => 0.98, :y => -0.24 # recursive call ends when size < 1
end
end
end
def setup # static setup
size 800, 800, P3D # I can only use P3D (unless full_screen) on linux opengl might work on mac/windows
configure_control
smooth
setup_the_spiral
end
def configure_control # setup control panel gui
control_panel do |c|
c.title = "Attitude Control"
c.slider :xrot, -3.1..3.1, 0.5
c.slider :yrot, -3.1..3.1, 0.5
c.slider :zrot, -3.1..3.1, 1.0
end
end
def draw # animation loop
background 0.8
lights
@spiral.render :cubeform, :start_x => 0, :start_y => 10, :start_z => -30, :size => height/300, :stop_size => 1, :color => [0, 0.8, 0.8]
@spiral.rotate_x xrot
@spiral.rotate_y yrot
@spiral.rotate_z zrot
end
Within the sketch :hue, :saturation and :brightness can be individually set. If you want to use :alpha, initialize :color with a 4 dimension array. Note, the color mode in the ruleset is hsb with range 0 .. 1.0. Increment and decrement is linear cf size where it is multiplicative.
Labels:
3D,
context free,
ruby processing
Wednesday, 8 September 2010
Experimental 3D Context Free DSL in ruby-processing, a bit of a monster?
This is at fairly experimental stage, I need to decide how to order rotations/translations. At the moment order is fixed by the library script, so it doesn't matter what order they are entered you get the default order!!!
This library cannot compete with StructureSynth which is a much more refined and complete program. However if you are used to ruby it may be easier to write your rules in ruby, rather than in the EisenScript syntax. The funky extra thing is, because it is based on processing you have the animation possibilities of the 'draw' loop. My monster in some directions could be seen as a beating heart.
To use the library, create a folder for you work say "work", create a sub-folder "library" and sub-sub-folder "test_free" (or whatever better name you can think of for the library). Put the library "test_free.rb" in the sub-sub-folder. Write you sketches in the "work" directory, and run them with "rp5 run my_sketch.rb". If you want to do it the really neat way use JEdit as your editor and my macro and commando tools, then you can run the sketch from the editor. The link to the tools describes live editing, which I haven't tried with this library so I would recommend just using the run mode for now.
If you are not a ruby meddler just get StructureSynth, and learn the EisenScript language. Otherwise you will need to get ruby-processing see link in my blog header to see how. My blog header also has link to the original Jeremy Ashkenas context-free.rb on github.
The test_free.rb library
# A Context-Free library for Ruby-Processing, inspired by
# contextfreeart.org and StructureSynth
# Built on Jeremy Ashkenas context free DSL script
module Processing
class ContextFree
include Processing::Proxy
attr_accessor :rules, :app, :xr, :yr, :zr
AVAILABLE_OPTIONS = [:x, :y, :z, :rx, :ry, :rz, :size, :color, :hue, :saturation, :brightness, :alpha]
HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
# Define a context-free system. Use this method to create a ContextFree
# object. Call render() on it to make it draw.
def self.define(&block)
cf = ContextFree.new
cf.instance_eval &block
cf
end
# Initialize a bare ContextFree object with empty recursion stacks.
def initialize
@app = $app
@graphics = $app.g
@finished = false
@rules = {}
@rewind_stack = []
@matrix_stack = []
@xr = 0
@yr = 0
@zr = 0
end
# Create an accessor for the current value of every option. We use a values
# object so that all the state can be saved and restored as a unit.
AVAILABLE_OPTIONS.each do |option_name|
define_method option_name do
@values[option_name]
end
end
# Here's the first serious method: A Rule has an
# identifying name, a probability, and is associated with
# a block of code. These code blocks are saved, and indexed
# by name in a hash, to be run later, when needed.
# The method then dynamically defines a method of the same
# name here, in order to determine which rule to run.
def rule(rule_name, prob=1, &proc)
@rules[rule_name] ||= {:procs => [], :total => 0}
total = @rules[rule_name][:total]
@rules[rule_name][:procs] << [(total...(prob+total)), proc]
@rules[rule_name][:total] += prob
unless ContextFree.method_defined? rule_name
self.class.class_eval do
eval <<-METH
def #{rule_name}(options)
merge_options(@values, options)
pick = determine_rule(#{rule_name.inspect})
@finished = true if @values[:size] < @values[:stop_size]
unless @finished
get_ready_to_draw
pick[1].call(options)
end
end
METH
end
end
end
# Rule choice is random, based on the assigned probabilities.
def determine_rule(rule_name)
rule = @rules[rule_name]
chance = rand * rule[:total]
pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
return pick.flatten
end
# At each step of the way, any of the options may change, slightly.
# Many of them have different strategies for being merged.
def merge_options(old_ops, new_ops)
return unless new_ops
# Do size first
old_ops[:size] *= new_ops[:size] if new_ops[:size]
new_ops.each do |key, value|
case key
when :size
when :x, :y, :z
old_ops[key] = value * old_ops[:size]
when :rz, :ry, :rx
old_ops[key] = value * (Math::PI / 180.0)
when :hue, :saturation, :brightness, :alpha
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key]] *= value
old_ops[:color] = adjusted
when :width, :height
old_ops[key] *= value
when :color
old_ops[key] = value
else # Used a key that we don't know about or trying to set
merge_unknown_key(key, value, old_ops)
end
end
end
# Using an unknown key let's you set arbitrary values,
# to keep track of for your own ends.
def merge_unknown_key(key, value, old_ops)
key_s = key.to_s
if key_s.match(/^set/)
key_sym = key_s.sub('set_', '').to_sym
if key_s.match(/(brightness|hue|saturation|alpha)/)
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key_sym]] = value
old_ops[:color] = adjusted
else
old_ops[key_sym] = value
end
end
end
# Doing a 'split' saves the context, and proceeds from there,
# allowing you to rewind to where you split from at any moment.
def split(options=nil, &block)
save_context
merge_options(@values, options) if options
yield
restore_context
end
# Saving the context means the values plus the coordinate matrix.
def save_context
@rewind_stack.push @values.dup
@matrix_stack << @graphics.get_matrix
end
# Restore the values and the coordinate matrix as the recursion unwinds.
def restore_context
@values = @rewind_stack.pop
@graphics.set_matrix @matrix_stack.pop
end
# Rewinding goes back one step.
def rewind
@finished = false
restore_context
save_context
end
# Render the is method that kicks it all off, initializing the options
# and calling the first rule.
def render(rule_name, starting_values={})
@values = {:x => 0, :y => 0, :z => 0,
:rz => 0, :ry => 0, :rx => 0,
:size => 1, :width => 1, :height => 1,
:start_x => width/2, :start_y => height/2, :start_z => 0,
:color => [0.5, 0.5, 0.5, 1],
:stop_size => 1.5}
@values.merge!(starting_values)
@finished = false
@app.reset_matrix
@app.rect_mode CENTER
@app.ellipse_mode CENTER
@app.no_stroke
@app.color_mode HSB, 1.0
@app.translate @values[:start_x], @values[:start_y], @values[:start_z]
self.send(rule_name, {})
end
def rotate_x rt
@xr = rt
end
def rotate_y rt
@yr = rt
end
def rotate_z rt
@zr = rt
end
# Before actually drawing the next step, we need to move to the appropriate
# location.
def get_ready_to_draw
@app.translate(@values[:x], @values[:y], @values[:z])
@app.rotate_x(@values[:rx] + xr)
@app.rotate_y(@values[:ry] + yr)
@app.rotate_z(@values[:rz] + zr)
end
# Compute the rendering parameters for drawing a shape.
def get_shape_values(some_options)
old_ops = @values.dup
merge_options(old_ops, some_options) if some_options
@app.fill *old_ops[:color]
return old_ops[:size], old_ops
end
# The shape primitives are sphere and cube
def cube(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.box(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an ordered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
def sphere(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.sphere_detail 10
@app.sphere(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an ordered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
end
end
My Test Script monster.rb
load_libraries :test_free, :control_panel
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :monster do
5.times do |i|
split do
cone :ry => 72 * i
rewind
end
end
end
rule :cone do # two equally weighted cone rules creates the beat
sphere :brightness => 1
cone :size => 0.96, :y => -0.24
end
rule :cone do
sphere :brightness => 1
cone :size => 0.97, :y => -0.28
end
end
end
def setup
size 800, 800, P3D
configure_control
smooth
setup_the_spiral
end
def configure_control
control_panel do |c|
c.title = "Attitude Control"
c.slider :xrot, -3.1..3.1, 0.5
c.slider :yrot, -3.1..3.1, 0.5
c.slider :zrot, -3.1..3.1, 1.0
end
end
def draw
background 0.8
lights
@spiral.render :monster, :start_x => 0, :start_y => 10, :start_z => -30, :size => height/200, :stop_size => 1, :color => [0, 0.8, 0.8]
@spiral.rotate_x xrot
@spiral.rotate_y yrot
@spiral.rotate_z zrot
end

This library cannot compete with StructureSynth which is a much more refined and complete program. However if you are used to ruby it may be easier to write your rules in ruby, rather than in the EisenScript syntax. The funky extra thing is, because it is based on processing you have the animation possibilities of the 'draw' loop. My monster in some directions could be seen as a beating heart.
To use the library, create a folder for you work say "work", create a sub-folder "library" and sub-sub-folder "test_free" (or whatever better name you can think of for the library). Put the library "test_free.rb" in the sub-sub-folder. Write you sketches in the "work" directory, and run them with "rp5 run my_sketch.rb". If you want to do it the really neat way use JEdit as your editor and my macro and commando tools, then you can run the sketch from the editor. The link to the tools describes live editing, which I haven't tried with this library so I would recommend just using the run mode for now.
If you are not a ruby meddler just get StructureSynth, and learn the EisenScript language. Otherwise you will need to get ruby-processing see link in my blog header to see how. My blog header also has link to the original Jeremy Ashkenas context-free.rb on github.
The test_free.rb library
# A Context-Free library for Ruby-Processing, inspired by
# contextfreeart.org and StructureSynth
# Built on Jeremy Ashkenas context free DSL script
module Processing
class ContextFree
include Processing::Proxy
attr_accessor :rules, :app, :xr, :yr, :zr
AVAILABLE_OPTIONS = [:x, :y, :z, :rx, :ry, :rz, :size, :color, :hue, :saturation, :brightness, :alpha]
HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
# Define a context-free system. Use this method to create a ContextFree
# object. Call render() on it to make it draw.
def self.define(&block)
cf = ContextFree.new
cf.instance_eval &block
cf
end
# Initialize a bare ContextFree object with empty recursion stacks.
def initialize
@app = $app
@graphics = $app.g
@finished = false
@rules = {}
@rewind_stack = []
@matrix_stack = []
@xr = 0
@yr = 0
@zr = 0
end
# Create an accessor for the current value of every option. We use a values
# object so that all the state can be saved and restored as a unit.
AVAILABLE_OPTIONS.each do |option_name|
define_method option_name do
@values[option_name]
end
end
# Here's the first serious method: A Rule has an
# identifying name, a probability, and is associated with
# a block of code. These code blocks are saved, and indexed
# by name in a hash, to be run later, when needed.
# The method then dynamically defines a method of the same
# name here, in order to determine which rule to run.
def rule(rule_name, prob=1, &proc)
@rules[rule_name] ||= {:procs => [], :total => 0}
total = @rules[rule_name][:total]
@rules[rule_name][:procs] << [(total...(prob+total)), proc]
@rules[rule_name][:total] += prob
unless ContextFree.method_defined? rule_name
self.class.class_eval do
eval <<-METH
def #{rule_name}(options)
merge_options(@values, options)
pick = determine_rule(#{rule_name.inspect})
@finished = true if @values[:size] < @values[:stop_size]
unless @finished
get_ready_to_draw
pick[1].call(options)
end
end
METH
end
end
end
# Rule choice is random, based on the assigned probabilities.
def determine_rule(rule_name)
rule = @rules[rule_name]
chance = rand * rule[:total]
pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
return pick.flatten
end
# At each step of the way, any of the options may change, slightly.
# Many of them have different strategies for being merged.
def merge_options(old_ops, new_ops)
return unless new_ops
# Do size first
old_ops[:size] *= new_ops[:size] if new_ops[:size]
new_ops.each do |key, value|
case key
when :size
when :x, :y, :z
old_ops[key] = value * old_ops[:size]
when :rz, :ry, :rx
old_ops[key] = value * (Math::PI / 180.0)
when :hue, :saturation, :brightness, :alpha
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key]] *= value
old_ops[:color] = adjusted
when :width, :height
old_ops[key] *= value
when :color
old_ops[key] = value
else # Used a key that we don't know about or trying to set
merge_unknown_key(key, value, old_ops)
end
end
end
# Using an unknown key let's you set arbitrary values,
# to keep track of for your own ends.
def merge_unknown_key(key, value, old_ops)
key_s = key.to_s
if key_s.match(/^set/)
key_sym = key_s.sub('set_', '').to_sym
if key_s.match(/(brightness|hue|saturation|alpha)/)
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key_sym]] = value
old_ops[:color] = adjusted
else
old_ops[key_sym] = value
end
end
end
# Doing a 'split' saves the context, and proceeds from there,
# allowing you to rewind to where you split from at any moment.
def split(options=nil, &block)
save_context
merge_options(@values, options) if options
yield
restore_context
end
# Saving the context means the values plus the coordinate matrix.
def save_context
@rewind_stack.push @values.dup
@matrix_stack << @graphics.get_matrix
end
# Restore the values and the coordinate matrix as the recursion unwinds.
def restore_context
@values = @rewind_stack.pop
@graphics.set_matrix @matrix_stack.pop
end
# Rewinding goes back one step.
def rewind
@finished = false
restore_context
save_context
end
# Render the is method that kicks it all off, initializing the options
# and calling the first rule.
def render(rule_name, starting_values={})
@values = {:x => 0, :y => 0, :z => 0,
:rz => 0, :ry => 0, :rx => 0,
:size => 1, :width => 1, :height => 1,
:start_x => width/2, :start_y => height/2, :start_z => 0,
:color => [0.5, 0.5, 0.5, 1],
:stop_size => 1.5}
@values.merge!(starting_values)
@finished = false
@app.reset_matrix
@app.rect_mode CENTER
@app.ellipse_mode CENTER
@app.no_stroke
@app.color_mode HSB, 1.0
@app.translate @values[:start_x], @values[:start_y], @values[:start_z]
self.send(rule_name, {})
end
def rotate_x rt
@xr = rt
end
def rotate_y rt
@yr = rt
end
def rotate_z rt
@zr = rt
end
# Before actually drawing the next step, we need to move to the appropriate
# location.
def get_ready_to_draw
@app.translate(@values[:x], @values[:y], @values[:z])
@app.rotate_x(@values[:rx] + xr)
@app.rotate_y(@values[:ry] + yr)
@app.rotate_z(@values[:rz] + zr)
end
# Compute the rendering parameters for drawing a shape.
def get_shape_values(some_options)
old_ops = @values.dup
merge_options(old_ops, some_options) if some_options
@app.fill *old_ops[:color]
return old_ops[:size], old_ops
end
# The shape primitives are sphere and cube
def cube(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.box(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an ordered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
def sphere(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.sphere_detail 10
@app.sphere(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an ordered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
end
end
My Test Script monster.rb
load_libraries :test_free, :control_panel
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :monster do
5.times do |i|
split do
cone :ry => 72 * i
rewind
end
end
end
rule :cone do # two equally weighted cone rules creates the beat
sphere :brightness => 1
cone :size => 0.96, :y => -0.24
end
rule :cone do
sphere :brightness => 1
cone :size => 0.97, :y => -0.28
end
end
end
def setup
size 800, 800, P3D
configure_control
smooth
setup_the_spiral
end
def configure_control
control_panel do |c|
c.title = "Attitude Control"
c.slider :xrot, -3.1..3.1, 0.5
c.slider :yrot, -3.1..3.1, 0.5
c.slider :zrot, -3.1..3.1, 1.0
end
end
def draw
background 0.8
lights
@spiral.render :monster, :start_x => 0, :start_y => 10, :start_z => -30, :size => height/200, :stop_size => 1, :color => [0, 0.8, 0.8]
@spiral.rotate_x xrot
@spiral.rotate_y yrot
@spiral.rotate_z zrot
end

Labels:
3D,
context free,
ruby processing
Saturday, 4 September 2010
Using the control panel and Toxi Processing Libraries in ruby processing
Here is a sketch that shows how easy it is to add controls to your sketch in ruby-processing.
#
# <p>GrayScottControl uses the seedImage() method to use a bitmap as simulation seed.
# In this demo the image is re-applied every frame and the user can adjust the
# F coefficient of the reaction diffusion to produce different patterns emerging
# from the boundary of the bitmapped seed. Unlike some other GS demos provided,
# this one also uses a wrapped simulation space, creating tiled patterns.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to locally disturb the simulation</li>
# <li>Use slider to adjust the F parameter of the simulation</li>
# <li>press reset! to do what?</li>
# <li>press save_graycsott to do save</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt (rubified by Martin Prout)
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottControl < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils','control_panel'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
attr_reader :gs, :tone_map, :img, :set_f
def setup
size 256, 256
setup_control
@gs = GrayScott.new width, height, true
@img = load_image "two_prong.png"
# create a duo-tone gradient map with 256 steps
# NB: use '::' in place of '.' here
@tone_map = ToneMap.new(0, 0.33, NamedColor::CRIMSON, NamedColor::WHITE, 256)
end
def draw
@gs.seed_image(img.pixels, img.width, img.height)
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
@gs.setF(0.02 + set_f * 0.001)
update_pixels
end
def setup_control
control_panel do |c|
c.title = "Control Panel"
c.slider :set_f, 0..9, 8
c.button :save_graycsott
c.button :reset!
end
end
def save_graycsott
no_loop
save_frame "toxi.png"
loop
end
def reset!
@gs.reset
end
end
#
# <p>GrayScottControl uses the seedImage() method to use a bitmap as simulation seed.
# In this demo the image is re-applied every frame and the user can adjust the
# F coefficient of the reaction diffusion to produce different patterns emerging
# from the boundary of the bitmapped seed. Unlike some other GS demos provided,
# this one also uses a wrapped simulation space, creating tiled patterns.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to locally disturb the simulation</li>
# <li>Use slider to adjust the F parameter of the simulation</li>
# <li>press reset! to do what?</li>
# <li>press save_graycsott to do save</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt (rubified by Martin Prout)
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottControl < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils','control_panel'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
attr_reader :gs, :tone_map, :img, :set_f
def setup
size 256, 256
setup_control
@gs = GrayScott.new width, height, true
@img = load_image "two_prong.png"
# create a duo-tone gradient map with 256 steps
# NB: use '::' in place of '.' here
@tone_map = ToneMap.new(0, 0.33, NamedColor::CRIMSON, NamedColor::WHITE, 256)
end
def draw
@gs.seed_image(img.pixels, img.width, img.height)
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
@gs.setF(0.02 + set_f * 0.001)
update_pixels
end
def setup_control
control_panel do |c|
c.title = "Control Panel"
c.slider :set_f, 0..9, 8
c.button :save_graycsott
c.button :reset!
end
end
def save_graycsott
no_loop
save_frame "toxi.png"
loop
end
def reset!
@gs.reset
end
end
Thursday, 2 September 2010
Tone map example of Gray-Scott diffusion using toxiclibs in ruby processing
Another rubified example, showing how to use some of the toxiclibs in ruby-processing. Any observations/questions could be made on the processing alternative implementations forum, which I often watch.
#
# <p>GrayScottToneMap shows how to use the ColorGradient & ToneMap classes of the
# colorutils package to create a tone map for rendering the results of
# the Gray-Scott reaction-diffusion.</p>
#
# <p><strong>Usage:</strong><ul>
# <li>click + drag mouse to draw dots used as simulation seed</li>
# <li>press any key to reset</li>
# </ul></p>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottToneMap < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
NUM_ITERATIONS = 10
attr_reader :gs, :tone_map
def setup()
size(256,256)
@gs= GrayScott.new width,height, false
@gs.set_coefficients 0.021, 0.076, 0.12, 0.06
# create a color gradient for 256 values
grad = ColorGradient.new
# NamedColors are preset colors, but any TColor can be added
# see javadocs for list of names:
# http://toxiclibs.org/docs/colorutils/toxi/color/NamedColor::html
# NB: use '::' in place of '.' here for these java constants
grad.add_color_at(0, NamedColor::BLACK)
grad.add_color_at(128, NamedColor::RED)
grad.add_color_at(192, NamedColor::YELLOW)
grad.add_color_at(255, NamedColor::WHITE)
# this gradient is used to map simulation values to colors
# the first 2 parameters define the min/max values of the
# input range (Gray-Scott produces values in the interval of 0.0 - 0.5)
# setting the max = 0.33 increases the contrast
@tone_map = ToneMap.new 0, 0.33, grad
end
def draw()
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
# update the simulation a few time steps
NUM_ITERATIONS.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
update_pixels
end
def key_pressed
@gs.reset()
end
end
#
# <p>GrayScottToneMap shows how to use the ColorGradient & ToneMap classes of the
# colorutils package to create a tone map for rendering the results of
# the Gray-Scott reaction-diffusion.</p>
#
# <p><strong>Usage:</strong><ul>
# <li>click + drag mouse to draw dots used as simulation seed</li>
# <li>press any key to reset</li>
# </ul></p>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottToneMap < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
NUM_ITERATIONS = 10
attr_reader :gs, :tone_map
def setup()
size(256,256)
@gs= GrayScott.new width,height, false
@gs.set_coefficients 0.021, 0.076, 0.12, 0.06
# create a color gradient for 256 values
grad = ColorGradient.new
# NamedColors are preset colors, but any TColor can be added
# see javadocs for list of names:
# http://toxiclibs.org/docs/colorutils/toxi/color/NamedColor::html
# NB: use '::' in place of '.' here for these java constants
grad.add_color_at(0, NamedColor::BLACK)
grad.add_color_at(128, NamedColor::RED)
grad.add_color_at(192, NamedColor::YELLOW)
grad.add_color_at(255, NamedColor::WHITE)
# this gradient is used to map simulation values to colors
# the first 2 parameters define the min/max values of the
# input range (Gray-Scott produces values in the interval of 0.0 - 0.5)
# setting the max = 0.33 increases the contrast
@tone_map = ToneMap.new 0, 0.33, grad
end
def draw()
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
# update the simulation a few time steps
NUM_ITERATIONS.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
update_pixels
end
def key_pressed
@gs.reset()
end
end
Labels:
Gray-Scott diffusion,
ruby processing,
toxiclibs
Wednesday, 1 September 2010
Another Example of Using Toxi Processing Libraries
Here is another rubified example, a sophisticated variant of Gray-Scott diffusion, using toxis processing libraries, this time using a bit mapped seed:-
#
# <p>GrayScottImage uses the seedImage() method to use a bitmap as simulation seed.
# In this demo the image is re-applied every frame and the user can adjust the
# F coefficient of the reaction diffusion to produce different patterns emerging
# from the boundary of the bitmapped seed. Unlike some other GS demos provided,
# this one also uses a wrapped simulation space, creating tiled patterns.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to locally disturb the simulation</li>
# <li>press 1-9 to adjust the F parameter of the simulation</li>
# <li>press any other key to reset</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottImage < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
attr_reader :gs, :tone_map, :img
def setup
size 256, 256
@gs = GrayScott.new width, height, true
@img = load_image "ti_yong.png"
# create a duo-tone gradient map with 256 steps
# NB: use '::' in place of '.' here for these java constants
@tone_map = ToneMap.new(0, 0.33, NamedColor::CRIMSON, NamedColor::WHITE, 256)
end
def draw
@gs.seed_image(img.pixels, img.width, img.height)
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
update_pixels
end
def key_pressed
case key
when '1', '9', '3', '2', '4', '6', '5', '7', '8'
@gs.setF(0.02 + (key.to_i - 1) * 0.001)
when 's'
save_frame "toxi.png"
else
@gs.reset()
end
end
end
I found that the image 'needed' to be 'disturbed' by a mouse-click/drag, before the numeric controls kicked in (it made me doubt my coding originally).
#
# <p>GrayScottImage uses the seedImage() method to use a bitmap as simulation seed.
# In this demo the image is re-applied every frame and the user can adjust the
# F coefficient of the reaction diffusion to produce different patterns emerging
# from the boundary of the bitmapped seed. Unlike some other GS demos provided,
# this one also uses a wrapped simulation space, creating tiled patterns.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to locally disturb the simulation</li>
# <li>press 1-9 to adjust the F parameter of the simulation</li>
# <li>press any other key to reset</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class GrayScottImage < Processing::App
load_libraries 'simutils','toxiclibscore','colorutils'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
include_package "toxi.color"
attr_reader :gs, :tone_map, :img
def setup
size 256, 256
@gs = GrayScott.new width, height, true
@img = load_image "ti_yong.png"
# create a duo-tone gradient map with 256 steps
# NB: use '::' in place of '.' here for these java constants
@tone_map = ToneMap.new(0, 0.33, NamedColor::CRIMSON, NamedColor::WHITE, 256)
end
def draw
@gs.seed_image(img.pixels, img.width, img.height)
@gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
# read out the V result array
# and use tone map to render colours
gs.v.length.times do |i|
pixels[i]=tone_map.getARGBToneFor(gs.v[i]) # NB: don't camel case convert here
end
update_pixels
end
def key_pressed
case key
when '1', '9', '3', '2', '4', '6', '5', '7', '8'
@gs.setF(0.02 + (key.to_i - 1) * 0.001)
when 's'
save_frame "toxi.png"
else
@gs.reset()
end
end
end
I found that the image 'needed' to be 'disturbed' by a mouse-click/drag, before the numeric controls kicked in (it made me doubt my coding originally).
Labels:
Gray-Scott diffusion,
ruby processing,
toxiclibs
Using the Toxi Processing Libraries in ruby processing
Toxi aka Karsten Schmidt has created some fantastic libraries for processing that make some otherwise difficult things very easy. Here is just one example that I've tried that could be very useful in generative art. To use processing libraries in your ruby processing sketch place each jar in folder of the same name as the jar. Then put that folder in library folder, where you've saved the sketch.
#
# <p>Hello Gray-Scott is a very basic demonstration of the underlying
# reaction-diffusion simulation. This model can be used to produce
# a wide variety of patterns, both static and animated and is therefore
# well suited for being combined with other generative techniques.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to draw dots used as simulation seed</li>
# <li>press any key to reset</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class HelloGrayScott < Processing::App
load_libraries 'simutils','toxiclibscore'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
attr_reader :gs
def setup()
size(256,256)
@gs=GrayScott.new width, height, false
@gs.set_coefficients 0.023, 0.074, 0.095, 0.03
end
def draw()
@gs.set_rect mouse_x, mouse_y, 20, 20 if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
gs.v.length.times do |i|
val = gs.v[i]*768
min = (255 <= val)? 255 : val # processing convenience min(x, y) doesn't work for me
col= 255 - min.to_i
pixels[i]=col<<16|col<<8|col|0xff000000
end
update_pixels
end
def key_pressed
@gs.reset
end
end
Reaction Diffusion at Wikipedia
Other interesting links:-
A 3D example in java
A very nice 2D example
http://toxiclibs.org/2010/02/simutils-grayscott/
#
# <p>Hello Gray-Scott is a very basic demonstration of the underlying
# reaction-diffusion simulation. This model can be used to produce
# a wide variety of patterns, both static and animated and is therefore
# well suited for being combined with other generative techniques.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to draw dots used as simulation seed</li>
# <li>press any key to reset</li>
# </ul>
#
#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
class HelloGrayScott < Processing::App
load_libraries 'simutils','toxiclibscore'
include_package "toxi.sim.grayscott"
include_package "toxi.math"
attr_reader :gs
def setup()
size(256,256)
@gs=GrayScott.new width, height, false
@gs.set_coefficients 0.023, 0.074, 0.095, 0.03
end
def draw()
@gs.set_rect mouse_x, mouse_y, 20, 20 if mouse_pressed?
load_pixels
10.times { @gs.update(1) }
gs.v.length.times do |i|
val = gs.v[i]*768
min = (255 <= val)? 255 : val # processing convenience min(x, y) doesn't work for me
col= 255 - min.to_i
pixels[i]=col<<16|col<<8|col|0xff000000
end
update_pixels
end
def key_pressed
@gs.reset
end
end
Reaction Diffusion at Wikipedia
Other interesting links:-
A 3D example in java
A very nice 2D example
http://toxiclibs.org/2010/02/simutils-grayscott/
Labels:
generative,
Gray-Scott diffusion,
ruby processing,
toxiclibs
Subscribe to:
Posts (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









