
H=600
W=600

Shoes.app :width => W + 200, :height => H + 20 do
  @drawn_rects = []

  def restart_dots
    @draw.checked = true
    if @the_image
      @the_image.remove
      @the_image = nil
    end
    draw_rects
    @button_down = nil
    @dots = 0
    @spot = [rand(@stack.width), rand(@stack.height)]
  end

  def start_fresh
    @rects = []
    restart_dots
  end

  # turns a rectangle with possibly negative width and height into a 
  # normal one for drawing purposes
  def absolute_rect(this_rect)
    return {
      :top =>  [ this_rect[:top], this_rect[:top] + this_rect[:height] ].min,
      :left => [ this_rect[:left], this_rect[:left] + this_rect[:width] ].min,
      :width => this_rect[:width].abs,
      :height => this_rect[:height].abs    
    }
  end

  def draw_rects
    if @show_rects.checked?
      @drawn_rects.each { |r| r.remove }
      @stack.append do
        @rects.each do | this_rect |
          nofill
          stroke rgb(1.0,1.0,1.0, 0.2)
          @drawn_rects << rect(absolute_rect(this_rect))
        end
      end
    else
      @drawn_rects.each { |r| r.remove }
    end
  end

  # called when you are done drawing a rectangle
  def finish_rect(left, top)
    @drawing_rect.remove if @drawing_rect
    rect_top, rect_left = @button_down
    @button_down = nil
    rect_bottom = top
    rect_right = left
    rect_width = rect_right - rect_left
    rect_height = rect_bottom - rect_top
    @rects << {:top => rect_top, :left => rect_left, 
      :width => rect_width, :height => rect_height}
    restart_dots
  end

  flow :height => '100%' do
    stack :width => 200 do
      button( "Clear and restart" ) do
        start_fresh
      end
      flow do
        @draw = check( :checked => true )
        para( "Go") 
      end
      flow do
        @show_rects = check( :checked => true ) { draw_rects }
        para( "Show rectangles") 
      end
      flow do
        @blur = check( :checked => false ) { restart_dots }
        para( "Blur") 
      end
      button( "Randomize color" ) do
        @dot_color=rgb(rand*0.5 + 0.5, rand*0.5 + 0.5, rand*0.5 + 0.5, 0.8)
        restart_dots
      end

      para "draw rectangles with your left mouse button; clear with a right click"
    end
    @stack = stack :width => -200, :height => '100%' do
      background black
    end
  end

  start_fresh
  batch = 600
  speed = 5
  animate speed do
    next if @button_down
    if @rects.length > 0 and @draw.checked?
      @dot_color ||=rgb(rand*0.5 + 0.5, rand*0.5 + 0.5, rand*0.5 + 0.5, 0.8)
      @dots += batch
      @stack.append do
        stroke @dot_color
        strokewidth 1
        unless @the_image
          @the_image =  image( :left => 0, :top => 0 )
        end
        batch.times do
          @the_image.rect( :left => @spot[0], :top => @spot[1], 
                             :width => 1, :height => 1, :center => true )
          @the_image.show()
          move_the_spot
        end
        @the_image.blur(1) if @blur.checked?
      end
    end
  end

  def move_the_spot
    x, y = @spot
    the_box = @rects[rand(@rects.length)]
    box_proportion_w = the_box[:width].to_f / @stack.width.to_f
    box_proportion_h = the_box[:height].to_f / @stack.height.to_f
    new_x = ( x * box_proportion_w  + the_box[:left]).to_i
    new_y = ( y * box_proportion_h + the_box[:top]).to_i
    @spot = [ new_x, new_y ]
  end

  # set button_down.  if button is already down,
  # something funny happened, like you drew a rectangle
  # bigger than the screen.  So release it.
  # while button_down is set, you'll see a 
  @stack.click do | button, left, top |
    left -= @stack.left
    if @button_down
      finish_rect(left, top)
    else
      @button_down = [top, left]
    end
  end

  # if button_down is set, we're in the process of
  # drawing a rectangle.  Outline in blue the
  # rectangle you'd get if you stopped drawing here.
  # if there's already a blue rectangle, remove the old one
  # first.
  @stack.motion do | left, top |
    left -= @stack.left
    if @button_down
      @drawing_rect.remove if @drawing_rect
      t1, l1 = @button_down
      r = absolute_rect( :left => l1,
                         :top => t1,
                         :width => left - l1,
                         :height => top - t1 )
      @stack.append do
        stroke blue
        @drawing_rect = rect( r )
      end
    end
  end

  @stack.release do | button, left, top |
    left -= @stack.left

    # right click = clear and start over

    if button == 2
      start_fresh
    else

      # left button = if we were drawing a rectangle,
      # we're done now

      if @button_down
        finish_rect(left, top)
      end
    end
  end
end

