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

Tuesday, 4 December 2012

Fish-eye shader revisited with ruby-processing and processing-2.0

Previously one of the most popular posts (700+ hits) on this blog was my glgraphics post with the fish eye shader. Andres has now incorporated some the shader stuff into processing-2.0, here is essentially the same sketch running in ruby-processing, built with processing-2.0 (which includes built in support for glsl shaders).
##
# Fish Eye
# 
# This fish-eye shader is useful for dome projection.
##

attr_reader :fisheye, :canvas, :use_fish_eye

def setup()
  size(640, 640, P3D)
  @canvas = create_graphics(width, height, P3D)
  @fisheye = load_shader("FishEye.glsl")
  @use_fish_eye = true
  fisheye.set("aperture", 180.0)
end

def draw()
  canvas.begin_draw()
  canvas.background(0)
  canvas.stroke(255, 0, 0)
  (0...width).step(10){ |i| canvas.line(i, 0, i, height)}
  (0...height).step(10){ |i| canvas.line(0, i, width, i)}
  canvas.lights()
  canvas.no_stroke()
  canvas.translate(mouse_x, mouse_y, 100)
  canvas.rotate_x(frameCount * 0.01)
  canvas.rotate_y(frameCount * 0.01)
  canvas.box(100)
  canvas.end_draw()
  shader(fisheye) unless !use_fish_eye
  image(canvas, 0, 0, width, height)
end

def mouse_pressed()
  if (use_fish_eye)
    @use_fish_eye = false
    resetShader()
  else
    @use_fish_eye = true
  end
end

PShader Example in ruby-processing

This example is running with my hacked version of ruby-processing (based on vanilla processing-2.0b6).

##
 # Monjori. 
 # 
 # GLSL version of the 1k intro Monjori from the demoscene 
 # (http://www.pouet.net/prod.php?which=52761)
 # Ported from the webGL version available in ShaderToy:
 # http://www.iquilezles.org/apps/shadertoy/
 # (Look for Monjori under the Plane Deformations Presets) 
##

attr_reader :monjori

def setup
  size(640, 360, P2D)
  noStroke()

  @monjori = loadShader("monjori.glsl")
  monjori.set("resolution", width, height)
end

def draw
  monjori.set("time", millis() / 1000.0)

  shader(monjori)
  # This kind of effects are entirely implemented in the
  # fragment shader, they only need a quad covering the  
  # entire view area so every pixel is pushed through the 
  # shader.   
  rect(0, 0, width, height)
end

A 3D-texture example with my hacked ruby-processing

To get ruby-processing working with processing-2.0b6 core (and the associated opengl/jogl) required a number of hacks. One of which seemed to lose the ability to run sketches that were explicitly sub-classed from Processing::App, but that is probably no bad thing. Here is the modified textured_cube example, note NORMALIZED is replace by NORMAL in processing-2.0 syntax:-


# TexturedCube
# by Dave Bollinger.
# 
# Drag mouse to rotate cube. Demonstrates use of u/v coords in 
# vertex and effect on texture().


def setup
  size 649, 360, P3D
  @rotx = PI/4
  @roty = PI/4
  @tex = load_image "berlin-1.jpg"
  texture_mode NORMAL  # Changed for processing 2.0
  fill 255
  stroke 44, 48, 32
end

def draw
  background 0
  no_stroke
  translate width/2, height/2, -100
  rotate_x @rotx
  rotate_y @roty
  scale 90
  texture_cube
end

def texture_cube

# Given one texture and six faces, we can easily set up the uv coordinates
# such that four of the faces tile "perfectly" along either u or v, but the other
# two faces cannot be so aligned.  This code tiles "along" u, "around" the X/Z faces
# and fudges the Y faces - the Y faces are arbitrarily aligned such that a
# rotation along the X axis will put the "top" of either texture at the "top"
# of the screen, but is not otherwised aligned with the X/Z faces. (This
# just affects what type of symmetry is required if you need seamless
# tiling all the way around the cube)

begin_shape QUADS

  texture @tex

  # +Z "front" face
  vertex -1, -1,  1, 0, 0
  vertex  1, -1,  1, 1, 0
  vertex  1,  1,  1, 1, 1
  vertex -1,  1,  1, 0, 1

  # -Z "back" face
  vertex  1, -1, -1, 0, 0
  vertex -1, -1, -1, 1, 0
  vertex -1,  1, -1, 1, 1
  vertex  1,  1, -1, 0, 1

  # +Y "bottom" face
  vertex -1,  1,  1, 0, 0
  vertex  1,  1,  1, 1, 0
  vertex  1,  1, -1, 1, 1
  vertex -1,  1, -1, 0, 1

  # -Y "top" face
  vertex -1, -1, -1, 0, 0
  vertex  1, -1, -1, 1, 0
  vertex  1, -1,  1, 1, 1
  vertex -1, -1,  1, 0, 1

  # +X "right" face
  vertex  1, -1,  1, 0, 0
  vertex  1, -1, -1, 1, 0
  vertex  1,  1, -1, 1, 1
  vertex  1,  1,  1, 0, 1

  # -X "left" face
  vertex -1, -1, -1, 0, 0
  vertex -1, -1,  1, 1, 0
  vertex -1,  1,  1, 1, 1
  vertex -1,  1, -1, 0, 1

  end_shape
