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

Thursday, 25 December 2014

Towards a zoom-able mandelbrot ruby-processing

Working toward a zoom-able mandelbrot (albeit somewhat speed limited in ruby-processing)
Parameter = Struct.new(:cx, :cy, :ch, :cw) # required for future

RATIO = 1.336
TMAX = 5_000

attr_reader :current, :default # required for future work

def setup
  size(534, 400, P2D)
  @default = Parameter.new(-0.53 - (1.4 * RATIO), -1.4, 2.8, 2.8 * RATIO)
  @current = default
  load_pixels
  no_fill
  stroke(255)
  create_mandelbrot(width, height)
  no_loop
end

def create_mandelbrot(w, h)
  y_zero = ->(y, param) { y * param.ch / height + param.cy }
  x_zero = ->(x, param) { x * param.cw / width + param.cx }
  h.times do |j|
    y0 = y_zero.call(j, current)
    w.times do |k|
      x0 = x_zero.call(k, current)
      x, y = 0, 0
      xsqr, ysqr = 0.0, 0.0
      t = 0
      while t < TMAX && (xsqr + ysqr < 4.0)
        y = x * y
        y += y + y0
        x = xsqr - ysqr + x0
        xsqr = x * x
        ysqr = y * y
        t += 1
      end
      if t < TMAX
        smth = t - Math.log(xsqr + ysqr)
        pixels[k + j * width] = Java::JavaAwt::Color.HSBtoRGB(smth / 500, 0.8, 0.9)
      else
        pixels[k + j * width] = 0
      end
    end
  end
  update_pixels
end

Tuesday, 23 December 2014

Lorenz Attractor Ruby-Processing

Here is a processing sketch that used a LinkedList (not needed in ruby version, an array is sufficient)
LENGTH = 10_000
SPEED = 10

load_library 'vecmath'

attr_accessor :particles, :a, :b, :c, :d

def setup
  size 512, 512, P3D
  ArcBall.init(self)
  frame_rate 30
  stroke color(0, 0, 0, 33)
  stroke_weight 2
  @a = 10
  @b = 28
  @c = 8 / 3
  @d = 0.75
  init_particles
end

def draw
  background(255)
  advance_particles(SPEED)
  draw_particles
end

def init_particles
  @particles = []
  LENGTH.times do
    add_particle(Vec3D.new 1.5, -1.5, 1.5) # need to start somewhere
  end
end

def add_particle(p)
  step = Vec3D.new(a * (p.y - p.x), p.x * (b - p.z) - p.y, p.x * p.y - c * p.z)
  step *= (d / step.mag)
  particles << p + step
end

def advance_particles(count)
  (1..count).each do
    particles.shift
    add_particle(particles.last)
  end
end

def draw_particles
  scale(8)
  particles.each_cons(2) do |a, b|
    stroke(color((b.x - a.x) * 255, (b.y - a.y) * 255, (b.z - a.z) * 255, 88))
    line(a.x, a.y, a.z - 30, b.x, b.y, b.z - 30)
  end
end

Thursday, 18 December 2014

Reading and writing to a csv file ruby-processing (version two)

I find it really surprising that one of most popular posts for ruby-processing is the original version here is an alternative slightly cleaner version:-
#
# Loading Tabular Data
# after Daniel Shiffman, by Martin Prout.
#
# This example demonstrates how to use CSV
# to retrieve data from a CSV file and make objects
# from that data.
#
# Here is what the CSV looks like:
#
#   x,y,diameter,name
#   160,103,43.19838,Happy
#   372,137,52.42526,Sad
#   273,235,61.14072,Joyous
#   121,179,44.758068,Melancholy
#
require 'csv'

load_library 'bubble'

attr_reader :bubbles, :data

def setup
  size(640, 360)
  load_data
end

def draw
  background(255)
  # Display all bubbles
  bubbles.run
  text_align(LEFT)
  fill(0)
  text('Click to add bubbles.', 10, height - 10)
end

def load_data
  # Load CSV file into an Array of Hash objects
  # headers: option indicates the file has a header row
  @bubbles = BubbleData.new
  CSV.foreach('data/data.csv', headers: true) do |row|
    x = row['x'].to_f
    y = row['y'].to_f
    d = row['diameter'].to_f
    n = row['name']
    # Make a Bubble object out of the data read
    bubbles << Bubble.new(x, y, d, n)
  end
end

