argius note

プログラミング関連

練習:グラフ描画クラス

グラフを書くとき、いちいち物理的な座標で描画すると面倒なので、論理座標で指定して、描くときに内部で物理座標に変換するような機能を持ったグラフ描画クラスを作ってみた。
新たな参考資料として、ここを見つけた。大いに役立った。感謝。
多少手直しが必要な部分もあるが、とりあえず使える、はず。

# graph.rb
require "tk"

class GraphScreen
  attr_accessor :x, :y
  attr_reader   :canvas, :range
  SIZE = 600
  ADJUSTER = 10
  def initialize range = (-2.0..2.0)
    @x, @y = 0.0, 0.0
    @min = first = range.first.to_f
    @max = last  = range.last.to_f
    @range = (@min..@max)
    @rate = SIZE / (@max - @min)
    x_min, x_zero, x_max = absolute_x(@min), absolute_x(0), absolute_x(@max)
    y_min, y_zero, y_max = absolute_y(@min), absolute_y(0), absolute_y(@max)
    @canvas = TkCanvas.new do
      background "white"
      width  SIZE + ADJUSTER * 2
      height SIZE + ADJUSTER * 2
      TkcLine.new(self, x_zero, y_min, x_zero, y_max)
      TkcLine.new(self, x_min, y_zero, x_max, y_zero)
      TkcText.new(self, x_zero + 30, y_min - 20) do |t|
        text first.to_s
        font TkFont.new(:size=>16)
      end
      TkcText.new(self, x_zero + 30, y_max + 20) do |t|
        text last.to_s
        font TkFont.new(:size=>16)
      end
      TkcText.new(self, x_min + 10, y_zero - 20) do |t|
        text first.to_s
        font TkFont.new(:size=>16)
      end
      TkcText.new(self, x_max - 10, y_zero - 20) do |t|
        text last.to_s
        font TkFont.new(:size=>16)
      end
      pack
    end
  end
  def absolute_x(x = @x) SIZE / 2 + x *  @rate + ADJUSTER end
  def absolute_y(y = @y) SIZE / 2 + y * -@rate + ADJUSTER end
  def dot(x = @x, y = @y) line(x, y, x, y) end
  def line(x1, y1, x2 = @x, y2 = @y)
    cx1 = absolute_x(x1)
    cy1 = absolute_y(y1)
    cx2 = absolute_x(x2)
    cy2 = absolute_y(y2)
    if (cx2 - cx1) * (cy2 - cy1) == 0 then cx1 += 1 end
    TkcLine.new(@canvas, cx1, cy1, cx2, cy2).configure(:fill=>"red")
    self
  end
  def move(x = 0, y = 0)
    @x += x
    @y += y
    self
  end
  def loop(rate = 1)
    @range.step(0.005 * rate) do |x|
      y = yield(x)
      dot(x, y)
    end
  end
end

module GraphModule
  def to_radian(degree) degree * Math::PI / 180 end
  def sin(degree) Math.sin(to_radian(degree)) end
  def cos(degree) Math.cos(to_radian(degree)) end
end