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

Showing posts with label jbox2d. Show all posts
Showing posts with label jbox2d. Show all posts

Monday, 6 April 2015

Using Forwardable in place of Processing::Proxy to mimic java inner class

In the following example we are using the pbox2d gem (in place of Dan Shiffmans Box2D-for-Processing to provide jbox2d. Here is the translated sketch:-
# The Nature of Code
# PBox2D example translated to use pbox2d gem by Martin Prout, and to use
# forwardable instead of Processing::Proxy to mimic java inner class access
# An uneven surface

require 'pbox2d'
load_library :surface

attr_reader :surface, :box2d, :particles

def setup
  size(500, 300)
  smooth 4
  # Initialize box2d physics and create the world
  @box2d = Box2D.new(self)
  box2d.init_options(gravity: [0, -20])
  box2d.create_world
  # to later set a custom gravity
  # box2d.gravity([0, -20])
  # Create the empty list
  @particles = []
  # Create the surface
  @surface = Surface.new(self)
end

def draw
  # If the mouse is pressed, we make new particles
  # We must always step through time!
  background(138, 66, 54)
  # Draw the surface
  surface.display
  # NB ? reqd to call mouse_pressed value, else method gets called.
  particles << Particle.new(self, mouse_x, mouse_y, rand(2.0..6)) if mouse_pressed?
  # Draw all particles
  particles.each(&:display)
  # Particles that leave the screen, we delete them
  # (note they have to be deleted from both the box2d world and our list
  particles.reject!(&:done)
  # Just drawing the framerate to see how many particles it can handle
  fill(0)
  text(format('framerate: %d', frame_rate.to_i), 12, 16)
end

Here is the associated library that uses Forwardable in place of Processing::Proxy. Place surface.rb in "library/surface" folder, or modify the sketch to use require_relative:-
require 'forwardable'

# The Nature of Code
# PBox2D example
# An uneven surface boundary
class Surface
  extend Forwardable
  # We'll keep track of all of the surface points
  def_delegators(:@app, :box2d, :width, :height, :begin_shape,
                 :end_shape, :fill, :stroke, :stroke_weight,
                 :vertex, :map1d, :noise)
  attr_reader :surface, :body, :y

  def initialize(app)
    @app = app
    @surface = []
    # This is what box2d uses to put the surface in its world
    chain = 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 + map1d(noise(xoff), (0..1.0), (-80..80))
      else
        @y = 100 + x * 1.1 + map1d(noise(xoff), (0..1.0), (-80..80))
      end
      # Store the vertex in screen coordinates
      surface << 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.processing_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 = 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)
    begin_shape
    vertex(width, 0)          # extra vertices so we can fill sky
    surface.map { |v| vertex(v.x, v.y) }     # the mountain range
    vertex(0, 0)              # extra vertices so we can fill sky
    end_shape
  end
end

# Using forwardable again
class Particle
  extend Forwardable
  def_delegators(:@app, :box2d, :push_matrix, :pop_matrix, :rotate,
                 :translate, :fill, :stroke, :stroke_weight, :noise,
                 :map1d, :ellipse, :line)
  # We need to keep track of a Body

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

  # Constructor
  def initialize(app, x, y, r)
    @app, @x, @y, @r = app, 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.body_coord(body)
    # Is it off the bottom of the screen?
    return false unless pos.y > box2d.height + r * 2
    kill_body
    true
  end

  def display
    # We look at each body and get its screen position
    pos = box2d.body_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 particle to the box2d world
  def make_body(x, y, r)
    # Define and create the body
    bd = BodyDef.new
    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)
    # Give it a random initial velocity (and angular velocity)
    body.set_linear_velocity(Vec2.new(rand(-10..10), rand(5..10)))
    body.set_angular_velocity(rand(-10..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

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
New updated version since (26 July 2014), attempts to set processing root automatically if there is no ~/.rp5rc. JRUBY config option allows users to default to using jruby-complete without the --nojruby flag.

Sunday, 2 March 2014

Updating to use Dan Shiffmans new Box2D library

Caught this on twitter

So here is the liquidy.rb example updated:-
# The Nature of Code
# <http:#www.shiffman.net/teaching/nature>
# Spring 2011
# Updated to use the updated library
# translated to ruby-processing 2 March 2014 by Martin Prout
# Box2DProcessing example

load_libraries :box2d_processing, :particle_system


# module PS is a wrapper for java packages, 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 = PS::Box2DProcessing.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.set_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)
  # 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 particle_system library, particle_system.rb
module PS
  include_package 'org.jbox2d.collision.shapes'
  include_package 'org.jbox2d.common'
  include_package 'org.jbox2d.dynamics'
  include_package 'shiffman.box2d'

  # Box2D Particle System
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2010
  # translated to ruby-processing Martin Prout

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

  class ParticleSystem

    attr_reader :particles, :x, :y

    def initialize(bd, num, x, y)
      @particles = []          # Initialize the Array
      @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.delete_if { |p| p.done}

    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
It occurs to me, that with a bit more work and I could create a ruby-processing library to simplify the use of JBox2D? Thus replacing need for both fisica and box2d_processing libraries. Except that Ricard Marxer and Daniel Shiffman have both created some very nice examples. See other examples here.

Tuesday, 7 May 2013

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

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