RealBasic University

このコラムはStone Table Softwareのオーナーであり、またREALbasic Developerの編集者でもあるMarc Zeedar氏により書かれたものを、著者の許可を得て翻訳したものです。この翻訳はHREM Researchにより提供されています。この日本語版へのご意見はRBU-Jまでご連絡下さい。

URL: http://www.applelinks.com/rbu/053/
INDEXに戻る

Zoomer: パート II

先週はREALbasicのDrawIntoコマンドをテストするための簡単な実験としてZoomerを始めました。その目的は、1つのウィンドウの内部を拡大して、別ウィンドウに表示する仮想拡大鏡を作成することです。

私たちはプロジェクトの基礎的な部分を設定して、このプロジェクトで重要なグローバル座標系とローカル座標系の違いについて学びました。今回は、ズームされたウィンドウを表示する実際のコードを作成し、Zoomerを完成させましょう。

drawZoomメソッド

前回はdrawZoomメソッドを作成しましたが、内容を空欄にしておきました。さてこれからいくらかのコードを入力していきましょう!

まず始めにすることは、zoomWindowが領域のズームされた部分より大きくならないように、そのサイズを調整することです。なぜそうするのでしょうか?そうしないとzoomWindowは実際にあるよりもより大きな画像を表示しようとします。例えば、ズームされた画像は200ピクセル幅ですが、ウィンドウ幅は300ピクセルだとしましょう。するとdrawPictureコマンドは、画像でzoomWindow全体を満たそうとします。画像は十分に大きくないので、メモリー上のランダム・データが画像として描かれることになります。それは実際には有害ではありませんが、次のように変に見えます。

これを防ぐ方法はいくつかありますが、私は簡単な方法でズームされた領域よりzoomWindowが大きくならないようにしました。ズームされた領域はzoomWindowの中で拡大されるので、zoomWindowの最大サイズはzoomSliderによって設定された倍率に基づいて変化します。(zoomWindowのmaxWidth属性とmaxHeight属性によって最大サイズを設定します。)

drawZoomのコードをここに示します。

   dim p as picture 
dim x1, y1, zW, zH as integer

// zoomWindowサイズを調節する
zoomWindow.maxWidth = editField1.width * zoomAmount
zoomWindow.maxHeight = editField1.height * zoomAmount
if zoomWindow.width > zoomWindow.maxWidth then
zoomWindow.width = zoomWindow.maxWidth
end if
if zoomWindow.height > zoomWindow.maxHeight then
zoomWindow.height = zoomWindow.maxHeight
end if

zW = zoomWindow.width * zoomAmount
zH = zoomWindow.height * zoomAmount

// ズームされた画像へのスペースを割り当てる
p = newPicture(zW, zH, 16)
// 本当はここでpがnilであるかそうでないか確かめる必要が
// ありますが、私が不精なのでしていません。
// pに割り当てるためのメモリが十分にあるとしましょう。

// これはウィンドウを画像オブジェクトへ描画します。
window1.drawInto p.graphics, 0, 0

// この部分はカーソルのポジションに基づいて、
// これから描画していくeditFieldの部分を計算します。
x1 = x - editField1.left
if x1 < 1 then
x1 = editField1.left
elseif x1 + zoomWindow.width / zoomAmount > editField1.width then
x1 = editField1.width - zoomWindow.width / zoomAmount + editField1.left
else
x1 = x
end if
y1 = y - editField1.top
if y1 < 1 then
y1 = editField1.top
elseif y1 + zoomWindow.height / zoomAmount > editField1.height then
y1 = editField1.height - zoomWindow.height / zoomAmount + editField1.top
else
y1 = y
end if

// これは実際にズームされた画像を描画します。
// zWとzHは拡大された画像サイズを表していることに
// 注意しましょう。
zoomWindow.graphics.drawPicture p, 0, 0, zW, zH, x1, y1, zoomWindow.width, zoomWindow.height

必要に応じてzoomWindowのサイズを調節した後、画像オブジェクトpへのスペースを割り当てます。pのサイズはzoomWindowのサイズをzoomAmount倍したものです。16ビットと32ビットの画像はほとんど同じように見えるので、メモリー節約のために16ビット画像(newPictureの16がそれです)で保存します。

