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

Friday 31 May 2013

A little offscreen buffer test with ruby-processing-2.0

A little sketch from the processing discussion board, makes me think that I should revive lazydogs mirror sketch for ruby-processing-2.0, now that would be cool.
attr_reader :pg
def setup
  size(200, 200, P2D)
  @pg = create_graphics(100, 100, P3D)
  smooth(8)
end
def draw
  background 50
  pg.begin_draw
  pg.background 150
  pg.stroke 255
  pg.stroke_weight 3
  pg.translate(50, 50, 0)
  pg.rotate_y 0.45
  pg.no_fill
  pg.box(40)
  pg.end_draw
  image(pg, width / 2 - 50, height / 2 - 50)
end

Wednesday 22 May 2013

Precast to a java object or use an alias method in ruby-processing (for overloaded java methods)


# alias_background.rb 
# 

class Java::ProcessingCore::PApplet
  java_alias :int_background, :background, [Java::int]
  java_alias :image_background, :background, [Java::ProcessingCore::PImage]
end

attr_reader :img, :show, :col_int, :back_image

def setup
  size(640, 360)
  @show = false
  @img = loadImage("test.png") # NB: test.png size should equal frame size
  #@back_image = img.to_java(Java::ProcessingCore::PImage)
end

def draw
  int_background(0)
  # background(0) # warns about ambigious method
  # background(img) if show
  image_background(img) if show # does not warn about ambiguous method
  # int_background(0) if show # uses a precast java object
end

def mouse_pressed
  @show = !show
end

Monday 20 May 2013

Updating to Use JRuby-1.7.4

I've recently updated ruby-processing to use jruby-1.7.4, and for Mac and Windows users, it has been updated to use processing-2.0 (untested, you might be the guinea-pig). Seems to work OK linux. I am not all interested in updating ruby-processing processing-1.5.1 to use jruby-1.7.4 so unless someone volunteers the next release of ruby-processing will be a huge leap (if you've got a crappy graphics card the experience may not be so wonderful!!!!). Processing 2.0 uses opengl2 for P2D and P3D rendering so if you've got a reasonable graphics card there are decent improvements to be had including easy access to glsl shaders and FBO (buffered on graphics card). Read this and get inspired http://codeanticode.wordpress.com/2013/06/04/processing-2-0-is-out-processing-2-0-is-in/

Tuesday 7 May 2013

Another PBox2D sketch, features a chain, and usual physics


# The Nature of Code
# <http://www.shiffman.net/teaching/nature>
# Spring 2010
# PBox2D example

# An uneven surface

load_library :pbox2d
load_library :surface

include SB

attr_reader :surface, :box2d, :particles

def setup
  size(500,300)
  smooth

  # Initialize box2d physics and create the world
  @box2d = PBox2D.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.set_gravity(0, -20)

  # Create the empty list
  @particles = []
  # Create the surface
  @surface = Surface.new(box2d)
end

def draw
  # If the mouse is pressed, we make new particles


  # We must always step through time!
  box2d.step

  background(138, 66, 54)
  # Draw the surface
  surface.display
  # NB question mark is reqd to call mouse_pressed value, else method gets called.
  particles << Particle.new(box2d, mouse_x, mouse_y, rand(2.0 .. 6)) if mouse_pressed?
  # Draw all particles
  particles.each do |p|
    p.display
  end
  # Particles that leave the screen, we delete them
  # (note they have to be deleted from both the box2d world and our list
  particles.each_with_index do |p, i|
    if (p.done)
      particles.delete_at(i)
    end
  end
  # Just drawing the framerate to see how many particles it can handle
  fill(0)
  text("framerate: #{frame_rate.to_i}", 12, 16)
end

The library module, which encapsulates the import of classes, and additional classes for surface and particles.
# The Nature of Code
# <http://www.shiffman.net/teaching/nature>
# Spring 2010
# PBox2D example

