うしブログ

うしブログ

趣味で運営する、GeoGebraの専門ブログ。

(作業メモ)StartPoint要検証(2行の場合;テキスト変更時未定義問題)

(要修復)ToggleButton・RollPolygonWithoutSlipping・貯金時計・直感力トレーニング

多角形の回転

課題

今回の課題は、Yahoo!知恵袋に投稿されている質問(URL:

http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13147636978

投稿者:kakah_denka)を題材に、Geogebraアプレット作成に挑戦します。

 

・自由な5点A,B,C,D,Eを頂点とする5角形を作成する(poly1)。

・点Aを固定する。

・点A以外のpoly1の頂点をドラッグすると、点Aを中心にpoly1が回転するようにする。

 

※通常の設定では、上記3番目の指示にある「点A以外のpoly1の頂点をドラッグ」を行うと、poly1の形が変わってしまいます。そこで、点A以外のpoly1の頂点をドラッグしても、poly1の形を変えずに回転移動させる方法を考える必要があります。

 

 

検討

poly1の形状を変えないという指定なので、i=B,C,D,Eとすると、点iには、「Distance[A,i]が一定である」という制約が課されることになります。

したがってまずは、Distance[A,i]=r_iとして、点iをPoint[Circle[A, r_i]]と定義することから始まります。

→点A以外のpoly1の頂点をドラッグすると、r_i自体が変わってしまい、形状を維持することができませんでした。どうやら、poly1に代わって、それと合同であり形状が固定された、回転用の5角形を用意する必要があるようです。

 

そこで、poly1はすべての頂点について、ドラッグすると自由に形状が変わるようにしておきます。そして、ボタンを押すと現在のpoly1と同じ位置に、poly1と合同な5角形poly2を表示し(同時にpoly1は非表示にする)、poly2を回転用の5角形とします。

poly1は形状を指定するための5角形、poly2はpoly1と合同な形状を維持したまま回転移動できる5角形、と使い分けるわけです。

 

完成したアプレット

 

初期画面では、回転用の5角形poly2が表示されています。点A以外の頂点をドラッグすると、点Aを中心に5角形が回転移動します。

左上の編集ボタンを押すと、形状変更用の5角形poly1(青色)が表示され、頂点をドラッグすることで自由に形状を変更できます。

さらに左上の確定ボタンを押すと、形状変更後のpoly2が表示され、また回転移動できるようになります。

 

作り方の解説

簡単なアプレットのようで、実はけっこう複雑です。順に作り方を説明します。

 

①poly1の作成

自由な5点A,B,C,D,Eを作成する。

poly1を、定義poly1=Polygon[A,B,C,D,E]として作成する。

 

②poly2の頂点となる5点を作成

A_1を、定義A_1=Aとして作成する。

 

rBを、定義rB=Distance[A, B]として作成する。

rCを、定義rC=Distance[A, C]として作成する。

rDを、定義rD=Distance[A, D]として作成する。

rEを、定義rE=Distance[A, E]として作成する。

 

B_1を、定義B_1=Point[Circle[A, rB]]として作成する。

C_1を、定義B_1=Point[Circle[A, rC]]として作成する。

D_1を、定義B_1=Point[Circle[A, rD]]として作成する。

E_1を、定義B_1=Point[Circle[A, rE]]として作成する。

 

※A_1, B_1, C_1, D_1, E_1は、poly2の頂点となります。

すなわち、poly2を、定義poly2=Polygon[A_1,B_1,C_1,D_1,E_1]として作成します。

 

※i=B,C,D,Eとして、点i_1に対してかかっている制約は、この時点では、「点Aとの距離がriである」ということだけです。のちほど③で、「点i_1は、点iと同一座標とする」という制約を課します。

 

③poly1の形を決める→ボタンを押す→poly1と同位置・合同なpoly2を表示する

switchを、定義switch=0として作成する。

poly1およびその頂点・線分の表示条件を、「switch==0」とする。

poly2およびその頂点・線分の表示条件を、「switch==1」とする。

 

ボタンbutton1を作成し、On Click スクリプトとして、以下を記述する。