end

def mouse_dragged
  rate = 0.01
  @rotx += ( pmouse_y - mouse_y ) * rate
  @roty += ( mouse_x - pmouse_x ) * rate
end

Sunday, 2 December 2012

Testing ruby-processing with processing-2.0

Very recently originalsurfmex forked ruby-processing here, with the noble intent of updating it to use jruby-1.7.0, and the then latest version of processing-2.0. It was unfortunate as that was version 2.0b5, which was a bit buggy. I just cloned the forked version onto my linux box and recompiled it with version-2.0b6 which is somewhat more settled  (but nowhere near current development version, where AWT has been replaced with NEWT).  Anyway here is snapshot of the result of running a modified jwishy (the control panel look a bit weird because I was using openjdk).
Anyway originalsurfmex fork doesn't really do it for 3D or without modification didn't work for me.  So I hacked it up myself to get 3D working after a fashion (I needed to ditch all references to online in code, got rid of overriden methods size and handleDraw in app.rb). I found I needed to include processing.opengl and possibly processing.event as well as processing.core.
More to follow.....

Tuesday, 28 August 2012

Control Panel Enhanced ArcBall Sketch

Here is an enhanced arcball sketch, features use of control panel for zoom facility, and drop down menu too choose which or whether rotation is frozen about an axis. See previous post for library.
load_libraries 'arcball', 'opengl', 'control_panel'
import "arcball"
import "opengl"

X = 0
Y = 1
Z = 2

attr_reader :my_ball, :zoom

def setup
  size(600, 600, OPENGL)
  setup_opengl
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
  control_panel do |c|
    c.title = 'Controller'
    c.slider  :zoom, 0..2.0, 0.0
    c.menu(:freeze, ['X-axis', 'Y-axis', 'Z-axis', 'free'], 'free') {|m| load_menu_item(m) }
  end
  @zoom = 0
end

def draw
  background(50, 50, 100)
  translate(width/2.0, height/2.0, zoom * width/2.0)
  define_lights
  update
  lights
  stroke(0)
  cube(my_ball.radius / 2.0)
end

def setup_opengl
  hint ENABLE_OPENGL_4X_SMOOTH     # optional
  hint DISABLE_OPENGL_ERROR_REPORT # optional
end

def update
  theta, x, y, z = my_ball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  my_ball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  my_ball.mouse_dragged(mouse_x, mouse_y)
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

def load_menu_item(m)
  case(m)
  when 'X-axis':
    my_ball.select_axis(X)
  when 'Y-axis':
    my_ball.select_axis(Y)
  when 'Z-axis':
    my_ball.select_axis(Z)
  when 'free'
    my_ball.select_axis(-1)
  end
end

def cube(sz)
  sz *= 0.5
  fill(200,  200,  200,  255)
  begin_shape(QUADS)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, +sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, -sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    end_shape
end

sketch screenshot using the gimp

Monday, 27 August 2012

A ArcBall library for ruby-processing

Here is my library, NB: three classes in a file named arcball.rb, and in a folder arcball nested in a library folder (NB: AVector is a low fat version of PVector for use here). Note you can put other ruby libraries in that folder. Note the use of :: separators and the Java prefix to call the EPSILON constant from vanilla processing.
class ArcBall
  attr_reader :center_x, :center_y, :v_down, :v_drag, :q_now, :q_drag, :q_down, :axis, :axis_set, :radius

  def initialize(cx, cy, radius)
    @center_x = cx
    @center_y = cy
    @radius = radius
    @v_down = AVector.new
    @v_drag = AVector.new
    @q_now = Quaternion.new
    @q_down = Quaternion.new
    @q_drag = Quaternion.new
    @axis_set = [AVector.new(1.0, 0.0, 0.0), AVector.new(0.0, 1.0, 0.0), AVector.new(0.0, 0.0, 1.0)]
    @axis = -1
  end

  def select_axis(axis)
    @axis = axis
  end

  def mouse2sphere(x, y)
    v = AVector.new((x - center_x) / radius, (y - center_y) / radius, 0)
    mag = v.x * v.x + v.y * v.y
    if (mag > 1.0)
      v.normalize
    else
      v.z = Math.sqrt(1.0 - mag)
    end
    v = constrain(v, axis_set[axis]) unless (axis == -1)
    return v
  end

  def mouse_pressed(x, y)
    @v_down = mouse2sphere(x, y)
    @q_down.copy(q_now)
    @q_drag.reset
  end

  def mouse_dragged(x, y)
    @v_drag = mouse2sphere(x, y)
    @q_drag.set(AVector.dot(v_down, v_drag), v_down.cross(v_drag))
  end


  def constrain(vector, axis)
    res = AVector.sub(vector, AVector.mult(axis, AVector.dot(axis, vector)))
    res.normalize
  end

  def update
    @q_now = Quaternion.mult(q_drag, q_down)
    quat2matrix(q_now)
  end

  def quat2matrix(q)
    q.get_value
  end
