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)

Thursday, 27 August 2015

Advanced shader sketches in JRubyArt

Andres Colubri has published some advanced shader experiments here for processing-3.0, this is one of the examples translated for JRubyArt (requires shader files from the repo. See also Andres write-up on Advanced-OpenGL. Cool sketch of a textured Earth with an independent cloud layer.
# Earth model with bump mapping, specular texture and dynamic cloud layer.
# Adapted from the THREE.js tutorial to processing by Andres Colubri,
# translated to JRubyArt by Martin Prout:
# http://learningthreejs.com/blog/2013/09/16/how-to-make-the-earth-in-webgl/

attr_reader :earth, :clouds, :earth_shader, :cloud_shader, :earth_rotation
attr_reader :clouds_rotation, :target_angle

def setup
  sketch_title 'Blue Marble'
  @earth_rotation = 0
  @clouds_rotation = 0
  earth_tex = load_image('earthmap1k.jpg')
  cloud_tex = load_image('earthcloudmap.jpg')
  alpha_tex = load_image('earthcloudmaptrans.jpg')
  bump_map = load_image('earthbump1k.jpg')
  spec_map = load_image('earthspec1k.jpg')
  @earth_shader = load_shader('EarthFrag.glsl', 'EarthVert.glsl')
  earth_shader.set('texMap', earth_tex)
  earth_shader.set('bumpMap', bump_map)
  earth_shader.set('specularMap', spec_map)
  earth_shader.set('bumpScale', 0.05)
  @cloud_shader = load_shader('CloudFrag.glsl', 'CloudVert.glsl')
  cloud_shader.set('texMap', cloud_tex)
  cloud_shader.set('alphaMap', alpha_tex)
  @earth = create_shape(SPHERE, 200)
  earth.setStroke(false)
  earth.setSpecular(color(125))
  earth.setShininess(10)
  @clouds = create_shape(SPHERE, 201)
  clouds.setStroke(false)
end

def draw
  background(0)
  translate(width / 2, height / 2)
  point_light(255, 255, 255, 300, 0, 500)
  target_angle = map1d(mouse_x, (0..width), (0..TWO_PI))
  @earth_rotation += 0.05 * (target_angle - earth_rotation)
  shader(earth_shader)
  push_matrix
  rotate_y(earth_rotation)
  shape(earth)
  pop_matrix
  shader(cloud_shader)
  push_matrix
  rotate_y(earth_rotation + clouds_rotation)
  shape(clouds)
  pop_matrix
  @clouds_rotation += 0.001
end

def settings
  size(600, 600, P3D)
end

Monday, 17 August 2015

Java-8-lambda and jruby extensions

Should we be getting excited about java-8-lambda for jruby extensions (working in NetBeans I got this suggestion when updating source from java-7 to java-8 having listened to Charlies talk at JVMLS2015 perhaps we should?
// Java seven

public static void createVec2(final Ruby runtime) {
    RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), new ObjectAllocator() {
            @Override
            public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
                return new Vec2(runtime, rubyClass);
            }
    });
    vec2Cls.defineAnnotatedMethods(Vec2.class);

}

// Java eight
public static void createVec2(final Ruby runtime) {
    RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), (Ruby runtime1, RubyClass rubyClass) -> new Vec2(runtime1, rubyClass));
    vec2Cls.defineAnnotatedMethods(Vec2.class);
}
Update 22 August 2015 just released JRubyArt 0.5.0 featuring java8lambda in production...

Friday, 14 August 2015

AABB for JRubyArt using keyword args

I'm adding a 2D axis aligned bounding box to JRubyArt, here's what it looks like, position can be conditionally updated (ie if block given, only updates if the block evaluates to true):-
# Axis aligned bounding box
class AABB
  attr_reader :center, :extent

  def initialize(center:, extent:)
    @center = center
    @extent = extent
  end

  def self.from_min_max(min:, max:)
    new(center: (min + max) * 0.5, extent: max - min)
  end

  def position(vec)
    return @center = vec unless block_given?
    @center = vec if yield
  end

  def scale(d)
    @extent *= d
  end

  def contains?(vec)
    rad = extent * 0.5
    return false unless (center.x - rad.x..center.x + rad.x).cover? vec.x
    (center.y - rad.y..center.y + rad.y).cover? vec.y
  end
