こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

続、VB 座標軸の回転?

昨日
http://okwave.jp/qa/q7871613.html
で質問したものです。
プログラムを作っていて上手に動かないので再度質問です。

xx = x0 - cx
yy = y0 - cy
x= xx * cosA - yy * sinA + cx
y= xx * sinA + yy * cosA + cy

x0 = x
y0 = y


xx:Xの中心からの相対座標
yy:Yの中心からの相対座標
x0:Xの現在地
y0:Yの現在地
cx:三角形の中心X
cy:三角形の中心Y
x:座標変換後のX
y:座標変換後のY
A:角度

このようにプログラムしたのですが、回転をさせると渦巻き状に広がってしまいます。
以下のようにプログラミングしたつもりなのですが、うまくいきません。
悪そうな部分を教えてくれるとありがたいです。

>>
リーダーの座標を実座標を差し引いて原点(0,0)とし、メンバーの座標をリーダーからの相対座標(x1,y1)(x2,y2)・・・・とします
例えばメンバー(x1,y1)をリーダー中心に半時計周りへ10度回転させた場合の、移動先の座標を(xx1,yy1)とすると・・・
θ=10度(VBではラジアン単位へ変換してください)
xx1=x1 * cosθ - y1 * sinθ
yy1=x1 * sinθ + y1 * cosθ

これで回転して移動した先xx1、yy1が求まります。

最初に差し引いた実座標を足して戻して完成です。

投稿日時 - 2013-01-02 23:43:38

QNo.7872743

すぐに回答ほしいです

質問者が選んだベストアンサー

そんなにおかしそうには思いませんが、気がつかないバグが潜んでいるのかなあ。

デバッガを使えば簡単なのですが、timerを使わずに、sleepなどの実行を遅らせる命令があれば、それ入れ、ループで回せばデバッガを使えるようになりませんかねえ。
それか、Timerやジョイスティックに依存しないサブルーチンを作ってユニットテストする方法もあります。
No.7に書いたプログラムは、ジョイスティック部分を除いて簡単なユニットテストをしております。複雑な動作はテストされていませんので思わぬ勘違いが潜んでいる可能性も無いとは言えませんので、後でもう少し複雑なテストをしてみます。

No.12に書いたDimの移動ですが、ブロックの中で宣言した場合には変数の寿命がブロックの寿命より長くなり、スコープと一致しないので、ブロックの外に出してスコープと変数の寿命が一致するほうが分かりやすいと思ったのですが、VBって変数の寿命はかなり複雑ですね。
プログラムの実行中に確実に一回しか初期化されないところに移動した方が間違いを防げるかもしれません。
VBのマニュアルざっと見ましたが、初期化はコンパイル時に行われるような記述が有ります。
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 1.0}} '左
は、コンパイル時にCos_step, Sin_stepは確定しているはずなので、値として大丈夫なはずですが、どうなのでしょうか。

デバッガを使えるようにプログラムを少し変更してテストするのが近道のような気がします。

投稿日時 - 2013-01-16 17:32:06

お礼

行列式を解体して一つ一つの計算式にしたところ無事に動きました!

行列ではなくなったので、●の数を増やすことは困難になりましたが無事に完成のようです。

ありがとうございました!!

投稿日時 - 2013-01-18 01:49:09

ANo.13

このQ&Aは役に立ちましたか?

12人が「このQ&Aが役に立った」と投票しています

回答(13)

ANo.12

あと一息のようですね。

さて、明らかなミスから
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '左
Dim afRs(,) As Single = {{Cos_step, Sin_step, 0.0}, {-Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '右
で3行3列目が0.0になっています。1.0でないとおかしくなります。私自身もよくやります。
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 1.0}} '左
Dim afRs(,) As Single = {{Cos_step, Sin_step, 0.0}, {-Sin_step, Cos_step, 0.0}, {0.0, 0.0, 1.0}} '右

後は、変数のスコープと寿命の関係から、初期化のタイミングを考える必要が有ります。
VB, VBA, VB.NETで変数のスコープと寿命がどうも違うようで、ほとんど理解できていないのですが、、、
理解が間違っているかも知れませんが、一応書いていきます。

