(追記:2013-05-15) Colorableを大幅アップデートしました(version 0.2.0)。その結果、この記事で書かれたバージョンとの互換性がなくなっています。現在の機能に関しては以下の記事を参考にしてください。

“Rubyで色を扱うライブラリ「Colorable」をアップデートしましたのでお知らせいたします” error


Rubyで「色」というものを扱う機会はそう多くはないでしょう。Rubyはどちらかと言うと、クライアントサイドつまり接客系ではなく、サーバーサイドつまり裏方系言語ですからね。それでも接客系言語に向けて色情報を渡したり、そのラッパーになったりする機会はあるとおもいます。そんなときはRubyでも色を扱う必要がでてきます。

実際、拙作GraphvizのRubyラッパー「Gviz」で色付きグラフを作るときには、色指定で色々と苦労します。

RubyGems.orgで検索すると、ANSIカラー以外の色関係のライブラリがそれなりの数ヒットします。しかし、ざっと見たところ自分の欲しい物が見つかりません。というか、なんか探すの面倒臭いんですよね..


そんなわけで… 車輪の再発明と思いつつも… 勉強を兼ねまして…


ColorableというRGBとかHSBとかの色を扱うライブラリを作りました^ ^; ちょっとまだ中途半端な出来ですが。

colorable | RubyGems.org | your community gem host


使い方

gem install colorableして、次のように使います。

Colorable::Color

require "colorable"

c = Colorable::Color.new(:alice_blue)

c # => rgb(240,248,255)
c.name # => "Alice Blue"
c.rgb # => [240, 248, 255]
c.hex # => "#F0F8FF"
c.hsb # => [208, 6, 100]

c2 = Colorable::Color.new([0, 255, 255])

c2 # => rgb(0,255,255)
c2.name # => "Aqua"
c2.rgb # => [0, 255, 255]
c2.hex # => "#00FFFF"
c2.hsb # => [180, 100, 100]

Color.newX11の色名称またはRGB値の配列を引数に取り、colorオブジェクトを生成します。色名称は文字列またはシンボルを受け付けます。そしてRGB値に基いてHEXおよびHSB(Hue, Saturation, Brightness)値を返します。

Colorには#next,#prevというメソッドがあります。これらはX11カラーにおける次の色、前の色を順次出力します。

c = Colorable::Color.new(:alice_blue)

c.next.name # => "Antique White"
10.times.map { c = c.next; c.name } # => ["Antique White", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "Blanched Almond", "Blue", "Blue Violet"]


c2 = Colorable::Color.new(:alice_blue)

c2.prev.name # => "Yellow Green"
10.times.map { c2 = c2.prev; c2.name } # => ["Yellow Green", "Yellow", "White Smoke", "White", "Wheat", "Violet", "Turquoise", "Tomato", "Thistle", "Teal"]

X11のカラーセット(144色)は環状になっているので#next,#prevは循環します。

これらのメソッドは、デフォルトでその名前順に色を出力しますが、:rgb, :hsbなどの並びを指定する引数を渡すことにより、その並びでの色を順に出力させることができます。

c = Colorable::Color.new(:alice_blue)

c.next(:rgb).name # => "Honeydew"
10.times.map { c = c.next(:rgb); c.name } # => ["Honeydew", "Azure", "Sandy Brown", "Wheat", "Beige", "White Smoke", "Mint Cream", "Ghost White", "Salmon", "Antique White"]


c2 = Colorable::Color.new(:alice_blue)

c2.prev(:hsb).name # => "Steel Blue"
10.times.map { c2 = c2.prev(:hsb); c2.name } # => ["Steel Blue", "Light Sky Blue", "Sky Blue", "Deep Sky Blue", "Light Blue", "Powder Blue", "Cadet Blue", "Dark Turquoise", "Cyan", "Aqua"]

Colorable::Colorset

多数の色をまとめて扱いたいときは、Colorable::Colorsetを使います。

cs = Colorable::Colorset.new

cs.first(10).map(&:name) # => ["Alice Blue", "Antique White", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "Blanched Almond", "Blue"]
cs.last(10).map(&:hex) # => ["#008080", "#D8BFD8", "#FF6347", "#40E0D0", "#EE82EE", "#F5DEB3", "#FFFFFF", "#F5F5F5", "#FFFF00", "#9ACD32"]

異なる並びのカラーセットを得たいときは、Colorset.[]メソッドを使います。

cs = Colorable::Colorset[:rgb]

cs.first(10).map(&:name) # => ["Black", "Navy", "Dark Blue", "Medium Blue", "Blue", "Dark Green", "Green2", "Teal", "Dark Cyan", "Deep Sky Blue"]
cs.last(10).map(&:rgb) # => [[255, 240, 245], [255, 245, 238], [255, 248, 220], [255, 250, 205], [255, 250, 240], [255, 250, 250], [255, 255, 0], [255, 255, 224], [255, 255, 240], [255, 255, 255]]

