TImageのようにオブジェクト別にクリックを取得でき、透明な背景に線を引けるクラスは?

解決


デル太  2006-03-25 02:26:19  No: 20679

次のようなことをしたいと考えています。

  1)1つのクラスから複数のオブジェクトを動的に生成し、配置する:実現できました
  2)各オブジェクトは背景を透明にして、線を引きたい:TImageとTPaintBoxで実現できました
  3)各オブジェクトが重なった場合でも、クリックした箇所の各オブジェクト内の色を取得したい:TImageとTPaintBoxで実現できました
    →親コントロールを通じて子コントロールの情報を取得できました
  4)各オブジェクトが持つ描画内容だけを対象に取得したい:TImageで実現できました
    →TPaintBoxだとオブジェクトの範囲内に別オブジェクトがあると、その色を取得してしまうので使えない

みなさんに教えてもらい、上記からTImageが一番適しているように思いましたが、幅と高さを指定できないという問題が出ています。
これは、別のスレッドで質問させていただいています。

この状況だとTImageよりももっと適したクラスがあるのではないかと思い、できれば知恵を貸していただけないでしょうか?
自作の方がよいのかもしれません。その場合もがんばりますので、このコントロールから派生して作れば?というアドバイスをいただけると幸いです。
よろしくお願いいたします。

上記を整理してみて、TLabelも試してみましたがTPaintBoxと同様とわかり手詰まりになってしまいました。
助言をいただけると助かります。


デル太  2006-03-25 03:23:25  No: 20680

TImageでサイズ変更ができました。
別スレッドに情報をまとめました。
お騒がせしてしまい、申し訳ありませんでした。

上記のような「どのクラスを使うといいか?」というのは変わらず難しい課題ですが引き続きがんばって参ります。
ありがとうございました。

□別スレッド:生成したTImageの幅と高さを設定するには?
https://www.petitmonte.com/bbs/answers?question_id=3790


デル太  2006-03-25 03:24:55  No: 20681

URLを間違えてしまいました。訂正します。

□別スレッド:生成したTImageの幅と高さを設定するには?
https://www.petitmonte.com/bbs/answers?question_id=3789


えーと  2006-03-25 08:57:56  No: 20682

TImage やら TPaintBox から派生するのはいかにも無駄っぽいです。
要は、自分が描画したいときに保持しているデータで自分を描画出来れば、
あとは、それを複数管理する「神の目」であるプログラマのコードで十分なのでは?

最初にしめした TZukei クラスの派生クラス プラス TPaintBox 一つ、プラス
TObjectList 一つでなんでも出来るような気がします。OnClick なんかは
PaintBox がすべて受け取って、その座標から管理しているオブジェクトが
簡単に分かりますし、クリックしたところの色だって Canvas.Pixels[] で
分かりますし。よほど簡単だと思います。


デル太  2006-03-25 23:37:27  No: 20683

えーとさん、いつもありがとうございます!

> 要は、自分が描画したいときに保持しているデータで自分を描画出来れば、

TImageを継承したクラスから生成したオブジェクトで実現中なのですが、TLineを参考に、自分に描画位置を保持させています。

> あとは、それを複数管理する「神の目」であるプログラマのコードで十分なのでは?

はい、ZukeiListを参考に、描画種類別ですが、ComponentListが各オブジェクトを管理しています。
まさに、ご指摘どおりに構築を進めている感じです。
なのに、ひとつわからない点があり、そのために難しいことをしているようです。

> 最初にしめした TZukei クラスの派生クラス プラス TPaintBox 一つ、プラス
> TObjectList 一つでなんでも出来るような気がします。

はい、この形でできれば、とてもシンプルになりそうですね。
右クリックメニューを実装するにも1つのPaintBoxに関連付けるだけで済むなど、メリットは大きいと感じます。

> OnClick なんかはPaintBox がすべて受け取って、
> その座標から管理しているオブジェクトが簡単に分かりますし、

ご指摘ありがとうございます。
この部分が理解できていない点と気づきました。
具体的なイメージがわかずにおります。

TLineは直線を表示するクラスですね。
なので、プロパティにやメンバにWidthやHeightを持たない=矩形領域がないオブジェクトが生成されますよね?

そうすると「オブジェクトLine1が描画した線をがクリックされたとき、Line1と特定できる」とおっしゃているように読み取りました。
そうなんです。これができるのが一番理想なのです!