プログラム全体として、
Sub ABCD
loop
No.11のお礼欄のプログラム
end loop
end Sub
のような感じでしょうか?
要するに、サブルイーチンはTimerで毎回呼び出されるのではなく、Timerによってその都度loopが実行されるように思っています。そうすると、変数の寿命は一旦動き出すとプログラムが終了するまであるように思うのですがどうでしょうか?
(根拠として、Dim af(,) の寿命がプログラム終了まで続いてもらわないとおかしいくなる)

そうすると、Dim afMs(,), Dim afLs(,), Dim afRs(,)も1回しか初期化されないように思われます。さいわい、afLs, afRsの角度は変わらないようですので、Dim宣言の場所をSub直後に移動して、loopの中で、変化するパラメータだけ変更するのはいかがでしょうか。
実質的には移動だけでは動作は変わらないと思います。

Sub ABCD
Dim Sin_step, Cos_step As Single
B = 1.0 / 24.0 * Math.PI
Sin_step = Math.Sin(B)
Cos_step = Math.Cos(B)
Dim formation0(,) As Single = {{robot1_x0, robot2_x0, robot3_x0}, {robot1_y0, robot2_y0, robot3_y0}, {1, 1, 1}} '基本隊形
Dim formation(,) As Single = {{robot1_x, robot2_x, robot3_x}, {robot1_y, robot2_y, robot3_y}, {1.0, 1.0, 1.0}} '変換後隊形
Dim afMs(,) As Single = {{1.0, 0.0, X_step}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}} '直進
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 1.0}} '左
Dim afRs(,) As Single = {{Cos_step, Sin_step, 0.0}, {-Sin_step, Cos_step, 0.0}, {0.0, 0.0, 1.0}} '右
Dim af(,) As Single = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}
Dim aft(,) As Single = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}
loop
No.11のお礼欄のプログラムの残り
ただし、
'直進
Case in3 To in4 'joystick中央付近
の後に
X_step= 2.0 * slottle_x
afMs(2,2)=X_step
を追加して、移動量を毎回変更。Bは使われていませんので
B = 0.0は削除

投稿日時 - 2013-01-15 12:31:02

お礼

少し変則的なプログラムをしています。
Sub ボタン1 クリック
Timer1.Enable=True
End Sub

Sub Timer1.Enable = True
Call afin(~変数~) '関数呼出し=Call
End Sub

Sub afin(~変数~)
行列宣言、計算式など
(今回のプログラム)
End Sub

このような形式にしているのは、TimerのSubがメインであり、afinで計算された変数を使ってさらに計算するため、アフィン変換を関数化しているからです。

今回のご指摘を受けて、Sub afinを解体し、Sub Timer1に編入してみましたが、改善はされませんでした。
(Timer1自体がループなので変わらないのは当たり前なんですがね。。。)
Timerを使わずにループを使えばいいんでしょうが、ほかの処理の関係で使わざるを得ない状態です。

値がバラけるのはどこかの行列式でインクリメントしてしまっているんでしょうか。。。

あとは半径が生まれるという問題が解決できれば完成にだいぶ近づく段階まできているので試行錯誤を繰り返してみます。

投稿日時 - 2013-01-16 03:23:01

ANo.11

1) VBは特有の作法は分かりませんので、CやFortran, Rubyあたりでは問題になりそうな所から、
No.9のお礼欄に書かれているプログラム全体がloopで回っているように思いますので、その前提ですが

最初の方に
B = -1 / 24 * Math.PI
とありますが、多くの言語では整数どおしの割り算の結果は整数になり、 -1 / 24とすると0になる場合が多いです。
B = -1.0 / 24.0 * Math.PI
とする方が無難かもしれません。

もうひとつ
Dim formation0(,) As Single = {{robot1_x0, robot2_x0, robot3_x0}, {robot1_y0, robot2_y0, robot3_y0}, {1, 1, 1}}
のようにDim ~ ={..のように宣言と初期値の設定がありますが、ループが2周目になった時に、 formation0などの配列が二重定義になってしまうように感じますが、VBでは宣言文の性格よりも代入文の性格が強いのでしたら問題が無いのかもしれません。エラーや警告が出ないのでしたら、問題ない可能性が高いですが。ちょっと気になる点です。

2) 言語の違いではなく、アルゴリズム上問題になりそうなところです。
最初の行に
Select Case joyx 'ジョイスティック
とあり、真ん中あたりに
Select Case joyx 'ジョイスティックX軸
とあります。二回ジョイスティックの状態を見ているのですが、同じ状態である保証はありません。同じ状態だという前提に書かれているように思いますので、一度の判断で分岐した方が良いと思います。

