- ベストアンサー
MATLABのプログラミングについて
マウスのカーソルの移動距離を計測したいのですが、 MATLABでできますでしょうか。 プログラムでどう組み込めばよいのか分らないため質問させて頂きました。 MATLAB初心者のため、詳しい方ご教授のほどお願いします。 左クリック(開始)→マウスを動かす→左クリック(終了) のようにクリックからクリックまでの移動距離を計測したいのです。 できれば、マウスカーソルの軌跡も表示させたいです...。 丸投げで聞いてしまい申し訳ないのですが、 緊急で必要になったため、質問させていただきました。 以下の環境を使っています。 -Windows 7 -R2010b
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
三度Kulesです。 ん~仕様が簡単に変わってしまうてのもなかなか不信感なんですけどね。 そもそも 左クリック(開始)→マウスを動かす→左クリック(終了) と クリック→ドラッグ→ドロップ だとだいぶ操作感が違うと思うんですが… 最初の仕様の指定はどこから来たものなんですかね? まあとりあえずそれぞれの処理を書きますね。 とりあえず共通で使われる変数名を決めときます。 ax…軸のハンドル f…Figureのハンドル flg_Click…クリック中かを表すフラグ pos…カーソルの軌跡を格納する distanse…カーソルの移動距離を格納する tmp…最終的には必要ない変数。tmp1,tmp2などのように使う。 1)クリック時 function Click %軸の中にimageがあり、なおかつ軸の中にポインタがあり、 さらにflg_Clickがfalseの時だけ実行し、 %それ以外の時はそのままreturnする tmp1=get(ax,'Children'); if ~any(strcmpi(get(tmp1,'Type'),'image');return;end; tmp1=get(ax,'CurrentPoint'); tmp2=get(ax,'Xlim'); tmp3=get(ax,'Ylim'); if tmp1(1,1)<tmp2(1)||tmp(1,1)>tmp2(2)||tmp1(1,2)<tmp3(1)||tmp1(1,2)>tmp3(2);return;end; if flg_Click;return;end; flg_Click=true; pos=tmp1(1,1:2); distanse=0; 2)ドラッグ時 function Drag % flg_ClickがtrueでカーソルがAxes内にある時のみ処理をする if ~flg_Click;return;end; tmp1=get(ax,'CurrentPoint'); tmp2=get(ax,'Xlim'); tmp3=get(ax,'Ylim'); if tmp1(1,1)<tmp2(1)||tmp(1,1)>tmp2(2)||tmp1(1,2)<tmp3(1)||tmp1(1,2)>tmp3(2);return;end; tmp2=pos(end,:); tmp3=sqrt(sum((tmp1(1,1:2)-tmp2).^2)); pos=[pos;tmp1(1,1:2)]; distance=distance+tmp3; 3)ドロップ function Drop % flg_ClickがtrueでカーソルがAxes内にある時のみ処理をする if ~flg_Click;return;end; tmp1=get(ax,'CurrentPoint'); tmp2=get(ax,'Xlim'); tmp3=get(ax,'Ylim'); if tmp1(1,1)<tmp2(1)||tmp(1,1)>tmp2(2)||tmp1(1,2)<tmp3(1)||tmp1(1,2)>tmp3(2);return;end; flg_Click=false; set(ax,'NextPlot','Add'); plot(ax,pos(:,1),pos(:,2)); set(ax,'NextPlot','Replace'); fprintf('移動距離:%f\n',distanse); おまけ:それぞれの関数が思ったタイミングで動作するよう設定 set(f,'WindowButtonDownFcn',Click); set(f,'WindowButtonMotionFcn',Drag); set(f,'WindowButtonUpFcn',Drop); 以上です。関数名、変数名のスペルが怪しいのはご愛嬌ということで。 いくつか注意点を。 1.実際に実行してみるとわかると思いますが、このままでは全く動きません。 多分「axは未定義の関数です」とか何とかいうエラーが出ると思います。 …まあデータのやり取りについてはあなたの書いた処理には書かれていませんでしたしね。 私も「処理の一部」としてわざと書かなかったわけですし。 いじわるを言うようですが、一連の処理を書く時は ・データを読みだす ・処理をする ・データを書きこむ が必要です(もちろん必要がない時もあります) どのようにデータ(軸のハンドルや軌跡の座標など)を読み出し、保存するかを決めなければこの関数達は完成しません。 2.私の作ったものとはかなり違う形になっています。 データをどのようにやりとりするかも含めて1つの関数で全て解決するようにしたため、構成はかなり違うことになっています(構造が複雑になるので読みにくくなってます) 3.例外的な操作をした時想定外の動作をします(下手するとエラーが出ます)。 Axes内でクリック→Axes外までドラッグ→その場でボタンを離す→再びAxes内にカーソルを戻してクリック→Axes内でドラッグ→Axes内でボタンを離す とすると、軌跡は1回目のクリック以降のものからのトータルが示され、距離も最初のクリックからの合計になります。 これは多分望んだ動作ではないですよね?これを回避するためどのようにしますか? また、 Axes内でクリック→Axes外までドラッグ→再びAxes内に戻してからボタンを離す とすると、Axes外に出た点から戻ってきた点が線で結ばれますし、その距離も移動距離に加えられます。 これも多分望んだ動作ではないですよね?これを回避するためどのようにしますか? 失礼を承知できついことを書きますが、 3.が想定できていない時点でまだ仕様すらきちんと固まってないな、と思います。 仕様は基本的に課題などでは人から与えられるものですが、仕様がさらっと変わるところをみるとそういうわけではないんですよね? だとしたら、通常の動作以外にどのようなことが起こるのかを想定して、その時はどのような動作をさせるのかを考える必要があります。 前も書きましたが、仕様→処理を決めるのに必要なのは言語に対する知識ではなく、想像力(自分が考えている通りの動作以外の操作をした時、どのようなことが起こりうるかを考える力)です。 想像を働かせる→それを達成するためにどのような処理を加えるか考える→所望の処理をさせるためにどのようなコードを書けばよいか勉強する という流れでプログラムのスキルは上がっていくものだと思います(人から与えられた必要性ではなく、自分の内から出た必要性の方が、勉強する気も起りますよね) 参考になれば幸いです。
その他の回答 (2)
- Kules
- ベストアンサー率47% (292/619)
再びKulesです。 ん~…(苦笑) 聞き方が悪かったですね。すみません。 プロに聞かれたら怒られるかも知れませんが、 何らかのプログラムを組む作業は、 ・仕様を決める(何をどうした時、どのようなことが起こるか) ・仕様を満たすために、どのような処理を行うか ・所望の処理を行うために、どのようなコードを書くか の3段階に分かれていると思います。 簡単な例で言うならば、 ・仕様…2つの整数M,Nを入力すると、50円のノートと80円のノートを合計M冊買うと合計金額がN円だった時の、それぞれの冊数を出力する ・処理…1:M,Nの入力を受け付ける 2:連立方程式X+Y=M;50X+80Y=N;を解く ・コード…省略します となります。 ただ、上の例で言うならば ・処理…1:M,Nの入力を受け付ける 2:鶴亀算方式でX,Yを求める でもいいわけですよね? 何が言いたいかといえば、 ・同じ仕様を、異なる処理で満足することができる。 ・仕様から処理を決めるまでの流れは、使う言語やツールに依存しない ということです。 で、あなたが書いた >左クリック(開始)→マウスを動かす→左クリック(終了) のようにクリックからクリックまでの移動距離を計測したいのです。 や >・カーソルを使い、画像上でなぞることができる > ・なぞった軌跡の距離を計測できる は「仕様」であって「処理」ではないと考えられます。 「処理」を考える上では、Matlabの知識は要りません。 もちろんあった方が「Matlabと相性のいい処理」を考える上では役に立つんですけどね。 実は私自身今日ちょっと試してみたら、あなたの言うような機能を満たす関数は割とすぐにできました。 ただ、これを作って思ったのは ・この関数を丸のまま渡しても初心者にはそのまま使うことしか出来ない→結局「脱初心者」とはいかない (人の書いた関数を読むというのは、特に最初のうちはとても困難です) ・私の書いた関数が、「ベストの処理」とは思えない→これを手本として渡すのはどうなん? (これをそのまま渡したところで、ちょっと目先を変えた課題には対応できない) ということでした。 以上を踏まえて、再度補足要求します。 ・仕様をより具体的に固めて下さい。 いつ(どんな時でも?Figureを選択している時のみ?ボタンか何かで制御?メニューバー(Figureの上欄にあるファイルとか表示とか並んでいるところです)に新たな項目を作る?その他?)どこ(fiugre上?Axes上(枠内)?その他?)をどうしたら(左クリック?右クリック?ホイール?ドラッグ?ダブルクリック?その他)どのような(線が描ける?移動距離が表示される(どこに?)?その他)動作をするのか。例外処理やエラーチェックはどうするか(例えば複数のFigureが重なっている時はどちらに対して処理を行うのか、など) ・その仕様を満足するために、どのような処理をするかを具体的に書いてください エラーチェックなどは最初は難しい(どのような例外が起きうるのか想像しにくいため)ので省略しても構いません。 一例として、私が書いた関数Fugaの仕様、処理の一部を書きます。 (実際に文書に書いたわけではありませんが、関数を書き始める前に考えたことです) ・仕様…メニューバーに新たな項目「Locus」を加え、その中に項目Memoryを作る。Memoryにチェックが入っている時のみ軌跡を描けるようにする。条件を満たしている時、クリック→ドラッグ→ドロップの動作で、クリックしてからドロップするまでの総移動距離をコマンドウィンドウに出力し、カーソルの動いた軌跡を青い線でプロットする。クリックするのはFigure内である必要がある。ドラッグ、ドロップではFigure外にカーソルがあってもよいが、Figure内で動いた方がいい(エラーチェックで問題なくできるが、とりあえず今は)。 ・処理 (1)クリックした時 Locus→Memoryにチェックが入っているか確かめる クリック中かどうかを表すflag_Clickをtrueにする クリックした点の、Axesにおける座標を取得する 移動距離を0にリセットする (2)ドラッグ(マウスポインタを移動した)時 Locus→Memoryにチェックが入っているか確かめる flg_Clickがtrueになってるか確かめる 現在のマウスポインタの、Axesにおける座標を取得し、予めもっている座標の情報に新たに加える 前の最新の点から今回の点までの距離を測り、これまでに保持していた移動距離に足す (3)ドロップ(マウスの左ボタンを離した時) Locus→Memoryにチェックが入っているか確かめる flg_Clickがtrueになってるか確かめる flg_Clickをfalseにする これまでに保持した座標をAxesにプロットする 移動距離をコマンドウィンドウに表示する この他に、「Locus→Memoryにチェックを入れる処理」「Locus→Memoryを作る処理」などがあります。 どんな言語であったとしても、入門者から私のような「初心者に毛が生えたぐらいのレベル」場合によっては中級者にとって一番大切なのは、「自分がこのようにしたい、と思った処理ができるようなコードが書けること」だと思います。 まずは、「どのようなコードを書くか」ではなく (>プログラムでどう組み込めばよいのか分らないため質問させて頂きました。 を考えるのではなく) どのような処理を行えばよいのかしっかり考えることが肝要かと思います。 最後の方(というか全体にわたって)お小言みたいになってしまいすみません。 補足いただければ、「所望の処理を行うためのコード」を書くことができると思います。 参考になれば幸いです。
補足
全くの無知な私にこんなに詳しく教えていただいて...本当にありがとうございます。 正直、できるだけ簡単に仕様を満たすことのできるプログラムが知りたいという思いで質問いたしました。 しかし、Kulesさんのおっしゃる通りで、コードをもらうだけでは何も成長しませんよね。非常に反省致しました...。 私はプログラミングの経験はほとんどなく(座学でCをさらっと学んだ程度)、 詳しい処理方法をどう書けばよいのかがわからない状態です。 matlabの基礎の基礎からやらねばならないのは重々承知ですが、 そのような時間も取れず、話にならん!と言われるのも覚悟で焦って質問した次第でございます。 Kulesさんの仕様、処理は私の思い描いていたものにかなり当てはまっています。 あえて言うならば、Axes内にユーザのカーソルが存在するときに動作できればと思います。 ほぼ一緒ですが、一応載せます>< ------------------------- Figure Windowに画像を表示させ、Axes内にユーザのカーソルが存在するときのみ描けるようにする。 条件を満たしている時、クリック→ドラッグ→ドロップの動作で、クリックしてからドロップするまでの移動距離をコマンドウィンドウに出力し、カーソルの軌跡をプロットする。 1)クリック時 Axes内にユーザのカーソルが存在するか確かめる クリック中かどうかを表すflag_Clickをtrueにする Axes内のクリックした点の座標を取得する 移動距離を0にリセットする 2)ドラッグ時 Axes内にユーザのカーソルが存在するか確かめる flag_Clickがtrueになっているか確かめる Axes内のカーソルの座標を取得し、予めもっている座標の情報に加える 前の最新の点から現在の点までの距離を測る その距離をこれまでに保持していた移動距離に足す 3)ドロップ Axes内にユーザのカーソルが存在するか確かめる flag_Clickがtrueになってるか確かめる flag_Clickをfalseにする これまでに保持した座標をAxesにプロットする 移動距離をコマンドウィンドウに表示する
- Kules
- ベストアンサー率47% (292/619)
ん~軌跡も表示させるってことなんで、 ・Axes上をクリックした時、その点の座標を出力 ってことでいいんですかね? (他にも、 ・Figure内をクリックした時、Figure内の位置を出力 とか、 ・Figure内をクリックした時、スクリーン内での位置を出力 などが考えられるので) p=get(gca,'CurrentPoint'); で、x,y,z座標(表と裏)の3×2の行列が出力されるので、 x座標、y座標を得たいのであれば、 x=p(1,1);y=p(1,2); とすればよいでしょう。 で、クリックした時に座標を出力したいのであれば、 set(gca,'ButtonDownFcn','Hoge'); としといて、関数Hoge(ファイル名Hoge.m)を別に作っておき、 その中でx,y座標を取るようにすればいいですね。 次に、x座標とy座標をどのように保存するか…ですが、ワークスペースを使っていいのであれば 関数Hogeを[x,y]=Hogeに変更して、 set(gca,'ButtonDownFcn','[x,y]=Hoge;'); とすれば実行した時点でx,yがワークスペースに出力されます。 それはダメなのであれば、 関数Hogeの中でguidata(gca,[x,y]); とすればFigureに値が保存されるので、後でp=guidata(gca);とすれば、 pにx座標、y座標が出力されるので後で使えます。 次に、「一度目のクリックから二度目のクリックまでの移動距離を求める」ということなので、クリックしたのが一度目か二度目かを判別する必要があります。これは…まあ関数内でpersistent変数flagを持たせるか、あるいはguidata(gca,[x,y,flag])でflagも保存しちゃうかですかね。 最後に、軌跡を描くということですが…直線で結ぶだけじゃなくて軌跡を描かないといけないんですかね? いずれにせよ「描く」の部分はplotでいいと思うのですが、軌跡を残すのは大変ですね。 まあset(gcf,'WindowButtonMotionFcn','Fuga'); として、関数Fuga内で ・p=guidata(gcf);でpにこれまでの軌跡を読み込む ・p0=get(gca,'CurrentPoint');=p=[p;p0(1,1:2)];としてpに新たな座標を追加 ・guidata(gcf,p)で新たな座標の組を保存 ですかね。ただこれだとfigure内でぐりぐりカーソル動かすとあっという間にpのサイズがエライことになってしまいますが。 こんな感じですかね… 具体的に「何を」「どうしたら」の部分の情報が足りてる気がするんで、このぐらいしか書けないです。 ましてや関数自体を書くことはできないですね。(書いてもその動作の説明でまた膨大な説明が必要になってしまいそうです) もう少しどのような動作をさせたいのか補足していただければ、もう少し有益な解答ができるかも知れません。 参考になれば幸いです。
補足
詳しく書いて頂き、本当にありがとうございます!! あいまいで申し訳ないです。 ずばりやりたいことというのは、 例えば△が描いてある画像を表示させて その△をマウスのカーソルでなぞって、そのなぞった軌跡の距離を計測したいのです。 ・カーソルを使い、画像上でなぞることができる ・なぞった軌跡の距離を計測できる 軌跡を残すのは難しいようなので、無くても問題ないです。 以上、加えた条件で教えて頂けることがございましたら、 是非宜しくお願いします。
お礼
回答ありがとうございます。 厳しいお言葉もしっかり受け止め、 教えていただいたことを参考に勉強していこうと思います。 本当に丁寧に教えていただきありがとうございました!