# Sand Traveler was a special commision, produced in Processing by 
# Jared Tarbell for Sonar 2004, Barcelona.
# This Ruby port has been tweaked, half of the (non-running) code exorcised, 
# color palette broadened, and done in negative.

# -- omygawshkenas

require 'ruby-processing'

class SandTraveler < Processing::App
  attr_reader :cities, :num, :slowdown, :num_travelers
  
  load_library 'control_panel'
  
  def setup
    
    control_panel do |c|
      c.slider :slowdown, 0.5..1.1
      c.slider :num_travelers, 0..50
    end
    
    render_mode P2D
    @dx, @dy = 0, 0
    @num = 150
    @cities = []
    @slowdown = 0.954
    @num_travelers = 13
    smooth
    reset_all
  end
  
  def draw
    @cities.each {|city| city.move }
  end
  
  def mouse_pressed
    reset_all
  end
  
  def reset_all
    @source_colors = [[@red || rand(255), @green || rand(255), @blue || rand(255), rand(30) + 45],
                     [@red_2 || rand(255), @green_2 || rand(255), @blue_2 || rand(255), rand(30) + 45]]
    background 0
    vt = 4.2
    vvt = 0.2
    ot = rand * PI * 2
    @num.times do |i|
      tinc = ot + (1.1 - i / @num) * 2 * i * (PI * 2) / @num
      vx, vy = vt * sin(tinc), vt * cos(tinc)
      @cities[i] = City.new(width/2+vx*2, height/2+vy*2, vx, vy, i)
      vvt -= 0.00033
      vt += vvt
    end
    @cities.each { |city| city.find_friend }
  end
  
  def some_color
    choice = @source_colors[rand(2)].dup
    choice.map! { |c| c + (rand * 120 - 60) }
    choice[3] = choice[3] * 0.7
    return choice
  end
end

class City
  include Math
  attr_reader :x, :y, :color
  # SLOWDOWN = 0.946
  
  def initialize(dx, dy, vx, vy, index)
    @app = $app
    @x, @y = dx, dy
    @vx, @vy = vx, vy
    @index = index
    @slowdown = 0.956
    @color = @app.some_color
  end
  
  def move
    if (@vx.abs + @vy.abs) > 0.01
      @vx += (@friend.x - @x) / 1000
      @vy += (@friend.y - @y) / 1000
      @vx *= @app.slowdown; @vy *= @app.slowdown
      @x += @vx; @y += @vy
      draw_travelers
    end
  end
  
  def find_friend
    @friend = @app.cities[(@index + rand(@app.num/5) + 1) % @app.num]
  end
  
  def draw_travelers
    @app.stroke *@friend.color
    @app.num_travelers.to_i.times do |i|
      t = rand * PI * 2
      dx = sin(t) * (@x - @friend.x) / 2 + (@x + @friend.x) / 2
      dy = sin(t) * (@y - @friend.y) / 2 + (@y + @friend.y) / 2
      if rand < 0.01
        dx += rand * 6 - 3
        dy += rand * 6 - 3
      end
      @app.point dx, dy
    end
  end
end

SandTraveler.new :width => 700, :height => 700, :full_screen => true
