Casio Python - 関数の作成と活用

Casioグラフ関数電卓の Python を使ってみる
- 関数の作成と活用:grp_color()関数の作成
<目次>
初版:2020/07/06
追記修正:2020/07/10
追記修正:2020/10/20
追記修正:2020/11/03
追記修正:2020/07/10
追記修正:2020/10/20
追記修正:2020/11/03
▲ 前の記事 - 4. Casio Python への移植 | ▼ 次の記事 - 6. グラフィックス出力関数の追加
5. 関数の作成と活用:grp_color()関数の作成 <fx-CG50 OS3.40以降>
前回作成した circle() 関数は以下のようになっています。
◆ circle() の書式
circle(x, y, r)
x: 整数型 (int型)、円中心の x 座標
y: 整数型 (int型)、円中心の y 座標
r: 整数型 (int型)、円の半径
◆ circle() の関数定義:
from casioplot import *
from math import *
#draw circle
def circle(x, y, r):
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x + r*cos(i*a)
if px<0 or px>383:
continue
py = y + r*sin(i*a)
from math import *
#draw circle
def circle(x, y, r):
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x + r*cos(i*a)
if px<0 or px>383:
continue
py = y + r*sin(i*a)
if py<0 or py>191:
continue
set_pixel(int(px), int(py))
show_screen()
continue
set_pixel(int(px), int(py))
show_screen()
この関数を少し拡張しようと思います。
点を描画する set_pixel() は、第一引数と第二引数でそれぞれX座標とy座標を指定しますが、第三引数で点の色を指定できるので、circle() 関数でも色を指定できるように拡張できます。
set_pixel() は、第一引数と第二引数を設定しなければエラーになりますが、第三引数は省略可能で、省略時はデフォルトで色が黒に指定されます。第三引数を指定した時だけ色を指定できる仕様です。そこで circle() 関数でも同様に、第三引数を省略可能で、かつ色指定できるように拡張してみます。
5.1 関数の引数について <fx-CG50, fx-9750GIII, fx-9860GIII OS3.40以降>
関数 func() を定義するには、def ステートメントを使います。
def func():
行末にコロン : が必要です。コロンの下の行からインデントを入れて関数の中身を記述します。インデントレベルが同じ範囲以上が、関数定義の範囲になります。
さて、引数1、引数2を追加するには、以下のように書きます。
def func(param1, param2):
sum = param1 + param2
print(sum)
第一引数と第二引数を加算した結果 sum を表示する関数ができました。
この関数を使う時は、例えば以下のようにします。
func(1,2)
これが実行されると、1 + 2 の結果を表示します。
1つめの引数が param1 に、2つめの引数が param2 に自動的に割り振られます。このように引数の位置で割り振りされる引数を "位置引数 (Positional Arguments)" と言います。
次に、省略可能な第三引数 param3 を追加するには、次のように記述します。
def func(param1, param2, param3=0):
sum = param1 + param2 + param3
print(sum)
param3 = 0 と記述すると、
param3 を省略するとデフォルトで 0 になるという意味で、省略した場合は、param3 に 0 が割り振られます。
スクリプトで
func(1, 2)
と書けば、1 + 2 + 0 の結果 3 が表示されます。
func(1, 2, 3)
とすれは、計算結果の 6 が表示されます。
このような param3 を "デフォルト引数 (Default Arguments)" と言います。
デフォルト引数は、位置引数の後に書く必要があります。
全ての引数をデフォルト引数にしてみます。全てにデフォルト値を設定する必要があります。
def func(param1=1, param2=2, param3=3):
sum = param1 + param2 + param3
print(sum)
このようにすると、全ての引数を省略できます。例えば、
func()
とすると、全てデフォルトの値を使うので、計算結果 6 が表示されます。
それでは、
func(param2=5)
とスクリプトで書くと、1 + 5 + 3 の結果 9 が表示されます。
このように、引数をキーワードとして使って、その値を同時に指定して記述することもできます。この記述方法では、記述位置を無視して記述でき、そのような引数を "キーワード引数 (Keyword Arguments)" と言います。
次のような関数定義をしてみます。
def func(param1, param2, param3=3, param4=4):
sum = param1 + param2 + param3 + param4
print(sum)
第一引数と第二引数は位置引数、第三引数と第四引数はデフォルト値のある引数です。
func(1, 2)
は、1 + 2 + 3 + 4 の結果 10 が表示されます。
func(1, 2, 5)
は、第4引数が省略されデフォルト値を使うので、1 + 2 + 5 + 4 の結果 12 が表示されます。
では、第三引数を標略して、第四引数に値を設定したい場合は、第四引数をキーワード引数にすればうまくゆきます。
func(1, 2, param4=10)
とすると、1 + 2+ 3 + 10 の結果 16 が表示されます。
func(param4=10, 1, 2)
とすると、エラーになります。キーワード引数は、位置引数の後に記述する必要があるからです。
Python の関数は、引数を柔軟に使えて便利だとお分かりだと思います。
5.2 関数内の変数の有効範囲 - 変数のスコープ <fx-CG50, fx-9750GIII, fx-9860GIII OS3.4以降>
以下のスクリプトを実行してみます。
sum = 100
def func(param1, param2):
sum = param1 + param2
print(sum)
func(1, 2)
関数定義の前(=外)に変数 sum があり、100 が代入されています。
関数定義内にも同じ名前の変数 sum があります。
このスクリプトの最後で実行される func() の結果は、3 となり、103 ではないことに留意してください。
関数定義の中の sum は、その有効範囲が関数内に限定され、関数の外の sum とは別物になります。func() が実行される時は、関数の外の sum の値の影響を受けないのです。メモリ上には、関数内の sum と間数外の sum の2つが存在します。より詳しく言えば、関数が実行されている間だけ、関数内の sum がメモリ上に作られ、関数の実行が終われば、関数内の sum は消滅します。
変数の有効範囲を スコープ (scope) と言います。関数内の変数のスコープは関数内に限定されるわけです。
5.3 circle()関数の拡張 <fx-CG50 OS3.40以降専用>
以下のように、デフォルト値を設定して、第4引数と第5引数を追加します。
第4引数には、set_pixel() に渡すRGBカラー設定をデフォルト色黒 (=(0, 0, 0)) で追加します。
第5引数には、VRAMから画面への転送を行うかどうかを 1 か 0 あるいは True か False で設定し、デフォルトは 1 (転送する) とします。
◆ 拡張 circle() の関数定義:
5.4 色指定関数を追加 <fx-CG50 OS3.40以降専用>
set_pixel() の色指定は、(r, g, b) というタプル型のデータで RGB値を使います。
5.4.1 RGBによる色指定
一般に RGB による色指定は、R = 0 ~ 255の整数、G = 0 ~ 255の整数、B = 0 ~ 255の整数 とそれぞれ 8bit、併せて 24 bit で色を決める方法で、この方法で指定する色を 24bit カラーと言います。
一方、fx-CG50 搭載の液晶は 24 bitカラーには対応しておらず、16 bit カラーで色が表現されます。
具体的には、R と B は 0 ~ 248 までの8の倍数 (それぞれ 4bit)、G が 0 ~252 までの4の倍数 (8bit)、併せて 16 bit カラーになっています。fx-CG50 の液晶は、24bitカラーで指定してもエラーになりませんが、16bitカラーに変換され、24bit 指定による細かい色調の違いを表現できません。たとえは、白は、(255, 255, 255) と指定しても実際は (248, 255, 248) と同じになります。
但し、PCでは24bit カラーが一般的なので、分かり易さから 24 bitカラーを使って色指定しても良いと思います。繰り返しますが、実際は 16bit カラーに変換されて表示されることは、頭にいれておくと良いと思います。
[2020/11/03 追記]
RGBによる色指定について、詳しくは ここ を参照してください。
5.4.2 タプル型
デフォルト引数の color=(0, 0, 0) は、変数 color をタプル (tuple) 型として初期化しています。タプル型はリスト型と似ている配列変数の一種です。リスト型は カギ括弧を使って [0, 0, 0] と定義しますが、タプル型はマル括弧を使います。リスト型は各要素を変更することが前提です。一方、タプル型は各要素を変更しないことが前提で、要素の変更を行おうとするとエラーになる、そのようなデータ型です。
5.4.3 色指定方法の拡張
fx-CG50 の Casio Basic では、RGB による色指定はできず、以下の7色設定に限られています。
・black
・blue
・red
・magenta
・green
・cyan
・yellow (チョット見えずらいです)
そこで、上記の文字列での色指定も可能にしたいと思います。さらに、上の7色に加えて白の色指定 (whilte) も追加する必要があります。なぜなら、前回作ったスクリプトで、テキストを頻繁に書き換えて表示するためには、一旦背景と同じ white で上書きして一旦消去した後、新たな文字列を表示する必要があったことを思い出してください。
その上で、whilte を加えて8色について、0 ~ 7 までの整数でのカラーコードで色指定を可能にすると、さらに便利で使いやすくなると思うので、整数での色指定方法も可能にしようと思います。
・カラーコード=0:'white'
・カラーコード=1:'black'
・カラーコード=2:'blue'
・カラーコード=3:'red'
・カラーコード=4:'magenta'
・カラーコード=5:'green'
・カラーコード=6:'cyan'
・カラーコード=7:'yellow'
タプル型のRGB、文字列型、そして整数型のカラーコードの3通りのいずれかを引数に指定すると、タプル型のRGB値を返す関数を作っておくと、circle() 以外にも、今後グラフィックス画面での描画関数を作る時に利用でき、この3通りの色指定ができて便利になりますね。
5.4.4 グラフィック画面での色指定関数 grp_color() 関数の作成
これから作る関数の枠組みは以下のようになります。
def grp_color(color=1):
必要な処理
return rgb
ここで、return ステートメントで返す rgb 変数はタプル型の rgb値とします。
この関数の引数は1つだけですが、3通りのデータ型が指定されるのが仕様なので、先ず最初に引数として渡される 変数 color のデータ型を判定する必要があります。判定した型に応じて、タプル型の rgb に変換し、それを返すようにすれば良いわけです。
def grp_color(color=1):
の引数 color=1 は、デフォルトで変数 color に整数型の 1 となることを示しています。言い換えれば引数を省略すれば color=1 となることを示しています。
▶ データ型の判定
データ型の判定を行う type() 関数が使えます。
Note: type()
そこで、grp_color() に渡される引数 color の型を調べて、タプル型、文字列型、整数型に応じて、それらをタプル型 (変数 c_rgb) に変換します。
def grp_color(color=1):
if type(color) is int:
c_rgb = (必要な式1)
elif type(color) is str:
c_rgb = (必要な式2)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (color[0], color[1], color[2])
return c_rgb
▶ 最初の if 文での処理
これは、整数のカラーコードを指定した時の処理です。カラーコードが代入されている color 変数について、0 から 7 までの8通りの場合分けを if 文で処理すれば良いですね。但し今回は、この処理をシンプルに記述するために、リストを使ってみます。詳しく言えば、タプル型の rgb 設定を要素に持つリストを作って、それを使います。このリストの要素は、インデックス 0 (リストの1つめの要素) が 白、インデックス 1 (リストの2つめの要素) が黒、・・・と、上で決めた色になるように、左から右へタプル型のデータを並べます。
rgb = [(255,255,255), (0,0,0), (0,0,255), (255,0,0), (255,0,255), (0,255,0), (0,255,255), (255,255,0)]
Note: リストの定義
Note: タプルの定義
繰り返しますが、ここで定義したリスト rgb は、3つの整数が要素になっているタプル型データで構成されています。このタプル型データは、rgb の色指定になっています。1つめの要素 rgb[0] は (255,255,255) つまり、白 (white) にしています。以下同様にして、インデックスに整数のカラーコードを指定すると、その色の rgb値(タプル型) になるように、要素を並べています。
・rgb[0] = (255,255,255) #white
・rgb[1] = (0,0,0) #black
・rgb[2] = (0,0,255) #blue
・rgb[3] = (255,0,0) #red
・rgb[4] = (255,0,255) #magenta
・rgb[5] = (0,255,0) #green
・rgb[6] = (0,255,255) #cyan
・rgb[7] = (255,255,0) #yellow
これを使った、最初の if 文での処理を具体化します。
def grp_color(color=1):
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
if type(color) is int:
c_egb = rgb[color] #ここまで完成
elif type(color) is str:
c_rgb = (必要な式2)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (color[0], color[1], color[2])
return c_rgb
このように、たった1行で記述できました。if文で color の値に応じて8通りの場合分けを記述するよりも遙かにシンプルになりました。Python のシーケンス(いわゆる配列です)、ここではリストですが、定義もシンプルで使うのも簡単で、なかなか便利なものですね。
▶ 1つめの elif での処理
ここでは、引数が文字列型 (str型) の時の処理で、変数 color は文字列です。ここでは文字列から対応するタプル型の rgb 値を得る処理を書きます。
if 文で処理しても良いのですが、ここでもリストを使えば、格段に簡潔に記述できます。具体的には、以下のような文字列リストを作って利用します。
c_name = ['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
文字列リスト c_name の各要素が、色を指定する文字列になっています。
文字列リスト c_name の要素のうち、変数 color に格納された文字列が、リストの何番目の要素なのか、つまりその要素のインデックス番号が分かれば、そのインデックス番号を使って rgb[インデックス番号] として、目的の rgb値が得られますね!
ここでは、そのような目的で使える .index() 関数(リスト型オプジェクトの帰属関数、あるいはメソッドとも言います) を使います。この関数はリスト型オブジェクトの帰属関数なので、リストオブジェクトとコンマ . で繋いで記述します。つまり、c_name.index(color) と記述します。
.index() 関数は、引数に要素を指定すると、その要素のインデックス(整数) を返しますので、引数に文字列変数である color を指定しています。
繰り返しになりますが、color変数(文字列変数) に目的の色を示す文字列が入っているので、
c_name.index(color)
は、color で指定された色の文字列のインデックスを返します。
従って、rgbリストにこのインデックスを指定すると、その要素が目的のタプル型 rgb です。
c_rgb = rgb[インデックス]
は、上で使った方法ですね!
つまり、
c_rgb = rgb[c_name.index(color)]
と書けば、1行で目的の変換ができました!
リストを活用することで、if 文なしで、とても簡潔に記述できました!
ここまでのコードを具体化します。
def grp_color(color=1):
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
c_name=['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
if type(color) is int:
c_rgb = rgb[color]
elif type(color) is str:
c_rgb = rgb[c_name.index(color)]
elif type(color) is tuple: #2つめの elif 節
c_rgb = color
else: #else節
c_rgb = (0, 0, 0)
return c_rgb
ここでは、スクリプトを一気に完成させました。2つめの elif 節と else 節の説明が後になりましたが、以下を確認してください。
▶ 2つめの elif での処理
これは、タプル型のrgb値の変数 color が渡されたときの処理なので、color をそのまま c_rgb に代入すれば良いですね!
▶ 最後の else での処理
color 変数が、int型、str型、tuple型のいずれでもないとき、else 節でデフォルトの黒、(0,0,0) を c_rgb に指定します。これはエラーでスクリプトの実行が強制中断されないための安全策です。例えば、整数でなく浮動小数点が color に渡される時は、例えば今作っている grp_color() の戻り値がそのまま set_pixel() に渡されるとエラーになります。そこで、else 節では、想定外の値、例えば浮動小数点が color に入っている時は、デフォルトの黒にして、エラーの発生を抑制する作戦です。このエラー抑制のための処理は、無くても大きな問題ではないと思いますが、スクリプトの動作が強制終了されるよりも、止まらないことを優先させたいと管理人は思うので、その好みを反映させました。
これで、基本機能の実装が終わりました。続いて想定される範囲でよく発生しそうなエラーを抑制するための処理を追加してゆきます。
▶ color に整数のカラーコードを指定する時、0 ~ 7 までの整数を前提にしていますが、これ以外の整数が指定されるとエラーになります。そこで、color が 0から7までの整数の場合に上の本来の処理を行い、そうでない場合は、デフォルトとして黒を指定するようにします。これは、上記のエラー抑制処理と同じ理由 (管理人の好み) に基づいています。
c_rgb = rgb[color]
を以下のように書き換えます。
if 0<=color<=7:
c_rgb = rgb[color]
else:
c_rgb = (0,0,0)
Note: Python の比較演算子
▶ color に文字列の色指定をする時は、あらかじめ決めている文字列以外が渡されるとエラーになります。そこで、予め決められた文字列の時は上の本来の処理を行い、そうでない場合は、黒を指定するようにします。
c_rgb = rgb[c_name.index(color)]
を以下のように書き換えます。
if color in c_name:
c_rgb = rgb[c_name.index(color)]
else:
c_rgb = (0,0,0)
Note: 帰属検査の in
以上で、grp_color() 関数を完成とします。コメントも追加して、完成したスクリプトを示します。
def grp_color(color=1):
"""Convert colorparameter to rgb in tuple
version 1.0
parameters:
--------------
color: code, name, rgb
0 'white' (255,255,255)
1 'black' (0,0,0)
2 'blue' (0,0,255)
3 'red' (255,0,0)
4 'magenta' (255,0,255)
5 'green' (0,255,0)
6 'cyan' (0,255,255)
7 'yellow' (255,255,0)
default:1 'black' (0,0,0)
Returns:
---------
tuple:(rgb)
"""
#set rgb from parameter (color)
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
c_name=['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
if type(color) is int:
if 0<=color<=7:
c_rgb = rgb[color]
else:
c_rgb = (0,0,0)
elif type(color) is str:
if color in c_name:
c_rgb = rgb[c_name.index(color)]
else:
c_rgb = (0,0,0)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (0, 0, 0)
return c_rgb
5.5 circle() 関数拡張の書き換え <fx-CG50 OS3.40以降専用>
これまで作成した grp_color() 関数を使って、circle() 関数の拡張版をさらに書き換えます。
◆ 書き換えた拡張 circle() の関数定義:
circle(x, y, r) とすると、色はデフォルトの黒、VRAMから画面転送(デフォルト設定)を行います。
▶ 色指定をしたい場合は、第三引数に3通りの方法、1) カラーコード、2) 色の文字列、3) タプル型のrgb指定、で色を指定可能。
▶ VRAMから画面転送を抑制したいときは、第四引数に 0 あるいは show=0 (第三引数を指定しない場合) と記述できます。
5.6 モンテカルロ法(2) - monteca2.py <fx-CG50 OS3.40以降専用>
最後に、grp_color() と 拡張 circle() を使って、モンテカルロ法(1) の出力をカラー化してみます。
円を赤で、点を青で描画するように変更しました。以下は変更した部分です;
circle(dx, dy, r, 'red') #red
set_pixel(int(dx+r*x), int(dy+r*y), grp_color('blue')) #blue
draw_string(36, 0, str(prev_outs), grp_color(0)) #white
draw_string(36, 16, str(prev_pai), grp_color(0)) #white
draw_string(0, 176, 'AC:Quit', grp_color(0)) #white
このように、タプル型のRGBで色指定するよりも、カラーコードや文字列で指定する方が、コーデイングが楽で分かりやすくなると思います。これを実現したくて、今回 grp_color() を作り、circle() を拡張しました。
詳しくは、monteca2.py をダウンロードして確認してください。
点を描画する set_pixel() は、第一引数と第二引数でそれぞれX座標とy座標を指定しますが、第三引数で点の色を指定できるので、circle() 関数でも色を指定できるように拡張できます。
set_pixel() は、第一引数と第二引数を設定しなければエラーになりますが、第三引数は省略可能で、省略時はデフォルトで色が黒に指定されます。第三引数を指定した時だけ色を指定できる仕様です。そこで circle() 関数でも同様に、第三引数を省略可能で、かつ色指定できるように拡張してみます。
5.1 関数の引数について <fx-CG50, fx-9750GIII, fx-9860GIII OS3.40以降>
関数 func() を定義するには、def ステートメントを使います。
def func():
行末にコロン : が必要です。コロンの下の行からインデントを入れて関数の中身を記述します。インデントレベルが同じ範囲以上が、関数定義の範囲になります。
さて、引数1、引数2を追加するには、以下のように書きます。
def func(param1, param2):
sum = param1 + param2
print(sum)
第一引数と第二引数を加算した結果 sum を表示する関数ができました。
この関数を使う時は、例えば以下のようにします。
func(1,2)
これが実行されると、1 + 2 の結果を表示します。
1つめの引数が param1 に、2つめの引数が param2 に自動的に割り振られます。このように引数の位置で割り振りされる引数を "位置引数 (Positional Arguments)" と言います。
次に、省略可能な第三引数 param3 を追加するには、次のように記述します。
def func(param1, param2, param3=0):
sum = param1 + param2 + param3
print(sum)
param3 = 0 と記述すると、
param3 を省略するとデフォルトで 0 になるという意味で、省略した場合は、param3 に 0 が割り振られます。
スクリプトで
func(1, 2)
と書けば、1 + 2 + 0 の結果 3 が表示されます。
func(1, 2, 3)
とすれは、計算結果の 6 が表示されます。
このような param3 を "デフォルト引数 (Default Arguments)" と言います。
デフォルト引数は、位置引数の後に書く必要があります。
全ての引数をデフォルト引数にしてみます。全てにデフォルト値を設定する必要があります。
def func(param1=1, param2=2, param3=3):
sum = param1 + param2 + param3
print(sum)
このようにすると、全ての引数を省略できます。例えば、
func()
とすると、全てデフォルトの値を使うので、計算結果 6 が表示されます。
それでは、
func(param2=5)
とスクリプトで書くと、1 + 5 + 3 の結果 9 が表示されます。
このように、引数をキーワードとして使って、その値を同時に指定して記述することもできます。この記述方法では、記述位置を無視して記述でき、そのような引数を "キーワード引数 (Keyword Arguments)" と言います。
次のような関数定義をしてみます。
def func(param1, param2, param3=3, param4=4):
sum = param1 + param2 + param3 + param4
print(sum)
第一引数と第二引数は位置引数、第三引数と第四引数はデフォルト値のある引数です。
func(1, 2)
は、1 + 2 + 3 + 4 の結果 10 が表示されます。
func(1, 2, 5)
は、第4引数が省略されデフォルト値を使うので、1 + 2 + 5 + 4 の結果 12 が表示されます。
では、第三引数を標略して、第四引数に値を設定したい場合は、第四引数をキーワード引数にすればうまくゆきます。
func(1, 2, param4=10)
とすると、1 + 2+ 3 + 10 の結果 16 が表示されます。
func(param4=10, 1, 2)
とすると、エラーになります。キーワード引数は、位置引数の後に記述する必要があるからです。
Python の関数は、引数を柔軟に使えて便利だとお分かりだと思います。
5.2 関数内の変数の有効範囲 - 変数のスコープ <fx-CG50, fx-9750GIII, fx-9860GIII OS3.4以降>
以下のスクリプトを実行してみます。
sum = 100
def func(param1, param2):
sum = param1 + param2
print(sum)
func(1, 2)
関数定義の前(=外)に変数 sum があり、100 が代入されています。
関数定義内にも同じ名前の変数 sum があります。
このスクリプトの最後で実行される func() の結果は、3 となり、103 ではないことに留意してください。
関数定義の中の sum は、その有効範囲が関数内に限定され、関数の外の sum とは別物になります。func() が実行される時は、関数の外の sum の値の影響を受けないのです。メモリ上には、関数内の sum と間数外の sum の2つが存在します。より詳しく言えば、関数が実行されている間だけ、関数内の sum がメモリ上に作られ、関数の実行が終われば、関数内の sum は消滅します。
変数の有効範囲を スコープ (scope) と言います。関数内の変数のスコープは関数内に限定されるわけです。
5.3 circle()関数の拡張 <fx-CG50 OS3.40以降専用>
以下のように、デフォルト値を設定して、第4引数と第5引数を追加します。
第4引数には、set_pixel() に渡すRGBカラー設定をデフォルト色黒 (=(0, 0, 0)) で追加します。
第5引数には、VRAMから画面への転送を行うかどうかを 1 か 0 あるいは True か False で設定し、デフォルトは 1 (転送する) とします。
◆ 拡張 circle() の関数定義:
from casioplot import *
from math import *
#draw circle
def circle(x, y, r, color=(0, 0, 0), show=1): #ここを変更
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x + r*cos(i*a)
if px<0 or px>383:
continue
py = y + r*sin(i*a)
from math import *
#draw circle
def circle(x, y, r, color=(0, 0, 0), show=1): #ここを変更
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x + r*cos(i*a)
if px<0 or px>383:
continue
py = y + r*sin(i*a)
if py<0 or py>191:
continue
set_pixel(int(px), int(py), color)
if show: #これを追加
show_screen()
continue
set_pixel(int(px), int(py), color)
if show: #これを追加
show_screen()
5.4 色指定関数を追加 <fx-CG50 OS3.40以降専用>
set_pixel() の色指定は、(r, g, b) というタプル型のデータで RGB値を使います。
5.4.1 RGBによる色指定
一般に RGB による色指定は、R = 0 ~ 255の整数、G = 0 ~ 255の整数、B = 0 ~ 255の整数 とそれぞれ 8bit、併せて 24 bit で色を決める方法で、この方法で指定する色を 24bit カラーと言います。
一方、fx-CG50 搭載の液晶は 24 bitカラーには対応しておらず、16 bit カラーで色が表現されます。
具体的には、R と B は 0 ~ 248 までの8の倍数 (それぞれ 4bit)、G が 0 ~252 までの4の倍数 (8bit)、併せて 16 bit カラーになっています。fx-CG50 の液晶は、24bitカラーで指定してもエラーになりませんが、16bitカラーに変換され、24bit 指定による細かい色調の違いを表現できません。たとえは、白は、(255, 255, 255) と指定しても実際は (248, 255, 248) と同じになります。
但し、PCでは24bit カラーが一般的なので、分かり易さから 24 bitカラーを使って色指定しても良いと思います。繰り返しますが、実際は 16bit カラーに変換されて表示されることは、頭にいれておくと良いと思います。
[2020/11/03 追記]
RGBによる色指定について、詳しくは ここ を参照してください。
5.4.2 タプル型
デフォルト引数の color=(0, 0, 0) は、変数 color をタプル (tuple) 型として初期化しています。タプル型はリスト型と似ている配列変数の一種です。リスト型は カギ括弧を使って [0, 0, 0] と定義しますが、タプル型はマル括弧を使います。リスト型は各要素を変更することが前提です。一方、タプル型は各要素を変更しないことが前提で、要素の変更を行おうとするとエラーになる、そのようなデータ型です。
5.4.3 色指定方法の拡張
fx-CG50 の Casio Basic では、RGB による色指定はできず、以下の7色設定に限られています。
・black
・blue
・red
・magenta
・green
・cyan
・yellow (チョット見えずらいです)
そこで、上記の文字列での色指定も可能にしたいと思います。さらに、上の7色に加えて白の色指定 (whilte) も追加する必要があります。なぜなら、前回作ったスクリプトで、テキストを頻繁に書き換えて表示するためには、一旦背景と同じ white で上書きして一旦消去した後、新たな文字列を表示する必要があったことを思い出してください。
その上で、whilte を加えて8色について、0 ~ 7 までの整数でのカラーコードで色指定を可能にすると、さらに便利で使いやすくなると思うので、整数での色指定方法も可能にしようと思います。
・カラーコード=0:'white'
・カラーコード=1:'black'
・カラーコード=2:'blue'
・カラーコード=3:'red'
・カラーコード=4:'magenta'
・カラーコード=5:'green'
・カラーコード=6:'cyan'
・カラーコード=7:'yellow'
タプル型のRGB、文字列型、そして整数型のカラーコードの3通りのいずれかを引数に指定すると、タプル型のRGB値を返す関数を作っておくと、circle() 以外にも、今後グラフィックス画面での描画関数を作る時に利用でき、この3通りの色指定ができて便利になりますね。
5.4.4 グラフィック画面での色指定関数 grp_color() 関数の作成
これから作る関数の枠組みは以下のようになります。
def grp_color(color=1):
必要な処理
return rgb
ここで、return ステートメントで返す rgb 変数はタプル型の rgb値とします。
この関数の引数は1つだけですが、3通りのデータ型が指定されるのが仕様なので、先ず最初に引数として渡される 変数 color のデータ型を判定する必要があります。判定した型に応じて、タプル型の rgb に変換し、それを返すようにすれば良いわけです。
def grp_color(color=1):
の引数 color=1 は、デフォルトで変数 color に整数型の 1 となることを示しています。言い換えれば引数を省略すれば color=1 となることを示しています。
▶ データ型の判定
データ型の判定を行う type() 関数が使えます。
Note: type()
オブジェクトを渡すとオブジェクトの型を返します。
そこで、grp_color() に渡される引数 color の型を調べて、タプル型、文字列型、整数型に応じて、それらをタプル型 (変数 c_rgb) に変換します。
def grp_color(color=1):
if type(color) is int:
c_rgb = (必要な式1)
elif type(color) is str:
c_rgb = (必要な式2)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (color[0], color[1], color[2])
return c_rgb
▶ 最初の if 文での処理
これは、整数のカラーコードを指定した時の処理です。カラーコードが代入されている color 変数について、0 から 7 までの8通りの場合分けを if 文で処理すれば良いですね。但し今回は、この処理をシンプルに記述するために、リストを使ってみます。詳しく言えば、タプル型の rgb 設定を要素に持つリストを作って、それを使います。このリストの要素は、インデックス 0 (リストの1つめの要素) が 白、インデックス 1 (リストの2つめの要素) が黒、・・・と、上で決めた色になるように、左から右へタプル型のデータを並べます。
rgb = [(255,255,255), (0,0,0), (0,0,255), (255,0,0), (255,0,255), (0,255,0), (0,255,255), (255,255,0)]
Note: リストの定義
要素を , で区切り、[ ] で囲むと、リストになります。要素を呼び出すには、0 から始まるインデックス(整数) で指定します。
Note: タプルの定義
要素を , で句切り、( ) で囲むと、タプルになります。要素を呼び出すには、0 から始まるインデックス(整数) で指定します。
繰り返しますが、ここで定義したリスト rgb は、3つの整数が要素になっているタプル型データで構成されています。このタプル型データは、rgb の色指定になっています。1つめの要素 rgb[0] は (255,255,255) つまり、白 (white) にしています。以下同様にして、インデックスに整数のカラーコードを指定すると、その色の rgb値(タプル型) になるように、要素を並べています。
・rgb[0] = (255,255,255) #white
・rgb[1] = (0,0,0) #black
・rgb[2] = (0,0,255) #blue
・rgb[3] = (255,0,0) #red
・rgb[4] = (255,0,255) #magenta
・rgb[5] = (0,255,0) #green
・rgb[6] = (0,255,255) #cyan
・rgb[7] = (255,255,0) #yellow
これを使った、最初の if 文での処理を具体化します。
def grp_color(color=1):
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
if type(color) is int:
c_egb = rgb[color] #ここまで完成
elif type(color) is str:
c_rgb = (必要な式2)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (color[0], color[1], color[2])
return c_rgb
このように、たった1行で記述できました。if文で color の値に応じて8通りの場合分けを記述するよりも遙かにシンプルになりました。Python のシーケンス(いわゆる配列です)、ここではリストですが、定義もシンプルで使うのも簡単で、なかなか便利なものですね。
▶ 1つめの elif での処理
ここでは、引数が文字列型 (str型) の時の処理で、変数 color は文字列です。ここでは文字列から対応するタプル型の rgb 値を得る処理を書きます。
if 文で処理しても良いのですが、ここでもリストを使えば、格段に簡潔に記述できます。具体的には、以下のような文字列リストを作って利用します。
c_name = ['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
文字列リスト c_name の各要素が、色を指定する文字列になっています。
文字列リスト c_name の要素のうち、変数 color に格納された文字列が、リストの何番目の要素なのか、つまりその要素のインデックス番号が分かれば、そのインデックス番号を使って rgb[インデックス番号] として、目的の rgb値が得られますね!
ここでは、そのような目的で使える .index() 関数(リスト型オプジェクトの帰属関数、あるいはメソッドとも言います) を使います。この関数はリスト型オブジェクトの帰属関数なので、リストオブジェクトとコンマ . で繋いで記述します。つまり、c_name.index(color) と記述します。
.index() 関数は、引数に要素を指定すると、その要素のインデックス(整数) を返しますので、引数に文字列変数である color を指定しています。
繰り返しになりますが、color変数(文字列変数) に目的の色を示す文字列が入っているので、
c_name.index(color)
は、color で指定された色の文字列のインデックスを返します。
従って、rgbリストにこのインデックスを指定すると、その要素が目的のタプル型 rgb です。
c_rgb = rgb[インデックス]
は、上で使った方法ですね!
つまり、
c_rgb = rgb[c_name.index(color)]
と書けば、1行で目的の変換ができました!
リストを活用することで、if 文なしで、とても簡潔に記述できました!
ここまでのコードを具体化します。
def grp_color(color=1):
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
c_name=['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
if type(color) is int:
c_rgb = rgb[color]
elif type(color) is str:
c_rgb = rgb[c_name.index(color)]
elif type(color) is tuple: #2つめの elif 節
c_rgb = color
else: #else節
c_rgb = (0, 0, 0)
return c_rgb
ここでは、スクリプトを一気に完成させました。2つめの elif 節と else 節の説明が後になりましたが、以下を確認してください。
▶ 2つめの elif での処理
これは、タプル型のrgb値の変数 color が渡されたときの処理なので、color をそのまま c_rgb に代入すれば良いですね!
▶ 最後の else での処理
color 変数が、int型、str型、tuple型のいずれでもないとき、else 節でデフォルトの黒、(0,0,0) を c_rgb に指定します。これはエラーでスクリプトの実行が強制中断されないための安全策です。例えば、整数でなく浮動小数点が color に渡される時は、例えば今作っている grp_color() の戻り値がそのまま set_pixel() に渡されるとエラーになります。そこで、else 節では、想定外の値、例えば浮動小数点が color に入っている時は、デフォルトの黒にして、エラーの発生を抑制する作戦です。このエラー抑制のための処理は、無くても大きな問題ではないと思いますが、スクリプトの動作が強制終了されるよりも、止まらないことを優先させたいと管理人は思うので、その好みを反映させました。
これで、基本機能の実装が終わりました。続いて想定される範囲でよく発生しそうなエラーを抑制するための処理を追加してゆきます。
▶ color に整数のカラーコードを指定する時、0 ~ 7 までの整数を前提にしていますが、これ以外の整数が指定されるとエラーになります。そこで、color が 0から7までの整数の場合に上の本来の処理を行い、そうでない場合は、デフォルトとして黒を指定するようにします。これは、上記のエラー抑制処理と同じ理由 (管理人の好み) に基づいています。
c_rgb = rgb[color]
を以下のように書き換えます。
if 0<=color<=7:
c_rgb = rgb[color]
else:
c_rgb = (0,0,0)
Note: Python の比較演算子
color>=0 and color<=7 としても良いですが、0<=color<=7 とできるのは、Python の便利なところです。
▶ color に文字列の色指定をする時は、あらかじめ決めている文字列以外が渡されるとエラーになります。そこで、予め決められた文字列の時は上の本来の処理を行い、そうでない場合は、黒を指定するようにします。
c_rgb = rgb[c_name.index(color)]
を以下のように書き換えます。
if color in c_name:
c_rgb = rgb[c_name.index(color)]
else:
c_rgb = (0,0,0)
Note: 帰属検査の in
ここでは、帰属(所属)検査のステートメント in を使っています。c_name は文字列リストなので、このリストに文字列 color が含まれているかどうかが in で検査できます。このあたりは、python の分かり易さ、使いやすさの良い例だと思います。
以上で、grp_color() 関数を完成とします。コメントも追加して、完成したスクリプトを示します。
def grp_color(color=1):
"""Convert colorparameter to rgb in tuple
version 1.0
parameters:
--------------
color: code, name, rgb
0 'white' (255,255,255)
1 'black' (0,0,0)
2 'blue' (0,0,255)
3 'red' (255,0,0)
4 'magenta' (255,0,255)
5 'green' (0,255,0)
6 'cyan' (0,255,255)
7 'yellow' (255,255,0)
default:1 'black' (0,0,0)
Returns:
---------
tuple:(rgb)
"""
#set rgb from parameter (color)
rgb=[(255,255,255),(0,0,0),(0,0,255),(255,0,0),(255,0,255),(0,255,0),(0,255,255),(255,255,0)]
c_name=['white', 'black', 'blue', 'red', 'magenta', 'green', 'cyan', 'yellow']
if type(color) is int:
if 0<=color<=7:
c_rgb = rgb[color]
else:
c_rgb = (0,0,0)
elif type(color) is str:
if color in c_name:
c_rgb = rgb[c_name.index(color)]
else:
c_rgb = (0,0,0)
elif type(color) is tuple:
c_rgb = color
else:
c_rgb = (0, 0, 0)
return c_rgb
5.5 circle() 関数拡張の書き換え <fx-CG50 OS3.40以降専用>
これまで作成した grp_color() 関数を使って、circle() 関数の拡張版をさらに書き換えます。
◆ 書き換えた拡張 circle() の関数定義:
def circle(x, y, r, color=1, show=1): #第4引数のデフォルト値を変更
rgb = grp_color(color) #これを追加
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x+r*cos(i*a)
if px<0 or px>383:
continue
py = y+r*sin(i*a)
rgb = grp_color(color) #これを追加
p = r*6.283
a = 2*pi/p
for i in range(p):
px = x+r*cos(i*a)
if px<0 or px>383:
continue
py = y+r*sin(i*a)
if py<0 or py>191:
continue
set_pixel(int(px), int(py), rgb) #第3引数を color から rgb に変更
if show:
show_screen()
continue
set_pixel(int(px), int(py), rgb) #第3引数を color から rgb に変更
if show:
show_screen()
circle(x, y, r) とすると、色はデフォルトの黒、VRAMから画面転送(デフォルト設定)を行います。
▶ 色指定をしたい場合は、第三引数に3通りの方法、1) カラーコード、2) 色の文字列、3) タプル型のrgb指定、で色を指定可能。
▶ VRAMから画面転送を抑制したいときは、第四引数に 0 あるいは show=0 (第三引数を指定しない場合) と記述できます。
5.6 モンテカルロ法(2) - monteca2.py <fx-CG50 OS3.40以降専用>
最後に、grp_color() と 拡張 circle() を使って、モンテカルロ法(1) の出力をカラー化してみます。
円を赤で、点を青で描画するように変更しました。以下は変更した部分です;
circle(dx, dy, r, 'red') #red
set_pixel(int(dx+r*x), int(dy+r*y), grp_color('blue')) #blue
draw_string(36, 0, str(prev_outs), grp_color(0)) #white
draw_string(36, 16, str(prev_pai), grp_color(0)) #white
draw_string(0, 176, 'AC:Quit', grp_color(0)) #white
このように、タプル型のRGBで色指定するよりも、カラーコードや文字列で指定する方が、コーデイングが楽で分かりやすくなると思います。これを実現したくて、今回 grp_color() を作り、circle() を拡張しました。
詳しくは、monteca2.py をダウンロードして確認してください。

▶ 目 次
▲ 前の記事 - 4. Casio Python への移植
▼ 次の記事 - 6. グラフィックス出力関数の追加
応援クリックをお願いします。励みになるので...