end

Here is an example using it (based on processing Basics/Input/MouseFunctions.pde but vastly superior to my thinking). Two AABB objects are used, one to define the window bounds so we can stop block going off screen (vanilla processing example fails to do that) and one to define the block area.
# Click on the box and drag it across the screen.

attr_reader :block, :block_locked, :over_block, :renderer, :bounds
BLOCK_WIDTH = 150

def setup
  sketch_title 'AABB Example'
  @block = Block.new(
    center: Vec2D.new(width / 2, height / 2),
    size: Vec2D.new(BLOCK_WIDTH, BLOCK_WIDTH))
  @locked = false
  @over_block = false
  @bounds = AABB.new(
    center: Vec2D.new(width / 2, height / 2),
    extent: Vec2D.new(width - BLOCK_WIDTH, height - BLOCK_WIDTH))
  @renderer = AppRender.new(self)
end

def draw
  background 0
  fill 153
  if block.over?(Vec2D.new(mouse_x, mouse_y))
    @over_block = true
    stroke 255
    fill 255 if block_locked?
  else
    @over_block = false
    stroke 153
  end
  # Draw the box as a shape
  begin_shape
  block.points_array.each { |vec| vec.to_vertex(renderer) }
  end_shape(CLOSE)
end

def block_locked?
  block_locked
end

def over_block?
  over_block
end

def mouse_pressed
  if over_block?
    @block_locked = true
    fill 255
  else
    @block_locked = false
  end
end

def mouse_dragged
  return unless block_locked?
  position = Vec2D.new(mouse_x, mouse_y)
  block.new_position(position) { bounds.contains? position }
end

def mouse_released
  @block_locked = false
end

def settings
  size 640, 360
  smooth 4
end

# Use class to contain block behaviour
class Block
  attr_reader :aabb

  def initialize(center:, size:)
    @aabb = AABB.new(center: center, extent: size)
  end

  def new_position(center, &block)
    @aabb.position(center.dup, &block)
  end

  def over?(vec)
    aabb.contains? vec
  end

  # use for shape
  def points_array
    a = aabb.center - aabb.extent * 0.5
    c = aabb.center + aabb.extent * 0.5
    b = Vec2D.new(c.x, a.y)
    d = Vec2D.new(a.x, c.y)
    [a, b, c, d]
  end

  # use for rect
  def points
    [aabb.center, aabb.extent]
  end
end

Thursday, 13 August 2015

Numeric#step with keyword arguments in JRubyArt (and jruby-9.0.0.0)

Since jruby-9.0.0.0 we can use many syntax features of ruby-2.1, most useful of these for JRubyArt is the ability to make code more readable/reliable with various keyword arguments changes here is one such example:-
# We extend the language of conditionals by adding the
# keyword "elsif". This allows conditionals to ask
# two or more sequential questions, each with a different
# action.

def setup
  background 0
  2.step(by: 2, to: width - 2) do |i|
    # If 'i' divides by 20 with no remainder
    # draw the first line..
    # else if 'i' divides by 10 with no remainder
    # draw second line, else draw third line
    if (i % 20) == 0
      stroke 255
      line i, 80, i, height / 2
    elsif (i % 10) == 0
      stroke 153
      line i, 20, i, 180
    else
      stroke 102
      line i, height / 2, i, height - 20
    end
  end
end

def settings
  size 640, 360
end

Thursday, 30 July 2015

Ruby-Processing now using pry for live coding mode

The latest release of ruby-processing uses pry for live coding, and this is genuinely exciting. Here is a live mode for a class wrapped jwishy.rb sketch, which I have listed in the pry console using '$' command (NB: click on images to see full size):-

This is all the class wrapping needed

class JWishy < Processing::App
  # bare sketch code
