はじめまして、シンジといいます。
VC++で描画ソフトを作ったのですが、
線を引いたら途切れ途切れになってしまいます。
うまく線を描画する方法を知っている人は教えてください。
開発環境
Win2000, Visual C++, CPU:1.8GHz, MM:256MB
ソース(描画の部分)
void CSampleView::OnMouseMove(UINT nFlags, CPoint point)
{
CDC* pDC = GetDC();
// ペンの設定
CPen p(PS_SOLID, 4, RGB(0,0,0));
CPen* oldp=pDC->SelectObject(&p);
// クリックの時に線を描画
if( nFlags && MK_LBUTTON)
{
pDC->MoveTo(point.x,point.y);
pDC->LineTo(point.x,point.y);
}
pDC->SelectObject(oldp);
}
OnMouseMove()でマウスの軌跡を記録し、OnPaint()またはOnDraw()で描画して下さい。
↑ ViewだからOnDraw()です。
>線を引いたら途切れ途切れになってしまいます。
線をひくなら下の2点は別座標出なきゃ。
pDC->MoveTo(point.x,point.y);
pDC->LineTo(point.x,point.y);
ちなみにLineToで指定した座標には描画されないらしい。
pDC->MoveTo(1,1);
pDC->LineTo(10,10);
の場合線が引かれるのは(1,1)から(9,9)まで
OnMouseMoveだけでの描画だと再描画時に消えてしまうので
山本先生の言うとおりにもしてみましょう。
山本八郎さん、とりあえずさん
教えてくれてありがとうございます。
指摘してくれたところを直したのですが、
まだ線が途切れてしまいます。
直したのは以下のところです。
・OnMouseMoveでマウスの位置を取得
・OnDraw()で描画
・MoveTo(),LineTo()をSetPixelVを利用
・メンバ変数にCPoint pitを追加
void CSampleView::OnDraw(CDC* pDC)
{
CSampleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: この場所にネイティブ データ用の描画コードを追加します。
pDC = GetDC();
// ペンの設定
CPen p(PS_SOLID, 4, RGB(0,0,0));
CPen* oldp=pDC->SelectObject(&p);
// 描画
pDC->SetPixelV(pit,RGB(0,0,0));
// pDC->MoveTo(pit.x,pit.y);
// pDC->LineTo(pit.x,pit.y);
pDC->SelectObject(oldp);
}
void CSampleView::OnMouseMove(UINT nFlags, CPoint point)
{
pit = point;
if( nFlags && MK_LBUTTON)
{
OnDraw(NULL);
}
}
メンバ変数CPoint pitは一点だけでなく全ての軌跡を保存できるように
配列かリストにして下さい。
OnMouseMove()では全ての軌跡を記録します。
OnMouseMove()からOnDraw()は呼び出さないで下さい。
代わりに線を書く部分の矩形をInvalidateRect(&rect)します。
取り合えず全領域をInvalidateRect(NULL)すればOKです。
OnPaint()では軌跡を最初から最後まで書いてください。
これができたら、もっと効率的な方法にしましょう。
修正したコードにも問題がありますが、それは山本さんが指摘なさっているので
初めのコードについて以下に述べます。
疑問点がいくつかあります
(1)
pDC ->MoveTo(point.x, point.y);
pDC ->LineTo(point.x, point.y);
では線は引けないのではないですか?(点は長さ0の線と考えれ
ば線が引けるという言い方も間違いではないのかもしれない)
始点と終点とが同じなのですから。とはいっても CDC::LintTo()
は始点から、この終点の手前まで線を引き、終点を次回の始点にする
という動作なので移動量が0の場合は SetPixel(point.x, point.y);
と同じ効果があるのかもしれませんが、このようなことは意識して
行ったことがないので判りません
(2)
クリックの時に線を描画というコメントが正しければ
CSampleView::OnMouseMove() では線は引けないでしょう
クリックというのはマウスボタンを押して離すことだからです
あなたのコードでは左ボタンを押している間、しかも
マウスが動いたときに引けない線を引こうとしている
(点を打つ)としか解釈できません
(3)
マウスが1ピクセルでも変化したらメッセージが発生して
OnMouseMove() が働くとお考えかもしれませんが、かなり
ゆっくりマウスを動かさない限りそのようなことが起こると
は思えません。マウスからは変化があるたびに通知があるわけでは
無いからです。USBマウスはどうだか知りませんが、従来のマウスは
一定間隔(時間)で現在の状態をマウスから通知するからです
点が打ちたいのではなく線が引きたいのであれば
以前の位置を覚えておいて、そこから現在の位置まで線を
引いて、現在の位置を次回に以前の位置として用いるのが
普通でしょう
そのためには CSampleView::OnLButtonDown() で線が引ける
状態になることを検出し、CSampleView::OnLButtonUp() で
線が引けない状態になったことを検出するといった工夫が
必要でしょう。マウスをキャプチャーしたいからですが
キャプチャーが不要ならここまでしなくても良いかも
しれません。CSampleView::OnLButtonDown() で始点を記録
して、CSampleView::OnMouseMove() では左ボタンが押して
いる状態なら始点から、現在位置まで線を引き、現在位置を
始点に代入して次回の CSampleView::OnMouseMove() での
始点として使えるようにするのが恐らくは普通でしょう
CSampleView::OnLButtonUp() では若し、マウスを
CSampleView::OnLButtonDonw() でキャプチャーをするのなら
キャプチャーを解除するといいでしょう
二つ上の私の発言のOnPaint()はOnDraw()の間違いです。m(_ _)m
お返事ありがとうございます。
山本さんからアドバイスいただいた方法でやると
まだ途切れて表現されてしまいます。
島さんの方法をすると
確かに途切れは無くなりましたが直線しか引けません。
僕が作りたいとイメージしているものは
Windowsについている文字認識(IMEパッド)の際の入力部分です。
この特徴は2つあります。
・線を引くと同時に描画
・線が途切れない
というものです。
↑これを言うの遅れてすみません。
自分でも色々調べてやってはいるのですがうまくいきません。
この2つを同時に満たす方法があれば教えてください。
山本さんや島さんのアドバイスを忠実に実行すればうまくいくはずですが。
描画するときは次のようにして線をつないでいきます。
pDC->MoveTo(m_points[0]);
for (i=0 ; i<m_countOfPoints ; i++) {
pDC->LineTo(m_points[i]);
}
m_pointsはPOINTの配列、m_countOfPointsが点の数です。
しんじさん、島と申します
(1),(2),(3) に関してはどうお考えでしょうか?
あなたのお考えがお聞きしたいのです
私の提案した方法では直線しか引けないとおっしゃっていますが
もともとのあなたのコードが CDC::LineTo() をお使いなのですから
線が引きたいと言うお話でしたから判断して直線だと受け取るのは
当たり前過ぎる話ではありませんか?(責めているのではなくて、
あなたの欲しい回答はあなたの説明次第で、得られたり得られなかったり
するのですよという事が言いたいのです)
それで、どのようなコードで直線しか引けないという事になったので
しょうか?
ゆっくりマウスを動かせば折れ線近似の曲線に見えるものが引けるとは
思うのですが...
横から失礼します。m(__)m
線を引いたら途切れ途切れになってしまいます。
と言うことですが、しんじさんの初めのコードを、
少し変えると、つながった線が引けるようになりました。
void CSampleView::OnMouseMove(UINT nFlags, CPoint point)
{
CDC* pDC = GetDC();
// ペンの設定
CPen p(PS_SOLID, 4, RGB(0,0,0));
CPen* oldp=pDC->SelectObject(&p);
// クリックの時に線を描画
if( nFlags && MK_LBUTTON)
{
pDC->MoveTo(point.x,point.y);
pDC->LineTo(point.x,point.y);
この部分を
vpt.push_back (point);
it = vpt.begin();
pDC->MoveTo (*it);
for ( ; it < vpt.end() ; it++) {
pDC->LineTo (*it);
}
このように変更しました。(kazumaさんの例を参考にしました。)
}
pDC->SelectObject(oldp);
}
ヘダーファイルの中に
#include <vector>
using namespace std;
クラスの定義の中に
vector <CPoint> vpt;
vector <CPoint> vpt;
vector<CPoint>::iterator it;
と定義する必要があります。
複数の線分のときは
vector<vector <CPoint> > vptAry;
しかし、まだ一筆書きですし、その他不都合がありますので、
山本さんや
OnDraw()
島さんの
CSampleView::OnLButtonUp()・・・
CSampleView::OnLButtonDonw()・・・
のアドバイスを取り入れることで、うまくできると思います。
島さん内容が不充分ですみませんでした。
今までマウスなどのデバイスを意識してプログラミングをしておらず
かなり勉強になりました。
(1)に関しては、
描画を知らなかったので、Webで調べてそこのをマネしました。
(2)に関しては、
マネしたページではうまく表示されていました。
(3)に関しては
マウスからすべての位置情報を正確に取得できると思っていたのですが、
そうじゃないみたいです。そこで、島さんのアドバイスの線をつないで近
似する方法を取ったら、先ほどうまく行きました。マウスダウンしている
時にPointを取得するようにして、マウスアップしたら出力するようにしました。
ソース
// クリック中の位置の取得
void CSampleView::OnMouseMove(UINT nFlags, CPoint point)
{
if( nFlags && MK_LBUTTON )
{
pit[i++] = point;
end = i-1;
if(i>=256)
{
i=0;
}
}
}
void CSampleView::OnLButtonUp(UINT nFlags, CPoint point)
{
CDC *pDC;
pDC = GetDC();
// ペンの設定
CPen p(PS_SOLID, 4, RGB(0,0,0));
CPen* oldp=pDC->SelectObject(&p);
// 描画
for(i=0;i<end;i++)
{
pDC->MoveTo(pit[i].x,pit[i].y);
pDC->LineTo(pit[i+1].x,pit[i+1].y);
}
pDC->SelectObject(oldp);
i=0;
}
現在はマウスダウンと同時に出力の機能を付け加えようとしています。
InvalidateRect or OnMouseMove()を触ればおそらく何とかなると思います。
はじめてソフトっぽいものができそうです。みなさんありがとうございました。
最後に書き込んでくれた人もありがとうございました。
#include <vector>は見た事がないけど、配列を楽に使えるというのは聞いた事あります
一目ではまだ理解するだけの能力はありませんが、何とか解読したいと思います。
しんじさん、島です
どうやらあなたには私の書き方が悪過ぎてキチンと伝えることが出来ない
ようです。(1),(2),(3) についてどう考えていますかとお尋ねしたのですが、
返ってきたのは「どうした」とか「どうなった」とかいったものでした。
何故お考えがお書きになれなかったのでしょうか?
それに、直線しか引けないコードを示してくださるようにというつもりで
>>それで、どのようなコードで直線しか引けないという事になったので
>>しょうか?
書いたのですが、あなたが示されたのは現在のコードのようです
>(3)に関しては
> マウスからすべての位置情報を正確に取得できると思っていたのですが、
> そうじゃないみたいです。
「そうじゃない み_た_い です」ではなくて、そうではないと既に説明してる
と思うのはどこか間違っていますか?
無駄口はさておき、サンプルコードを示す方がお判りいただけると思うので
以下に示してみます。下の例では
CPoint m_OldPos;
vector <CPoint> m_VecPos;
は CSampleView のメンバーにしてありますが
(m_VecPos は CSampleDoc のメンバーでも良いでしょう)
void CSampleView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_OldPos=point;
m_VecPos.push_back(point);
}
void CSampleView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_VecPos.push_back(point);
m_VecPos.push_back(CPoint(-1,-1)); //一筆書きの終わりを示すことにする
}
void CSampleView::OnMouseMove(UINT nFlags, CPoint point)
{
if (nFlags & MK_LBUTTON) {
CDC *pDC =GetDC();
CPen pen;
pen.CreatePen(PS_SOID, 4, RGB(0,0,0));
CPen *pOldPen =oDC ->SelectObject(&pen);
pDC ->MoveTo(mOldPos);
pDC ->LineTo(point);
m_OldPos =pos;
m_VecPos.push_back(point);
pDC ->SelectObject(pOldPen);
}
}
OnDraw() でどうするかはご自分でお考え下さい
島さん、僕のためにここまで書いていただきありがとうございます。
先ほど動かしてみて、感動しました。
実際にこういうの作れるんだって思いました。
OnMouseMove()での描画の仕方やクリックの検出の仕方を
あまりイメージできなかったのですが、プログラムを見てわかりました。
今回掲示板に質問して教科書では得られないことがたくさん得られました。
また分からないことがあれば、相手の質問の意図や自分の意図をよく考えますので
今後ともよろしくお願いします。