いったん画像オブジェクトを取り入れたら、window1pのグラフィクス・オブジェクトへ描画するためにdrawIntoコマンドを使います。drawIntoコマンドを理解するいちばん簡単な方法は、ウィンドウのスクリーンショットを撮るようなものだと考えればいいでしょう。私たちの場合では、そのスクリーンショットをpへ保存しています。その後、pを大きなサイズにする代わりにzoomWindowへ描画します。またdrawIntoコマンドを使うとき、描画するための座標を(0,0)に設定していることに注意しましょう。それはwindow1の左上端がpの左上端に描画されることを意味しています。

Zoomerの場合、 その目的はwindow1自体ではなく、ただeditField1を拡大して描画することです。それは拡大されたeditFieldのみを表示するので、zoomWindowの"視界"を制限しなければならないことを意味します。

それをするには2つの方法があります。1つ目は、editField1のサイズに関連してpのサイズを決めることです。2つ目は、どこから描画し始めるかを制御することによって、pのどの部分がzoomWindowへ描画されるかを調整します。

これを理解するには、まずdrawPictureメソッドがどのように働くのか理解する必要があります。ここにREALbasicのオンラインヘルプがあります。

始めの3つのパラメータは簡単です。それは画像オブジェクトとそのオブジェクトをどこから描き始めるかを指定する2つの座標です。その後のパラメータはオプション的なものです。([角括弧]にはさまれたものはオプションだと覚えておいて下さい。)drawPictureのいちばん簡単な使用方法は、drawPicture p, 0, 0とすることでしょう。しかし今回は、画像のサイズを操作したいので、分かりにくい補助パラメータを使わなければなりません。したがって、次にそれが何を意味しているのか正確に把握していきましょう。

最初の2つの補助パラメータは、コピー先の幅と高さのパラメータです。つまり簡単に言うと、画像はこの幅と高さに合わせてサイズ変更されます。destWidthまたはdestHeightが、画像の実際の幅と高さより小さい値ならば、その画像は縮小されて描画されます。destWidthまたはdestHeightが画像の実際の幅と高さより大きい値ならば、画像は拡大されるでしょう。現在の画像サイズと同じ値を入力すれば、拡大、縮小されることなくそのままのサイズで描画されます。

ここでこの2つのパラメータをwidth/height * percentのような形式の百分率だと見なしてください。よって次のようなコマンド、

 drawPicture p, 0, 0, p.width * 50%, p.height * 50% 

pを50%のサイズで描画するでしょう。同様に、

 drawPicture p, 0, 0, p.width * 150%, p.height * 150% 

pを150%のサイズで描画するでしょう。

もちろん、実際にREALbasicは百分率を直接は理解しません。だから50%は.5、150%は1.5といった小数で表す必要があります。それからさらに、drawPictureコマンドは行の後ろにパラメータを足さないと働かないので、これをどのように使うのか知っておくのも大事です。

次のsourceXsourceYパラメータは、最初はイメージをつかむのは少し難しいです。それはどこから描画し始めるかについての始点座標を示しています。もしそれが(0,0)ならば、pの左上端から描画し始めることになります。ゼロよりも大きな値ならば、画像の左部と上部を切り取って、画像の中央寄りから描画し始めます。

また、描画したいpの幅と高さを指定するsourceWidthsourceHeightの説明なしで、sourceXsourceYを理解するのは難しいでしょう。これらのパラメータは、抜き取るpの一部分がどれくらいの幅と高さであるか示しています。この4つのパラメータで、pの全体、あるいはpの一部の長方形部分を抜き出すことができて、それをどんなサイズ(拡大、縮小、同サイズ)にでも描画できます。

すべてのpを抜き出すためには、sourceXパラメータとsourceYパラメータとして(0,0)を指定し、 sourceWidthパラメータとsourceHeightパラメータにはp.widthp.heightを指定します。

一部分のpを抜き出すためには、抜き出したい長方形領域の左部と上部の座標と、その長方形の幅と高さも指定します。図で見ると容易に理解できます。