どうやると、実現できるのでしょうか?
ご指導いただけますでしょうか?

  1)PaintBoxがクリックを受け取る
  2)「その座標を描画したオブジェクト」を見つける

という流れなのかな?
2)は直線の方程式や楕円の方程式から求めることになりますか?
2)を分割すると、次のようにイメージできました。

  2-1)各オブジェクトに自分の描いた図形に該当するか判定するメソッドを持たせる
  2-2)神の目が、各オブジェクトに座標で問い合わせる
  2-3)該当するオブジェクトが私が書いたよ!と手を挙げる(Trueを返すなど)

2-3)の中で方程式を使うのかな?
方程式なら楕円の描画線に限る場合と領域内を含む場合とどちらも対応できそうですね。

この形で考えてみたらだんだんイメージが湧いてきました!
クリックした箇所の図形が重なっている場合、複数のオブジェクトが手を挙げるのでそれにも対応できますね。
一番上(最後に描画した図形)だけを扱いたいなら、ComponentListの登録順に処理しますから、最後に手を挙げた図形だけを扱えばいいですね。

こんな感じのイメージで正しいでしょうか?

また、ご迷惑でなければ、方程式で座標を判定する方法について一例を教えていただけると嬉しいです。
どうぞ、お力添えをよろしくお願いいたします。


えーと  2006-03-26 07:52:03  No: 20684

TZukei に

function IsIncludePoint(x, y: integer): Boolean;virtual; abstract;

のようなメソッドを設定して、下位クラスに実装させます。

TLine の場合、点が与えられれば、線までの
距離が決まりますから、ある距離より小さければ「手を挙げる」ということで。
実際は線分なので、端点からの距離も計算が必要かも知れません。
四角は簡単。円や楕円は面倒かな? 数学の問題ですね。調べてください。


えーと  2006-03-26 08:37:40  No: 20685

すこし考えてみました。

デル太さんがどのような図形を想定してるのか、いまいち分かりません
ので、線は線分、四角は枠だけ、楕円はわっかだけ、として厳密に描かれている
ピクセルをクリックしたときだけ、「手をあげる」簡単な方法を思いつき
ました。

PaintBox と同じサイズの Bitmap を用意して、PixelFormat を pf24bit にして
ObjectList と同様に、どこかにしまっておきます。そして、TZukei の
protected フィールドに FColor:TColor をもうけ、派生クラスの TLine や
TEllipse を動的に作成するときに、順番に色を設定します。

予め ColorCounter: integer; としておいて

FColor := TColor(ColorCounter);
Inc(ColorCounter);

とすると、個々のオブジェクトは違った色を持つことになります。そして、
PaintBox に自分を描くのと全く同様にしてしまってある Bitmap にも
一度だけ描きます。ただし、そのときは、自分の色で描きます。ユーザが PaintBox
をクリックしたとき、その座標で Bitmap の Pixels[] で色を調べると
背景色(デフォルトのビットマップは白 = $FFFFFF)と違う場合、どのオブジェクトが
クリックされたのか色を比較して分かると言うわけです。

TColor は integer にキャストできますので、if で簡単に一致が確認できます。

枠や輪っかではなく、領域としたい場合は、ビットマップの Canvas のブラシに
も同じ色を設定する(bsSolid で)といいです。もちろん、輪っかのときは bsClear で。

図形が重なっていると、最後に描かれた図形だけが手をあげます。これはユーザの
感覚と一致するのではないでしょうか。


スタテツ  2006-03-26 18:32:02  No: 20686

興味を持ったので作ってみました。
http://www.studio-fe.hiroishi.org/files/DDraw002.zip

できること
  四角、線を描く
  複数選択および拡大縮小
  Delete
今後欲しい機能
  文字を描く
  ポリゴン対応
  保存、メタファイルへの書き出し(WMF)

暇があったらもう少しがんばってみます。


デル太  2006-03-26 20:54:54  No: 20687

えーとさん、ありがとうございます。
今、えんぴつと紙を使って考えていました。

  ・図形は自分の判定方法(方程式)を知っているから、判定処理を図形ごとに持たせればいいんだ!

と気づきました。
そして、昨晩のひとつめのメッセージを拝見して、そのアイデアでOKとわかりました。自分のアイデアが使えるとわかるのはとても安心できます。ありがとうございます。

クリックした点が線上にあるかどうかは、たとえば直線なら1次方程式を解けばいいのでしょうか?
手書きでやるときは、始点終点からy=ax+bのa,bを求め、クリック座標が条件を満たすか判断できます。
ただ、プログラムではどうやるものなのでしょう?この点、経験がなくて・・。
始点終点からa,bを求めること自体、1次方程式を解くことになりますよね?
何か方法がありそうですね。調べてみようと思います。

