テキストボックスから入力された数値で座標をとり、線を書く

タグの編集
投稿者 ゆうさく  (社会人) 投稿日時 2018/6/6 19:01:17
コードは下記です

Public Class Form1    
Private zahyou(2) As Point


Private Sub txt()

        zahyou(0).X = Val(TextBox1.Text) 

        zahyou(0).Y = Val(TextBox2.Text)

        zahyou(1).X = Val(TextBox3.Text)

        zahyou(1).Y = Val(TextBox4.Text)

        zahyou(2).X = Val(TextBox5.Text)

        zahyou(2).Y = Val(TextBox6.Text)

    Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height)
        Dim g As Graphics = Graphics.FromImage(canvas)
    Call txt()
        For i As Integer = 1 To zahyou.Count - 1

            g.DrawLine(Pens.Black, zahyou(0), zahyou(1))
        Next



    End Sub

 やりたいことは2つあります

1つめは
zahyou(0).X = Val(TextBox1.Text) 
zahyou(0).Y = Val(TextBox2.Text)
      ・
      ・
      ・
      ・
を繰り返しの処理でまとめたいです


2つめは

g.DrawLine(Pens.Black, zahyou(0), zahyou(1))
g.DrawLine(Pens.Black, zahyou(1), zahyou(2))
g.DrawLine(Pens.Black, zahyou(0), zahyou(2))

                  

今は勉強のためzahyouのところを固定にしているんですけれども
これも繰り返しの処理でまとめたいです。
zahyou()←の中に何を入れていいのかわかりません。

例えば
g.DrawLine(Pens.Black, zahyou(0), zahyou(0))

ようにzahyouがかぶらないようにもしたいのですが
調べてみても思いつきませんでした。


よろしくお願いいたします。

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/6/7 11:58:32
提示頂いたプログラムが不完全なので、やりたいことが曖昧になってしまっています。(End Sub は書き忘れかだろうと思いますが)

たとえば、Bitmap を生成していますが、それを表示したり保存したりするコードが無いですよね。
結果として、生成した Bitmap や Graphics を破棄することもせず、単に作り続けて放置するだけのプログラムになっています。

一応、Form1 の Paint イベントで処理しているという事は、Form1 上に描画したいのだと思いますが、そのような画面設計だと、TextBox が描画領域に重なって配置され、邪魔になってしまうと思います。
「Form ではなく PictureBox への描画に切り替える」か「座標入力フォームと描画先のフォームを別画面にする」などした方が良いかもしれません。


> を繰り返しの処理でまとめたいです
TextBox のかわりに、DataGridView などを使うようにするか、
もしくは、代入式の右辺も配列にする(TextBox の配列)と良いでしょう。


> g.DrawLine(Pens.Black, zahyou(0), zahyou(0))
> ようにzahyouがかぶらないようにもしたいのですが

3 座標の場合は△を描くとして…。
4 座標の場合は□を描くのか☒を描くのかが分からなかったので、両方のパターンで書いてみました。

Dim maxIndex As Integer = zahyo.Length - 1
For i = 0 To maxIndex
    For j = 0 To maxIndex
        If i <> j Then
            e.Graphics.DrawLine(Pens.Black, zahyo(i), zahyo(j))
        End If
    Next
Next



Dim maxIndex As Integer = zahyo.Length - 1
For i = 1 To maxIndex
    e.Graphics.DrawLine(Pens.Black, zahyo(i - 1), zahyo(i))
Next
If maxIndex > 0 Then
    e.Graphics.DrawLine(Pens.Black, zahyo(maxIndex), zahyo(0))
End If



> よろしくお願いいたします。
サンプルを書いてみました。
http://www.vb-user.net/junk/replySamples/2018.06.07.11.55/Sample30338.zip

お使いの VB バージョンが分からなかったので、
比較的古めのバージョン(VB2008)で作成してあります。
投稿者 (削除されました)  () 投稿日時 2018/6/7 13:16:03
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/6/7 13:19:04
> 両方のパターンで書いてみました。
すべての点を繋ぐ場合のコードを、少し修正。

> For i = 0 To maxIndex
>  For j = 0 To maxIndex

これだと、A⇒B 方向の線分と B⇒A 方向の線分が重ねて描画されてしまうので

For i = 0 To maxIndex - 1
 For j = i + 1 To maxIndex

の方が良さそうです。 
投稿者 ゆうさく  (社会人) 投稿日時 2018/6/10 21:38:42
教えていただいたのを参考にし、3つ 座標を入れて△を描くこうとするとおかしなことになります

座標 (150,100)(100,200)(300,200)を入力してるんですけれども
実施すると (0,0)(100,200)(300,200)
の3点を結んだ三角形になっています。

(150,100)(100,200)(300,200)
この3つを結んだ三角形を作りたいです。

        For i = 0 To maxIndex - 1
            For j = i + 1 To maxIndex
                If i <> j Then
                    g.DrawLine(Pens.Black, zahyo(i), zahyo(j))
                End If
            Next
        Next

予想なんですけれども
例えば
i = 0 の時  g.DrawLine(Pens.Black, zahyo(0), zahyo(1))
i = 1 の時  g.DrawLine(Pens.Black, zahyo(1), zahyo(2))
i = 2 の時  g.DrawLine(Pens.Black, zahyo(2), zahyo(3))

になっていると思います

