突然はなしが変わってRuby/Tkでアニメーションを作る話(その2)

いろいろ遊んでみてやっと出来たものです。鎖みたいなのが一方を固定して落とした場合という感じのイメージです。計算は適当だし、見た目もなんか変です。物理のセンスが無いことが身に染みました。

require 'tk'
require 'singleton'

class Viewer<TkFrame
  include TkComposite

  WIDTH = 512
  HEIGHT = 512

  def initialize_composite
    @frame.configure(:width=>WIDTH, :height=>HEIGHT)
    @canvas = TkCanvas.new(@frame)
    @canvas.configure(:width=>WIDTH, :height=>HEIGHT)
    @canvas.grid('row'=>0, 'column'=>0, 'sticky'=>'news')
    @frame.grid_propagate(false)
  end

  def draw_oval(x, y, r)
    r2 = r / 2
    TkcOval.new(@canvas, x - r2, HEIGHT - y + r2, x + r2, HEIGHT - y - r2, 'fill' => :red)
  end
end

class Field
  include Singleton

  def initialize
    @view = Viewer.new
    @view.pack(:expand=>true, :fill=>:both)
  end

  def draw_oval(x, y, r)
    @view.draw_oval(x, y, r)
  end
end

class Ball
  @@field = Field.instance
  OBJNUM = 40

  def initialize(prev)
    @prev = prev
    @va = 0
    @a = 0
    @r = prev.r
    @x = prev.x + @r * Math.cos(@a)
    @y = prev.y + @r * Math.sin(@a)
    @shape = @@field.draw_oval(@x, @y, @r)
    @no = prev.no + 1
  end
  attr :x
  attr :y
  attr :r
  attr :no

  def redraw
    r2 = @r / 2
    @shape.coords = [
      @x - r2, 
      Viewer::HEIGHT - @y + r2, 
      @x + r2, 
      Viewer::HEIGHT - @y - r2,
      ]
  end

  def compute_next_pos
    dx = @x - @prev.x
    dy = @y - @prev.y
    fx = 0          #((dx / @r) * 0.1) / (OBJNUM - @no + 2)
    fy = 0.03
    @va = (- dy * fx -  dx * fy) / @r
    @a += @va
    @x = @prev.x + @r * Math.cos(@a)
    @y = @prev.y + @r * Math.sin(@a)
    if @y < 0 then
      @va = 0
      @y = 0
    end
    redraw

    true
  end
end

class BallFix<Ball
  def initialize(r, x, y)
    @r = r
    @x = x
    @y = y
    @no = 0
    @shape = @@field.draw_oval(@x, @y, @r)
  end

  def compute_next_pos
  end
end

objs = []
prev = BallFix.new(10, 250, 500)
objs.push prev
(0..Ball::OBJNUM).each do |n|
  prev = Ball.new(prev)
  objs.push prev
end
  
count = 0
objnum = 1
timer = TkAfter.start(10, -1, lambda {
                        objs.each do |b|
                          b.compute_next_pos
                        end
                    })

Tk.mainloop