3) 行列の積の計算が4カ所ありますが、残念ながら積にはなっていません。
簡単な方からみると、最後の方の
formation(i, j) = af(i, k) * formation0(k, j)
だと、formation(i, j) の各要素を事前に0にしておき,加算する必要があります。
For i = 0 To 2
For j = 0 To 2
formation(i, j) =0.0
Next
Next
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
formation(i, j) = formation(i, j)+af(i, k) * formation0(k, j)
Next
Next
Next
のような感じになります。

その上の
af(i, j) = af(i, k) * afLs(k, j)
af(i, j) = af(i, k) * afMs(k, j)
af(i, j) = af(i, k) * afRs(k, j)
の3つですが、直前のafの値に代入していますし、ループの最中にafの要素がどんどん書き変わっていますのでおかしくなります、一時的に変数aftを作り0で初期化します。
For i = 0 To 2
For j = 0 To 2
aft(i, j) =0.0
Next
Next
次に
aft(i, j) = aft(i, j)+af(i, k) * afLs(k, j)
のようにafが変わらないように行列の計算し、後で
For i = 0 To 2
For j = 0 To 2
af(i, j) =aft(i, j)
Next
Next
のようにコピーする方法をとる必要があります。
他にもあるかも知れませんが、まず気がついたところです。

投稿日時 - 2013-01-14 14:46:10

お礼

ありがとうございます!

先程まで制作していました。
ご指摘された部分を直しました。
行列の宣言についてはサブルーチンの外で宣言をするとエラーが消えないため、サブルーチンの中です。
そのほかはエラーなしです。

現状、直進については問題なし。
robot1_xそのほかも順調に値を変えます。
しかし、回転の部分に問題がありました。

回転をすると画面外に出るほど値が大きくなったりします。
時折画面に戻ってきます。
その時に表示されている隊形を見ると、前後の隊形で回転をしているので、隊形の回転自体には成功しているようです。
問題は隊形がその場で回転せずに、隊形を回転させながら最初の位置を原点に大きな半径を持って回転することです。
(わかりづらい表現で申し訳ありません)

観測している値を見ると、隊形の中心から他のロボットたちは±20の位置にいるので中心から±20で回転をしなけらばならないのに、±1000程度の移動をするので結果として画面から切れています。

これとは別なのですが、直進する際のラジアンはB=0.0なのかB=B
なのかのどちらでしょうか?
現状はB=0.0にしています。

改善したプログラムです。

'X軸を基軸としてアフィン変換
Dim Sin_step, Cos_step As Single
Sin_step = Math.Sin(B)
Cos_step = Math.Cos(B)
Dim X_step As Single '直進距離
X_step = 2.0 * slottle_x
Dim i, j, k As Integer '行列計算用
Dim formation0(,) As Single = {{robot1_x0, robot2_x0, robot3_x0}, {robot1_y0, robot2_y0, robot3_y0}, {1, 1, 1}} '基本隊形
Dim formation(,) As Single = {{robot1_x, robot2_x, robot3_x}, {robot1_y, robot2_y, robot3_y}, {1.0, 1.0, 1.0}} '変換後隊形
Dim afMs(,) As Single = {{1.0, 0.0, X_step}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}} '直進
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '左
Dim afRs(,) As Single = {{Cos_step, Sin_step, 0.0}, {-Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '右
Dim af(,) As Single = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}
Dim aft(,) As Single = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}

'アフィン変換
'aftを0で初期化
For i = 0 To 2
For j = 0 To 2
aft(i, j) = 0.0
Next
Next

Select Case joyx 'ジョイスティックX軸
'左
Case inmin To in3 'joystick左寄り
B = -1.0 / 24.0 * Math.PI
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
aft(i, j) = aft(i, j) + af(i, k) * afLs(k, j)
Next
Next
Next

'直進
Case in3 To in4 'joystick中央付近
B = 0.0
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
aft(i, j) = aft(i, j) + af(i, k) * afMs(k, j)
Next
Next
Next

