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

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

No comments:

Post a Comment

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