これはZoomerで行っているそのものを示しています。薄い背景画はp全体です。しかし、zoomWindowpよりも小さいので、pの一部分だけを描画しています。いま見ているzoomWindowの左上端の座標は、sourceXパラメータとsourceYパラメータとして渡した値です。またそのpの一部分の幅と高さは、zoomWindowの幅と高さです。

この左上端の座標を計算するには、簡単な数学が必要です。長方形の部分はユーザのカーソルがどこにあるかに基づいて抜き出されていることを覚えておいて下さい。新しい左上端の座標はx1y1によって表されます。実際のユーザのマウスの位置はxyです。

前回のレッスンでもやったように、これはローカルウィンドウ座標です。そのため、これをeditField1の座標に変換したいですね。これはxyからeditField1lefttopを引くことによって得られます。

 x1 = x - editField1.left 
y1 = y - editField1.top

上式はeditField1に対して変換された座標を与えます。もしここで止めても、Zoomerは動くでしょう。それは実際に試してください。一時的にif-thenのところを以下のようにコメント・アウトして、プログラムを実行してみましょう。

   // ここでカーソルの位置に基づいて、描画しようとしている
// editFieldの場所を計算します。
x1 = x - editField1.left
'if x1 < 1 then
'x1 = editField1.left
'elseif x1 + zoomWindow.width / zoomAmount > editField1.width then
'x1 = editField1.width - zoomWindow.width / zoomAmount + editField1.left
'else
'x1 = x
'end if
y1 = y - editField1.top
'if y1 < 1 then
'y1 = editField1.top
'elseif y1 + zoomWindow.height / zoomAmount > editField1.height then
'y1 = editField1.height - zoomWindow.height / zoomAmount + editField1.top
'else
'y1 = y
'end if
 

こうすると、描画する部分はeditField1に制限されなくなります。 Zoomerはウィンドウ全体をズームし、さらにメモリのランダム・データまでもが表示されます。

もちろんこれは害にはならないし、興味ある現象ではありますが、望む画像ではありませんね。私たちはただeditField1をズームしたいだけなので、editField1の範囲内に収まるように、x1y1を調整する必要があります。だから、複雑に見えるif-thenが行っているのは、x1editField1.leftあるいはeditField1.widthより小さくならないようにすることです!

コードのelseifの部分は次のようになります。

 elseif x1 + zoomWindow.width / zoomAmount > editField1.width then 

これはただ拡大されていない実際の幅に戻した値を与えています。zoomWindowの幅はズームされた幅を表していることを覚えておいて下さい。それをeditField1.widthと比べるとしたら、りんごとオレンジを比べるようなものでしょう。倍率で割ることによってそれを調整して実際のサイズに値を戻した後、2つの変換量の実際の幅を比べています。これはeditField1の右側にきたものをzoomWindowに確実に表示させないようにします。

(ここでは幅についてのみ説明していますが、高さについても幅の値の代わりに高さの値を使うだけでまったく同様です。)

これらをすべて行った結果は、window1におけるeditField1の位置の範囲内のx1y1の値です。この公式で、x1はあまりにも左や右に寄り過ぎたりせず、同様にy1も高かったり、低くなり過ぎたりしません。

コードの最後の部分は、"シンプル"な drawPictureコマンドです。今までに私たちは全ての計算を済ませました。今度はその画像の一部分を描画するだけです。pzoomWindowの左上端である(0,0)の位置から描画し始め、ズームされた幅と高さの値を示すzWzHを幅と高さのパラメータとして渡します。x1y1について行ってきたすべての作業は、その座標から描画し始めるためにdrawPictureに与えられます。そして最後に、描画するpの部分の幅と高さをzoomWindowの幅と高さに設定します(zoomWindowはズームされた図で全体が満たされることを意味します)。

  zoomWindow.graphics.drawPicture p, 0, 0, zW, zH, x1, y1, zoomWindow.width, zoomWindow.height 

理解できましたか?それほど難しくはないでしょう。このような似た形のパラメータを扱うのは少し混乱しますが、REALbasic 4は本当に役立ちますよ。コード・エディタ(Code Editor)のdrawPictureテキストをクリックすると、コマンドとそのパラメータの概略を示してくれるウィンドウが表示されます。

