例題に学ぶJythonの世界
先に示したコードの断片は「例題のための例題」であって、それ自身が付加価値を与えるものではありません。そこで、Jythonで広がるJavaプログラミングの可能性を模索するために、小さな付加価値を与える事例として「サイクロイド」を紹介します。
サイクロイドとは
サイクロイドとは、円を任意の曲線に沿って転がしたとき、その円周上の定点が描く軌跡です。ここでは、最も簡単な事例として、円を直線に沿って転がします。ボタン[animate]を押すと、円周上(青)の定点(黄)が描く軌跡(赤)が描かれます。
おかみさん、数学の時間ですよ
実際の開発では、要求仕様に記述された内容を理解するのに、(クライアントにとっては自明である)特定の専門知識が必要になることがあります。例えば今回の場合、サイクロイドに関する数学の基礎知識が必要です。そこで、数学の基礎を簡単に復習しておきます。ただし、数学が苦手という皆さんは、ざっと目を通すか、思い切って読み飛ばし、次節に進んでください。それでもかまわない理由は、後で紹介します。
円を直線に沿って転がすとき、その円周上の点Pが軌跡OPを描くものとします。このとき、X軸と円との接点Qに対して、直線OQと弧PQとの長さが等しくなります。そこで、少しずつ移動する点Pの座標が求まるなら、これらの点を短い直線で連結することで、連続するサイクロイド曲線が描けます。ここで、円の中心からX軸に下ろした垂線と動径rとのなす角度をθします。
x座標を得るには、点Pから下ろした垂線とX軸との交点Pxを利用します。このとき、2つの線分の長さはOQ = rθおよびPxQ = r sinθとなるので、OPx = OQ-PxQ = rθ-r sinθとなります。
y座標を得るには、点Pから下ろした垂線とY軸との交点Pyおよび、円の中心点Cから下ろした垂線とY軸との交点Cyを利用します。このとき、2つの線分の長さはOCy = rおよびCyPy = -r cosθとなるので、OPy = OCy+ CyPy = r-r cosθとなります。
アニメーションとは
アニメーションの原体験は、パラパラ漫画でした。教科書の余白に鉛筆で描いた悪戯書きが、パラパラめくるだけで動いて見える様は、授業中に見つからないかとのスリルも手伝って、感動モノでした。映画では、コマ撮りした連続写真を1秒間に24枚という速度で再生して、実際に動いているように見せます。アニメーションの語源は、ラテン語の霊魂とも言われ、動かないものに命を与えて動かす様を表現しています。
サイクロイドを描くには
サイクロイドを描くときには、円周上の定点が描く軌跡をアニメーションを使って再現します。ここでは、その準備として、簡単な図形を動かします。今後の連載では、コードの詳細にも触れますが、今はその概要だけを把握してください。
from time import * x, y, w, h = 10, 10, 40, 40 shape = Rectangle2D.Double(x, y, w, h) width = 3 def paint(g): CanvasPanel.paint(panel, g) g.color = Color.yellow g.fill(shape) stroke = BasicStroke(width) g.color = Color.blue g.fill(stroke.createStrokedShape(shape)) panel = CanvasPanel() panel.paint = paint times, seconds = 100, 0.02 def animate(e): for i in range(times): sleep(seconds) shape.setFrame(x+i, y, w, h) bounds = shape.bounds bounds.grow(width, width) panel.paintImmediately(bounds) button = JPanel(layout=GridLayout(1, 0)) button.add(JButton("animate", actionPerformed=animate)) frame = JFrame(defaultCloseOperation=JFrame.EXIT_ON_CLOSE, size=(160, 110), title="Rectangle & Rectangle2D", layout=BorderLayout()) frame.add(panel, BorderLayout.CENTER) frame.add(button, BorderLayout.SOUTH) frame.visible = True
関数animate
は、ボタンを押したときに、panel
内に図形を描く方法を示します。
組み込み関数sleep(seconds)
は、指定した秒数seconds
だけ実行を遅延します。それには、モジュールtime
をimport
しておく必要があります。
Javaで記述された関数void paintImmediately(Rectangle r)
は、指定した領域r
内をすぐに再描画します。ここで、paint
を利用したのでは、移動が終了した後に図形が描かれるので、アニメーション効果が得られません。
図形を描くときには、その外枠の幅だけ領域を拡げる必要があります。そのため、Javaで記述された関数Rectangle getBounds()
によって獲得した矩形領域を、Javaで記述された関数void grow(int h,int v)
を使って対象領域を拡げます。
このコードを実行すると、ウィンドウが現われます。ボタン [animate] を押すと、0.02 秒ごとに位置が異なる図形が描かれます。すると、あたかも図形が移動しているように見えます。再びボタンを押すと、2度目以降は、新たな図形が現われてから移動するのが分かります。
次回予告
前編では、Jythonによって広がる新しいプログラミングの可能性の一端として、サイクロイド曲線をアニメーションで描画するサンプルプログラムを紹介しました。後編では、このプログラムの具体的な解説を進めつつ、今後の連載の概観を示します。