cs = Colorable::Colorset[:hsb]

cs.first(10).map(&:name) # => ["Black", "Dim Gray", "Gray2", "Dark Gray", "Gray", "Silver", "Light Gray", "Gainsboro", "White Smoke", "White"]
cs.last(10).map(&:hsb) # => [[302, 49, 85], [322, 89, 78], [327, 92, 100], [330, 59, 100], [338, 73, 69], [341, 6, 100], [341, 49, 86], [349, 91, 86], [351, 25, 100], [352, 29, 100]]

Gvizとともに使う

さて、ColorableがColor#nextColorsetを使って色の並びを取得できることがわかりました。これをGvizを使って視覚的に表現してみます。ここでは名前順、RGB順およびHSB順の色の並びを比較してみますね。Gvizはgem i gvizで取得します。

まずは名前順です。graph.ruファイルを用意して次のコードを打ち込みます。

# graph.ru
require "colorable"

color = Colorable::Color.new(:black)

global layout:'neato', size:20
nodes shape:'circle', style:'filled'

loop do
  nxt = color.next
  route color.name.to_id => nxt.name.to_id
  [color, nxt].each do |c|
    fcolor = c.dark? ? '#FFFFFF' : '#000000'
    node c.name.to_id, label:c.name, fillcolor:c.hex, fontcolor:fcolor
  end
  color = nxt
  break if color.name == "Black"
end

save :color, :png

各色をノードとしColor#nextで順に次の色を名前順に呼び出していきます。routeメソッドで隣合うノード同士を順につないでいき、最初の色(”Black”)が現れたところでloopを抜けます。

このファイルのあるディレクトリでgvizコマンドを実行すれば、以下の出力が得られます。

Alt ColorLing noshadow

名前順の色リングができました。

ノードのサイズがばらばらですから、これを固定することでよりビジュアルに訴えるものに仕上げます。

require "colorable"

color = Colorable::Color.new(:black)

global layout:'neato', size:20
+ nodes shape:'circle', style:'filled', width:4

loop do
  nxt = color.next
  route color.name.to_id => nxt.name.to_id
  [color, nxt].each do |c|
    fcolor = c.dark? ? '#FFFFFF' : '#000000'
    node c.name.to_id, label:c.name, fillcolor:c.hex, fontcolor:fcolor
  end
  color = nxt
  break if color.name == "Black"
end

save :color, :png

出力です。

Alt ColorLing noshadow

名前は隠れちゃいましたけど、なかなかキレイですね。しかし色の並びが名前順なので、色変化としての並びはばらばらです。

今度はRGB順でリングを作ってみます。nextに:rgbの引数を渡します。

require "colorable"

color = Colorable::Color.new(:black)

global layout:'neato', size:20
nodes shape:'circle', style:'filled', width:4

loop do
+  nxt = color.next(:rgb)
  route color.name.to_id => nxt.name.to_id
  [color, nxt].each do |c|
    fcolor = c.dark? ? '#FFFFFF' : '#000000'
    node c.name.to_id, label:c.name, fillcolor:c.hex, fontcolor:fcolor
  end
  color = nxt
  break if color.name == "Black"
end

save :color, :png

出力です。

Alt ColorLing noshadow

かなりキレイな配色になりました。

しかしRGBカラーモデル(混色系)は各色成分の量で変化していくので、人間の色認識の感覚からはズレていてちょっと不十分な印象です。

今度はHSBカラーモデル(顕色系)を試します。nextの引数を:hsbに変えます。

require "colorable"

color = Colorable::Color.new(:black)

global layout:'neato', size:20
nodes shape:'circle', style:'filled', width:4

loop do
+  nxt = color.next(:hsb)
  route color.name.to_id => nxt.name.to_id
  [color, nxt].each do |c|
    fcolor = c.dark? ? '#FFFFFF' : '#000000'
    node c.name.to_id, label:c.name, fillcolor:c.hex, fontcolor:fcolor
  end
  color = nxt
  break if color.name == "Black"
end

save :color, :png

出力です。

Alt ColorLing noshadow

人間が見てより自然な色変化になりました。

(正直もっと綺麗なグラデーションになることを期待したんですけど…もしかしたらHSBの演算が間違っているのかもしれません。あとで検証してみます。)

最後に、ノードのwidthを20にして再度HSBの並びを出力してみます。

Alt ColorLing noshadow

Graphvizでこんなに綺麗なリングが描けるなんて、ちょっと感動しませんか?

以上、「Colorable」ライブラリおよびそのGvizにおける使用例を紹介しました。


(追記:2014-3-3) Gvizについてのまとめ頁を作りました。

Gvizの目次 - Rubyの世界からGraphvizの世界にこんにちは!


A Color of His Own by Leo Lionni



blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS