うしブログ

うしブログ

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

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

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

多角形の辺に沿って点を動かす、5つの方法

課題

下図のように、任意の多角形オブジェクト(poly1)の辺に沿って、点Pを動かしたい。実現方法を考えよう。

f:id:usiblog:20190709221047g:plain

以下では、①〜⑤の、5つの方法を紹介する。

後述するが、①②の方法では、poly1が正多角形でない場合(すなわち、辺の長さが異なる場合)には、Pを等速で動かすことができない。

辺の長さにかかわらず、Pを等速で動かしたい場合には、③④の方法が有効である。

⑤は、④の簡便法である(描画の正確性は欠けるが、④よりは作成が容易)。

 

 

方法①:単にpoly1上の点をアニメーションさせる

P = Point[poly1]

としてPを作成したうえで、Pのアニメーションを開始する。

すると、poly1の辺に沿って、Pを動かすことができる。

f:id:usiblog:20190709221834g:plain

方法②:スライダーと連動させる

数値オブジェクトnを作成する。nのスライダーを、最小値-5、最大値5で作成する。

P = Point[poly1, Mod[n, 1]]

としてPを作成する。

nのアニメーションを開始することで、poly1の辺に沿って、Pを動かすことができる。

f:id:usiblog:20190709223705g:plain

なお、パス・パラメータの意義については、下記の公式Wiki参照。

Geometric Objects - GeoGebra Manual

PathParameter Command - GeoGebra Manual

方法③:スライダーを変速で動かすことで、Pの等速移動を実現する

上記の方法①②では、Pの動く速度は、辺の長さに比例する。つまり、辺の長さに応じて、Pの速度が変わってしまう。

Pを等速で動かしたい場合は、辺の長さに応じて、nのアニメーション速度を変えるのが有効である。以下では、その具体的な方法を紹介する。

 

まずは、poly1の頂点のリストを作成する。

vertexList = {Vertex[poly1]}

次に、poly1の辺のリストを作成する。

sideList = Sequence[Segment[Element[vertexList, t], Element[vertexList, If[t < Length[vertexList], t + 1, 1]]], t, 1, Length[vertexList]]

点Pが現在、poly1のどの辺上にあるのかを特定し、その辺の長さを取得する。

nowLength = Sum[Zip[If[P ≟ ClosestPoint[s, P], Length[s], 0], s, sideList]]

最後に、方法②の、nのアニメーション速度を、「任意の定数 / nowLength」に設定する。例えば、

0.2 / nowLength

など。

これで、nのアニメーションを開始すると、Pが等速で動くようになる。

f:id:usiblog:20190709224151g:plain

方法③による見本アプレット

下記URLから、方法③によって作成したアプレットにアクセスできる。

多角形の辺上を等速で動く点 – GeoGebra

 

方法④:スライダーを等速で動かすことで、Pの等速移動を実現する

方法③は、nのアニメーション速度を、辺の長さに応じて変化させることで、Pの等速移動を実現した。

しかし、実用的には、nもPも、等速で動かしたい、という場合も多いだろう。そこで、これを実現する方法を、以下で紹介する。

f:id:usiblog:20190710023258g:plain

 

ステップ1 環境整備

poly1の頂点のリスト(vertexList)、および辺のリスト(sideList)を、以下の定義で作成する。この点は、方法③と共通である。

vertexList = {Vertex[poly1]}

sideList = Sequence[Segment[Element[vertexList, t], Element[vertexList, If[t < Length[vertexList], t + 1, 1]]], t, 1, Length[vertexList]]

 

ステップ2 nに対応するパス・パラメータを計算する

ここでは、nが0から1まで、等速で変化するに伴って、Pがpoly1の辺上を、等速で1周する、という挙動の実現を目指すことにする。

 

まずは、poly1の辺の長さの合計(totalLength)を得る。

totalLength = Sum[sideList]

次に、poly1の辺の長さを、sideListの要素の左から、順に1つずつ足していった場合の累計を、リストで取得する(sumOfSideLength)。

sumOfSideLength = Zip[Sum[sideList, IndexOf[s, sideList]], s, sideList]

例えば、下図のように、poly1 = Polygon[A,B,C]であり、sideListの要素の値が、順に3,4,5である場合(以下「仮設例」という)には、sumOfSideLengthは、{AB, AB+BC, AB+BC+CA}によって計算でき、その値は、{3,7,12}である。

f:id:usiblog:20190710025423p:plain

 

そして、sumOfSideLengthの要素のうち、Mod[n, 1] * totalLength の値(Pの移動距離を、nの1次関数として表したもの)を超えないものを抽出する(beforeAll)。

beforeAll = KeepIf[t < Mod[n, 1] totalLength, t, sumOfSideLength]

仮設例でいえば、n = 10/12のとき、すなわち、Mod[n, 1] * totalLength = 10であるとき、beforeAllの値は、{3,7}である。

 

beforeAllの要素数

Length[beforeAll]

は、あるnにおいて、Pがすでに通過し終わった辺の数を意味する。仮設例で説明すると、n = 10/12のとき、Pはすでに、長さ3の辺、長さ4の辺の2つを通過し終わり、長さ5の辺上を、3だけ進んだ地点に存在している。この場合、Length[beforeAll]の値は、通過し終わった辺の数「2」である。

これを利用して、Pがすでに通過し終わった辺のうち、最後に通過し終わった辺の末端(仮設例では、点C)におけるパス・パラメータを計算する(basicParam、仮設例では2/3)。

basicParam = Length[beforeAll] / Length[vertexList]

 

beforeAllの最後の要素を、数値オブジェクトとして得る(beforeLast)。

beforeLast = Element[Last[Join[{{0}, beforeAll}]], 1]

beforeLastの値は、Pが既に通過し終わった辺の、長さの合計を意味する。上記仮設例では、既に通過し終わった辺AB、BCの長さの和であり、その値は7である。

 

sumOfSideLengthの要素のうち、Mod[n, 1] * totalLength の値を超える、最小の要素の値を、数値オブジェクトとして得る(afterFirst、上記仮設例では12)。

afterFirst = Element[First[KeepIf[u ≥ Mod[n, 1] totalLength, u, sumOfSideLength]], 1]

 

beforeLast, n, afterFirstの3つの値の関係から、Pのパス・パラメータ(totalParam)を計算する。

extraParam = (Mod[n, 1] totalLength - beforeLast) / (afterFirst - beforeLast) / Length[vertexList]

totalParam = basicParam + extraParam

extraParamは、Pのパス・パラメータと、basicParamの差分であり、多角形の辺におけるパス・パラメータの定義に従って計算することができる。

上記仮設例では、

extraParam = { (10 - 7) / (12 - 7) } / 3 = 1/5

totalParam = 2/3 + 1/5 = 13/15

である。

 

ステップ3 Pを作成する

Pを、

P = Point[poly1, totalParam]

として作成すれば、Pは、nの等速変化に応じて、等速移動する。

 

参考:オリジナルツール「PointOnSide」

以上の作業は、非常に煩雑であるが、要はpoly1およびnを引数として、Pを定義付ける作業に他ならない。そのため、以上の作業は、poly1およびnを引数とし、Pを戻り値とする、オリジナルツールにまとめることができる。

PointOnSide Tool - GeoGebra

 

本ツールは、多角形と数値オブジェクトを指定すれば、当該数値オブジェクトの等速変化に伴い、多角形の辺上を等速に移動するような点を返すことができる。

f:id:usiblog:20190710031413g:plain

オリジナルツールによる見本アプレット

下記リンクより、上記オリジナルツールを使用した、見本アプレットにアクセス可能である。

多角形の辺上を等速で動く点_パラメータも等速版 – GeoGebra

 

方法⑤:簡便法

f:id:usiblog:20190710113308g:plain

上図のように、poly1の辺上に、「できるだけ」均等に、点列(pointList)を作成し、

P = Point[pointList, Mod[n,1]]

とすれば、方法④と、ほぼ同様の挙動を実現できる。これは、方法④の簡便法と位置付けられる。

pointListの定義は、方法④におけるvertexList、および、数値オブジェクトfrequencyを所与として、

pointList = Flatten[Sequence[Sequence[Dilate[Element[vertexList, If[s + 1 ≤ Length[vertexList], s + 1, 1]], t frequency / Distance[Element[vertexList, s], Element[vertexList, If[s + 1 ≤ Length[vertexList], s + 1, 1]]], Element[vertexList, s]], t, 0, Div[Distance[Element[vertexList, s], Element[vertexList, If[s + 1 ≤ Length[vertexList], s + 1, 1]]], frequency]], s, 1, Length[vertexList]]]

である。

frequencyは、点列の間隔である。これを小さくとれば(0.01程度)、挙動の見た目は、方法④の場合と、ほぼ変わらない。

なお、下図は、frequency = 0.76の場合と、0.01の場合の挙動である。赤い点Pが、方法⑤によるものであり、黒い点P_{tool}が、方法④によるものである。

f:id:usiblog:20190710114107g:plain

 

f:id:usiblog:20190710114425g:plain

方法⑤によるアプレットには、下記URLからアクセス可能である。

多角形の辺上を等速で動く点_パラメータも等速版_簡便法 – GeoGebra

 

方法④の別アプローチ

方法④の別アプローチ(IfやDilateコマンドを用いたもの)によるツールも作成した。

PointOnSide Tool _ using Dilate and If - GeoGebra