#
# animated wave demo (called by 'widget')
#
# based on Tcl/Tk8.5a2 widget demos

# destroy toplevel widget for this demo script
if defined?($aniwave_demo) && $aniwave_demo
  $aniwave_demo.destroy 
  $aniwave_demo = nil
end

# create toplevel widget
$aniwave_demo = TkToplevel.new {|w|
  title("Animated Wave Demonstration")
  iconname("aniwave")
  positionWindow(w)
}

# create label
msg = TkLabel.new($aniwave_demo) {
  font $font
  wraplength '4i'
  justify 'left'
  text 'This demonstration contains a canvas widget with a line item inside it. The animation routines work by adjusting the coordinates list of the line.'
}
msg.pack('side'=>'top')

# create frame
TkFrame.new($aniwave_demo) {|frame|
  TkButton.new(frame) {
    text 'Dismiss'
    command proc{
      tmppath = $aniwave_demo
      $aniwave_demo = nil
      tmppath.destroy
    }
  }.pack('side'=>'left', 'expand'=>'yes')

  TkButton.new(frame) {
    text 'See Code'
    command proc{showCode 'aniwave'}
  }.pack('side'=>'left', 'expand'=>'yes')

}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')

# animated wave
class AnimatedWaveDemo
  def initialize(frame, dir=:left)
    @direction = dir

    # create canvas widget
    @c = TkCanvas.new(frame, :width=>300, :height=>200, 
                      :background=>'black')
    @c.pack(:padx=>10, :pady=>10, :expand=>true)

    # Creates a coordinates list of a wave. 
    @waveCoords = []
    @backupCoords = []
    n = 0
    (-10..300).step(5){|n| @waveCoords << [n, 100]; @backupCoords << [n, 100] }
    @waveCoords << [n, 0]; @backupCoords << [n, 0]
    @waveCoords << [n+5, 200]; @backupCoords << [n+5, 200]
    @coordsLen = @waveCoords.length

    # Create a smoothed line and arrange for its coordinates to be the
    # contents of the variable waveCoords.
    @line = TkcLine.new(@c, @waveCoords, 
                        :width=>1, :fill=>'green', :smooth=>true)

    # Main animation "loop". 
    # Theoretically 100 frames-per-second (==10ms between frames)
    @timer = TkTimer.new(10){ basicMotion; reverser }

    # Arrange for the animation loop to stop when the canvas is deleted
    @c.bindtags_unshift(TkBindTag.new('Destroy'){ @timer.stop })
  end

  # Basic motion handler. Given what direction the wave is travelling
  # in, it advances the y coordinates in the coordinate-list one step in
  # that direction.
  def basicMotion
    @backupCoords, @waveCoords = @waveCoords, @backupCoords
    (0...@coordsLen).each{|idx|
      if @direction == :left
        @waveCoords[idx][1] = @backupCoords[(idx+1 == @coordsLen)? 0: idx+1][1]
      else
        @waveCoords[idx][1] = @backupCoords[(idx == 0)? -1: idx-1][1]
      end
    }
    @line.coords(@waveCoords)
  end

  # Oscillation handler. This detects whether to reverse the direction
  # of the wave by checking to see if the peak of the wave has moved off
  # the screen (whose size we know already.)
  def reverser
    if @waveCoords[0][1] < 10
      @direction = :right
    elsif @waveCoords[-1][1] < 10
      @direction = :left
    end
  end

  # animation control
  def move
    @timer.start
  end

  def stop
    @timer.stop
  end
end

# Start the animation processing
AnimatedWaveDemo.new($aniwave_demo, :left).move