SetCoords[B_1,x(B),y(B)]
SetCoords[C_1,x(C),y(C)]
SetCoords[D_1,x(D),y(D)]
SetCoords[E_1,x(E),y(E)]
SetValue[switch,1]

※これは、 i=B,C,D,Eとして、button1を押すことによって、点i_1の座標を点iと同じ座標に変更するものです。さらに、switchを1にすることで、poly1およびその頂点・線分を非表示にし、かつpoly2およびその頂点・線分を表示するものです。

 

button1の表示条件を、「switch==0」とする。

 

④poly2の点A以外の頂点をドラッグすると、残る点が点Aを中心に回転移動するようにする

点B_1のOn Update スクリプトを、以下のように記述する。

If[switch==1, SetValue[C_1,Rotate[C,Angle[B,A,B_1],A]]]
If[switch==1, SetValue[D_1,Rotate[D,Angle[B,A,B_1],A]]]
If[switch==1, SetValue[E_1,Rotate[E,Angle[B,A,B_1],A]]]

Angle[B,A,B_1]は、点B_1がドラッグによって、点Bからどれだけズレたかを、点Aを中心とする回転移動の角度として表したものです。上記スクリプトは、点B_1がドラッグされることと連動して、点A_1以外の残ったpoly2の頂点(C_1, D_1, E_1)を、もとの点からAngle[B,A,B_1]だけ、中心Aで回転移動させるものです。これによって、B_1をドラッグしても、残りの点が連動するため、poly2の形状を維持することができます。C_1, D_1, E_1をドラッグした場合の、他の点の連動も、同様に設定します(下記参照)。

 

点C_1のOn Update スクリプトを、以下のように記述する。

If[switch==1, SetValue[B_1,Rotate[B,Angle[C,A,C_1],A]]]
If[switch==1, SetValue[D_1,Rotate[D,Angle[C,A,C_1],A]]]
If[switch==1, SetValue[E_1,Rotate[E,Angle[C,A,C_1],A]]]

 

点D_1のOn Update スクリプトを、以下のように記述する。

If[switch==1, SetValue[B_1,Rotate[B,Angle[D,A,D_1],A]]]

If[switch==1, SetValue[C_1,Rotate[C,Angle[D,A,D_1],A]]]

If[switch==1, SetValue[E_1,Rotate[E,Angle[D,A,D_1],A]]]

 

点E_1のOn Update スクリプトを、以下のように記述する。

If[switch==1, SetValue[B_1,Rotate[B,Angle[E,A,E_1],A]]]

If[switch==1, SetValue[C_1,Rotate[C,Angle[E,A,E_1],A]]]
If[switch==1, SetValue[D_1,Rotate[D,Angle[E,A,E_1],A]]]

 

⑤poly2を回転移動する→ボタンを押す→poly1を、現在のpoly2と同位置・合同になるように表示する

ボタンbutton2を作成し、On Click スクリプトとして、以下を記述する。

SetValue[switch,0]
SetCoords[B,x(B_1),y(B_1)]
SetCoords[C,x(C_1),y(C_1)]
SetCoords[D,x(D_1),y(D_1)]
SetCoords[E,x(E_1),y(E_1)]

button2の表示条件を、「switch==1」とする。

 

※これは、button2をクリックすることで、switchを0に変更し、poly1のA以外の頂点の座標を、回転移動後のpoly2の対応する頂点と同じ座標に変更するものです。これによって、poly2の現在位置とピッタリ同じ位置に、形状変更が可能なpoly1を表示することができます。スクリプトの2〜4行目を記述しないと、button2を押したとき、poly1は回転移動前の位置に表示されるため、操作の連続性に欠けてしまいます。

 

(注意)予期せぬOn Updateスクリプトの実行を回避せよ!

※ここで非常に重要なのは、SetValue[switch,0]」を、必ずスクリプトの先頭行(1行目)に記述することです。もし以下のように、SetValue[switch,0]」を末尾に記述すると、何が起こるでしょうか。

SetCoords[B,x(B_1),y(B_1)]
SetCoords[C,x(C_1),y(C_1)]
SetCoords[D,x(D_1),y(D_1)]
SetCoords[E,x(E_1),y(E_1)]