def mouse_pressed
  bubbles << Bubble.new(mouse_x, mouse_y, rand(40..80), 'blah')
  # If there are more than 10 bubbles delete the oldest bubble
  bubbles.shift if bubbles.size > 10
  # Writing the csv data back to the same file, (also specify UTF-8 format)
  CSV.open('data/data.csv', 'w:UTF-8') do |csv|
    csv << %w(x y diameter name) # create csv headers
    bubbles.each do |bubble|
      csv << bubble.to_a       # write back bubble data
    end
  end
  # And reloading it
  load_data
end

# A run module
module Runnable
  def run
    each(&:display)
    each { |item| item.rollover(mouse_x, mouse_y) }
  end
end

# Enumerable class holds bubble data
class BubbleData
  extend Forwardable
  def_delegators(:@bubbles, :each, :<<, :size, :shift)
  include Enumerable, Runnable

  def initialize
    @bubbles = []
  end
end

Tuesday, 16 December 2014

Experimenting with jruby-complete-SNAPSHOT-9.0.0.0 and ruby processing

Anyone wishing to explore ruby-2.1+ syntax with ruby-processing should get hold of jruby-complete-SNAPSHOT-9.0.0.0.jar (compile it yourself) rename the jar to jruby-complete.jar and manually replace the jruby-complete.jar in the ruby-processing gem (lives in lib/ruby folder). Gems typically live in .gem/ruby/2.1.0/gems folder depending on your system unless you use rvm or other such nasty stuff. To ensure jruby-complete use add 'JRUBY: false' to your ~/.rp5rc settings (or use the --nojuby flag). Also use watch rather than run to run sketches (to avoid the failure in draw loop bug). Here is a sketch I posted earlier using 2.1 syntax, but here I explore using 'format' to present rationals....
After experimentation you can easily restore the original jruby-complete by running 'rp5 setup install' (no download required since we keep copy of the original in the vendors folder).
def setup
  size 640, 250
  background 10
  f = createFont("Arial", 24, true)
  third = 1 / 3r     # since ruby 2.1.0
  quarter = 1 / 4r
  add = format("%s + %s = %s", third, quarter, third + quarter)
  subtract = format("%s - %s = %s", third, quarter, third - quarter)
  multiply = format("%s * %s = %s", third, quarter, third * quarter)
  text_font(f, 24)
  fill(220)
  text("Math blackboard ruby-processing", 80, 50)
  text(add, 110, 100)
  text(subtract, 110, 150)
  text(multiply, 110, 200)
end


PS: this one of the sketches not affected by draw loop bug. May'be we don't need to wait too long for a preview version?

Monday, 24 November 2014

Radical Proxy experiment for JRubyArt

Here I create a custom (abstract) Proxy class as a java extension for JRubyArt, which when appropriately initialized, gives implementing classes access to processing pre(), draw() and post() loops. With example usage (by a custom Array class), note the empty draw loop.... It probably doesn't matter whether the particle gets updated in pre(), draw() or post() loops. See original vanilla processing code in action here as js version. Note how each module/particle has it's own copy of the update and draw code in those versions. Whereas in JRubyArt a single object, the custom array has this code, and the particle is just a simple Struct.
package processing.core;

/**
 *
 * @author Martin Prout
 */
public abstract class Proxy {

    private final PApplet app;

    /**
     * Useful accessors
     */
    public int width, height;

    /**
     *
     * @param app Applet
     */
    public Proxy(PApplet app) {
        this.app = app;
        this.width = app.width;
        this.height = app.height;
        setActive(true);
    }

    /**
     * Extending classes must implement
     */
    public abstract void pre();

    /**
     * Extending classes must implement
     */
    public abstract void draw();

    /**
     * Extending classes must implement
     */
    public abstract void post();

    private void setActive(boolean active) {
        if (active) {
            this.app.registerMethod("pre", this);
            this.app.registerMethod("draw", this);
            this.app.registerMethod("post", this);
            this.app.registerMethod("dispose", this);
        } else {
            this.app.unregisterMethod("pre", this);
            this.app.unregisterMethod("draw", this);
            this.app.unregisterMethod("post", this);
        }
    }

    /**
     * Simple signature
     * @param col
     */
    public void background(int col) {
        this.app.background(col);
    }

    /**
     * Simple signature
     * @param col
     */
    public void fill(int col) {
        this.app.fill(col);
    }

    /**
     * Simple signature
     * @param col
     */
    public void stroke(int col) {
        this.app.stroke(col);
    }

