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)

Saturday, 19 July 2014

Ruby-processing at version 2.5.0 on github

Can't wait to try it out? The latest version of ruby-processing is now available at github. It is a bit trickier to build than previous versions and requires an installed jruby, ruby-compiler (gem) see instructions here. Actually the build is really easy just do "rake" and you are done (but you need to set your procssing root and you might not have ruby-compiler installed?). The compiled jruby extensions offer an interesting way in which to extend ruby-processing to use java libraries (not necessarily restricted to processing libraries), but especially with libraries that rely on reflection in their interface. The best exemplar of this the ArcBall utility, that uses reflection under the hood (to register pre()) so we can implement ArcBall functionality in a ruby processing sketch in one line (updates are sent to sketch via pre()). Obvious candidates for such an approach is java JBox2D, so would not have to rely on Dan Schiffmans PBox2D (now Box2DProcessing) or Ricard Marxers fisica, which could build on ruby-processings Vec2D.

ArcBall sketch code:-
# 1. drag mouse to rotate arcball
# 2. hold down x, y, or z to constrain arcball rotation to that axis
#

load_library :vecmath

# initialize arcball in setup and you are done...........

  # either provide arc ball center (sketch is translated to center)

  ArcBall.init(self, width/2.0, height/2.0)

  # or alternative form, where camera is set to point at center

  ArcBall.init(self)

DegLut sketch code:-

# DegLut.sin() and DegLut.cos() values

load_library :fastmath
# draw minute markers for analogue clock
  (0..360).step(6) do |a|
    x = 100 + DegLut.cos(a) * 72
    y = 100 + DegLut.sin(a) * 72
    point x, y
  end

A complete example showing how to initialize AppRender, and use it to allow Vec2D to be written (by sketch authors) directly as PApplet vertices
#
# Morph. 
# 
# Changing one shape into another by interpolating
# vertices from one to another
#
load_library :vecmath

attr_reader :circle, :square, :morph, :state, :v1, :v2, :renderer

ALPHA = Math::PI / 4.0
OMEGA = TAU + ALPHA
THETA = Math::PI / 20.0

def setup
  size(640, 360)
  @circle = []
  @square = []
  @morph = []
  @state = false
  @renderer = AppRender.new(self)
  frame_rate(15)
  # Create a circle using vectors pointing from center
  (ALPHA .. OMEGA).step(THETA) do |angle|
    # Note we are not starting from 0 in order to match the
    # path of a circle.  
    circle << Vec2D.from_angle(angle) * 100
    # Let's fill out morph Array with blank Vec2Ds while we are at it
    morph << Vec2D.new
  end

  # A square is a bunch of vertices along straight lines
  # Top of square
  (-50 .. 50).step(10) do |x|
    square << Vec2D.new(x, -50)
  end
  # Right side
  (-50 .. 50).step(10) do |y|
    square << Vec2D.new(50, y)
  end
  # Bottom, NB: can't negative step ruby so use your loaf
  5.downto(-5) do |x|
    square << Vec2D.new(x * 10, 50)
  end
  # Left side
  5.downto(-5) do |y|
    square << Vec2D.new(-50, y * 10)
  end
end

def draw
  background(51)

  # We will keep how far the vertices are from their target
  @total_distance = 0

  # Look at each vertex
  circle.length.times do |i|
    # Are we lerping to the circle or square?
    v1 = (state)?  circle[i] : square[i]
    # Get the vertex we will draw
    v2 = morph[i]

    # Lerp to the target
    v2.lerp!(v1, 0.1)
    # Check how far we are from target
    @total_distance += v1.dist(v2)
  end

  # If all the vertices are close, switch shape
  if (@total_distance < 0.08)
    @state = !state
  end

  # Draw relative to center
  translate(width/2, height/2)
  stroke_weight(4)
  # Draw a polygon that makes up all the vertices
  begin_shape
  no_fill
  stroke(255)
  morph.each do |v|
    v.to_vertex(renderer)
  end
  end_shape(CLOSE)
end

Wednesday, 9 July 2014

Exploring PApplet with jruby

There is a lot of clever stuff that goes on in jruby, that allows you to treat java objects as if they were ruby. Recently I had been interested in refactoring the ruby-processings library loader, but that may be premature since changes seem to be afoot with the jruby class loader?

