GTK+ で落書き 12(Ruby)

三角形の内心・外心・重心・垂心・傍心
自分で求めた公式を使って、三角形の内接円と外接円を Ruby で描いてみました。
 
20170908123010

描画には自作の Gem 'oekaki' を使っています。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura

require 'oekaki'
require 'ostruct'

Width, Height = 400, 300

A = Vector[-0.2,  0.8]
B = Vector[-0.8, -0.3]
C = Vector[ 0.8, -0.3]

incircle     = OpenStruct.new
circumcircle = OpenStruct.new

l, m, n = (B - A).norm, (C - B).norm, (A - C).norm
q = l + m + n

incircle.p = A * (m / q) + B * (n / q) + C * (l / q)
incircle.r = sqrt((q / 2 - l) * (q / 2 - m) * (q / 2 - n) * 2 / q)

a = A[0] * (B[1] - C[1]) + B[0] * (C[1] - A[1]) + C[0] * (A[1] - B[1])

circumcircle.p = Vector[(A.norm ** 2 * (B[1] - C[1]) + B.norm ** 2 * (C[1] - A[1]) +
                            C.norm ** 2 * (A[1] - B[1])) / a,
                        (A.norm ** 2 * (C[0] - B[0]) + B.norm ** 2 * (A[0] - C[0]) +
                            C.norm ** 2 * (B[0] - A[0])) / a] / 2
circumcircle.r = (A - circumcircle.p).norm

class Vector
  def to_w
    l = Height / 2
    Vector[Width / 2 + self[0] * l, l - self[1] * l]
  end
end


Oekaki.app width: Width, height: Height do
  draw do
    clear
    
    color(0, 65535, 0)
    line(A.to_w[0], A.to_w[1], B.to_w[0], B.to_w[1])
    line(C.to_w[0], C.to_w[1], B.to_w[0], B.to_w[1])
    line(A.to_w[0], A.to_w[1], C.to_w[0], C.to_w[1])
    
    color(65535, 65535, 0)
    circle(false, incircle.p.to_w[0], incircle.p.to_w[1], incircle.r * Height / 2)
    
    color(65535, 0, 65535)
    circle(false, circumcircle.p.to_w[0], circumcircle.p.to_w[1],
       circumcircle.r * Height / 2)
  end
end

 
定数 A, B, C が三角形の頂点の座標なので、いろいろ変えて遊んでみて下さい。例えば

A = Vector[-0.7,  0.6]
B = Vector[-0.2, -0.8]
C = Vector[ 0.7, -0.4]

にすると
20170908134541
となります。ちなみに座標はウィンドウのそれではなく、原点がウィンドウの中心となるようなふつうの x-y座標系で、Y方向の最大最小値がそれぞれ 1, -1 になっています。

なお、Gem 'oekaki' のバージョンを 0.1.1 に上げました。変更点はメソッド Tool#clear の引数を省略した場合、黒色で画面クリアが行われるようにしたことです。上のコードはそれを前提に書かれています。
 

追記


こんなのも描いてみました。コードは下。
移動する内接円 · GitHub

OpenGL で正多面体を回転させてみる(Ruby)

Ruby 用に OpenGL を手軽に使うライブラリ「miniopengl.rb」を書いたので、その例として 5つの正多面体すべてを回転させてみました。
 

 
正多面体のデータはここより頂戴しました。ありがとうございます。polyhedrons_obj.zip をダウンロードして解凍し、フォルダを下のコードと同じディレクトリに置いて下さい。

コードは以下です。miniopengl.rb はこのコードと同じディレクトリに配置して下さい。当然ながら OpenGL が使えるようにしなければなりません。必要な準備はここを参照して下さい。また、並列処理を行う Gem 'parallel' を使っています。これはインストール($ gem install parallel)しておいて下さい。

動作は Linux Mint 18.2, Ruby 2.3.3 で確認しました。Gem 'parallel' でおそらく Kernel.#fork を使っているので、Windows では動かないと思われます。

require_relative 'miniopengl'
require 'parallel'

filenames = []
1.upto(5) {|i| filenames << "./polyhedrons_obj/r" + ("%02d" % i) + ".obj"}
data = []