SetValue[switch,0]

button2はswitch=1で表示されるボタンなので、1〜4行目のSetCoordsコマンドは、当然のことながらswitch=1のもとで実行されます。すると、以下のことが起こります。

 

・button2をクリック

・switch=1のもとで、Bの座標がB_1の座標と同じものに更新される(button2のOn Click スクリプトの1行目)。・・・

・switch=1のもとで、Bの座標を定義に含んでいるrBが更新される(②のrBの定義を参照)。

・switch=1のもとで、rBを定義に含んでいるB_1の座標が更新される(②のB_1の定義を参照)。

※button2のクリック前後で、B_1の座標自体は変わりませんが、B_1の定義に使われているrBの値が再入力されるため、B_1の座標は更新されたものと扱われます。

・switch=1もとでB_1の座標が更新されたので、B_1のOn Updateスクリプトが実行される(④を参照)。

※B_1のOn Updateスクリプトは、以下のような内容でしたね。

If[switch==1, SetValue[C_1,Rotate[C,Angle[B,A,B_1],A]]]
If[switch==1, SetValue[D_1,Rotate[D,Angle[B,A,B_1],A]]]
If[switch==1, SetValue[E_1,Rotate[E,Angle[B,A,B_1],A]]]

命令はswitch==1を条件に実行されるようになっているので、ここでは当然実行されます。

ところで、現在 Angle[B,A,B_1]=0です。なぜなら、上記ののところで、Bの座標とB_1の座標は一致しているからです。そのため、B_1のOn Updateスクリプトは、「C_1をCに移動し、D_1をDに移動し、E_1をEに移動せよ」という命令となります。C, D, Eは、回転移動前の点ですので、この命令は、「回転移動した点C_1, D_1, E_1を、回転移動前の位置に戻せ」という意味になります。・・・★★

・button2のOn Click スクリプトの2〜4行目は、実質的に無意味となります。なぜなら、上記★★において既に、CとC_1, DとD_1, EとE_1はそれぞれ、座標が一致しているからです。その座標は、ドラッグによる回転移動前の座標です。

・switchが0に変更される(button2のOn Click スクリプトの5行目)。

(おわり)

 

結局button2を押すと、点Bの座標だけが回転移動後の点B_1の座標と同じものに更新され、点C, D, Eは回転移動前のまま、という結果になります(下図参照)。

f:id:usiblog:20160613073600p:plain

5角形(poly2)を半時計周りに回転移動した(回転の軌跡を残像表示している)。

 

f:id:usiblog:20160613073701p:plain

その後button2を押すと、poly1が表示された(青い多角形)。点Bは回転移動後の点に移っているが、点C, D, Eは回転移動前の点のままだ。

 

SetValue[switch,0]」をスクリプトの先頭行(1行目)に記述することで、上記★★のところで、B_1のOn Update スクリプトが実行されるのを回避することができます。なぜなら、button2のOn Click スクリプトの1行目で、既にswitch=0となっているので、その後点B, rB, B_1と更新されていっても、B_1のOn Update スクリプトの実行条件であるswitch==1が満たされることがないからです。

したがって、④でB_1, C_1, D_1, E_1のOn Update スクリプトに、逐一If[switch==1,・]を記述すること、およびbutton2のOn Click スクリプトの1行目にSetValue[switch,0]を記述することが、非常に重要です。

また、アプレットの動作が思い通りにいかないときは、予期しないところでOn Update スクリプトが実行されていないかをチェックすることが大切であることも分かります。

 

⑥仕上げ

poly1およびその頂点を青色、poly2およびその頂点を灰色にする。

点Aを白抜きの点にする。

button2の見出しを「編集」、button1の見出しを「確定」とする。

その他、使いやすいようにレイアウト上の工夫を行い、完成。

 

その他

RigidPolygon[点1, 点2, 点3, ...]:頂点をドラッグしても形状が変わることのない多角形を作成する。点1をドラッグすると多角形を平行移動でき、点2をドラッグすると回転移動できる。