H=500
W=800

Shoes.app :width => W, :height => H + 50 do

  def start_fresh
    background black, :curve => 5
    @button_down = nil
    @spot = [rand(W), rand(H)]
    @dots = 0
    @rects = []
    nofill
    stroke white
  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

  # 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}
    @stack.clear do
      @dots = 0
      background black
      stroke rgb(1.0,1.0,1.0, 0.2)
      @rects.each do | this_rect |
        rect(absolute_rect(this_rect))
      end
      stroke rgb(rand*0.5 + 0.5, rand*0.5 + 0.5, rand*0.5 + 0.5, 0.8)
    end
  end

  background azure

  stack do
    @stack = stack :height => H, :width => W  do
      start_fresh
      batch = 400
      speed = 5
      animate speed do
        if @rects.length > 0 and @dots < batch * speed * 15
          @dots += batch
          @stack.append do
            shape :left => 0, :top => 0 do
              batch.times do
                x, y = @spot
                rect :left => x, :top => y, :width => 1, :height => 1
                the_box = @rects[rand(@rects.length)]
                box_proportion_w = the_box[:width].to_f / W.to_f
                box_proportion_h = the_box[:height].to_f / H.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
              
            end
          end
        end
      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 
      click do | button, left, top |
        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.
      motion do | left, top |
        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

      release do | button, left, top |

        # right click = clear and start over

        if button == 2
          @rects = []
          @stack.clear do
            @dots = 0
            background black
          end
          return
        else

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

          if @button_down
            finish_rect(left, top)
          end
        end
      end
    end
    stroke rgb(0,0,0,0.5)
    para "draw rectangles with your left mouse button; clear with a right click."
  end
end
