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

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

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