Anyway what is the current situation?
You can simply 'require a jar' to add the jar contents to your CLASSPATH, you can then start using the java classes as if they were ruby objects see PApplet example below:-
require 'java'
require '/home/tux/processing-2.2.1/core/library/core.jar'
app = Java::ProcessingCore::PApplet.new
puts "inspect: #{app.inspect}"
app.set_size(200, 200)
puts "\nto_s: #{app.to_s}"
# print the default renderer
puts "\nrenderer: #{app.sketch_renderer}"
# prints a list of methods including '_size'
puts "\nmethods:\n #{ app.methods.reject{ |method| /_size/ !~ method.to_s} }"


Output:-

inspect: #<Java::ProcessingCore::PApplet:0x4656c38e>

to_s: processing.core.PApplet[panel0,0,0,200x200,invalid,layout=java.awt.FlowLayout]

renderer: processing.core.PGraphicsJava2D

methods:
 [:text_size, :unregister_size, :register_size, :preferred_size_set?, :preferred_size=, :maximum_size=, :set_size, :is_minimum_size_set, :preferred_size, :set_preferred_size, :is_minimum_size_set?, :is_maximum_size_set?, :preferred_size_set, :set_maximum_size, :get_size, :minimum_size_set, :maximum_size_set?, :minimum_size=, :get_maximum_size, :get_minimum_size, :is_preferred_size_set?, :maximum_size, :minimum_size_set?, :maximum_size_set, :set_minimum_size, :is_preferred_size_set, :minimum_size, :get_preferred_size, :is_maximum_size_set]


Alternatively you can make use of the global CLASSPATH directly:-
require 'java'
$CLASSPATH << '/home/tux/processing-2.2.1/core/library/core.jar'
app = Java::ProcessingCore::PApplet.new
puts "inspect: #{app.inspect}"
app.set_size(200, 200)
puts "\nto_s: #{app.to_s}"
# print the default renderer
puts "\nrenderer: #{app.sketch_renderer}"
# prints a list of methods including '_size'
puts "\nmethods:\n #{ app.methods.reject{ |method| /_size/ !~ method.to_s} }"

There is yet another way to do it using classloader directly:-

require 'java'
F = java.io.File
class_loader = JRuby.runtime.jruby_class_loader
class_loader.add_url(F.new('/home/tux/processing-2.2.1/core/library/core.jar').to_url)
app = Java::ProcessingCore::PApplet.new
puts "inspect: #{app.inspect}"
app.set_size(200, 200)
puts "\nto_s: #{app.to_s}"
# print the default renderer
puts "\nrenderer: #{app.sketch_renderer}"
# prints a list of methods including '_size'
puts "\nmethods:\n #{ app.methods.reject{ |method| /_size/ !~ method.to_s} }"



In ruby-processing exported sketches, the jars are currently added to the java CLASSPATH (ie on the java side) this may not be necessary anymore (no Applets), the current export process can mess with nested libraries (on the plus side you only need specify a directory to include the contained jars).
java -cp ".:../lib/ruby/*:../lib/*:../lib/library/*" org.jruby.Main ../lib/ruby-processing/runners/run.rb drawolver.rb

Tuesday, 8 July 2014

Pondering whether to hide implementation detail of Renderers

In a previous post I posted code using my new renderer interface, that exposed the user to the fact that the renderer object were java objects (partly because I thought it would be more transparent, but that would be lost on many users?). But I could easily hide it, by changing vecmath.rb as follows:-
require 'rvecmath'

Java::ProcessingVecmathArcball::ArcballLibrary.new.load(JRuby.runtime, false)
Java::ProcessingVecmathVec2::Vec2Library.new.load(JRuby.runtime, false)
Java::ProcessingVecmathVec3::Vec3Library.new.load(JRuby.runtime, false)

AppRender = Java::ProcessingVecmath::AppRender
ShapeRender = Java::ProcessingVecmath::ShapeRender

The wise could use renderer.inspect to get the following, but it might not be obvious that they should do.
"#<Java::ProcessingVecmath::ShapeRender:0x49c2ebcc>"