# An uneven surface boundary
module SB

  include_package 'org.jbox2d.collision.shapes'
  include_package 'org.jbox2d.common'
  include_package 'org.jbox2d.dynamics'
  java_import 'pbox2d.PBox2D'



  class Surface
    # We'll keep track of all of the surface points
    attr_reader :surface, :body, :box2d, :y, :width, :height


    def initialize b2d
      @box2d = b2d
      @surface = []
      @width, @height = $app.width, $app.height
      # This is what box2d uses to put the surface in its world
      chain = SB::ChainShape.new

      # Perlin noise argument
      xoff = 0.0

      # This has to go backwards so that the objects  bounce off the top of the surface
      # This "edgechain" will only work in one direction!
      (width + 10).step(-10, -5) do |x|
        # Doing some stuff with perlin noise to calculate a surface that points down on one side
        # and up on the other

        if (x > width/2)
          @y = 100 + (width - x)*1.1 + map(noise(xoff),0,1,-80,80)
        else
          @y = 100 + x*1.1 + map(noise(xoff),0,1,-80,80)
        end

        # Store the vertex in screen coordinates
        surface << SB::Vec2.new(x, y)

        # Move through perlin noise
        xoff += 0.1

      end

      # Build an array of vertices in Box2D coordinates
      # from the ArrayList we made
      vertices = []
      surface.each do |surf|
        vertices << box2d.coord_pixels_to_world(surf)
      end
     # Create the chain!
      chain.createChain(vertices, vertices.length)
      # The edge chain is now attached to a body via a fixture
      bd = SB::BodyDef.new
      bd.position.set(0.0, 0.0)
      @body = box2d.createBody(bd)
      # Shortcut, we could define a fixture if we
      # want to specify frictions, restitution, etc.
      body.createFixture(chain, 1)
    end

    # A simple function to just draw the edge chain as a series of vertex points
    def display
      stroke_weight(2)
      stroke(0)
      fill(135, 206, 250)
      beginShape
        vertex(width, 0)      # extra vertices so we can fill sky
        surface.each do |v|
          vertex(v.x, v.y)    # the mountain range
        end
        vertex(0, 0)          # extra vertices so we can fill sky
      endShape
    end
  end

  class Particle
    # We need to keep track of a Body

    attr_reader :body, :box2d, :x, :y, :r

    # Constructor
    def initialize(b2d, x, y, r)
      @box2d, @x, @y, @r = b2d, x, y, r
      # This function puts the particle in the Box2d world
      make_body(x, y, r)
    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
      pos = box2d.get_body_pixel_coord(body)
      # Is it off the bottom of the screen?
      if (pos.y > $app.height + r * 2)
        kill_body
        return true
      end
        return false
    end

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

    # This function adds the rectangle to the box2d world
    def make_body(x, y, r)
      # Define and create the body
      bd = SB::BodyDef.new
      bd.position = box2d.coord_pixels_to_world(x,y)
      bd.type = SB::BodyType::DYNAMIC
      @body = box2d.world.create_body(bd)
      # Make the body's shape a circle
      cs = SB::CircleShape.new
      cs.m_radius = box2d.scalar_pixels_to_world(r)
      fd = SB::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)
      # Give it a random initial velocity (and angular velocity)
      body.set_linear_velocity(SB::Vec2.new(rand(-10 .. 10), rand(5 .. 10)))
      body.set_angular_velocity(rand(-10 .. 10))
    end
  end
end

Liquid like particles in ruby-processing (Shiffmans liquidy example)

Here is another pbox2d sketch in ruby-processing, where I've used a module to wrap java imports and extra classes:-

# The Nature of Code
# <http://www.shiffman.net/teaching/nature>
# Spring 2011
# PBox2D example

# Box2D particle system example

load_library :pbox2d
load_library :particle_system

# module PS is a wrapper for java imports, and Boundary and Particle classes
include PS

attr_reader :box2d, :boundaries, :systems

