# particles.rb
# by daniel kauer 
# 02/07/08
$particle_count=30

Shoes.app :height => 500, :width => 500 do
  pSys = ParticleSystem.new(self,width/2,100,$particle_count)

  pSys.collision_areas << Area.new(0,250,200,20)
  pSys.collision_areas << Area.new(0,50,20,150)

  pSys.collision_areas << Area.new(300,250,175,200)
  pSys.collision_areas << Area.new(150,450,100,20)
  
  lsec=Time.now.to_i
  fps,framecnt=0,0
  animate 60 do |frame|
    secs = Time.now.to_i
   clear do
    background gradient gray(200), gray(100)
    strokewidth 1.5
    para (fps.to_s + ' fps'), :font => 'Verdana 11px'
    
    pSys.x= width/2+Math::sin(frame/10.0)*200
    pSys.y= 100+Math::cos(frame/2.0)*10
    pSys.spawn
    pSys.run
    
    framecnt+=1
    if secs>lsec
      fps=framecnt
      framecnt,lsec=0,secs
    end
   end
 end
end

class ParticleSystem
  attr_accessor :collision_areas,:x,:y
  
  def initialize(shoes,x,y,pnum)
    @shoes,@x,@y,@pnum=shoes,x,y,pnum
    @particles=[]
    @collision_areas=[]
  end
  
  def spawn
    if @particles.length<@pnum
      @particles << Particle.new(@shoes,@x,@y)
    end
  end
  
  def run
    @shoes.fill @shoes.gray(50,200)
    @shoes.oval @x,@y,5,5
    
    remove_list=[]
    @particles.each do |particle|
      particle.move
      @collision_areas.each do |area|
        particle.bounce area if particle.inside? area
      end
      particle.draw
      if particle.outside?(Area.new(0,0,@shoes.width,@shoes.height)) || particle.dead?
        remove_list << particle
      end
    end
    remove_list.each {|item| @particles.delete item}
    
    @collision_areas.each do |area|
      area.draw @shoes
    end
  end
end

class Particle
  $gravity = 1.0
  $v_max = 10.0
  $size_max = 7.5
  $grow_speed=0.5
  
  def initialize(shoes,x,y)
    @shoes,@x,@y=shoes,x,y
    @vx,@vy=init_speed,init_speed
    @size, @life = 0, 100
  end
  
  def init_speed
    rand*$v_max-$v_max/2
  end
  
  def move
    @vy+=$gravity
    @x+=@vx
    @y+=@vy
    @size+=$grow_speed unless @size > $size_max
    @life-=1
  end
  
  def switch_vel
    @vx=@vx*-1
    @vy=@vy*-1
  end
  
  def bounce(area)
    switch_vel; move; switch_vel # take a step back
    if point_inside?(@x+@vx,@y,area)
      @vx=@vx*-0.8
    end
    if point_inside?(@x,@y+@vy,area)
      @vy=@vy*-0.8
    end
    move
  end
  
  def draw
    @shoes.fill @shoes.gray(250,@life)
    @shoes.stroke @shoes.gray(250,@life+50)
    @shoes.oval @x,@y,@size,@size
  end
  
  def point_inside?(x,y,area)
    if x>area.left && x <area.left+area.width && y>area.top && y< area.top+area.height 
      true
    else
      false
    end
  end
  
  def inside?(area)
    point_inside? @x, @y, area
  end
  
  def outside?(area)
    not inside?(area)
  end
  
  def dead?
    if @life<1
      true
    else
      false
    end
  end
  
end


class Area
  attr_accessor :left, :top, :width, :height
  def initialize(left,top,width,height)
    @left,@top,@width,@height=left,top,width,height
  end
  
  def draw(shoes)
    shoes.rect(left,top,width,height)
  end
end