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

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

しかも,スライダを使って,インタラクティブにR,G,Bの値を変えられるとよい。この場合は,R,B,Gそれぞれの値の変化と色の変化を感覚的に捉えることができる。
スライダの作り方については先日調べた。まず Tkinter で作り方を調べ,次にmatplotlib を使った。(Tkinter の記事はない。マニュアルにはいれてある)
問題は,円が重なった部分をどう描くかだ。
matplotlib と Tkinter では図の描き方が異なる。はじめは Tkinter で発想したがちょっと面倒かと思って,matplotlib で作成に取り掛かる。
何が違うのかを説明しておこう。
Tkinter には,円(楕円),弧(扇形,弓形),多角形を描く関数がある。2つの円が重なった部分,3つの円が重なった部分については,弓型と三角形を組み合わせれば描けるだろう。

これが当初の予想。何が「ちょっと面倒」かというと,弧を描くのに,円の一部として描くのだが,その円は正方形に内接する円として描く。中心と半径ではないのだ。そこが面倒に思えたのだ。実際には発想を変えて扇形を組み合わせて描いた。
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
同様にして,他の部分も描けばよい。
できあがったのがこれ。

まあ,いいのだが,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つを重ねて使う。

あとで書いた図が上に来るのでこの順で書いていけばよい。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)かくして,次のものができる。
さて,できたはいいが,役に立つかどうかは別の話。
情報の授業で使うのだが,プログラミングの教材とするには難しい。まあ,生徒のプログラミング学習用には使えなくても,教員用としては,「弧でかこまれた図形をどう描くか」ということのアイデアとして参考にはなるだろう。