一度どのようにコマンドが働くかを理解してしまえば、このヒントは完全に記述してあるオンラインヘルプをスクロールしてみなくとも、記憶を呼び覚ますのには十分です。

ふうっ!盛り沢山の内容でしたが、なんとか終わりました。この簡単なデモはとくに役に立つわけではありませんが、レッスンから有用なテクニックを学んだことと思います。こちらは使用中のZoomerのアニメーションです。

今週のREALbasicチュートリアルの完全なプロジェクト・ファイル(リソースを含む)を入手したい場合は、ここからダウンロードしてください。

次週

いわゆる、ペイント・プログラムを作ります。

Letters

最初のメールはWilliamさんからで、REALbasicについてのとても一般的な質問です。

まず始めに、私の分数についての質問に、以前のコラムでアドバイスをいただきどうもありがとうございました。そこでの提案は私のプロジェクトの方向付けを与えてくれました。しかし、私のプロジェクトが大きくなるにつれて、新たな問題が出てきました。

グローバル変数のようなものは存在するでしょうか?私は整数値としてdを設定し、いろいろなウィンドウやボタンでdの数値を変化させたいと思っています。私はdを整数としてグローバルに設定はできるのですが、その値はグローバルではありません。私は不可視のスタティック・テキストを用いて、すべてのウィンドウとボタンにそれをdとして設定するという解決策を見つけました。しかし、もっと容易な方法があるべきだと思います。私は何か見落としているのでしょうか?

モジュールに単純に属性(変数)を加えてください。モジュールは属性、定数やメソッドの集まりです。それはファイル・メニューの"New Module"を選択することによって得られます。モジュールに加えたものはすべて、プログラム全体でグローバルになります。

たとえウィンドウに属性を付け加えたとしても、それはそのウィンドウの内部で作用し、グローバルではありません。しかし、モジュールは内部ではなく、あなたのプロジェクト全体に作用するので、その意味でグローバルです。(理解はできますが、REAL Sofware社はもっと詳しく説明する必要がありますね。)

ところで、オブジェクトの参照によってウィンドウの属性を利用できますよ。例えば、isDoneと呼ばれるブール属性をもつWindow1があるとしましょう。別のウィンドウやルーチンはWindow1.isDoneとすることによって、isDone(これがグローバルでないにもかかわらず)を参照することができます。時によっては、グローバルを使うよりは、こちらの方法のほうがより役に立つでしょう。(グローバルはプログラム実行中のすべての時にメモリーを消費します。)

次に、Squidさんという名前の男性からお便りいただきました。断っておきますが、それは彼のメールの中で書いてあったものです。(私がつけたのではないと誓います!)〔Squidはイカの意味です〕

こんにちは!

とてもすばらしいコラムで、いい仕事ですね。私が買った"初心者"向けに書かれた本より役に立ちます!ところで、私はプログラムを書いていますが、あるウィンドウでボタンが押されると、別のウィンドウを開くようにする方法が分かりません。とても初歩的(really basic 駄洒落ではないです)なことで、もう既に以前のコラムで答えられているかもしれませんが、私は最近になってRBUを見つけたので、まだレッスン11までしか進んでいません。

こんにちはSquidさん!

やりたいことは簡単ですよ。ボタンのアクション・イベント(Action event)で、開きたいウィンドウの名前を入力し、ピリオドを加えて、Showメソッドとします。こんな感じになります。

 window2.show 

またこんなこともできますよ。

 window2.showModal 

これは、もし2つ目のウィンドウがダイアログで文書ウィンドウでない時に、これは2つ目のウィンドウをモーダル・ダイアログ(プログラムの他のすべてのアクションは中断されます)として開きます。

これについて詳しく知りたい場合は、ダイアログ・ボックスを扱っているいくつかのRBUプログラムに目を通してみてください。


RBU-Jの通知サービス!コラムが発表されるたびに日本語版REALbasic Universityのお知らせの emailがあなたに届きます。登録・削除は ここ から。

INDEXに戻る