'右
Case in4 To inmax 'joystick右寄り
B = 1.0 / 24.0 * Math.PI
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
aft(i, j) = aft(i, j) + af(i, k) * afRs(k, j)
Next
Next
Next
End Select

'afにaftを代入
For i = 0 To 2
For j = 0 To 2
af(i, j) = aft(i, j)
Next
Next

'アフィン変換合成
For i = 0 To 2
For j = 0 To 2
formation(i, j) = 0.0
Next
Next
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
formation(i, j) = formation(i, j) + af(i, k) * formation0(k, j)
Next
Next
Next

robot1_x = formation(0, 0) '一行一列成分
robot1_y = formation(1, 0)
robot2_x = formation(0, 1)
robot2_y = formation(1, 1)
robot3_x = formation(0, 2)
robot3_y = formation(1, 2)

投稿日時 - 2013-01-15 00:12:59

ANo.10

ものすごく忙しかったので先ほど見たところです。
やっと時間ができたので、明日にでもきっちり見てみます。お待ちください。
ぱっと見た感じで理解できそうです。
他の方も気がつかれたらよろしく。

投稿日時 - 2013-01-13 22:32:32

お礼

ありがとうございます!
このプログラムはTimerで回しています(while~loopのようなもの)

inmin,in1,in2,~,inmaxというのはjoystickの傾き値の具合です。
これで左、右、中央を判断しています。
この部分を使った他のプログラムでは大丈夫でした。

X_step = 2.0 * slottle_x
slottle_xとはjoystickのy軸の傾きの度合いです。
傾きに比例して0~1で変化するようにしています。

Dim~As Single というのは変数の宣言です。

このプログラムでrobot1_x,robot1_yの値を検出すると、どちらも0になってしまいます。

robot1_x,robot1_yは書いてはいませんが上の方で定義、値の代入済みです。

お忙しい中ありがとうございます。
どうぞよろしくお願いします。

投稿日時 - 2013-01-14 00:16:56

ANo.9

No.8のお礼欄の通りです。
行列の中の1.0をわすれて0.0にしてしまうことがありますので、注意してください。

結構美しいでしょう。

投稿日時 - 2013-01-05 23:46:16

お礼

お久しぶりです。まだこのページを覚えてらしていれば良いのですが。。。

ここ一週間でプログラムを制作してみました。
行列の使い方などを図書館で専門書を借りて学んでみたりしたので時間が掛かり、すぐにお礼とお返事をできなくて申し訳ありません。

制作したプログラムを見て欲しいのです。
現状、すべての値が0になるのでどこがおかしいのかと。。。
VBをかじっていないようですが、VBはCがわかればわかる簡単な言語ですので、よろしければお願いします。

プログラムからコピペしたので、変な改行があって見づらいかもしれません。
inmin~inmaxはjoystickの傾きです。
ほかの関数はある程度、サンプルプログラムに合わせました。

Select Case joyx 'ジョイスティック
Case inmin To in3 '左
B = -1 / 24 * Math.PI
Case in3 To in4 '直進
B = B
Case in4 To inmax '右
B = 1 / 24 * Math.PI
End Select