    /**
     * Access applet if we must
     * @return
     */
    public PApplet app() {
        return this.app;
    }

    /**
     * required for processing
     */
    public void dispose() {
        setActive(false);
    }
}

# Demonstrates possible syntax for creating a custom array of objects.

UNIT = 40
attr_reader :custom_array

def setup
  size 640, 360
  wide_count = width / UNIT
  height_count = height / UNIT
  @custom_array = CustomArray.new(self)
  height_count.times do |i|
    wide_count.times do |j|
      custom_array.add_object(j * UNIT, i * UNIT, UNIT / 2, UNIT / 2, rand(0.05..0.8))
    end
  end
  no_stroke
end

def draw
end

# The Particle object

Particle = Struct.new(:x, :y, :mx, :my, :size, :speed, :xdir, :ydir)

require 'forwardable'

# The custom Array (that can access pre, post and draw loops by reflection)
# Also Proxy has access to background(int), fill(int) and stroke(int)
class CustomArray < Java::ProcessingCore::Proxy
  extend Forwardable
  def_delegators(:@objs, :each, :<<)
  include Enumerable

  attr_reader :app

  def initialize(app)
    @app = app
    @objs = []
  end

  def add_object(mx, my, x, y, speed)
    self << Particle.new(x.to_i, y.to_i, mx, my, UNIT, speed, 1, 1)
  end

  def post
    each do |obj|
      update_x obj
      next unless obj.y >= UNIT || obj.x <= 0
      obj.ydir *= -1
      obj.y += obj.ydir
    end
  end

  def update_x(obj)
    obj.x += obj.speed * obj.xdir
    return if (0..UNIT).cover? obj.x
    obj.xdir *= -1
    obj.x += obj.xdir
    obj.y += obj.ydir
  end

  def pre
  end

  def draw
    background(0)
    fill(255)
    each do |obj|
      app.ellipse(obj.mx + obj.x, obj.my + obj.y, 6, 6)
    end
  end
end

JRubyArt is a development branch of ruby-processing, that is currently going its own way (ie somewhat different implementation)

Thursday, 13 November 2014

Custom Contact Listener for pbox2d and ruby-processing

require 'pbox2d'
require_relative 'lib/custom_listener'
require_relative 'lib/particle'
require_relative 'lib/boundary'

attr_reader :box2d, :particles, :wall

def setup
  size 400, 400
  @box2d = Box2D.new(self)
  box2d.create_world
  box2d.add_listener(CustomListener.new)
  @particles = []
  @wall = Boundary.new(box2d, width / 2, height - 5, width, 10)
end

def draw
  background(255)

  if (rand < 0.1)
    particles << Particle.new(box2d, rand(width), 20, rand(4..8))
  end
  particles.each{ |p| p.display(self) }
  particles.reject!(&:done)
  wall.display(self)
end

Custom Listener
class CustomListener
  include ContactListener

  def begin_contact(cp)
    # Get both fixtures
    f1 = cp.getFixtureA
    f2 = cp.getFixtureB
    # Get both bodies
    b1 = f1.getBody
    b2 = f2.getBody
    # Get our objects that reference these bodies
    o1 = b1.getUserData
    o2 = b2.getUserData
    return unless (o1.respond_to?(:change) && o2.respond_to?(:change))
    o1.change
    o2.change
  end

  def end_contact(cp)
  end

  def pre_solve(cp, m)
  end

  def post_solve(cp, ci)
  end

end

Boundary class
CENTER ||= Java::ProcessingCore::PConstants::CENTER

class Boundary
  include PB
  attr_reader :box2d, :x, :y, :w, :h, :b
  def initialize(b2d, x, y, w, h)
    @box2d, @x, @y, @w, @h = b2d, x, y, w, h
    sd = PolygonShape.new
    box2dW = box2d.scale_to_world(w / 2)
    box2dH = box2d.scale_to_world(h / 2)
    # We're just a box
    sd.setAsBox(box2dW, box2dH);


    # Create the body
    bd = BodyDef.new
    bd.type = BodyType::STATIC;
    bd.position.set(box2d.processing_to_world(x,y))
    @b = box2d.create_body(bd)

    # Attached the shape to the body using a Fixture
    b.create_fixture(sd, 1)
    b.setUserData(self)
  end

  # Draw the boundary, if it were at an angle we'd have to do something fancier
  def display(app)
    app.fill(0)
    app.stroke(0)
    app.rectMode(CENTER)
    app.rect(x,y,w,h)
  end