filenames.each do |fn|
  open(fn, "r") do |io|
    ar = [[], []]
    io.each_line do |l|
      l.chomp!
      a = l[2..-1].split(" ")
      (l[0] == "v") ? ar[0] << a.map(&:to_f) : ar[1] << a.map {|i| i.to_i - 1}
    end
    data << ar
  end
end


Parallel.each(data, in_processes: 5) do |vertex, apexes|
  MiniOpenGL.app width: 400, height: 400 do
    clear_color(0, 0, 0.3)
    
    draw do
      clear
      color(0, 1, 0)
      glEnableClientState(GL_VERTEX_ARRAY)
      glVertexPointer(3, GL_DOUBLE, 0, vertex.flatten)
      apexes.size.times {|i| glDrawElements(GL_LINE_LOOP, apexes[i].size,
         GL_UNSIGNED_BYTE, apexes[i])}
      display
    end
  
    reshape do |w, h|
      viewport(0, 0, w, h)
      init_projection
      perspective(30, w / h.to_f, 3, 10)
      init_modelview
      look_at(3, 4, 5, 0, 0, 0, 0, 1, 0)
    end
  
    repeat(30) do
      modelview
      rotate(5, 0, 1, 0)
      redisplay
    end
  end
end

Gem 'parallel' によって 5つのウィンドウが同時に立ち上がります。


※Gem 'parallel' に関しては下を参照。
rubyで簡単並列処理(Parallel)
マルチスレッド/プロセスまとめ(Ruby編)


Ruby のサンプルコードについて


結城先生が仰っていることは大事なことだと思います。いちばん大変なのは初心者のときです。とにかく簡単なコードを動かす(つまりは 'Hello World!')のがわからない。僕はプログラミングに関してまわりに誰も聞く人がいなかったし今もいないので、(いまでもまだ)いつも簡単なことがわからない。そういうことが、初心者を卒業してしまった人にはなかなかわかりにくいのですね。

でもこれは自戒なのである。自分も説明するのが結構めんどうでサボったりするのだよなあ…。結城先生の本はいつも親切である。

 
Ruby は「A Programmer's Best Friend」ってことなのだが、言語自体は初心者にも friendly なのに、それを動かすことに関しては初心者にあまり親切でないと思う。特に Windows で使うのが大変。僕が初めて Ruby を使ったのは Windows でだったのだが、とりあえず使うエディタがなくて困った記憶がある。ぐぐって何とか「サクラエディタ」をインストールして使ったのだった。このあたり、絶対つまらぬところで損をしていると思う。ここは Python を見習った方がいいのではないか。

RubyGem 'Oekaki' ver.0.1.0 Release

RubyGem 'oekaki'のヴァージョン 0.1.0 をリリースしました。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
 
新しいメソッド Tool#clear, Tool#get_window_size, Event#window_changed を追加しました。

  • Tool#clear(color = nil) は画面のクリアです。color はクリアする色です。
  • Tool#get_window_size は現在の画面の大きさを配列 [width, height] に入れて返します。
  • Event#window_changed {...} はウィンドウの状態が替わったとき(最大化など)、ブロック内が呼ばれます。
  • デフォルトでウィンドウのリサイズができないようにしました。リサイズしたい場合は、Oekaki.app(resizable: true) {..} のように指定して下さい。

また、ヴァージョン 0.0.11 ではメソッド Tool#circle が追加されています。

  • Tool#circle(fill, x, y, r, color = nil) は中心 (x, y)、半径 r の円を書きます。fill が true ならば塗りつぶし、false ならば円周のみ描画されます。

 
これらに対応した
GTK+ で落書き 11(Ruby) - Camera Obscura
を書き直してみました。ウィンドウの最大化に対応しています。コードは下です。
円が降ってくる(スクリーンセーバーもどき) · GitHub

GTK+ で落書き 11(Ruby)


スクリーンセーバーみたいなものを Ruby で描いてみました。円が大きくなったり小さくなったりしながら落下していきます。

自画自賛ですけれど、これ結構好きです。

描画には自作の Gem 'oekaki' を使っています。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura

require 'oekaki'

Width, Height = if ARGV.size == 2
  ARGV.map(&:to_i)