def setup
  size(400,300)
  smooth
  # Initialize box2d physics and create the world
  @box2d = PBox2D.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.set_gravity(0, -20)
  # Create ArrayLists 
  @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)
  # We must always step through time!
  box2d.step
  # 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 do |wall|
    wall.display
  end
end


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

Here is the module/library
module PS
  include_package 'org.jbox2d.collision.shapes'
  include_package 'org.jbox2d.common'
  include_package 'org.jbox2d.dynamics'
  java_import 'pbox2d.PBox2D'


  # Box2D Particle System
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2010

  # A class to describe a group of Particles
  # An ArrayList is used to manage the list of Particles 

  class ParticleSystem

    attr_reader :particles, :x, :y

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

    def run
      # Display all the particles
      particles.each do |p|
        p.display
      end
      # Particles that leave the screen, we delete them
      # (note they have to be deleted from both the box2d world and our list

      particles.each_with_index do |p, i|
        if (p.done)
          particles.delete_at(i)
        end
      end
    end

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

    # A method to test if the particle system still has particles
    def dead
      particles.empty?
    end

  end

  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2012
  # PBox2D example

  # A Particle

  class Particle
    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(PS::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.get_body_pixel_coord(body)
      # Is it off the bottom of the screen?
      if (pos.y > $app.height + 20)
        kill_body
        return true
      end
      return false
    end

    # Drawing the box
    def display
      # We look at each body and get its screen position
      pos = box2d.get_body_pixel_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
        noFill
        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 = PS::BodyDef.new
      bd.type = PS::BodyType::DYNAMIC

      bd.position.set(box2d.coord_pixels_to_world(center))
      @body = box2d.create_body(bd)

      # Give it some initial random velocity
      body.set_linear_velocity(PS::Vec2.new(rand(-1 .. 1), rand(-1 .. 1)))

      # Make the body's shape a circle
      cs = PS::CircleShape.new
      cs.m_radius = box2d.scalar_pixels_to_world(r)

      fd = PS::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
  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2012
  # PBox2D example

  # A fixed boundary class (now incorporates angle)



  class Boundary

    attr_reader :box2d, :b, :x, :y, :w, :h #, :a

    def initialize(b2d, x, y, w, h, a)
      @box2d = b2d
      @x = x
      @y = y
      @w = w
      @h = h

      # Define the polygon
      sd = PS::PolygonShape.new

      # Figure out the box2d coordinates
      box2dW = box2d.scalar_pixels_to_world(w/2)
      box2dH = box2d.scalar_pixels_to_world(h/2)
      # We're just a box
      sd.set_as_box(box2dW, box2dH)


      # Create the body
      bd = PS::BodyDef.new
      bd.type = PS::BodyType::STATIC
      bd.angle = a
      bd.position.set(box2d.coord_pixels_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

end

Sunday 5 May 2013

Using Shiffmans PBox2D library in ruby processing

Recently I've published some sketches using the fisica library (using this library in ruby-processing is slightly problematic owing to a somewhat overly complicated polymorphic structure, and a protected abstract class that is supposed to provide "public" methods), here is a look at the Shiffman library that does essentially the same thing (as fisica) by providing a wrapper for the JBox2D java physics library. However it might be just as easy for rubyists to work directly with JBox2D, a note of caution you can't have both the fisica and the pbox2d libraries installed at he same time. Anyway it did at least provide an interesting exercise converting the following example to ruby-processing. Features to note are the required syntax to call the java constants STATIC and DYNAMIC, ie precede constant with '::' and not a '.', and the need to explicitly "to_java" required in the set "Array of Vec2" argument CustomShape class. Also it appears since we are including the library inside the Processing sketch/module we do no seem to require to include Processing::Proxy in the classes.
# The Nature of Code
# <http://www.shiffman.net/teaching/nature>
# Spring 2011
# PBox2D example

# Basic example of falling rectangles
load_library :pbox2d
load_library :custom_shape

# module B2D is a wrapper for java imports, and Boundary and CustomShape classes
include B2D

attr_reader :box2d, :boundaries, :polygons

def setup
  size(640,360)
  smooth
  # Initialize box2d physics and create the world
  @box2d = PBox2D.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.set_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)
  # We must always step through time!
  box2d.step
  # Display all the boundaries
  boundaries.each do |wall|
    wall.display
  end

  # Display all the polygons
  polygons.each do |cs|
    cs.display
  end

  # polygons that leave the screen, we delete them
  # (note they have to be deleted from both the box2d world and our list
  polygons.each_with_index do |polygon, i|
    if polygon.done
      polygons.delete_at(i)
    end
  end
end

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

Here are the supporting classes, wrapped as a ruby module in a "library" custom_shape.rb, note the B2D prefix is needed in the module wrapped classes to access the "include_package" java packages, this is a jruby 'feature' they have not been able to get round as yet.
module B2D
  include_package 'pbox2d'
  include_package 'org.jbox2d.collision.shapes'
  include_package 'org.jbox2d.common'
  include_package 'org.jbox2d.dynamics'
  java_import 'pbox2d.PBox2D'


  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2011
  # PBox2D example
  # A rectangular box

  class CustomShape

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

    # Constructor
    def initialize(b2d, x, y)
      # Add the box to the box2d world
      @box2d = b2d
      make_body(B2D::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.get_body_pixel_coord(body)
      # Is it off the bottom of the screen?
      if (pos.y > $app.height)
        kill_body!
        return true
      end
      return false
    end

    # Drawing the box
    def display
      # We look at each body and get its screen position
      pos = box2d.get_body_pixel_coord(body)
      # Get its angle of rotation
      a = body.get_angle

      f = body.get_fixture_list
      ps = f.get_shape


      rect_mode(CENTER)
      push_matrix
      translate(pos.x, pos.y)
      rotate(-a)
      fill(175)
      stroke(0)
      begin_shape
        # For every vertex, convert to pixel vector
        ps.get_vertex_count.times do |i|
          v = box2d.vector_world_to_pixels(ps.get_vertex(i))
          vertex(v.x, v.y)
        end
      end_shape(CLOSE)
      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 = B2D::PolygonShape.new

      vertices = []
      vertices << box2d.vector_pixels_to_world(B2D::Vec2.new(-15, 25))
      vertices << box2d.vector_pixels_to_world(B2D::Vec2.new(15, 0))
      vertices << box2d.vector_pixels_to_world(B2D::Vec2.new(20, -15))
      vertices << box2d.vector_pixels_to_world(B2D::Vec2.new(-10, -10))
      sd.set(vertices.to_java(Java::OrgJbox2dCommon::Vec2), vertices.length)

      # Define the body and make it from the shape
      bd = B2D::BodyDef.new
      bd.type = B2D::BodyType::DYNAMIC
      bd.position.set(box2d.coord_pixels_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 .. 5), rand(2 .. 5)))
      body.set_angular_velocity(rand(-5 .. 5))
    end
  end

  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2012
  # PBox2D example

  # A fixed boundary class (now incorporates angle)



  class Boundary

    attr_reader :box2d, :b, :x, :y, :w, :h #, :a

    def initialize(b2d, x, y, w, h, a)
      @box2d = b2d
      @x = x
      @y = y
      @w = w
      @h = h

      # Define the polygon
      sd = B2D::PolygonShape.new

      # Figure out the box2d coordinates
      box2dW = box2d.scalar_pixels_to_world(w/2)
      box2dH = box2d.scalar_pixels_to_world(h/2)
      # We're just a box
      sd.set_as_box(box2dW, box2dH)


      # Create the body
      bd = B2D::BodyDef.new
      bd.type = B2D::BodyType::STATIC
      bd.angle = a
      bd.position.set(box2d.coord_pixels_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
end

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