Ruby Processing

Here is my blog in which I will describe my experiments with ruby-processing, find out more about ruby-processing at:- https://github.com/jashkenas/ruby-processing compatible with processing-2.2.1 and https://github.com/monkstone/cf3ruby for my version of the cfdg DSL (context-free-art)

Tuesday, 14 October 2014

More progress with JRubyArt (Alternative ruby-processing implementation)

Now JRubyArt can run with a post-install vendored jruby-complete (this is is unfortunately still required for some sketches owing to a difference in permissions ask @headius).
Comparison
ruby-processingJRubyArt
rp5 setup installk9 setup install
rp5 --nojruby run sketch.rbk9 run sketch.rb
rp5 run sketch.rbjruby sketch.rb

NB: Regular ruby-processing can be made to always run with vendored jruby-complete in ~/.rp5rc config file, but default is to use an installed jruby. MRI ruby can be used start sketches with run mode, but jruby is used to actually run the sketches.

Sunday, 12 October 2014

Transducers in ruby-processing

Transducers may be useful in ruby-processing for dealing with all sorts of algorithmic stuff. but in many cases if your brain can take it map, might be equally good. Here is example substituting a regular map with a transducer, I will have to see if I can find a more useful example:-

                                                            
require 'transducers'                                                                
                                                                                     
class GameOfLife < Processing::App                                                
  T = Transducers                                                                    
                                                                                      
  def random_data                                                                    
    T.transduce(T.map{ rand(1000) < ALIVE_START }, :<<, [], 0..row * column)
  end                                                                                
end                                                                                  

Monday, 6 October 2014

Vec3D to shape vertex in JRubyArt

Here is a shader sketch that will run with regular jruby and the JRubyArt gem, converted from ruby-processing. The original processing sketch by Andres Colubri used PVector, the use Vec3D allows us to chain vector operations, and further (thanks to the ShapeRender interface) write them directly to the PShape verticies.

 
# Trefoil, by Andres Colubri
# A parametric surface is textured procedurally
# by drawing on an offscreen PGraphics surface.
#
# Features (Vec3D).to_normal(renderer) and (Vec3D).to_vertex_uv(renderer, u, v)
# see line 55 for inititialization of renderer where obj is an instance of PShape
# renderer = ShapeRender.new(obj)
require 'jruby_art'

