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

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

No comments:

Post a Comment

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