end
Bare sketches instead get listed as the super class, but to be honest listing of the code in the pry console is not that exciting see embedded video, the exciting bit is to be able to futz with sketch code from the console and watch it affect the running console (but why not try it for yourself). In the "Wishy Worm" example we change the background of the sketch by changing the @back_color variable, and change the bluish accessor method to a custom method that returns bluish as function of the y_wiggle variable

But you can do so much more, including 'ls' available methods, and remember pry has a built in help...


live from monkstone on Vimeo.

Live coding in ruby-processing now using pry

Bounce.rb code used in video
You should also checkout the pry 'edit' function, which allows you to use your favourite editor to edit a method say 'JWishy#draw'

Sunday, 26 July 2015

Yet more experiments with JRubyArt and pry

My experience of ruby-processing with pry (for live mode) has not been promising, however things change so I thought I would give it another go, since the live IRB, has not been that wonderful either:-
The live.rb code
# An pry shell for live coding.
# Will start with your sketch.

require_relative 'base'
Processing.load_and_run_sketch

ARGV.clear # So that pry doesn't try to load them as files.

require 'pry'
$app.pry

The sketch k9 create fred 300 300 --wrap
class Fred < Processing::App
  def setup
    sketch_title 'Fred'
  end

  def draw

  end

  def settings
    size 300, 300, FX2D
    # smooth # here
  end
end

k9 live fred.rb

Turns out it was a really lucky choice to experiment with FX2D sketch (as there might be threading or other issues with JAVA2D), another strategy that seemed to work well is to redefine the sketch draw loop from pry (still can't see a serious use case, but it is a bit of fun). IRB can handle a yield block in draw loop (with block_given? guard) but it doesn't play too well with pry.

Tuesday, 14 July 2015

A more refined (ruby-2.2) interface for java classes with mixed arguments

Since ruby-2.0 it has been possible to use keywords as first class arguments, and ruby-2.1 introduced the required keyword argument (these are available to JRubyArt and potentially ruby-processing since jruby-9.0.0.0). So I thought it would be sensible to make use of them to make say the parameters of an ArcBall library more descriptive:-
# experimental ArcBall class
class ArcBall
  attr_reader :applet, :x, :y, :r
  def initialize(app:, center_x: nil, center_y: nil, radius: nil)
    @applet = app
    @x = (center_x.nil?) ? app.width * 0.5 : center_x
    @y = (center_y.nil?) ? app.height * 0.5 : center_y
    @r = (radius.nil?) ? app.width * 0.8 : radius
  end

  def to_s
    format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)',
           applet, x, y, r)
  end
end

Applet = Struct.new(:width, :height) do
  def to_s
    'MyApplet'
  end
end

applet = Applet.new(800, 600)
puts ArcBall.new(app: applet).to_s
puts ArcBall.new(app: applet, center_x: 300, center_y: 200, radius: 800).to_s

Output:
ArcBall.new(applet: MyApplet, centre_x: 400, centre_y: 300, radius: 640)
ArcBall.new(applet: MyApplet, centre_x: 300, centre_y: 200, radius: 800)

Testing whether concept works in practice for JRubyArt (using a local ArcBall library in sketchbook for test purposes);-
load_library :arcball

def setup
  sketch_title 'Arcball Box'
  ArcBall.new(app: self)
  fill 180
end

def draw
  background 50
  box 300, 300, 300
end

def settings
  size 600, 600, P3D
  smooth 8
end

# experimental ArcBall class
class ArcBall
  include_package 'arcball'
  attr_reader :applet, :x, :y, :r
  def initialize(app:, center_x: nil, center_y: nil, radius: nil)
    @applet = app
    @x = (center_x.nil?) ? app.width * 0.5 : center_x
    @y = (center_y.nil?) ? app.height * 0.5 : center_y
    @r = (radius.nil?) ? app.width * 0.8 : radius
    app = JarcBall.new applet.to_java, x.to_java(:float), y.to_java(:float), r.to_java(:float)
    app.set_active true
  end

  def to_s
    format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)',
           applet, x, y, r)
  end
end

And indeed it does work if you supply suitable java types to the "hidden" java ArcBall class.

Followers

About Me

My Photo
I am currently developing JRubyArt a new version of ruby-processing for JRuby-9.0.0.0 and processing-3.0.