else
  [1000, 700]
end

Max_r, Min_r = 40, 10
ColorMax = 65535
MaxNum = 60

class Circle
  def initialize(ob)
    @slot = ob
    renewal
    @y = rand(Height)
  end
  
  def renewal
    @max_r = rand(Max_r - Min_r) + Min_r
    @x =  rand(Width)
    @y = -rand(@max_r)
    @color = [rand(ColorMax), rand(ColorMax), rand(ColorMax)]
    @fall_step = 1 + rand * 3
    @r = 1
    @r_step = rand * 0.2 + 0.8
  end

  def paint
    @slot.color(@color[0], @color[1], @color[2])
    @slot.circle(true, @x, @y, @r)
    @y += @fall_step
    @r += @r_step
    @r_step *= -1 if @r > @max_r or @r < 1
    renewal if @y > Height + Max_r
    true
  end
end


Oekaki.app width: Width, height: Height do
  circles = []
  MaxNum.times {circles << Circle.new(self)}

  draw do
    color(0, 0, 0)  
    rectangle(true, 0, 0, Width, Height)
  end

  timer(80) do
    color(0, 0, 0)  
    rectangle(true, 0, 0, Width, Height)
    circles.each(&:paint)
  end
end

円によって落下速度や点滅測度が微妙に変えてあります。円の総数はいつも同じで、定数 MaxNum に入っています。

Gem 'oekaki' のバージョンは最新の 0.0.11 が必要です。古いバージョンならば、上の @slot.circle(true, @x, @y, @r)@slot.arc(true, @x - @r, @y - @r, 2 * @r, 2 * @r, 0, 64 * 360) に代えて下さい。それで動きます。


追記
以上は Linux Mint 18.2, Ruby 2.3.3 で確認しましたが、Windows 8.1, Ruby 2.2.2 [i386-mingw32] でもそのまま動くことを確認しました。ただ、Windows 版ではなぜか画面がちらつきますし、Linux Mint で実行したよりも動きがなめらかでないです。

GTK+ でスクロールするテキスト・ウィンドウを作る(Ruby)

RubyGTK+ を使ってテキストを表示させるウィンドウを作ってみました。Gem 'gtk2' を使っています。
 

window = ScrolledTextWindow.open

5.times do |i|
  window.write "send #{i}"
  window.write "  ok!\n"
  sleep(rand(2) + 1) 
end
  
window.close

これでこんな風に表示されます。

write で改行のたびにリアルタイムで書き込まれます。スクロールもします。ただし、残念ながら close してもウィンドウは消えません。ウィンドウのボタンで消去して下さい。
ウィンドウの縦横幅、タイトルを指定することもできます。ScrolledTextWindow.open(width: 300, height: 200, title: "Window") みたいな感じ。

scrolled_text_window.rb

require 'gtk2'

class ScrolledTextWindow
  def self.open(width: 500, height: 200, title: "Text Window")
    #IO{read, write}, Gtk::Window{w}, Gtk::ScrolledWindow{sw}, Gtk::TextView{t}
    #Gtk::TextBuffer{b}, control
    
    read, write = IO.pipe
    control = Object.new
    
    control.define_singleton_method(:write) do |text|
      write.print text
    end
    
    control.define_singleton_method(:close) do  
      write.close
    end
    
    fork do
      write.close
      
      w = Gtk::Window.new
      w.signal_connect("destroy") {Gtk.main_quit}
      w.set_size_request(width, height)
      w.title = title
      
      sw = Gtk::ScrolledWindow.new
      sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
      
      t = Gtk::TextView.new
      t.modify_font(Pango::FontDescription.new("12"))
      sw.add(t)
      w.add(sw)
      
      b = t.buffer
      Thread.new(t, b) do |t, b|
        loop do
          b.insert_at_cursor(read.gets)
          t.scroll_mark_onscreen(b.create_mark(nil, b.end_iter, true))
        end
      end
      
      Gtk.quit_add(0) {read.close}
      w.show_all
      Gtk.main
    end
    
    read.close
    control
  end
end

 

※参考
GTK+ で簡単なテキスト入力(Ruby) - Marginalia
noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(3)- ボタン