'X軸を基軸としてアフィン変換
Dim Sin_step, Cos_step As Single
Sin_step = Math.Sin(B)
Cos_step = Math.Cos(B)
Dim X_step As Single '直進距離
X_step = 2.0 * slottle_x
Dim i, j, k As Integer '行列計算用
Dim formation0(,) As Single = {{robot1_x0, robot2_x0, robot3_x0}, {robot1_y0, robot2_y0, robot3_y0}, {1, 1, 1}} '基本隊形
Dim formation(,) As Single = {{robot1_x, robot2_x, robot3_x}, {robot1_y, robot2_y, robot3_y}, {1.0, 1.0, 1.0}} '変換後隊形
Dim afMs(,) As Single = {{1.0, 0.0, X_step}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}} '直進
Dim afLs(,) As Single = {{Cos_step, -Sin_step, 0.0}, {Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '左
Dim afRs(,) As Single = {{Cos_step, Sin_step, 0.0}, {-Sin_step, Cos_step, 0.0}, {0.0, 0.0, 0.0}} '右
Dim af(,) As Single = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}

'アフィン変換
Select Case joyx 'ジョイスティックX軸
'左
Case inmin To in3 'joystick左寄り
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
af(i, j) = af(i, k) * afLs(k, j)
Next
Next
Next

'直進
Case in3 To in4 'joystick中央付近
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
af(i, j) = af(i, k) * afMs(k, j)
Next
Next
Next

'右
Case in4 To inmax 'joystick右寄り
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
af(i, j) = af(i, k) * afRs(k, j)
Next
Next
Next
End Select

'アフィン変換合成
For i = 0 To 2
For j = 0 To 2
For k = 0 To 2
formation(i, j) = af(i, k) * formation0(k, j)
Next
Next
Next

robot1_x = formation(0, 0) '一行一列成分
robot1_y = formation(1, 0)
robot2_x = formation(0, 1)
robot2_y = formation(1, 1)
robot3_x = formation(0, 2)
robot3_y = formation(1, 2)

投稿日時 - 2013-01-12 01:26:50

ANo.8

formtion0はアフィン変換で一番右側の列ベクトルです。列ベクトルを3つ合わせて行列にしただけです。
1行1列 リーダーのx
1行2列 リーダーのy
2行1列 サブ1のx
2行2列 サブ1のy
3行1列 サブ2のx
3行2列 サブ2のy
ではなく、行列が逆です。
1行1列 リーダーのx
2行1列 リーダーのy
1行2列 サブ1のx
2行2列 サブ1のy
1行3列 サブ2のx
2行3列 サブ2のy
それと、Ystep=2にしたいのなら、X軸方向を向いている編隊を、formtion0修正してY軸方向に向けてください。
Xstepは0に変更してください。(Y軸方向を向く編隊にして、afMsをY軸方向の移動だけにする)

投稿日時 - 2013-01-05 22:44:17

お礼

それぞれの座標をあらわすのは列ベクトルですから
|x|
|y|
|1|
でしたね。
これをリーダー、サブ1、サブ2の順において行列にしているわけですか。

それと、Xを進行方向にとっていて、Xstep,cosA,sinAで座標をとっているからYstepは考えなくてよかったのか!!
勘違いしてました。

夜通しちょっと作ってみます!!

投稿日時 - 2013-01-05 23:11:18

ANo.7

アフィン変換はNo.1でリンクしているものと同じ書き方です。
Rubyで書いてありますが、多分やり方は理解できると思います。
afが順次変化していきますが、formation0, afMs, afLs, afRsは変化しません。
編隊の中心座標と、向いている方向はafの値を見るとわかります。
大きな勘違がないといいのですが、(多分大丈夫だと思います)


require "matrix"
Xf1=1.0
Xf2=-1.0
Yf2=1.0
Xstep=2.0
Cos_step=Math.cos(15.0/180.0*Math::PI)
Sin_step=Math.sin(15.0/180.0*Math::PI)
# formation0[0,0], formation0[1,0]がリーダーの初期位置
# formation0[0,1], formation0[1,1]がメンバーの初期位置
# formation0[0,2], formation0[1,2]がメンバーの初期位置
# 0,0が編隊の回転中心です
formation0=Matrix[[Xf1, Xf2, Xf2], [0.0, Yf2, -Yf2], [1.0, 1.0, 1.0]]
# 1ステップの前進移動 formation0の進行方向の成分(X方向)だけです
afMs=Matrix[[1.0, 0.0, Xstep], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
# 1ステップの回転
afLs=Matrix[[Cos_step, -Sin_step, 0.0], [Sin_step, Cos_step, 0.0], [0.0, 0.0, 1.0]]
afRs=Matrix[[Cos_step, Sin_step, 0.0], [-Sin_step, Cos_step, 0.0], [0.0, 0.0, 1.0]]
# アフィン変換行列の初期値(何も移動しない)
af=Matrix[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
loop{
case ジョイスティックの位置
when 前 then af=af*afMs
when 左 then af=af*afLs
when 右 then af=af*afRs
end
formation=af*formation0
# formation[0,0], formation[1,0]がリーダーの現在位置
# formation[0,1], formation[1,1]がメンバーの現在位置
# formation[0,2], formation[1,2]がメンバーの現在位置
}

投稿日時 - 2013-01-05 20:47:04

お礼

わざわざソースコードを書いていただき、ありがとうございます!
概ねどうしたらいいか理解できました!
afが変わっていくから進んでいくことが納得いきました!
あと少しだけ分からないところ・確認したいところがあるので質問します。

Xf1=1.0
Xf2=-1.0
Yf2=1.0
Xstep=2.0

XstepがX方向の移動量ってことはわかったのですが、他の変数が何なのかがちょっと・・・  (Ystepも2.0でいいんですよね?)
次の行列formation0が基本行列みたいなので座標なんだとは思いますが。
変数がなんなのかがわからないために

formation0=Matrix[[Xf1, Xf2, Xf2], [0.0, Yf2, -Yf2], [1.0, 1.0, 1.0]]
のところが少し考えています・・・(符号など)
値を代入すると2行目が凸の三角形になることは分かりました。


# 0,0が編隊の回転中心です

これはリーダーのx座標のことなのでしょうか?

あとは座標のことですが、formation0,formationは3行3列の行列で
1行1列 リーダーのx
1行2列 リーダーのy
2行1列 サブ1のx
2行2列 サブ1のy
3行1列 サブ2のx
3行2列 サブ2のy
それぞれの3列目は無視していいと理解しています。

投稿日時 - 2013-01-05 22:22:24

ANo.6

質問者さんから素早いレスポンスがあるので、回答する側も気分がのってきます。
お礼欄に書くと回答者にすぐメールが来ますので、こちらの確認する手間が省けますので、できればお礼欄でお願いします。
(補足欄はここに見にこないと分からないのです)、中には質問者さんのお礼率を見てとやかく言う人もおりますので、お礼欄の方がそう取られないので、書き込めるお礼欄を使い果たしてしまったら、やむなく補足欄にという質問者さんもいます。sabrina485さんが今やっているように補足欄に書くのが本来の使い方なのでしょうが、使う側としてはその方が使いやすいように思います。

さて、No.5の補足欄についてですが、
書いておられるように、現在位置が順次動いていくのですから、現在位置をデータとしてもっておき、ジョイステックの状態によって順次動かしていく発想が自然なのですが、考えたのは、編隊の最初の座標を覚えておき、現在位置に変換する式の係数を変えていくという方式です。(前者のやり方では、式の係数を変えず現在位置を変えていく方法)

ジョイスティックの状態によって変換行列を変えていく方が多くのメリットがあるように思います。
少なくとも、進行方向をを知らなくても、編隊の先頭方向に動いてくれることや、前回の回答の拡大される問題も簡単に解決します。
変換行列を変えていくのもものすごく簡単なのです。

本日中にはソースコードを書き込んでおきます(VBではないですが、やっていることはVBより分かりやすと思います)

投稿日時 - 2013-01-05 10:03:52

お礼

ありがとうございます。
二つのやり方で、使うデータの保持などが違っていたのですね。
変数を保持しておくなど私の中でごちゃごちゃになっていた部分があったみたいです。

ソースコードは大変助かります。お待ちしています。

投稿日時 - 2013-01-05 13:49:43

ANo.5

3行3列と3行3列の積のサブルーチンはできましたでしょうか。これさえできれば、もう完成したようなものです。

VBは全く使ったことがないので、二次元配列を関数の値として返せたら便利なのですが、CやFortranのイメージからすると、関数ではなく、サブルーチンになるのではないかと思います。
まずは確認。それから進めましょう。

それとちょっと気になったところが、
>A=A±1/24π
ではなく、
A=±1/24πになるはずなのですが、Aは回転の角速度なので。単なる書き間違いだと思いいますが。

投稿日時 - 2013-01-04 17:49:52

補足

このリンク先のページの一番下の式がアフィン変換を表す敷みたいです。
http://msdn.microsoft.com/ja-jp/library/vstudio/8667dchf(v=vs.100).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

Matrixの宣言がよくわからなかったので回転・平行移動の行列を分解しました。
(長々と書いてたのですが添削してたら消えてしまいました)
x'= x*cosA - y*sinA + Tx
y'= x*sinA + y*cosA + Ty


前回のご回答で気になった点があるので質問です。
>例えば、回転中心0,0, リーダーの初期座標1,0 メンバーの座標0, 1と0, -1とした三角形としたときに、これは最後まで変化させません。
列ベクトルのところにそれぞれこれらの変化しない値を入れて計算するだけです。
基本的には移動先の座標は直接計算時には考慮しませんが、列ベクトルのx=0, y=0として同じように計算したときに、中心の現在いる座標が計算できます。

三角形は移動するので座標は変わっていきますよね?
最後まで変化させないということは、最初の三角形の座標は別に取っておくということでしょうか?

それを使って計算するということはどうやって前進するのでしょう?
Txが平行移動の移動量とのことですが、これに計算結果を代入するのでしょうか?(Tx=x1)
一回目の計算をx1、二回目の計算をx2とすると
x2 = x*cosA - y*sinA + x1
(x、yは初期位置?)
でもこれじゃ最初の一歩が動かない・・・

それともTxは移動する量で
x’を計算
前進するために
X=X+x’

毎日考えすぎておかしな発言をしてるかもしれません。。。

投稿日時 - 2013-01-05 02:43:32

ANo.4

質問欄の式を確認しましたが、基本的には正しいです。
というのも、ちょっと気になる点を確認します。
1) cosθ, sinθの値は、それぞれθが10度固定で、一回の計算で10度回転して、36回やると元の位置に戻りますよね。
2) 何回も回転させると渦巻き状に拡がるのでしょうか?数回の回転で拡がるのであれば別の問題があるのかも知れません。
かなりの回転回数で拡がるのでしたら、計算誤差が蓄積している可能性が高いです。
sinθとcosθを別々に計算するのではなく、例えば
cosθ=sqrt(1-sinθ*sinθ)
にすると良くなるかもしれませんし、倍精度計算にするともう少し軽減されるかも知れません。
しかしながら、誤差の蓄積は避けられませんので、時々は隊形の位置関係から補正をかける必要があるように思います。

さて、アフィン変換に話を戻しますが、多くの利点があります。
質問欄のプログラムだと、現在の位置と、進行方向を正確に把握していないとおかしくなるのですが、
アフィン変換を使うと、現在の位置と進行方向は必要なく、簡単な計算でいつも進行方向に進んでくれます。
いきなり全部書いてしまうのは失礼かと思いますので、追加で質問があってからにしますが、非常に簡単にでします。また、上に書きました拡がる問題も簡単な方法で解決します。
(分からないことがあれば、どんどん書き込んでください)

投稿日時 - 2013-01-04 15:58:22

補足

毎回ありがとうございますm(__)m
現状ではIF文で左右に傾いてる時A=A±1/24π、
dim A as single 
timerはinterval 200
でやっています。
>cosθ=sqrt(1-sinθ*sinθ)
これは高校一年生で習ったsinとcosの置き換えの式ですね!

精度の問題もありそうですね。変えてやってみます。
広がる問題も、三角形の中心からdまで離れても良い、それ以上はdとする、という方法をとって解決しようとしたのですが、回転中にうまく機能しませんでした・・・

アフィン変換をここ数日で勉強してみて作ったプログラムなのですが、わかっている人から見ると問題があるようですね(汗)

アフィン変換が便利そうで調べてみたところ、VBへの組み込み方に苦労しています。

しかし、現在位置と進行速度が必要ないというご説明から、なにか根本的なところで自分は勘違いをしていそうです・・・

答えから自分で考えてアフィン変換の使い方について納得していきたいので、リーダーだけでも構いませんのでVBでのアフィン変換について組み込み方をご教授していただけると大変助かります。

投稿日時 - 2013-01-04 16:34:19

ANo.3

No.1の補足欄について
行列の計算は習っていないとして書きます。

行列の積の計算をリンクしておきます。
http://naop.jp/text/c/gyouretu3.html

3行3列かける3行3列の場合の結果は3行3列になります。
最後のところは3行3列と3行1列との積になりますが、結果は3行1列になります。3行3列の場合の1列目だけと同じ結果です。
計算順序はひたすら左から計算していけば正しく計算できます。
そして最後の計算結果の1行1列目と、1行2列目がx'とy'となります。

例えば、回転中心0,0, リーダーの初期座標1,0 メンバーの座標0, 1と0, -1とした三角形としたときに、これは最後まで変化させません。
列ベクトルのところにそれぞれこれらの変化しない値を入れて計算するだけです。
基本的には移動先の座標は直接計算時には考慮しませんが、列ベクトルのx=0, y=0として同じように計算したときに、中心の現在いる座標が計算できます。

残りの質問については後で考えてみます。

投稿日時 - 2013-01-03 23:29:16

ANo.2

No.1の訂正です。
>そして、移動・回転・リーダーの行ベクトルで
行ベクトルではなく列ベクトルですね、

行列の積は、行列計算用のライブラリがあることが多いのですが、VBはあるかどうかはわかりません。
3x3の行列ですので簡単です、無ければサブルーチンを作れば良いのではないかと思います。

それとちょっと補足ですが、
まず元の図形のリーダーとメンバーはX軸方向を進行方向とするようにします。
そして移動1をX方向だけの移動にします。(そして徐々に移動量を大きくして)
回転1・移動1・リーダーの列ベクトル(あるいはメンバーの列ベクトル)を計算すると、回転1の方向にリーダーを先頭に移動することができます。
また、回転1・移動1・回転2・リーダーの列ベクトルで、回転2だけを変化させます。
さらに、回転1・移動1・回転2・移動2・リーダーの列ベクトルで移動2だけを変化させます。(移動は全てX軸方向だけ)

いろいろやってみてください。面白い結果がでるはずです(実際には確認していないので、間違いがあるかも知れませんが、今日の調子の悪い頭の中では完成しています??)

投稿日時 - 2013-01-03 22:10:41

ANo.1

前回の質問のNo.2です。
ちょっと質問欄の式を眺めていたのですが、今日の私の状態では理解不能なので、アフィン変換での説明をします。
前回のよりは多少分かり易そうなのがあったのでリンクしておきます。
http://imagingsolution.blog107.fc2.com/blog-entry-284.html

平行移動の所の、Tx, Tyをcx, cyとして、回転のθをAにして、右の項のx, yを例えば1, 0にします。
(中心0,0が回転中心で、リーダーの位置を1,0にしています。(適宜変えてみてください)
そして、移動・回転・リーダーの行ベクトルで行列計算します。(要するにリーダーを回転した後で平行移動する)
それで角度Aを変化させてみてください。cx, cyを中心にして、半径1できれいに回転するするはずです。

メンバーの位置も入れてみてください。こちらもきれいにできるはずです。

この場合のコツは、x,yに相当するリーダーやメンバーの座標を変えないで、位置はアフィン変換行列の移動や回転のパラメータだけを変化させて決めます。
また途中から進行方向が変わる場合も、移動・回転・移動・回転・リーダーの行ベクトルの様に変換を重ねていくと、複雑な動きも簡単にできるはずです。

投稿日時 - 2013-01-03 21:48:35

補足

前回に引き続きご回答ありがとうございます!

このプログラムはTimerを使ってずっと回しています。
アフィン変換の行列をVBで再現するときは
X' = X*cosA - Y*sinA + Tx
Y' = X*sinA + Y*cosA + Ty
とプログラムすれば良いのでしょうか?
これを関数化してリーダー、付属の○それぞれの(X,Y)座標を渡して結果を表示すればいいのか、それとも(X,Y)は隊形の中心からの相対座標なのかと悩み中です。


質問に書いたプログラムですが、わかりづらくてすいません。
詳しく解説すると、上のプログラムはリーダーの部分だけを抜粋しました。
まず回転するために隊形の中心部分を原点としました。(cx、cy)
隊形の三角形の中心部分(cx,cy)を原点とし、それからリーダーがどこにいるかの相対座標(xx,yy)を計算するために現在地(x0,y0)から引きました。
xx = x0 - cx (隊形の中心を原点とした回転後の座標)

それから、回転変換の式を使って回転後の座標(x,y)を計算し、それに引いた座標(cx,cy)を足して本当の回転後の座標を出しました。
x= xx * cosA - yy * sinA + cx

yも同様に出し表示させると、前進するときはいいのですが、回転させると3体の○が渦巻きのように広がっていきます。

質問が多くてすいません。
わかる範囲だけでいいのでご回答していただけると助かります。

投稿日時 - 2013-01-03 23:04:58