Actually it just occurred to me that inspect is a synonym for toString, that I should explicitly provide....
Well I learnt something new there it is not synonym after all, but chose to create a toString anyway.

Monday, 7 July 2014

Development plans for ruby-processing

The next release of ruby-processing will likely be version 2.5.0 (current release 2.4.4 using jruby-1.7.12).  The reason why this will not be a minor release is that I intend to incorporate my pre-compiled vecmath and fastmath libraries as developed on JRubyArt. Both these libraries have been created as jruby extensions (they are written in java, but can be loaded into the jruby runtime as ruby). Apart from the possible efficiency gains of using pre-compiled code, the compiled version of vecmath greatly simplifies the use of my ArcBall utility (only one line of code is required to include ArcBall functionality in a sketch).  What I propose to do is to roll back ruby processing commits to the 2.4.4 release, and set that as the stable branch (for the foreseeable future). I see no compelling reasons to issue anymore 2.4 point releases (eg to bump to jruby-1.7.13, and there seems no likelihood of any further processing 2.0 releases, now development has switched up to version 3.0).




JRubyArt has been using the much heralded jruby-9000, but that will only be at a preview release this summer, see tweet:-



Drawolver featuring new libraries, degree precision look up tables, and to_vertex utility for Vec3D:-
# Drawolver: draw 2D & revolve 3D

# Example shows how to use the vecmath library, including AppRender utility.
# On the ruby side features the use each_cons, a possibly a rare use for this 
# ruby Enumerable method?
# 2010-03-22 - fjenett (somewhat revised by Martin Prout 2014-07-06)

load_library :vecmath, :fastmath

attr_reader :drawing_mode, :points, :rot_x, :rot_y, :vertices, :renderer

def setup
  size 1024, 768, P3D
  @renderer = Java::ProcessingVecmath::AppRender.new(self)
  frame_rate 30
  reset_scene
end

def draw
  background 0
  if (!drawing_mode)
    translate(width/2, height/2)
    rotate_x rot_x
    rotate_y rot_y
    @rot_x += 0.01
    @rot_y += 0.02
    translate(-width/2, -height/2)
  end
  no_fill
  stroke 255
  points.each_cons(2) { |ps, pe| line ps.x, ps.y, pe.x, pe.y}

  if (!drawing_mode)
    stroke 125
    fill 120
    lights
    ambient_light 120, 120, 120
    vertices.each_cons(2) do |r1, r2|
      begin_shape(TRIANGLE_STRIP)
      r1.zip(r2).each { |v1, v2| v1.to_vertex(renderer); v2.to_vertex(renderer)}
      end_shape
    end
  end
end

def reset_scene
  @drawing_mode = true
  @points = []
  @rot_x = 0.0
  @rot_y = 0.0
end

def mouse_pressed
  reset_scene
  points << Vec3D.new(mouse_x, mouse_y)
end

def mouse_dragged
  points << Vec3D.new(mouse_x, mouse_y)
end

def mouse_released
  points << Vec3D.new(mouse_x, mouse_y)
  recalculate_shape
end

def recalculate_shape
  @vertices = []
  points.each_cons(2) do |ps, pe|
    b = (points.last - points.first).normalize!
    a = ps - points.first
    dot_product = a.dot b
    b *= dot_product
    normal = points.first + b
    c = ps - normal
    vertices << []
    (0..360).step(12) do |ang|
      e = normal + c * DegLut.cos(ang)
      e.z = c.mag * DegLut.sin(ang)
      vertices.last << e
    end
  end
  @drawing_mode = false
end


Sketch demonstrating use of ShapeRender (ShapeRender and AppRender both implement the Render interface which is used by Vec3D (and Vec2D) to_vertex, to_vertex_uv and to_normal methods, see here for earlier version without using ShapeRender, NB: the FastMath of that version has not survived:
# 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 62 for inititialization of renderer where obj is an instance of PShape
# renderer = Java::ProcessingVecmath::ShapeRender.new(obj)

load_library :vecmath

attr_reader :pg, :trefoil

def setup
  size(1024, 768, P3D)

  texture_mode(NORMAL)
  noStroke

  # 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 = Java::ProcessingVecmath::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)
  tangU = eval_point(u + 0.01, v)
  tangV = eval_point(u, v + 0.01)
  tangU -= p
  tangV -= p
  tangV.cross(tangU).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 = sin(t)
  cost = cos(t)
  sint15 = sin(1.5 * t)
  cost15 = 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 = cos(s)
  sins = 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

Sketch demonstrating new ArcBall utility, all that's needed to allow mousewheel zoom, and mouse drag to rotate the cube (this form uses camera 'under the hood' to centralize the cube in sketch viewpane, alternative form translates the sketch to provided coordinates):-
load_library :vecmath

attr_reader :my_cube

def setup
  size(600, 600, P3D)
  smooth(16)
  ArcBall.init(self)
  @my_cube = create_shape(BOX, 300, 300, 300)
  my_cube.set_fill(color(100, 10, 100))

end

def draw
  background(50, 50, 100)
  define_lights
  lights
  stroke(0)
  shape(my_cube)
end

def define_lights
  ambient(20, 20, 20)
  ambient_light(50, 50, 50)
  point_light(30, 30, 30, 200, -150, 0)
  directional_light(0, 30, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2)
end

Tuesday, 24 June 2014

Decoupling Sub Classes Using Hook Messages (in ruby-processing)

About time I posted a bit more code:-
# An example demonstrating the use of a hook to decouple subclasses in 
# ruby-processing. The base class here is Spin, and we have included 
# post_initialize as the hook (it does nothing in the base class). SpinArm
# and SpinSpots both subclass Spin, but only SpinSpots requires the hook to
# change the initialization. Note the use the hook means the subclass does 
# not need to call super

def setup
  size 640, 360
  @arm = SpinArm.new({ x: width/2, y: height/2, s: 0.01 })
  @spots = SpinSpots.new({ x: width/2, y: height/2, s: -0.02, d: 90.0 })
end

def draw
  background 204
  @arm.display
  @spots.display
end


class Spin

  attr_accessor :x, :y, :speed
  attr_accessor :angle

  def initialize(args = {})
    @x, @y = args[:x], args[:y]
    @speed = args[:s]
    @angle = args[:angle] || 0.0
    post_initialize(args)  # this is the hook
  end

  def update
    @angle += speed
  end

  def post_initialize args
    nil
  end

end


class SpinArm < Spin # inherit from (or "extend") class Spin
  # NB: initialize inherited from Spin class

  def display
    stroke_weight 1
    stroke 0
    push_matrix
    translate x, y
    update
    rotate angle
    line 0, 0, 165, 0
    pop_matrix
  end
end


class SpinSpots < Spin
  attr_accessor :dim

  def post_initialize args
    @dim = args[:d]
  end

  def display
    no_stroke
    push_matrix
    translate x, y
    update
    rotate angle
    ellipse(-dim/2, 0, dim, dim)
    ellipse(dim/2, 0, dim, dim)
    pop_matrix
  end
end

Ruby 2D games using ruby-processing

Morohoshi-san is developing a ruby games application based on dxruby_sdl here is the tweet from ruby-gems:-




This is a sketch from dxruby_sdl on github run on my linux box (better if I had game controllers I am sure).

Here is the project on github.

Wednesday, 18 June 2014

JRubyArt is now using java extensions to generate ruby (Vecmath, ArcBall, DegLut)

Still waiting for @headius to confirm I am the right track with my java extensions to jruby. But I am not waiting forever so now my updated JRubyArt now includes these extensions:-
  1. A vecmath library, featuring Vec2D and Vec3D classes (written in java, but loaded as ruby)
  2. Also featured in the vecmath library is an ArcBall class  (also written in java, but loaded as ruby)
  3. A deglut library which is  now all, java uses a sin90 look up table to create sin cos functionality(is actually slightly faster than regular math sin/cos)
The extension stuff is written up here 

Thing to do:-
  1. Fix overloaded methods for color? eg background, fill, stroke
  2. Experiment with jafama (again, why is Jonathan Feinberg including google guava libs)
  3. improve build now at least a two stage process
  4. Clone and BFG clobber processing-2.2.1 snapshot?
  5. Create a renderer class that is initialized with PApplet, and has medods to send vertex(arg), normal(arg), curvedVertex(arg) etc. where arg is an instance of Vec3D?

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.