ふたつめのメッセージは、ユーザへの描画とは別に判定用の領域を持つ、ということですよね?
色による判定は色が増えると見にくそう、と感じていましたのでよろこんで拝見しました。
データベース的に表現すると、表示画像と判定画像はピクセルの座標で関連付けることになりますよね?
この方法はシンプルですね。とてもわかりやすいです。

図形が重ならなければ、色による判定はなくてもOKですが、重なる場合、後から描いた図形が既に描かれた図形を上書きするので、後からの図形が手を挙げることになる・・、そう理解できました。
ご指摘のように、ユーザの感覚に近いですね。

スタテツさん、作成したいただいたとこのこと、ありがとうございます。
あ、お名前からなんとなくホームページを拝見したことあるような気がします。
お世話になっております。

ただ、どうも、自宅のマシンからダウンロードできません。
ときどき、サイトによってNGなことがあるので、明日以降職場で試してみます。
楽しみにしています。
どうぞ、すぐに削除なさらないでくださいね。
よろしくお願いします。


ぽんちゃん  2006-03-27 02:36:22  No: 20688

いろんなやり方が出てますね。
おもしろそうなので、
他のやり方も紹介させてください。

判定処理を図形ごとに持たせたいのであれば、
Win32のリージョンAPIを使って、Delphiのコンポーネントを
変形させる、というやり方もあるかもしれません。これも参考に
してみてください。

例    

//(PanelとMamoを用意)
procedure TForm1.FormCreate(Sender: TObject);
var
  rgn: HRGN;
begin
  rgn := CreateEllipticRgn(5,5,Panel1.Width-5,Panel1.Height-5);
  Panel1.Color := clRed;
  SetWindowRgn(Panel1.Handle, rgn, true);  
  DeleteObject(rgn);
end;

//マウスが入ればMemoに出力
procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Memo1.Lines.Add('Enter');
end;

ただし、TImageには使えないので、代わりにTPanelを
使ってみました。
なので、本格的にTPanelに描画するなら、
Win32APIのGetDCなどで、デバイスコンテキストを所得して、
TPanelに描画するやり方になります。

リージョンはかなり複雑な図形も作ろうと思えば、
できます。詳しくはWin32APIのサイトなどを参考にしてください。


デル太  2006-03-27 03:13:15  No: 20689

ぽんちゃんさん、ありがとうございます。
これも面白い発想ですね!?
発想を変えて、既存のオブジェクトの機能を判定用に応用できると便利ですし楽しいです。

この形だと、TImageを個別に持つよりもメモリなどで有利になるのかな?と素人考えで感じますが、実際どうなのでしょうね?

実は直線判定をWEBで調べていて、結構簡単に判定できそうだと感じていました。
楕円なども同じように式を見つけられればできそうです。パラメータはきっとDelphiの楕円描画関数などと同じになるのかな?
と考えてみて、各オブジェクト用に見えないところに判定用のBitmapをもうひとつ用意すれば?と思いつきました。
その都度、各オブジェクトが描画して座標部分の色を判定する形です。

でも、これだと、別にTImageを持つのとあまり変わらないと気づきました。
その都度やるだけ、かえって遅そうだし・・。

そう考えて、直線は方程式型でやり、楕円などをぽんちゃんさんのサンプル方式でというのも便利な感じがしました。

>なので、本格的にTPanelに描画するなら、
>Win32APIのGetDCなどで、デバイスコンテキストを所得して、
>TPanelに描画するやり方になります。

今までこの掲示板で教えていただいて、Panelへの描画はなんとかなりそうです。
ただ、問題はPanelにTransparentプロパティがないみたいなんですよね。
透明化できるかどうかが課題になると思います。
やり方があるのかな・・?

領域判定だけでも使えればと、ぽんちゃんさんのサンプルでVisibleをFalseにしてみたら、マウスイベントが起きませんね。これ、あたりまえですね・・。
でも、場面によってはとても有効な考え方にできそうです。
ご提案、ありがとうございました!

□直線判定の参考になるページ
−点と線の領域判定
http://bal4u.dip.jp/mt/program/archives/2004/12/post_3.html

−☆もっと簡単に−線分交差判定−☆
http://www5d.biglobe.ne.jp/~tomoya03/shtml/algorithm/Intersection.htm


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加