Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Monday, 25 February 2013

My Fork is now at Processing-2.0 and JRuby-1.7.4

Finally it might all be coming together, following the recent release of JRuby-1.7.4 and the even more recent release of Processing-2.0, I have updated my ruby-processing fork here. Please fork/clone it (includes many examples translated from the vanilla processing release...), especially if you are on Windows or Mac, and let me know how you get on.....
Building and testing is as simple running rake in the ruby-processing folder.
Linux users might want to substitute the Rakefile in the vendors folder with this gist, apparently OSX tar doesn't like the --wildcard flag which seems to required on linux for pattern matching on the basis that there's probably more of them than us I've changed to the OSX compatible version (wont matter when it gets to gem stage anyway)....
Bare rake builds and tests the gem (don't worry if one test fails by deadlock, that threaded test by moomar is just a bit too clever, I should really fix it..). To re-run test "rake test" to install "rake install" (super user is reqd on linux, unless you just install in your home directory, but then make sure RP5 is on your path).
The easiest way to develop with ruby-processing is to use TextmateSublime text 2.0 if you are on a Mac or JEdit (available with extensions/plugins that allow the running of the sketch directly from the editor, NB: snippets for textmate need updating to be bare sketches) for Windows / Linux users (jEdit option should also work on the Mac, but won't look as nice as the other editors).
For simple sketches you can also make use of the watch mode for "live" editing, runs on "save".
You could of course use vim as an editor here is video proof, it should also be possible to run directly from the vim editor, but I can't tell whether that's being used in the video!!!
However the following entry will do it, and display output in a new console alongside the current vim console!!!!
:let f=expand("%")|vnew|execute '.!rp5 run "' . f . '"'
Or just to simply run the sketch from ruby
:!rp5 run %  

EMacs should also work, or any other macro enabled editor.
Here is trefoil sketch running from jEdit on my linux box:-

Wednesday, 20 February 2013

Shader API changed in latest processing version at GitHub

Looks as there will a simpler (well at least abbreviated) shader language in processing. This is probably a good idea as it may de-couple processing from changes to native glsl whatever that is. Anyway more work to on my ruby-processing shader examples ho-hum. Anyway it got me thinking would it be nice to have a text editor for this glsl language, turns out there is community contributed mode over at JEdit, with a bit of hacking I guess I can create a mode tuned for processing, perfect...
   1 #define PROCESSING_COLOR_SHADER
   2 
   3 uniform float time;
   4 uniform vec2 resolution;
   5 
   6 // NEBULA - CoffeeBreakStudios.com (CBS)
   7 // Work in progress...
   8 //
   9 // 3148.26: Switched from classic to simplex noise
  10 // 3148.27: Reduced number of stars
  11 // 3249.0:  Switched to fast computed 3D noise. Less quality but ~ 2x faster
  12 // 3249.5:  Removed use of random number generator to gain performance
  13 // 3265.0:  Added rotation: glsl.heroku.com/e#3005.1
  14 
  15 //Utility functions
  16 
  17 vec3 fade(vec3 t) {
  18   return vec3(1.0,1.0,1.0);//t*t*t*(t*(t*6.0-15.0)+10.0);
  19 }
  20 
  21 vec2 rotate(vec2 point, float rads) {
  22   float cs = cos(rads);
  23   float sn = sin(rads);
  24   return point * mat2(cs, -sn, sn, cs);
  25 } 
  26 
  27 vec4 randomizer4(const vec4 x)
  28 {
  29     vec4 z = mod(x, vec4(5612.0));
  30     z = mod(z, vec4(3.1415927 * 2.0));
  31     return(fract(cos(z) * vec4(56812.5453)));
  32 }
  33 
  34 // Fast computed noise
  35 // http://www.gamedev.net/topic/502913-fast-computed-noise/
  36 
  37 const float A = 1.0;
  38 const float B = 57.0;
  39 const float C = 113.0;
  40 const vec3 ABC = vec3(A, B, C);
  41 const vec4 A3 = vec4(0, B, C, C+B);
  42 const vec4 A4 = vec4(A, A+B, C+A, C+A+B);
  43 
  44 float cnoise4(const in vec3 xx)
  45 {
  46     vec3 x = mod(xx + 32768.0, 65536.0);
  47     vec3 ix = floor(x);
  48     vec3 fx = fract(x);
  49     vec3 wx = fx*fx*(3.0-2.0*fx);
  50     float nn = dot(ix, ABC);
  51 
  52     vec4 N1 = nn + A3;
  53     vec4 N2 = nn + A4;
  54     vec4 R1 = randomizer4(N1);
  55     vec4 R2 = randomizer4(N2);
  56     vec4 R = mix(R1, R2, wx.x);
  57     float re = mix(mix(R.x, R.y, wx.y), mix(R.z, R.w, wx.y), wx.z);
  58 
  59     return 1.0 - 2.0 * re;
  60 }
  61 float surface3 ( vec3 coord, float frequency ) {
  62   
  63   float n = 0.0;  
  64     
  65   n += 1.0  * abs( cnoise4( coord * frequency ) );
  66   n += 0.5  * abs( cnoise4( coord * frequency * 2.0 ) );
  67   n += 0.25 * abs( cnoise4( coord * frequency * 4.0 ) );
  68   n += 0.125  * abs( cnoise4( coord * frequency * 8.0 ) );
  69   n += 0.0625 * abs( cnoise4( coord * frequency * 16.0 ) );
  70   
  71   return n;
  72 }
  73   
  74 void main( void ) {
  75   float rads = radians(time*3.15);
  76   vec2 position = gl_FragCoord.xy / resolution.xy;
  77   position += rotate(position, rads);
  78   float n = surface3(vec3(position*sin(time*0.1), time * 0.05)*mat3(1,0,0,0,.8,.6,0,-.6,.8),0.9);
  79   float n2 = surface3(vec3(position*cos(time*0.1), time * 0.04)*mat3(1,0,0,0,.8,.6,0,-.6,.8),0.8);
  80       float lum = length(n);
  81       float lum2 = length(n2);
  82 
  83   vec3 tc = pow(vec3(1.0-lum),vec3(sin(position.x)+cos(time)+4.0,8.0+sin(time)+4.0,8.0));
  84   vec3 tc2 = pow(vec3(1.1-lum2),vec3(5.0,position.y+cos(time)+7.0,sin(position.x)+sin(time)+2.0));
  85   vec3 curr_color = (tc*0.8) + (tc2*0.5);
  86   
  87   
  88   //Let's draw some stars
  89   
  90   float scale = sin(0.3 * time) + 5.0;
  91   vec2 position2 = (((gl_FragCoord.xy / resolution) - 0.5) * scale);
  92   float gradient = 0.0;
  93   vec3 color = vec3(0.0);
  94   float fade = 0.0;
  95   float z = 0.0;
  96   vec2 centered_coord = position2 - vec2(sin(time*0.1),sin(time*0.1));
  97   centered_coord = rotate(centered_coord, rads);
  98   
  99   for (float i=1.0; i<=60.0; i++)
 100   {
 101     vec2 star_pos = vec2(sin(i) * 250.0, sin(i*i*i) * 250.0);
 102     float z = mod(i*i - 10.0*time, 256.0);
 103     float fade = (256.0 - z) /256.0;
 104     vec2 blob_coord = star_pos / z;
 105     gradient += ((fade / 384.0) / pow(length(centered_coord - blob_coord), 1.5)) * ( fade);
 106   }
 107 
 108   curr_color += gradient;
 109   
 110   gl_FragColor = vec4(curr_color, 1.0);
 111 }

Friday, 15 February 2013

Hairy Ball (Esfera by David Pena)

Plenty of spurious code to ignore when translating this sketch to ruby-processing.
#
# Esfera
# by David Pena.  
# Somewhat re-factored for ruby-processin
# by Martin Prout
# Distribucion aleatoria uniforme sobre la superficie de una esfera. 
#

QUANTITY = 16000

attr_reader :orb, :phi, :radius, :rx, :ry

def setup
  size(800, 600, P3D)
  @rx = 0
  @ry =0
  no_smooth
  @radius = height/3.5
  @orb = HairyOrb.new
  factory = HairFactory.new(radius)
  QUANTITY.times do
    orb << factory.createHair
  end
end

def draw
  background(0)
  translate(width/2,height/2)
  rxp = ((mouse_x - (width/2))*0.005)
  ryp = ((mouse_y - (height/2))*0.005)
  @rx = (rx*0.9)+(rxp*0.1)
  @ry = (ry*0.9)+(ryp*0.1)
  rotate_y(rx)
  rotate_x(ry)
  fill(0)
  no_stroke
  sphere(radius)
  orb.render self, radius
  if (frame_count % 10 == 0)
    puts(frame_rate)
  end
end

class HairyOrb < Array
  include Processing::Proxy

  def render app, radius
    @app = app
    self.each do |hair|
      off = (app.noise(app.millis() * 0.0005, sin(hair.phi)) - 0.5) * 0.3
      offb = (app.noise(app.millis() * 0.0007, sin(hair.z) * 0.01) - 0.5) * 0.3
      thetaff = hair.theta + off
      phff = hair.phi + offb
      x = radius * cos(hair.theta) * cos(hair.phi)
      y = radius * cos(hair.theta) * sin(hair.phi)
      za = radius * sin(hair.theta)
      xo = radius * cos(thetaff) * cos(phff)
      yo = radius * cos(thetaff) * sin(phff)
      zo = radius * sin(thetaff)
      xb = xo * hair.len
      yb = yo * hair.len
      zb = zo * hair.len
      app.stroke_weight(1)
      app.begin_shape(LINES)
      app.stroke(0)
      app.vertex(x, y, za)
      app.stroke(200, 150)
      app.vertex(xb, yb, zb)
      app.end_shape()
    end
  end
end

class HairFactory
  attr_reader :radius
  def initialize radius
    @radius = radius
  end

  def createHair
    z = rand(-radius .. radius)
    phi = rand * TWO_PI
    len = rand(1.15 .. 1.2)
    theta = asin(z / radius)
    return Hair.new(z, phi, len, theta)
  end

end

class Hair
  attr_reader :z, :phi, :len, :theta
  def initialize z, phi, len, theta
    @z, @phi, @len, @theta   = z, phi, len, theta
  end
end

Wednesday, 13 February 2013

Conway game of life using shaders in ruby-processing

Hot off the press this example has only just been posted by Andrés Colubri as a vanilla processing example at github (I'm not sure he really meant to create a P3D sketch initially though, P2D seems to work just as well) see java/examples/Topics/Shaders/Conway/data/conway.glsl for shader.
# GLSL version of Conway's game of life, ported from GLSL sandbox:
# http://glsl.heroku.com/e//207.3
# Exemplifies the use of the buffer uniform in the shader, that gives
# access to the previous frame.
attr_accessor :pg, :conway

def setup
  size(400, 400, P3D)
  @pg = createGraphics(400, 400, P2D)
  pg.no_smooth
  @conway = load_shader("data/conway.glsl")
  conway.set("resolution", width.to_f, height.to_f)
end

def draw
  conway.set("time", millis() / 1000.0)
  xm = map(mouse_x, 0, width, 0, 1)
  ym = map(mouse_y, 0, height, 1, 0)
  conway.set("mouse", xm, ym)
  pg.begin_draw
  pg.background(0)
  pg.shader(conway)
  pg.rect(0, 0, pg.width, pg.height)
  pg.end_draw
  image(pg, 0, 0, width, height)
end

Friday, 8 February 2013

A graphical example of deep jruby voodoo in ruby-processing

More experiments with casting a jruby class to a java class, which seems to be required to use the processing-2.0 feature of registerMethod (uses reflection, very unwise to mind). Known "methods" include pre, draw, mouseEvent, keyEvent, dispose.

Here is the sketch code test_register.rb:-
# A simple demonstration of vanilla processing # 'reflection' methods 
# in ruby-processing see register_send.rb for the guts...

require_relative 'register_send'

def setup
  size 200, 200
  RegisterSend.new self
  no_loop
end

def draw
  fill(0, 0, 200)
  ellipse(120, 120, 60, 60)
end
Here's where the voodoo gets done in register_send.rb (ruby class becomes a java class and registers methods with the processing $app):-
require 'jruby/core_ext' # required to allow become_java!

# This class demonstrates how to use 'reflection' methods in ruby-processing
# NB: the class must become a java object to get registered. This is an
# advanced feature in vanilla processing, mainly used by libraries.
class RegisterSend
  attr_reader :parent

  def initialize(parent)
    @parent = parent
    parent.java_send :registerMethod, [java.lang.String, java.lang.Object], :draw, self
    parent.java_send :registerMethod, [java.lang.String, java.lang.Object], :pre, self
  end

  def pre
    puts 'before draw'
    parent.background(100)
  end

  def draw
    puts 'at begin draw...'
    parent.fill(200, 100)
    parent.ellipse(100, 100, 60, 60)
  end
  # putting become_java! here works OK
  become_java!
end
Here is the resulting sketch and the console, shows output to console:-




















Actually I'm a bit worried about all this voodoo, it's bad enough that processing is making so much use of this reflection crap, without me making it worse here. Also I can't seem to register either mouseEvent or keyEvent as yet (methods requiring parameters which should probably be a simple java object, that can get cast later to an event?).

Deep jruby voodo in ruby-processing

Finally I've cracked it, I can now call processing registerMethod (new since processing 2.0) directly from ruby processing. Requires a bit of jruby voodoo though (creating a java class from jruby) here is how I did with a simple test example:-
require 'jruby/core_ext'

class TestRegister

  def initialize parent
    parent.java_send :registerMethod, [Java::JavaLang::String, java.lang.Object], :draw, self
    parent.java_send :registerMethod, [Java::JavaLang::String, java.lang.Object], :pre, self
  end

  def self.pre
    puts "before draw"
  end

  def self.draw
    puts "at begin draw"
  end

end

cls = TestRegister.become_java!


Here is the test:-
load "./register.rb"

def setup
  size 200, 200
  fred = TestRegister.new self
  # no_loop
end

def draw
end

Here is the result:-
before draw
at begin draw.........


pre is called before every draw....
So now you can do quite sophisticated things in a "ruby-processing" library for example, like you would have done in a java library (next to do is work it into my arcball library). Quandary is whether to make it more available to general ruby processing users (I think not for now, Im just amazed it worked...).

Monday, 4 February 2013

Sand Traveler Refactored and Updated

Just checking that this classic ruby processing example will run with my updated ruby-processing. The main change is that the sketch is now a bare sketch. Other changes I've made include the use of the Processing::Proxy mixin and avoiding bare calls to global $app, and using the ruby 1.9 feature rand range. I've also made use of displayWidth and displayHeight variables (NB: these must be camel case currently) to display full size on screen. I would recommend letting the sketch "grow" a bit before messing with controls to get best effect, see those travellers move....
# Sand Traveler was a special commision, produced in Processing by 
# Jared Tarbell for Sonar 2004, Barcelona.
# This Ruby port has been tweaked, half of the (non-running) code exorcised, 
# color palette broadened, and done in negative.
# refactored and updated for processing-2.0

# -- omygawshkenas

attr_reader :cities, :num, :slowdown, :num_travelers, :panel

load_library 'control_panel'
def setup
  size displayWidth, displayHeight, P2D
  control_panel do |c|
    c.slider :slowdown, 0.5..1.1
    c.slider :num_travelers, 0..50
    @panel = c
  end
  @dx, @dy = 0, 0
  @num = 150
  @cities = []
  @slowdown = 0.954
  @num_travelers = 13
  reset_all
end
def draw
  panel.visible = self.visible
  cities.each {|city| city.move }
end
def mouse_pressed
  reset_all
end
def reset_all
  @source_colors = [[@red || rand(255), @green || rand(255), @blue || rand(255), rand(45 .. 75)],
  [@red_2 || rand(255), @green_2 || rand(255), @blue_2 || rand(255), rand(45 .. 75)]]
  background 0
  vt = 4.2
  vvt = 0.2
  ot = rand * PI * 2
  num.times do |i|
    tinc = ot + (1.1 - i / num) * 2 * i * (PI * 2) / num
    vx, vy = vt * sin(tinc), vt * cos(tinc)
    cities << City.new(self, width/2+vx*2, height/2+vy*2, vx, vy, i)
    vvt -= 0.00033
    vt += vvt
  end
  cities.each { |city| city.find_friend }
end
def some_color
  choice = @source_colors.sample.dup
  choice.map! { |c| c + rand(-60 .. 60) }
  choice[3] = 0.7 * choice[3]
  return choice
end
class City
  include Processing::Proxy
  attr_reader :x, :y, :color, :app, :friend, :vx, :vy
  # SLOWDOWN = 0.946
  def initialize(app, dx, dy, vx, vy, index)
    @app = app
    @x, @y = dx, dy
    @vx, @vy = vx, vy
    @index = index
    @slowdown = 0.956
    @color = app.some_color
  end
  def move
    if (vx.abs + vy.abs) > 0.01
      @vx += (friend.x - x) / 1000
      @vy += (friend.y - y) / 1000
      @vx *= app.slowdown; @vy *= app.slowdown
      @x += vx; @y += vy
      draw_travelers
    end
  end
  def find_friend
    @friend = app.cities[(@index + rand(app.num/5) + 1) % app.num]
  end
  def draw_travelers
    stroke *friend.color
    app.num_travelers.to_i.times do |i|
      t = rand * PI * 2
      dx = sin(t) * (x - friend.x) / 2 + (x + friend.x) / 2
      dy = sin(t) * (y - friend.y) / 2 + (y + friend.y) / 2
      if rand < 0.01
        dx += rand(-3.0 .. 3)
        dy += rand(-3.0 .. 3)
      end
      point dx, dy
    end
  end
end

Sunday, 3 February 2013

rspec and re-factoring cs_grammar

I have been taking another look a context sensitive L-Systems when I found this. Which kind of inspired me to have another look at my cs_lsysteme library, and I made use of the rspec framework to do some re-factoring of my cs_grammar lib. Here is the updated rspec that describes the expected performance (context no longer scrolls around the 'production string' in either direction, this behaviour matches lmusej):-
require 'cs_grammar'

IGNORE='[]'

describe "Grammar" do
  context "given axiom 'baaaaaa'" do
    context "with function b<a" do
      describe "#generate(x)" do
        it "should equal 'baaaaaa' at zero iteration" do
          Grammar.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(0).should == 'baaaaaa'
        end
        it "should equal 'abaaaaa' after one iteration" do
          Grammar.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'abaaaaa'
        end
        it "should equal 'aabaaaa' after one iteration" do
          Grammar.new('abaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'aabaaaa'
        end
        it "should equal 'aabaaaa' after two iterations" do
          Grammar.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(2).should == 'aabaaaa'
        end
      end
    end
    context "with function b<a and ignore []" do
      describe "#generate(x)" do
        it "should equal 'baaa[a]aa' at zero iteration" do
          Grammar.new('baaa[a]aa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(0).should == 'baaa[a]aa'
        end
        it "should equal 'abaaaaa' after one iteration" do
          Grammar.new('baaa[a]aa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'abaa[a]aa'
        end
        it "should equal 'aabaaaa' after one iteration" do
          Grammar.new('abaa[a]aa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'aaba[a]aa'
        end
        it "should equal 'aabaaaa' after two iterations" do
          Grammar.new('baaa[a]aa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(2).should == 'aaba[a]aa'
        end
      end
    end
    context "with function a>b" do
      describe "#generate(x)" do
        it "should equal 'aaaaaaab' at zero iteration" do
          Grammar.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(0).should == 'aaaaaaab'
        end
        it "should equal 'aaaaaba' after one iteration" do
          Grammar.new('aaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaaaba'
        end
        it "should equal 'aaaabaa' after one iteration" do
          Grammar.new('aaaaaba', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaabaa'
        end
        it "should equal 'aaaabaa' after two iterations" do
          Grammar.new('aaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(2).should == 'aaaabaa'
        end
      end
    end
  end
end

Here is the revised library code:-
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class Grammar
  attr_accessor :lh_context
  attr_reader :axiom, :context, :no_context, :idx, :ignore, :lh_buf
  def initialize(axiom, rules, ignore = '')
    @axiom = axiom
    @no_context = {}
    @context = {}
    @ignore = ignore
    @lh_context = false
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @lh_context = true
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = 0
    @lh_buf = [] if lh_context  # create buffer to store lefthand context
    prod.gsub!(/./) do |ch|
      if lh_context
        lh_buf << ch unless ignore.include? ch # store lh context
      end
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == '<'
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == lh_buf[lh_buf.length - 2] # use context lh sensitive rule
      elsif context[ch][1] == '>'
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == get_rh_context(prod[idx .. prod.length]) # use context rh sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end

  def get_rh_context prod
    index = 0
    if ignore != ''
      while (ignore.include? prod[index])
        index += 1
      end
    end
    return prod[index]
  end
end


Followers

Blog Archive

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2