見出し画像

Python:RGB混色図を作る

 以前,次のようなRGBの混色図(光の三原色:加法混色)を作った。

画像2

使ったのは matplotlib.patches。
色を変えるにはプログラムコードのRGBコードを書き換える。教科書に載っている Webセーフカラー に色見本を確かめる,といった学習活動に使える。プログラムコードも短いので(23行)プログラミングの練習としてもよいだろう。
 一口にRGB混色図といっても,何をどう使うかによっていろいろな学習活動が考えられる。ワードプロセッサでの色指定を使ったり,Inkscape などの作図ツールを使ったり,テキストエディタでSVGファイルを作ってブラウザで表示したり,あるいは,HTML・CSSを使うという方法もある。いずれも筆者が授業で試みている。

 しかし,教科書などに載っている,光の3原色の加法混色図を作りたい。

画像3

 しかも,スライダを使って,インタラクティブにR,G,Bの値を変えられるとよい。この場合は,R,B,Gそれぞれの値の変化と色の変化を感覚的に捉えることができる。
 スライダの作り方については先日調べた。まず Tkinter で作り方を調べ,次にmatplotlib を使った。(Tkinter の記事はない。マニュアルにはいれてある)
 問題は,円が重なった部分をどう描くかだ。

 matplotlib と Tkinter では図の描き方が異なる。はじめは Tkinter で発想したがちょっと面倒かと思って,matplotlib で作成に取り掛かる。
何が違うのかを説明しておこう。

Tkinter には,円(楕円),弧(扇形,弓形),多角形を描く関数がある。2つの円が重なった部分,3つの円が重なった部分については,弓型と三角形を組み合わせれば描けるだろう。

画像4

これが当初の予想。何が「ちょっと面倒」かというと,弧を描くのに,円の一部として描くのだが,その円は正方形に内接する円として描く。中心と半径ではないのだ。そこが面倒に思えたのだ。実際には発想を変えて扇形を組み合わせて描いた。

 matplotlib.pyplot では,媒介変数で表された曲線を描けるが,中を塗ることはできない。matplotlib.patches では,円や多角形などの図が描けて,中を塗ることもできる。多角形は頂点のリストを与えればよいので,両方を組み合わせれば円が重なった部分が描ける。
 描いたあとは,いずれも,スライダで色を変えることができる。

matplotlib で作る

 matplotlib では,プロットデータを作っておいて,多角形として描く。プロットデータとは,曲線を描くときにとる,曲線上の点の座標データのことだ。弧なので,円の一部として,媒介変数表示を使えばよい。
たとえば,RとGの重なりのところは次のようにする。

#Gの円の原点と半径
orgx = 2
orgy = 2
r = 1
th = np.pi/3
rcenter = [orgx+r/2, orgy+r/2*np.sqrt(3)]
gcenter = [orgx,orgy]
bcenter = [orgx+r, orgy]
rgdata = makepoly(gcenter, th, 2*th,
                 rcenter, 3*th, 4*th,
                 bcenter, 3*th, 2*th
                )
rg = patches.Polygon(rgdata, fc='#FFFF00')
ax.add_patch(rg)
plt.show()

makepoly() は3つの弧のプロットデータをつなげるユーザー定義関数で,さらに下請けに1つの弧のプロットデータを作る関数 makearc() がある。

# 弧のプロットデータを作る 中心座標,開始角,終了角
def makearc(c,s,e):
   d = (e - s) / 50
   ret = []
   for i in range(51):
       p = [c[0] + np.cos(s + i*d),c[1] + np.sin(s + i*d)]
       ret.append(p)
   return ret
   
# 3つの弧を結ぶ
def makepoly(c1,s1,e1,c2,s2,e2,c3,s3,e3):
   ret1 = makearc(c1,s1,e1)
   ret2 = makearc(c2,s2,e2)
   ret3 = makearc(c3,s3,e3)
   return ret1 + ret2 + ret3

画像5

同様にして,他の部分も描けばよい。
できあがったのがこれ。

画像1

 まあ,いいのだが,16進法でコードを表示するのがどうもうまくいかない。文字は,text コマンド(関数)で表示するのだが,これをあとから変えることができない。

 やっているうちに,はじめに考えたTkinter でのやり方がそれほど面倒ではないことがわかった。発想を変えて,扇形を組み合わせればよい。

Tkinter で作る

 Tkinter には,円弧を描くメソッドがあり,デフォルトで扇形に塗られる。これを利用する。