class Trefoil < Processing::AppGL

  attr_reader :pg, :trefoil

  def setup
    size(1024, 768, P3D)
    texture_mode(NORMAL)
    no_stroke
    # Creating offscreen surface for 3D rendering.
    @pg = create_graphics(32, 512, P3D)
    pg.begin_draw
    pg.background(0, 0)
    pg.noStroke
    pg.fill(255, 0, 0, 200)
    pg.end_draw
    # Saving trefoil surface into a PShape3D object
    @trefoil = create_trefoil(350, 60, 15, pg)
  end

  def draw
    background(0)
    pg.begin_draw
    pg.ellipse(rand(0.0..pg.width), rand(0.0..pg.height), 4, 4)
    pg.end_draw
    ambient(250, 250, 250)
    pointLight(255, 255, 255, 0, 0, 200)
    push_matrix
    translate(width/2, height/2, -200)
    rotate_x(frame_count * PI / 500)
    rotate_y(frame_count * PI / 500)
    shape(trefoil)
    pop_matrix
 end

  # Code to draw a trefoil knot surface, with normals and texture 
  # coordinates. Makes of the Vec3D Render interface (uses ShapeRender here).
  # Adapted from the parametric equations example by Philip Rideout:
  # http://iphone-3d-programming.labs.oreilly.com/ch03.html

  # This function draws a trefoil knot surface as a triangle mesh derived
  # from its parametric equation.
  def create_trefoil(s, ny, nx, tex)
    obj = create_shape()
    obj.begin_shape(TRIANGLES)
    obj.texture(tex)
    renderer = ShapeRender.new(obj)
    (0 ... nx).each do |j|
      u0 = j.to_f / nx
      u1 = (j + 1).to_f / nx
      (0 ... ny).each do |i|
        v0 = i.to_f / ny
        v1 = (i + 1).to_f / ny
        p0 = eval_point(u0, v0)
        n0 = eval_normal(u0, v0)
        p1 = eval_point(u0, v1)
        n1 = eval_normal(u0, v1)
        p2 = eval_point(u1, v1)
        n2 = eval_normal(u1, v1)
        # Triangle p0-p1-p2      
        n0.to_normal(renderer)
        (p0 * s).to_vertex_uv(renderer, u0, v0)
        n1.to_normal(renderer)
        (p1 * s).to_vertex_uv(renderer, u0, v1)
        n2.to_normal(renderer)
        (p2 * s).to_vertex_uv(renderer, u1, v1)
        p1 = eval_point(u1, v0)
        n1 = eval_normal(u1, v0)
        # Triangle p0-p2-p1      
        n0.to_normal(renderer)
        (p0 * s).to_vertex_uv(renderer, u0, v0)
        n2.to_normal(renderer)
        (p2 * s).to_vertex_uv(renderer, u1, v1)
        n1.to_normal(renderer)
        (p1 * s).to_vertex_uv(renderer, u1, v0)
      end
    end
    obj.end_shape
    return obj
  end

  # Evaluates the surface normal corresponding to normalized 
  # parameters (u, v)
  def eval_normal(u, v)
    # Compute the tangents and their cross product.
    p = eval_point(u, v)
    tang_u = eval_point(u + 0.01, v)
    tang_v = eval_point(u, v + 0.01)
    tang_u -= p
    tang_v.cross(tang_u).normalize! # it is easy to chain Vec3D operations
  end

  # Evaluates the surface point corresponding to normalized 
  # parameters (u, v)
  def eval_point(u, v)
    a = 0.5
    b = 0.3
    c = 0.5
    d = 0.1
    s = TAU * u
    t = (TAU * (1 - v)) * 2
    sint = Math.sin(t)
    cost = Math.cos(t)
    sint15 = Math.sin(1.5 * t)
    cost15 = Math.cos(1.5 * t)
    r = a + b * cost15
    x = r * cost
    y = r * sint
    z = c * sint15
    dv = Vec3D.new(
      -1.5 * b * sint15 * cost - y,
      -1.5 * b * sint15 * sint + x,
      1.5 * c * cost15)
    q = dv.normalize     # regular normalize creates a new Vec3D for us
    qvn = Vec3D.new(q.y, -q.x, 0).normalize!  # chained Vec3D operations
    ww = q.cross(qvn)
    coss = Math.cos(s)
    sins = Math.sin(s)
    Vec3D.new(
      x + d * (qvn.x * coss + ww.x * sins),
      y + d * (qvn.y * coss + ww.y * sins),
      z + d * ww.z * sins)
  end
end

Trefoil.new(title: 'Trefoil', fullscreen: true, bgcolor: '#000000')





JRubyArt shader sketch running from netbeans

Probably the most convenient way to explore glsl shader sketches from ruby is to use NetBeans with jruby-plugin, jruby_art gem (and optionally c/c++ plugin). Made possible by Andres Colubri processing opengl work, tutorial here:-

Saturday, 4 October 2014

Simple ArcBall in JRubyArt running from netbeans

Things are moving fast here, now we can offer full arcball functionality with JRubyArt (and just one line of code for user!!!). Rotation via mouse drag, zoom with mousewheel.Interesting seems I can get rid of Processing:: prefix for arcball by "including Processing" in the AppGL class...
require 'jruby_art'

class MySketch < Processing::AppGL
  def setup
    size 200, 200, P3D
    Processing::ArcBall.init(self)
    fill 200, 0, 0
    stroke 0
  end

  def draw
    lights
    background 0
    box 100, 100, 100
  end
end

MySketch.new(title: 'My Sketch')

Rubygems with latest JRubyArt

With regular ruby-processing, the only way to use gems was to use an installed version of jruby (well apart from using some complicated bundler tool). Interestingly currently you seem to be able to use jruby-complete to launch a sketch with JRubyArt (providing youve've installed the gem with your installed jruby). Now the interest in using jruby-complete is all about being able to run sketches that load_image, shaders etc. Many sketches run just fine with an installed jruby.
                                                   
java -jar jruby-complete game_of_life.rb                                    

Here is the sketch
# game_of_life.rb featuring MDArray in ruby-processing
# A Processing implementation of Game of Life
# By Joan Soler-Adillon
#
# Press SPACE BAR to pause and change the cell's values with the mouse
# On pause, click to activate/deactivate cells
# Press R to randomly reset the cells' grid
# Press C to clear the cells' grid
#
# The original Game of Life was created by John Conway in 1970.
#
require 'jruby_art'
require 'mdarray'

class GameOfLife < Processing::App
  CELL_SIZE = 5
  ALIVE = true
  DEAD = false
  ALIVE_START = 150
  WIDTH = 960
  HEIGHT = 640
  SKIP = 10
  INTERVAL = 100

  attr_reader :pause, :cells, :row, :column, :last_time, :alive, :cells_buffer

  def setup
    size WIDTH, HEIGHT
    @row = WIDTH / CELL_SIZE
    @column = HEIGHT / CELL_SIZE
    background 0
    stroke_weight 2
    @last_time = 0
    @pause = false
    @cells = MDArray.boolean([row, column], random_data)
    @alive = color(100, 255, 100)
    stroke(48, 100)
    no_smooth
  end

  def draw
    background(0)
    # Draw live cells
    (0...row).each do |x|
      (0...column).each do |y|
        if cells.get([x, y])
          fill(alive)
          rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
        end
      end
    end
    # Iterate if timer ticks
    unless pause
      tick!
      @last_time = millis
    end if millis - last_time > INTERVAL

    # Create  new cells manually on pause
    if pause && mouse_pressed?
      # Map and avoid out of bound errors (use map1d range and range.clip)
      x_cell_over = (map1d(mouse_x, (0..width), (0..row))).to_i
      x_cell_over = (0..row - 1).clip(x_cell_over)
      y_cell_over = (map1d(mouse_y, (0..height), (0..column))).to_i
      y_cell_over = (0..column - 1).clip(y_cell_over)

      # Check against cells in buffer
      if cells_buffer.get([x_cell_over, y_cell_over])  # Cell is alive
        cells.set([x_cell_over, y_cell_over], DEAD) # Kill
        fill(0) # reflect changed status
      else  # Cell is dead
        cells.set([x_cell_over, y_cell_over], ALIVE) # Make alive
        fill(alive) # Fill alive color
      end

    elsif pause && !mouse_pressed?  # And then save to buffer once mouse goes up
      # Save cells to buffer (so we operate with one array keeping the other intact)
      @cells_buffer = cells.copy
    end
  end

  def tick!  # When the clock ticks
    # Save cells to buffer (so we operate with one array keeping the other intact)
    @cells_buffer = cells.copy
    # Visit each cell:
    (0...row).each do |x|
      (0...column).each do |y|
        # And visit all the neighbours of each cell
        neighbours = 0 # We'll count the neighbours
        (x - 1..x + 1).each do |xx|
          (y - 1..y + 1).each do |yy|
            # Make sure you are not out of bounds
            next unless [(xx >= 0), (xx < row), (yy >= 0), (yy < column)].all? { |in_bounds| in_bounds == true }
            # Make sure to check against self
            unless [(xx == x), (yy == y)].all? { |is_self| is_self == true }
              if cells_buffer.get([xx, yy]) # true == ALIVE
                neighbours += 1 # Check alive neighbours and count them
              end # alive
            end # End of if self
          end # End of yy loop
        end # End of xx loop
        # We've checked the neighbours: apply rules in one line (only in ruby)!
        cells.set([x, y], (cells_buffer.get([x, y])) ? ((2..3) === neighbours) : (neighbours == 3))
      end # End of y loop
    end # End of x loop
  end # End of function

  def key_pressed
    case key
    when 'r', 'R'
      # Restart: reinitialization of cells
      @cells = MDArray.boolean([row, column], random_data)
    when ' ' # On/off of pause
      @pause = !pause
    when 'c', 'C' # Clear all
      @cells = MDArray.boolean([row, column], DEAD)
    end
  end

  def random_data
    (0...row * column).map { rand(1000) < ALIVE_START }
  end
end

GameOfLife.new(title: 'Game of Life mdarray version')

Alternative Ruby-Processing implementation

Currently I am working on a new version of JRubyArt to create an alternative ruby wrapper for processing (to ruby-processing).
  • Sketches run as regular ruby scripts (just need jruby)
  • Vecmath libraries developed for ruby-processing built in
  • You can use Netbeans-8.0.1 to develop and run sketches
  • From netbeans you load and use other gems with your sketches
  • Has a blank sketch creator built in
  • Is in development (yet to support opengl sketches, libraries)
Some sketches as with regular ruby-processing (load image etc) require jruby-complete to run, but with netbeans this is not a problem:-


Followers

About Me

My Photo
Consolidating my online identity as monkstone. I am a 64 bit linux user and advocate of open source software, you can sometimes find me on the processing forum.