end
Particle class
class Particle
  include PB
  attr_accessor :body
  attr_reader :box2d, :radius, :col

  def initialize(b2d, x, y, r)
    @box2d, @x, @y, @radius = b2d, x, y, r
    # This function puts the particle in the Box2d world
    make_body(x, y, radius)
    @col = -5263441
    body.setUserData(self)
  end

  # This function removes the particle from the box2d world
  def kill_body
    box2d.destroy_body(body)
  end

  # Change color when hit
  def change
    @col = -65536 # red
  end

  # Is the particle ready for deletion?
  def done
    # Let's find the screen position of the particle
    pos = box2d.body_coord(body)
    # Is it off the bottom of the screen?
    return false unless (pos.y > box2d.height + radius * 2)
    kill_body
    true
  end

  def display(app)
    # We look at each body and get its screen position
    pos = box2d.body_coord(body)
    # Get its angle of rotation
    a = body.get_angle
    app.push_matrix
    app.translate(pos.x, pos.y)
    app.rotate(a)
    app.fill(col)
    app.stroke(0)
    app.stroke_weight(1)
    app.ellipse(0, 0, radius * 2, radius * 2)
    # Let's add a line so we can see the rotation
    app.line(0, 0, radius, 0)
    app.pop_matrix
  end

  # Here's our function that adds the particle to the Box2D world
  def make_body(x, y, r)
    # Define a body
    bd = BodyDef.new
    # Set its position
    bd.position = box2d.processing_to_world(x, y)
    bd.type = BodyType::DYNAMIC
    @body = box2d.create_body(bd)
    # Make the body's shape a circle
    cs = CircleShape.new
    cs.m_radius = box2d.scale_to_world(r)
    fd = FixtureDef.new
    fd.shape = cs
    # Parameters that affect physics
    fd.density = 1
    fd.friction = 0.01
    fd.restitution = 0.3
    # Attach fixture to body
    body.create_fixture(fd)
    body.set_angular_velocity(rand(-10.0..10))
  end
end

Sunday, 2 November 2014

Using the pbox2d gem in JRubyArt

Things are little bit more complicated for JRubyArt (compared with ruby-processing) as I am currently refusing to use the Processing::Proxy sugar (that allows you in part to mimic the inner classes of vanilla processing) in JRubyArt as it is kind of bad practice to expose variables and methods willy nilly.
require 'jruby_art'
require 'pbox2d'
require_relative 'lib/custom_shape'

class Polygons < Processing::App
  # Basic example of falling rectangles
  attr_reader :box2d, :boundaries, :polygons

  def setup
    size(640, 360)
    # Initialize box2d physics and create the world
    @box2d = Box2D.new(self)
    box2d.create_world
    # We are setting a custom gravity
    box2d.gravity(0, -20)
    # Create Arrays
    @polygons = []
    @boundaries = []
    # Add a bunch of fixed boundaries
    boundaries << Boundary.new(box2d, width / 4, height - 5, width / 2 - 50, 10, 0)
    boundaries << Boundary.new(box2d, 3 * width / 4, height - 50, width / 2 - 50, 10, 0)
    boundaries << Boundary.new(box2d, width - 5, height / 2, 10, height, 0)
    boundaries << Boundary.new(box2d, 5, height / 2, 10, height, 0)
  end

  def draw
    background(255)
    # Display all the boundaries
    boundaries.each { |wall| wall.display(self) }
    # Display all the polygons
    polygons.each { |poly| poly.display(self) }
    # polygons that leave the screen, we delete them
    # (note they have to be deleted from both the box2d world and our list
    polygons.reject!(&:done)
  end

  def mouse_pressed
    polygons << CustomShape.new(box2d, mouse_x, mouse_y, height)
  end
end

Polygons.new(title: 'Polygons')

CLOSE = Java::ProcessingCore::PConstants::CLOSE   # these are just ints
CENTER = Java::ProcessingCore::PConstants::CENTER