(1) Rの円を描く
(2) Gの円を描く。あとから描いたものが前面にくる
(3) Bの円を描く
(4) RとGの共通部分を,2つの扇形を使って描く
(5) GとBの共通部分を,2つの扇形を使って描く
(6) BとRの共通部分を,2つの扇形を使って描く
(7) RGBの共通部分を,3つの扇形を使って描く

たとえば,(4) は次の2つを重ねて使う。

画像6

あとで書いた図が上に来るのでこの順で書いていけばよい。R,G,Bの円とこの扇形を描いたところまでを示そう。

import numpy as np
import tkinter as tk
root = tk.Tk()
root.title("RGBカラー")
root.geometry("400x450")
canvas = tk.Canvas(root, width=400, height=300, bg='white') 
ox = 100
oy = 120
r = 80
th = np.pi/3
rapex = [ox+r/2, oy-r/2*np.sqrt(3), ox+5*r/2, oy-r/2*np.sqrt(3)+2*r]
gapex = [ox, oy, ox+2*r, oy+2*r]
bapex = [ox+r, oy, ox+3*r, oy+2*r]
canvas.create_oval(
      rapex[0], rapex[1], rapex[2], rapex[3], 
      width=0, 
      fill='#FF0000', 
      tag='red'
  )
canvas.create_oval(
      gapex[0], gapex[1], gapex[2], gapex[3], 
      width=0, 
      fill='#00FF00', 
      tag='green'
  )
canvas.create_oval(
      bapex[0], bapex[1], bapex[2], bapex[3], 
      width=0, 
      fill='#0000FF', 
      tag='blue'
  )
#RG
canvas.create_arc(
      rapex[0], rapex[1], rapex[2], rapex[3], 
      start=180, extent=120, 
      width=0, 
      outline='#FFFF00', 
      fill='#FFFF00', 
      tag='rg1'
  )
canvas.create_arc(
      gapex[0], gapex[1], gapex[2], gapex[3], 
      start=0, extent=120, 
      width=0, 
      outline='#FFFF00', 
      fill='#FFFF00', 
      tag='rg2'
  )
canvas.pack()
root.mainloop()

この調子で全部で12の図を描くことになる。同じような処理なので,コピーアンドペーストで書き換えればいいのだが,それでも結構な手間だ。

スライダの設定は同じようなもの。描かれている図形の属性:色を変える関数をかいて,スライダを動かしたら呼び出すようにする。Rのスライダを作って,赤を帰られるようにしたのが次のコード。

rgbcode=[255, 255, 255]
#リスト rgbcode=[R, G, B] を文字列 RGB に変換 
def rgb2code(r, g, b):
   ret = '#' + format(r, '02X')
   ret = ret + format(g, '02X')
   ret = ret + format(b, '02X')
   return ret
# 色を変える 
def changecolor():
   r = rgbcode[0]
   g = rgbcode[1]
   b = rgbcode[2]
   color = rgb2code(r, 0, 0)
   canvas.itemconfig("red", outline=color, fill=color)
   color = rgb2code(0, g, 0)
   canvas.itemconfig("green", outline=color, fill=color)
   color = rgb2code(0, 0, b)
   canvas.itemconfig("blue", outline=color, fill=color)
   color = rgb2code(r, g, 0)
   canvas.itemconfig("rg1", outline=color, fill=color)
   color = rgb2code(r, g, 0)
   canvas.itemconfig("rg2", outline=color, fill=color)
# スライダの戻り値を rgbcode に渡して fillcolor に渡す 
def setr(n):
   rgbcode[0] = int(n)
   changecolor()
   
# スライダを作る
scr = tk.Scale(from_=0, to=255, 
              variable=tk.IntVar(value=255), 
              command=setr, 
              length=200, 
              orient='horizontal'
             ) 
label_r = tk.Label(text='Red')
# ラベルとスライダを配置 
label_r.place(x=30, y=320) 
scr.place(x=100, y=310) 

これに,GとBのスライダを作って追加する。

RGBコードの16進法表示は次のようにする。

info = tk.Label(text='#FFFFFF', font=("", 24), bg='white')
info.place(x=160, y=10) 

スライダで得た値に変えるために,changecolor() に次の行を追加する。

    info.config(text=color)

かくして,次のものができる。

さて,できたはいいが,役に立つかどうかは別の話。
情報の授業で使うのだが,プログラミングの教材とするには難しい。まあ,生徒のプログラミング学習用には使えなくても,教員用としては,「弧でかこまれた図形をどう描くか」ということのアイデアとして参考にはなるだろう。