end

class Quaternion
  attr_reader :w, :x, :y, :z

  def initialize(w = 1.0,  x = 0,  y = 0,  z = 0)
    @w, @x, @y, @z = w,  x,  y,  z
  end

  def reset
    @w = 1.0
    @x = 0.0
    @y = 0.0
    @z = 0.0
  end

  def set(w, v)
    @w = w
    @x = v.x
    @y = v.y
    @z = v.z
  end

  def copy(q)
    @w = q.w
    @x = q.x
    @y = q.y
    @z = q.z
  end

  def self.mult(q1, q2)      # class method   
    x0 = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
    y0 = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z
    z0 = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x
    w0 = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
    Quaternion.new(w0,  x0,  y0,  z0)
  end

  def get_value
    sa = Math.sqrt(1.0 - w * w)
    sa = 1.0 unless (sa >= Java::processing::core::PConstants::EPSILON)
    [Math.acos(w) * 2, x / sa, y / sa, z / sa]
  end
end

class AVector

  attr_accessor :x, :y, :z

  def initialize(x = 0, y = 0, z = 0)
    @x, @y, @z = x, y, z
  end

  def add(vector)
    AVector.new(vector.x + x, vector.y + y, vector.z + z)
  end

  def normalize
    orig_dist = Math.sqrt(x * x + y * y + z * z)
    @x /= orig_dist
    @y /= orig_dist
    @z /= orig_dist
    self
  end

  def self.dot(v1, v2)
    v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
  end

  def self.mult(v, scalar)
    AVector.new(v.x * scalar, v.y * scalar, v.z * scalar)
  end

  def self.sub(v1, v2)
    AVector.new(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)
  end

  def cross(v)
    AVector.new(y * v.z - v.y * z,  z * v.x - v.z * x, x * v.y - v.x * y)
  end

end

This is the test class test_arcball.rb:-
load_libraries 'arcball', 'opengl'
import "arcball"
import "opengl"

X = 0
Y = 1
Z = 2

attr_reader :my_ball

def setup
  size(600, 600, OPENGL)
  setup_opengl
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
end

def draw
  background(50, 50, 100)
  translate(width/2.0, height/2.0)  # @todo add zoom via z, using control_panel
  define_lights
  update
  lights
  stroke(0)
  cube(my_ball.radius)
end

def setup_opengl
  hint ENABLE_OPENGL_4X_SMOOTH     # optional
  hint DISABLE_OPENGL_ERROR_REPORT # optional
end

def update
  theta, x, y, z = my_ball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  my_ball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  my_ball.mouse_dragged(mouse_x, mouse_y)
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

def key_pressed                # @todo select via control_panel instead
  case(key)
  when 'x':
    my_ball.select_axis(X)
  when 'y':
    my_ball.select_axis(Y)
  when 'z':
    my_ball.select_axis(Z)
  end
end

def key_released
  my_ball.select_axis(-1)
end

def cube(sz)
  sz *= 0.5
  fill(200,  200,  200,  255)
  begin_shape(QUADS)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, +sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, -sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    end_shape
end



Sunday, 17 June 2012

A Gui for my pixelation library

Over on my other blog I recently described an application for pixelating pictures in processing, but also using the cfdg program (CF3), here is the program adapted for ruby-processing, with an added gui using "control_panel"




load_libraries 'pde2cfdg', 'control_panel'
import "pde2cfdg"

#
# Two cfdg files are written to the sketch folder one for design one for data,
# this means it easier to edit the design (eg to replace CIRCLE with a custom shape)
# The output png file is written to the sketch folder,
#

attr_reader :cfdg, :dot_size

def setup
    size(640, 512)
    background(255)
    setup_panel
    colorMode(HSB, 1.0)
    @cfdg = ProcessingToCF.new(self)
    cfdg.set_dot_size(dot_size)
    cfdg.setPathToCFDG("/home/tux/CF3/cfdg")
    cfdg.get_input
    cfdg.writeCFDG
end

def setup_panel
    control_panel do |c|
      c.title = "Control:"
      c.slider(:dot_size, 1..6, 3)
      c.button(:re_run)
    end
end

def re_run
  cfdg.set_dot_size(dot_size)
  cfdg.writeCFDG
end

def draw
  if (cfdg.finished)
    img = load_image(cfdg.outFile)
    image(img, 0, 0, img.width, img.height)
  end
end
Here's a screenshot of application in action, the artist is Phil Sutton RA

Followers

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