class CustomShape
  include PB
  # We need to keep track of a Body and a width and height
  attr_reader :body, :box2d, :height

  # Constructor
  def initialize(b2d, x, y, height)
    # Add the box to the box2d world
    @box2d, @height = b2d, height
    make_body(PB::Vec2.new(x, y))
  end

  # This function removes the particle from the box2d world
  def kill_body!
    box2d.destroy_body(body)
  end

  # Is the particle ready for deletion?
  def done
    # Let's find the screen position of the particle
    pos = box2d.body_coord(body)
    # Is it off the bottom of the screen?
    return false unless pos.y > height
    kill_body!
    true
  end

  # Drawing the box
  def display(app)
    # We look at each body and get its screen position
    pos = box2d.body_coord(body)
    # Get its angle of rotation
    a = body.get_angle
    f = body.get_fixture_list
    ps = f.get_shape
    app.rect_mode(CENTER)
    app.push_matrix
    app.translate(pos.x, pos.y)
    app.rotate(-a)
    app.fill(175)
    app.stroke(0)
    app.begin_shape
    # For every vertex, convert to pixel vector
    ps.get_vertex_count.times do |i|
      v = box2d.vector_to_processing(ps.get_vertex(i))
      app.vertex(v.x, v.y)
    end
    app.end_shape(CLOSE)
    app.pop_matrix
  end

  # This function adds the rectangle to the box2d world
  def make_body(center)
    # Define a polygon (this is what we use for a rectangle)
    sd = PB::PolygonShape.new
    vertices = []
    vertices << box2d.vector_to_world(PB::Vec2.new(-15, 25))
    vertices << box2d.vector_to_world(PB::Vec2.new(15, 0))
    vertices << box2d.vector_to_world(PB::Vec2.new(20, -15))
    vertices << box2d.vector_to_world(PB::Vec2.new(-10, -10))
    sd.set(vertices.to_java(Java::OrgJbox2dCommon::Vec2), vertices.length)
    # Define the body and make it from the shape
    bd = PB::BodyDef.new
    bd.type = PB::BodyType::DYNAMIC
    bd.position.set(box2d.processing_to_world(center))
    @body = box2d.create_body(bd)
    body.create_fixture(sd, 1.0)
    # Give it some initial random velocity
    body.set_linear_velocity(Vec2.new(rand(-5.0..5), rand(2.0..5)))
    body.set_angular_velocity(rand(-5.0..5))
  end
end

class Boundary
  include PB
  attr_reader :box2d, :b, :x, :y, :w, :h
  def initialize(b2d, x, y, w, h, a)
    @box2d, @x, @y, @w, @h = b2d, x, y, w, h
    # Define the polygon
    sd = PB::PolygonShape.new
    # Figure out the box2d coordinates
    box2d_w = box2d.scale_to_world(w / 2)
    box2d_h = box2d.scale_to_world(h / 2)
    # We're just a box
    sd.set_as_box(box2d_w, box2d_h)
    # Create the body
    bd = PB::BodyDef.new
    bd.type = PB::BodyType::STATIC
    bd.angle = a
    bd.position.set(box2d.processing_to_world(x, y))
    @b = box2d.create_body(bd)
    # Attached the shape to the body using a Fixture
    b.create_fixture(sd, 1)
  end

  # Draw the boundary, it doesn't move so we don't have to ask the Body for location
  def display(app)
    app.fill(0)
    app.stroke(0)
    app.stroke_weight(1)
    app.rect_mode(CENTER)
    a = b.get_angle
    app.push_matrix
    app.translate(x, y)
    app.rotate(-a)
    app.rect(0, 0, w, h)
    app.pop_matrix
  end
end

Saturday, 1 November 2014

Creating a gem wrapper for jbox2d in ruby-processing

The mechanism for including Daniel Shiffmans PBox2D for processing into ruby-processing is unnecessarily complicated, and I had always thought there should be an easier way. So here it is jbox2d wrapped as a gem using some jruby sugar (and avoiding the nasty reflection nonsense that Dan used, although under the hood jruby may do something similar).
The ruby-processing sketch
require 'pbox2d'
require_relative 'lib/particle_system'
attr_reader :box2d, :boundaries, :systems

def setup
  size(400,300)
  @box2d = Box2D.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.gravity(0, -20)

  # Create Arrays
  @systems = []
  @boundaries = []
  # Add a bunch of fixed boundaries
  boundaries << Boundary.new(box2d, 50, 100, 300, 5, -0.3)
  boundaries << Boundary.new(box2d, 250, 175, 300, 5, 0.5)
end

def draw
  background(255)
  # Run all the particle systems
  if systems.size > 0
    systems.each do |system|
      system.run
      system.add_particles(box2d, rand(0..2))
    end
  end
  # Display all the boundaries
  boundaries.each(&:display)
end

def mouse_pressed
  # Add a new Particle System whenever the mouse is clicked
  systems << ParticleSystem.new(box2d, 0, mouse_x, mouse_y)
