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)

Wednesday, 29 October 2014

Toxiclibs (or any other processing library) as a rubygem

As a proof of principle I have created gem that wraps some of Karsten Schmidts (aka toxi @toxi, aka postspectacular k@thi.ng) toxiclibs as a ruby-gem. This gem has been shown to usable with ruby-processing (or its development version JRubyArt) but if yokolet is right it can also be used with clojure, and other jvm languages (by using JRuby Embed, formerly RedBridge). Here is more on RedBridge and clojure.
require 'toxiclibs'

include Toxi

#
# <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
#

NUM_ITERATIONS = 10

attr_reader :gs, :tone_map

def setup
  size(256,256)
  @gs= Toxi::GrayScott.new width,height, false
  @gs.set_coefficients 0.021, 0.076, 0.12, 0.06
  # create a color gradient for 256 values
  grad = Toxi::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, Toxi::NamedColor::BLACK)
  grad.add_color_at(128, Toxi::NamedColor::RED)
  grad.add_color_at(192, Toxi::NamedColor::YELLOW)
  grad.add_color_at(255, Toxi::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 = Toxi::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



Here is another example running a JRubyArt sketch from netbeans.

Monday, 27 October 2014

For the next version of ruby-processing examples will not be included in the gem.

You will still be able to install using "rp5 setup unpack_samples" which will just download and install the sketches for you:-

Example-Sketches

Many of the vanilla processing example sketches have been translated to ruby-processing, and they are mainly written as 'bare' sketches (ie not class wrapped) in keeping with the original processing. At runtime these sketches the get wrapped into a Sketch class. Should you prefer you can still write class wrapped sketches, these will work equally well with ruby processing. Certain sketches must be run with JRuby-Complete (load_image and shader sketches), this is some java permissions thing with jruby. You should also checkout the Nature of Code Examples in ruby and for the beginner Learning Processing with Ruby.

Partial Catalogue (for the lazy)

  1. Basic

    1. structure
    2. objects
    3. arrays
    4. input
    5. shape
    6. image
    7. control
  2. Topics

    1. shaders
    2. lsystems
  3. Libraries

    1. fastmath
    2. vecmath
    3. control-panel

Saturday, 25 October 2014

Sketch featuring Vec2D to_curve_vertex (JRubyArt)

########
# Soft Body by Ira Greenberg
# Softbody dynamic simulation using curve_vertex
# and curve_tightness (and new Vec2D to_curve_vertex)
########
require 'jruby_art'

# SoftBody class wrapped JRubyArt sketch features Vec2D :to_curve_vertex
class SoftBody < Processing::App
  attr_reader :accel, :center, :frequency, :radius, :rot_angle
  attr_reader :organic_constant, :nodes, :renderer, :angle, :node_start
  SPRINGING = 0.0009
  DAMPING = 0.98
  NODES = 5

  def setup
    size 640, 360
    @renderer = AppRender.new(self)
    init_node
    no_stroke
    frame_rate 30
  end

  def init_node
    @accel = Vec2D.new
    @center = Vec2D.new(width / 2, height / 2)
    @radius = 45
    @organic_constant = 1
    @rot_angle = -90
    @nodes = (0...NODES).map { Vec2D.new }
    @frequency = (0...NODES).map { rand(5..12) }
    @angle = Array.new(NODES, 0)
  end

  def draw
    fill(0, 100)
    rect(0, 0, width, height)
    update
    draw_shape
    move_shape
  end

  def draw_shape
    curve_tightness(organic_constant)
    fill 255
    begin_shape
    nodes.each { |vec| vec.to_curve_vertex(renderer) }
    nodes.take(NODES - 1).each { |vec| vec.to_curve_vertex(renderer) }
    end_shape(Java::ProcessingCore::PConstants::CLOSE)
  end

  def update
    delta = Vec2D.new(mouse_x - center.x, mouse_y - center.y)
    delta *= SPRINGING
    @accel += delta
    @center += accel
    @accel *= DAMPING
    @organic_constant = 1 - (((accel.x).abs + (accel.y).abs) * 0.1)
    @node_start = create_start
  end

  def create_start
    (0...NODES).map do |n|
      Vec2D.new(
        center.x + DegLut.cos(n * (360 / NODES)) * radius,
        center.y + DegLut.sin(n * (360 / NODES)) * radius
        )
    end
  end

  def move_shape
    (0...NODES).each do |i|
      nodes[i] = Vec2D.new(
      node_start[i].x + DegLut.sin(angle[i]) * (accel.x * 2),
      node_start[i].y + DegLut.sin(angle[i]) * (accel.y * 2)
      )
      angle[i] = frequency[i] + angle[i]
    end
  end
end

SoftBody.new(title: 'Soft Body')

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:-

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.