i = 2 の時に g.DrawLine(Pens.Black, zahyo(0), zahyo(2))
にしたいんですけど、どこを修正すればいいでしょうか?

Public Class Form1
    Private zahyo(8) As Point

Private Sub txt()


        zahyo(0).X = Val(TextBox1.Text)

        zahyo(0).Y = Val(TextBox2.Text)

        zahyo(1).X = Val(TextBox3.Text)

        zahyo(1).Y = Val(TextBox4.Text)

        zahyo(2).X = Val(TextBox5.Text)

        zahyo(2).Y = Val(TextBox6.Text)

        zahyo(3).X = Val(TextBox7.Text)

        zahyo(3).Y = Val(TextBox8.Text)

        zahyo(4).X = Val(TextBox9.Text)

        zahyo(4).Y = Val(TextBox10.Text)

        zahyo(5).X = Val(TextBox11.Text)

        zahyo(5).Y = Val(TextBox12.Text)

        zahyo(6).X = Val(TextBox13.Text)

        zahyo(6).Y = Val(TextBox14.Text)

        zahyo(7).X = Val(TextBox15.Text)

        zahyo(7).Y = Val(TextBox16.Text)



    End Sub

    Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height)
        Dim g As Graphics = Graphics.FromImage(canvas)

        Call txt()


        Dim maxIndex As Integer = zahyo.Length - 1
        For i = 0 To maxIndex - 1
            For j = i + 1 To maxIndex
                If i <> j Then
                    g.DrawLine(Pens.Black, zahyo(i), zahyo(j))
                End If
            Next
        Next

       PictureBox1.Image = canvas

    End Sub
End Class
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/6/11 17:13:37
Visual Basic のどのバージョンをお使いですか?


> 座標 (150,100)(100,200)(300,200)を入力してるんですけれども
それだと、座標が「3 つ」しか無いですよね。

TextBox の数が幾つであろうと、提示頂いたコードにおいては、
最終的に座標を管理しているのは
> Private zahyo(8) As Point
という、zahyo(0)~zahyo(8) の「9 つの座標」であるはずです。


> 実施すると (0,0)(100,200)(300,200)
> の3点を結んだ三角形になっています。
テキストボックスの入力値にとらわれず、
変数 zahyo に代入された 9 つの値を確認してみてください。
その中に、(0, 0) を保存しているデータが混入してはいませんでしたか?

(0, 0) が紛れ込んでいたなら、(0, 0) を結ぶ線分が描画されるのは当然の結果なので、
まずは (0, 0) が含まれていないことを確認するのが先決です。

もし、9 つすべてを使いたいわけのではなく、3 つだけでよいのなら、
 案1) zahyo 配列は 9 つにせず、3 つにしておく(あるいは動的に増減させる)
 案2)「使わない座標」と「(0, 0) という座標」を区別できるよう管理方法を見直す
などを再検討してみてください。



> 予想なんですけれども
『予想』するだけで終えず、きちんと『確認』しておきましょう。

ステップ実行で一行ずつ処理を追い、それぞれの変数に
どのような値が代入されているのか確認すれば、予想が正しいかどうか分かりますよね。
http://rucio.a.la9.jp/main/dotnet/shokyu/standard41.htm


> zahyo(0).X = Val(TextBox1.Text)
「zahyo(0).X = Val(TextBox1.Text)」のような変換は望ましくありません。
Val ではなく、Integer.TryParse を使うようにしましょう。
 If Integer.TryParse(TextBox1.Text, zahyo(0).X)


Val による変換結果は Double 型です。しかし、代入先となる、zahyo(0).X の型は
Integer 型なので、代入式の左右でデータ型が不一致になってしまっているからです。

また、Val 関数をさけるべき利用はもう一つあります。Val 関数は、
Val("99.8%") → InvalidCastException
Val("1.8E308") → OverflowException
のように、引き渡す文字列によってはエラーになってしまう可能性があるためです。


TryParse なら、どのような文字列を渡してもエラーにはなりませんし、
入力値が "" の場合と "0" の場合を区別して処理させることもできます。



> Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
>  Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height)
>  Dim g As Graphics = Graphics.FromImage(canvas)

このコードも悪手です。Paint イベントで Bitmap や Grapchis を作り直すべきではありません。
Paint イベントを使う場合は、e.Graphics に対して描画を行うようにすべきです。
(先週、るきおさんが書かれたサンプルコードもそうなっていましたよね)


もし、Bitmap を毎回生成するのなら、それは Paint イベントが呼ばれたタイミングではなく、
座標値が変更された場合に行うべきです。座標値が変化しない限りは、
Bitmap を描きなおす必要も無いのですから。


また、Bitmap や Graphics を作り直すにしても、作ったまま放置するのではなく、
未使用になったオブジェクトは、Dispose メソッド(あるいは Using ブロック)を用いて
破棄するようにすべきです。

Call txt()
Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height)
Using g As Graphics = Graphics.FromImage(canvas)
  Dim maxIndex As Integer = zahyo.Length - 1
  For i = 0 To maxIndex - 1
    For j = i + 1 To maxIndex
      g.DrawLine(Pens.Black, zahyo(i), zahyo(j))
    Next
  Next
End Using
Dim oldImage = PictureBox1.Image
PictureBox1.Image = canvas
If oldImage IsNot Nothing Then
 oldImage.Dispse()
End If