end

The associated (local) library
require 'forwardable'

module Runnable
  def run
    reject! { |item| item.done }
    each { |item| item.display }
  end
end

class ParticleSystem
  include Enumerable, Runnable
  extend Forwardable
  def_delegators(:@particles, :each, :reject!, :<<, :empty?)
  def_delegator(:@particles, :empty?, :dead?)

  attr_reader :x, :y

  def initialize(bd, num, x, y)
    @particles = []          # Initialize the Array
    @x, @y = x, y            # Store the origin point  
    num.times do
      self << Particle.new(bd, x, y)
    end
  end

  def add_particles(bd, n)
    n.times do
      self << Particle.new(bd, x, y)
    end
  end
end

# A Particle
require 'pbox2d'

class Particle
  include Processing::Proxy, PB
  TRAIL_SIZE = 6
  # We need to keep track of a Body

  attr_reader :trail, :body, :box2d

  # Constructor
  def initialize(b2d, x, y)
    @box2d = b2d
    @trail = Array.new(TRAIL_SIZE, [x, y])
    # Add the box to the box2d world
    # Here's a little trick, let's make a tiny tiny radius
    # This way we have collisions, but they don't overwhelm the system
    make_body(PB::Vec2.new(x, y), 0.2)
  end

  # This function removes the particle from the box2d world
  def kill_body
    box2d.destroy_body(body)
  end

  # Is the particle ready for deletion?
  def done
    # Let's find the screen position of the particle
    pos = box2d.body_coord(body)
    # Is it off the bottom of the screen?
    return false unless (pos.y > $app.height + 20)
    kill_body
    true
  end

  # Drawing the box
  def display
    # We look at each body and get its screen position
    pos = box2d.body_coord(body)
    # Keep track of a history of screen positions in an array
    (TRAIL_SIZE - 1).times do |i|
      trail[i] = trail[i + 1]
    end
    trail[TRAIL_SIZE - 1] = [pos.x, pos.y]
    # Draw particle as a trail
    begin_shape
    no_fill
    stroke_weight(2)
    stroke(0, 150)
    trail.each do |v|
      vertex(v[0], v[1])
    end
    end_shape
  end

  # This function adds the rectangle to the box2d world
  def make_body(center, r)
    # Define and create the body
    bd = PB::BodyDef.new
    bd.type = PB::BodyType::DYNAMIC
    bd.position.set(box2d.processing_to_world(center))
    @body = box2d.create_body(bd)
    # Give it some initial random velocity
    body.set_linear_velocity(PB::Vec2.new(rand(-1.0..1), rand(-1.0..1)))
    # Make the body's shape a circle
    cs = PB::CircleShape.new
    cs.m_radius = box2d.scale_to_world(r)
    fd = PB::FixtureDef.new
    fd.shape = cs
    fd.density = 1
    fd.friction = 0  # Slippery when wet!
    fd.restitution = 0.5
    # We could use this if we want to turn collisions off
    # cd.filter.groupIndex = -10
    # Attach fixture to body
    body.create_fixture(fd)
  end
end

class Boundary
  include Processing::Proxy, PB
  attr_reader :box2d, :b, :x, :y, :w, :h

  def initialize(b2d, x, y, w, h, a)
    @box2d, @x, @y, @w, @h = b2d, x, y, w, h
    # Define the polygon
    sd = PB::PolygonShape.new
    # Figure out the box2d coordinates
    box2d_w = box2d.scale_to_world(w / 2)
    box2d_h = box2d.scale_to_world(h / 2)
    # We're just a box
    sd.set_as_box(box2d_w, box2d_h)
    # Create the body
    bd = PB::BodyDef.new
    bd.type = PB::BodyType::STATIC
    bd.angle = a
    bd.position.set(box2d.processing_to_world(x, y))
    @b = box2d.create_body(bd)
    # Attached the shape to the body using a Fixture
    b.create_fixture(sd, 1)
  end

  # Draw the boundary, it doesn't move so we don't have to ask the Body for location
  def display
    fill(0)
    stroke(0)
    stroke_weight(1)
    rect_mode(CENTER)
    a = b.get_angle
    push_matrix
    translate(x, y)
    rotate(-a)
    rect(0, 0, w, h)
    pop_matrix
  end
end

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 Andrés 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 Andrés Colubri processing opengl work, tutorial here:-

Followers

Blog Archive

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