C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

Visual Basicで簡易CADを作成 /shiro (25/07/25(Fri) 20:50) #103783
Re[1]: Visual Basicで簡易CADを作成 /KOZ (25/07/27(Sun) 14:35) #103784
  └ Re[2]: Visual Basicで簡易CADを作成 /shiro (25/07/28(Mon) 20:17) #103785
    ├ Re[3]: Visual Basicで簡易CADを作成 /kiku (25/07/29(Tue) 08:49) #103786
    └ Re[3]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/07/29(Tue) 14:06) #103791
      └ Re[4]: Visual Basicで簡易CADを作成 /shiro (25/07/30(Wed) 12:22) #103792
        └ Re[5]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/07/30(Wed) 15:46) #103794
          └ Re[6]: Visual Basicで簡易CADを作成 /shiro (25/07/31(Thu) 07:34) #103795
            └ Re[7]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/07/31(Thu) 10:48) #103796
              └ Re[8]: Visual Basicで簡易CADを作成 /shiro (25/08/01(Fri) 06:15) #103797
                ├ Re[9]: Visual Basicで簡易CADを作成 /kiku (25/08/01(Fri) 08:47) #103798
                │└ Re[10]: Visual Basicで簡易CADを作成 /shiro (25/08/02(Sat) 03:40) #103803
                └ Re[9]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/01(Fri) 10:46) #103800
                  ├ Re[10]: Visual Basicで簡易CADを作成 /kiku (25/08/01(Fri) 10:50) #103801
                  └ Re[10]: Visual Basicで簡易CADを作成 /shiro (25/08/02(Sat) 03:38) #103802
                    └ Re[11]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/03(Sun) 11:50) #103805
                      └ Re[12]: Visual Basicで簡易CADを作成 /shiro (25/08/04(Mon) 05:10) #103806
                        └ Re[13]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/04(Mon) 11:36) #103808
                          └ Re[14]: Visual Basicで簡易CADを作成 /shiro (25/08/04(Mon) 18:59) #103811
                            └ Re[15]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/04(Mon) 19:43) #103812
                              └ Re[16]: Visual Basicで簡易CADを作成 /shiro (25/08/05(Tue) 12:42) #103814
                                └ Re[17]: Visual Basicで簡易CADを作成 /shiro (25/08/05(Tue) 12:44) #103815
                                  └ Re[18]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/05(Tue) 18:02) #103817
                                    └ Re[19]: Visual Basicで簡易CADを作成 /shiro (25/08/05(Tue) 19:28) #103819
                                      └ Re[20]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/05(Tue) 20:39) #103821
                                        └ Re[21]: Visual Basicで簡易CADを作成 /shiro (25/08/06(Wed) 12:22) #103822
                                          └ Re[22]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/06(Wed) 14:42) #103825
                                            ├ Re[23]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/06(Wed) 17:53) #103828
                                            │└ Re[24]: Visual Basicで簡易CADを作成 /shiro (25/08/06(Wed) 19:13) #103830
                                            │  ├ Re[25]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/06(Wed) 19:44) #103832
                                            │  │└ Re[26]: Visual Basicで簡易CADを作成 /shiro (25/08/07(Thu) 07:06) #103834
                                            │  │  └ Re[27]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/07(Thu) 12:18) #103835
                                            │  │    └ Re[28]: Visual Basicで簡易CADを作成 /shiro (25/08/07(Thu) 19:14) #103836
                                            │  │      └ Re[29]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/07(Thu) 20:25) #103837
                                            │  │        ├ Re[30]: Visual Basicで簡易CADを作成 /shiro (25/08/08(Fri) 03:34) #103838
                                            │  │        └ Re[30]: Visual Basicで簡易CADを作成 /shiro (25/08/08(Fri) 07:29) #103839
                                            │  │          └ Re[31]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/08(Fri) 09:40) #103841
                                            │  │            └ Re[32]: Visual Basicで簡易CADを作成 /shiro (25/08/09(Sat) 05:05) #103842
                                            │  │              └ Re[33]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/09(Sat) 14:24) #103843
                                            │  │                └ Re[34]: Visual Basicで簡易CADを作成 /shiro (25/08/09(Sat) 19:18) #103844
                                            │  └ Re[25]: Visual Basicで簡易CADを作成 /魔界の仮面弁士 (25/08/06(Wed) 19:53) #103833
                                            └ Re[23]: Visual Basicで簡易CADを作成 /shiro (25/08/18(Mon) 12:41) #103847
                                              └ Re[24]: Visual Basicで簡易CADを作成 /kiku (25/08/20(Wed) 15:09) #103848
                                                └ Re[25]: Visual Basicで簡易CADを作成 /shiro (25/08/20(Wed) 18:35) #103849
                                                  └ Re[26]: Visual Basicで簡易CADを作成 /kiku (25/08/21(Thu) 09:03) #103850
                                                    └ Re[27]: Visual Basicで簡易CADを作成 /shiro (25/08/21(Thu) 22:01) #103851
                                                      └ Re[28]: Visual Basicで簡易CADを作成 /kiku (25/08/22(Fri) 08:45) #103852
                                                        └ Re[29]: Visual Basicで簡易CADを作成 /shiro (25/08/22(Fri) 13:20) #103853
                                                          └ Re[30]: Visual Basicで簡易CADを作成 /kiku (25/08/22(Fri) 16:45) #103854
                                                            └ Re[31]: Visual Basicで簡易CADを作成 /shiro (25/08/22(Fri) 20:33) #103855
                                                              └ Re[32]: Visual Basicで簡易CADを作成 /kiku (25/08/25(Mon) 08:42) #103857


親記事 / ▼[ 103784 ]
■103783 / 親階層)  Visual Basicで簡易CADを作成
□投稿者/ shiro (1回)-(2025/07/25(Fri) 20:50:26)

分類:[VB.NET/VB2005 以降] 

Visual Basicで簡易CADを作成したいため、下記のようにグリッド座標を作成しました。
18*18個のマス目が出来ます。点線は半分ズレた柱を配置するためです。

最初に「柱」を選択し、柱を配置したい線の交点にマウスを近づけると、一番近い交点を認識し、そこに柱の■を配置したいです。
柱の数だけ連続して処理したいです。

その後、「壁」を選択し、柱■の2点間をラバーバンド機能でマウスで長方形で配置したいです。

グリッド座標を作成するまでは出来たのですが、その後の処理をするコードで失敗していました。
どのようなコードで記述すれば上手く行くか、アドバイス頂けると有難いです。


                記

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

Dim i As Integer
Dim j As Integer

Dim pen As New Pen(Color.Black, 1)

For i = 0 To 36
If (i Mod 2 = 1) Then
pen.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawLine(pen, 100, 100 + 10 * i, 460, 100 + 10 * i)
Else
pen.DashStyle = Drawing2D.DashStyle.Solid
e.Graphics.DrawLine(pen, 100, 100 + 10 * i, 460, 100 + 10 * i)
End If
Next

For j = 0 To 36
If (j Mod 2 = 1) Then
pen.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawLine(pen, 100 + 10 * j, 100, 100 + 10 * j, 460)
Else
pen.DashStyle = Drawing2D.DashStyle.Solid
e.Graphics.DrawLine(pen, 100 + 10 * j, 100, 100 + 10 * j, 460)
End If
Next

End Sub

[ □ Tree ] 返信 編集キー/

▲[ 103783 ] / ▼[ 103785 ]
■103784 / 1階層)  Re[1]: Visual Basicで簡易CADを作成
□投稿者/ KOZ (489回)-(2025/07/27(Sun) 14:35:25)
No103783 (shiro さん) に返信
> 最初に「柱」を選択し、柱を配置したい線の交点にマウスを近づけると、一番近い交点を認識し、そこに柱の■を配置したいです。
> 柱の数だけ連続して処理したいです。
> その後、「壁」を選択し、柱■の2点間をラバーバンド機能でマウスで長方形で配置したいです。

ここが参考になるかも。
https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103784 ] / ▼[ 103786 ] ▼[ 103791 ]
■103785 / 2階層)  Re[2]: Visual Basicで簡易CADを作成
□投稿者/ shiro (2回)-(2025/07/28(Mon) 20:17:16)

> ここが参考になるかも。
> https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm

非常に参考になりました。
MouseDown、MouseMoveで、ラバーバンドも上手く描けました。有難うございます。

簡易CADですので、MouseDownで描いた柱等が誤った場合が想定されました。
情報提供頂いたHPには消去方法がありませんでした。

右クリック等で、MouseDownで描いた図形を削除するVBの方法をご存じでしたらご教授いただければ幸いです。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103785 ] / 返信無し
■103786 / 3階層)  Re[3]: Visual Basicで簡易CADを作成
□投稿者/ kiku (483回)-(2025/07/29(Tue) 08:49:21)
No103785 (shiro さん) に返信
>>ここが参考になるかも。
>>https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm
>
> 右クリック等で、MouseDownで描いた図形を削除するVBの方法をご存じでしたらご教授いただければ幸いです。

上記リンクの「四角形をクリックすると色が変わる」がそのまま参考になると思います。

1.MouseDown発生
2.右クリックを判定
3.右クリックした場所が四角形内かどうか判定し、オブジェクトを特定
4.polygonsから特定されたオブジェクトを削除
5.PictureBoxを強制的に再描画

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103785 ] / ▼[ 103792 ]
■103791 / 3階層)  Re[3]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3871回)-(2025/07/29(Tue) 14:06:15)
No103783 (shiro さん) に返信
> Dim i As Integer
> For i = 0 To 36
このような書き方をするのは、VB.NET 2002 までです。

VB.NET 2003 以降のバージョンにおいては、For ループのカウンタ変数は事前宣言せずに
 For i As Integer = 0 To 36
のように「For ループ内でのみ使える局所変数」とすることが強く推奨されます。

さらに VB2008 以降では、初期値指定のある変数宣言時には型宣言を省略できるようになったため
 For i = 0 To 36
のように、As Integer 部分も省略する記法が一般的になりました。結果的に、現行バージョンでは
For ループのカウンタ変数は、明示的な変数宣言無しで使っているように見える書き方になっています。


> Dim pen As New Pen(Color.Black, 1)
Pen、Brush、Font などは IDisposable なオブジェクトであるため、
自身で New したものは、使用しなくなった時点で Dispose の呼び出しが必須となります。
VB2005 以降をお使いであれば Using ブロックで囲むのが良いでしょう。

すなわち、「Dim pen As New Pen(Color.Black, 1)」ではなく
 Using pen As New Pen(Color.Black, 1)
  :
 End Using
の構文にします。

ただし Dispose するのは「自身で生成したオブジェクト」に限られます。
Pens.Black や Brushed.Red などは自身で作成したものではなく、
共有オブジェクトなので、勝手に Dispose してはいけません。


Imports System.Drawing.Drawing2D
Public Class Form1
  'マス目の位置
  Private Const marginSize As Integer = 100 '左上の余白サイズ
  Private Const gridCellSize As Integer = 10 '小マスの大きさ
  Private Const gridCount As Integer = 36  '縦横の実線マスの数

  Private ReadOnly edgePos As Integer = marginSize + gridCellSize * gridCount

  Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    Using gridPen As New Pen(Color.Black)
      '方眼を描く
      For i = 0 To gridCount
        gridPen.DashStyle = If(i Mod 2 = 0, DashStyle.Solid, DashStyle.Dot)
        Dim linePos = marginSize + gridCellSize * i
        e.Graphics.DrawLine(gridPen, marginSize, linePos, edgePos, linePos)
        e.Graphics.DrawLine(gridPen, linePos, marginSize, linePos, edgePos)
      Next
    End Using
  End Sub
End Class



No103785 (shiro さん) に返信
>>ここが参考になるかも。
>>https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm
> 非常に参考になりました。
> MouseDown、MouseMoveで、ラバーバンドも上手く描けました。有難うございます。

MouseDown / MouseMove イベントは、何をどこに描くのかという「座標情報」を定めています。
座標情報が確定したところで Invaliedate メソッドを呼び出して、描画依頼を飛ばします。

Paint イベントでは、事前に定められた座標情報に従って、柱や壁やラバーバンドを描く処理だけを記述します。


> 情報提供頂いたHPには消去方法がありませんでした。
> 右クリック等で、MouseDownで描いた図形を削除するVBの方法をご存じでしたらご教授いただければ幸いです。

最後に、「クリックした図形の色を変える」サンプルがありましたよね。

あのサンプルでは Polygon クラスを自作して、
 Private polygons As New List(Of Polygon)()
で管理されていました。

今回の場合、Polygon ではなく Pillar クラスや Wall クラスを自作しているかと思いますが、
考え方としては同じです。

選択された図形を管理できるようになっているのですが、その List(Of ) コレクションから
不要な図形を RemoveAt (あるいは Remove) すれば、その図形を削除できることになります。
図形情報を Add / RemoveAt した後は、Invaliedate を呼び直すことで再描画されます。


右クリックの判定を行いたいのであれば、MouseDown イベントの段階で
 If e.Button.HasFlag(MouseButtons.Right) Then
で判定すれば良いでしょう。ちなみに、Control / Alt / Shift 判定も加えるなら
 If ModifierKeys.HasFlag(Keys.Control) Then
 If ModifierKeys.HasFlag(Keys.Alt) Then
 If ModifierKeys.HasFlag(Keys.Shift) Then
を使うことができます。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103791 ] / ▼[ 103794 ]
■103792 / 4階層)  Re[4]: Visual Basicで簡易CADを作成
□投稿者/ shiro (3回)-(2025/07/30(Wed) 12:22:13)
kiku 様

魔界の仮面弁士 様


回答有難うございます。VBでの壁画は始めたばかりで分からない点が多く参考になります。
画像削除方法を勉強してみます。

削除の前に問題が見つかり、自己解決出来なかったので投稿させて頂きます。
柱と壁を別々に動かすことは成功し、それをToolStripMenuItemで「柱・壁」をselect文で場合分けをしようとしました。
下記がその部分です。SnapToGridはFunctionで定めました。

初期設定で柱が設定されており、任意のグリッド座標に柱の四角形が配置されます。
その後、壁を選択し、ラバーバンド機能で柱間に壁を配置できました。
そこで、さらに柱を選択し柱を配置すると、全ての壁が消えてしまいます。

壁が消えないようにするにはどうすれば良いでしょうか。

最終的に、間違って配置した柱や壁も削除機能で削除し、最後に確定した配置から、
座標値を取得し、計算に進みたいと思っています。


           記

Public Class Form1

Private snappedRectangles As New List(Of Rectangle)


Private Sub Form1_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load

element = "col"
柱ToolStripMenuItem.Checked = True

'描画・オフ
drawFlag = False

End Sub


'================================================================================
'PictureBoxのMouseDownイベント
'--------------------------------------------------------------------------------
Private Sub PictureBox1_MouseDown(
ByVal sender As System.Object,
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureBox1.MouseDown

'描画
Select Case element

'柱
Case "col"

Dim snapPoint As Point = SnapToGrid(e.Location)

' 四角形の中心を交点に合わせる
Dim rect As New Rectangle(
snapPoint.X - rectangleSize \ 2,
snapPoint.Y - rectangleSize \ 2,
rectangleSize,
rectangleSize
)

snappedRectangles.Add(rect)
PictureBox1.Invalidate() ' 再描画

'描画フラグ・オン
drawFlag = True

'壁
Case "wall"

Dim g As Graphics
Dim wallPen As Pen

Dim snapPoint2 As Point = SnapToGrid(e.Location)

Dim rect2 As New Point(
snapPoint2.X - rectangleSize \ 2,
snapPoint2.Y - rectangleSize \ 2
)

'1回目のクリック
If drawFlag = False Then

'開始位置を取得
ptStart.X = snapPoint2.X
ptStart.Y = snapPoint2.Y

'描画フラグ・オン
drawFlag = True

'終了位置の初期化
ptEnd.X = -1
ptEnd.Y = -1

'2回目のクリック
Else

'ラバーバンドを消す
If ptEnd.X <> -1 Then
Call DrawRubberLine(ptStart, ptEnd)

End If

' 終了位置を取得
ptEnd.X = snapPoint2.X
ptEnd.Y = snapPoint2.Y


g = PictureBox1.CreateGraphics()

wallPen = New Pen(Color.Blue)
wallPen.Width = 5

'描画

g.DrawLine(wallPen, ptStart, ptEnd)

'描画フラグ・オフ
drawFlag = False

End If
End Select
End Sub

'--------------------------------------------------------------------------------
'PictureBoxのMouseMoveイベント
'--------------------------------------------------------------------------------
Private Sub PictureBox1_MouseMove(
ByVal sender As Object,
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureBox1.MouseMove

'描画
Select Case element

Case "wall"

Dim snapPoint2 As Point = SnapToGrid(e.Location)

Dim rect2 As New Point(
snapPoint2.X - rectangleSize \ 2,
snapPoint2.Y - rectangleSize \ 2
)

'描画フラグ・オフのとき
If drawFlag = False Then
Exit Sub
End If

'ラバーバンドを消す
If ptEnd.X <> -1 Then
Call DrawRubberLine(ptStart, ptEnd)

End If

' 終了位置を取得
ptEnd.X = snapPoint2.X
ptEnd.Y = snapPoint2.Y

Call DrawRubberLine(ptStart, ptEnd)

End Select

End Sub


Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

For Each rect In snappedRectangles
g.FillRectangle(Brushes.Red, rect)
g.DrawRectangle(Pens.Black, rect)
Next
End Sub

'--------------------------------------------------------------------------------
'ラバーバンド(直線)を描画
'--------------------------------------------------------------------------------
Private Sub DrawRubberLine(ByVal p1 As Point, ByVal p2 As Point)

'スクリーン座標に変換
p1 = PictureBox1.PointToScreen(p1)
p2 = PictureBox1.PointToScreen(p2)

'ラバーバンドを描画
'ControlPaint.DrawReversibleLine(p1, p2, Color.White)

ControlPaint.DrawReversibleLine(p1, p2, Color.Red)
End Sub

Private Sub 柱ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 柱ToolStripMenuItem.Click

element = "col"

'メニューの状態
柱ToolStripMenuItem.Checked = True
壁ToolStripMenuItem.Checked = False

'描画・オフ
drawFlag = False

End Sub

Private Sub 壁ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 壁ToolStripMenuItem.Click

element = "wall"

'メニューの状態
柱ToolStripMenuItem.Checked = False
壁ToolStripMenuItem.Checked = True

'描画・オフ
drawFlag = False

End Sub
End Class
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103792 ] / ▼[ 103795 ]
■103794 / 5階層)  Re[5]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3873回)-(2025/07/30(Wed) 15:46:27)
No103792 (shiro さん) に返信
> 回答有難うございます。VBでの壁画は始めたばかりで分からない点が多く参考になります。
壁画というか、描画かな?

掲示板にコードを投稿する時は、
 投稿モード:通常モード / 図表モード
の選択肢を、「通常モード」から「図表モード」に変更しておいてください。
そうしないと、空白やタブでのインデントが潰れてしまいますので。

もし、「通常モード」のままでインデントを維持したいのであれば、
連続する半角スペース を 全角スペース に置き換えておくことでも代用できます。
全角空白を混入させられない言語の時は都合が悪いですけれどね。


インデントが崩れていないかを確認したい場合は、右下の送信ボタンの近くにある
「プレビュー」にチェックをいれて、投稿前に事前確認することもできます。


> 画像削除方法を勉強してみます。
たとえば、先のサンプルの最後にある
「マウスをなぞった位置にリアルタイムに四角形を描画し、マウスを離すとその位置に四角形を追加し、後で追加された四角形をクリックすると色が変わる」
について。
https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm#A5

ここに下記のコードを加えると、選択状態の黄色い矩形が取り除かれます。

Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick
  Dim selectedPolygon As Polygon = polygons.FirstOrDefault(Function(p) p.IsSelected)
  If selectedPolygon IsNot Nothing Then
    '選択済みの矩形があれば削除
    polygons.Remove(selectedPolygon)
    PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
  End If
End Sub


もしも元のサンプルを拡張して、複数選択できるようにしていた場合はこのようにします。

Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick
  For n = polygons.Count - 1 To 0 Step -1
    '削除するとインデックス番号がズレるので、後ろの番号から探していく
    If polygons(n).IsSelected Then
      polygons.RemoveAt(n)
    End If
  Next
  PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
End Sub


上記ではダブルクリックで制御しています。
ダブルクリックで削除する方式では操作しにくいという場合は、別途、削除ボタンを用意し、
それを押したときに IsSelected = True なものを取り除く方式にしても良いでしょう。
あるいは、右クリックやコンテキストメニューなどで指示する手法を採用することもできますね。



> そこで、さらに柱を選択し柱を配置すると、全ての壁が消えてしまいます。
たとえば…

最初は、柱が 0 本、壁が 0 枚の状態です。
この場合、Paint イベントでは「方眼グリッドと、0 本の柱と、0 枚の壁」を描画するようにします。

柱を選択して、マウス操作で柱を建てる場所を決めました(MouseUp/MouseDown で座標を決めていきます)。
これにより、柱が 1 本、壁が 0 枚の状態になりました、
この場合、Paint イベントでは「方眼グリッドと、1 本の柱と、0 枚の壁」を描画するようにします。

同様にして、柱を 4 本建てました;左上、右上、左下、右下と正方形の頂点位置にくるように。
この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、0 枚の壁」を描画します。

さらに、壁を 3 枚描きました。上壁、右壁、下壁を作るイメージです。
この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、3 枚の壁」を描画します。壁が【コ】の字に描かれます。

右クリック等で右側の壁を一枚選択して、その壁だけを削除し、再描画を依頼します。
この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、2 枚の壁」を描画します。壁が【=】の字に描かれます。


すなわち、それぞれのオブジェクト(壁や柱)が、どの座標に描かれているのかを
List(Of ) 等で保持しておく必要があるということです。これは、Visual Basic 中学校サンプルでいう
 Private polygons As New List(Of Polygon)
のことです。現状はそれが無いため、再描画に耐えられるコードになっていません。


> それをToolStripMenuItemで「柱・壁」をselect文で場合分けをしようとしました。
変数 element の宣言が無いようですが、Form1 上に
 Private element As String
があるのでしょうか?


> g = PictureBox1.CreateGraphics()
これは使わないでください。

CreateGraphics は一時的な描画であり、恒久的に描画結果を残すものではありません。
ウィンドウのリサイズなどで容易に失われてしまうものです。
また、Invalidate が呼び出された時点で、CreateGraphics による描画結果は失われることになります。

CreateGraphics の呼び出しは行わないようにして、描画処理を
PictureBox1_Paint イベントの引数 e.Graphics に集約させるべきです。

KOZ さんが紹介してくださった Visual Basic 中学校 のサンプルにおいても、
MouseMove/MouseDown 中に Graphics を操作したりはせず、
Graphics での描画処理は、すべて Paint イベント内だけで行っていましたよね。


とはいえ、ラバーバンドのように一時的な描画に対しては、CreateGrapchis でも
用を足せるのですが、その場合も、Dispose の呼び出しは忘れないようにしてください。

Graphics は、Pen や Brush と同様に「破棄が必要なオブジェクト(IDisposable)」です。

CreateGraphics や Graphics.FromImage によって生成した Graphics オブジェクトは、
使用後に処分せねばならない、ということです。Graphics を自分で生成して使うケースでは
保持させる Graphics 変数を Using ブロックで囲んでおくことが望ましいです。

※ただし、Paint イベントの e.Graphics プロパティで得られる Graphics オブジェクトについては
 自分で生成したインスタンスではないため、処分してはいけません。


もしも描画する内容が多く、すべてを Paint イベント毎回再描画するとコストが高すぎる場合には、
永続的に表示し続けたい画像を Bitmap として用意する方法を併用できます。


たとえば、描画先のキャンバス(たとえば PictureBox1)と同サイズの Bitmap を
Dim bmp As New Bitmap(幅, 高さ) などで作成して
 Using g = Graphics.FromImage(bmp)
  …
 End Using
を使って描画した後、PictureBox1.Image = bmp で割り当てるという手法です。

この時、背景画像(PictureBox1.BackgroundImage)と前景画像(PictureBox1.Image)を併用しても構いません。
割り当てた Bitmap の背景部が、完全透過あるいは半透明だった場合、その下の画像や背景色が
透けて描画されますので、これを CAD のレイヤーのように使うことができます。

レイヤー数を増やしたい場合には、透過背景の Bitmap を複数枚用意しておき、
それが g.DrawImage メソッドで重ね合わせて描画することで対応できます。

なお、Bitmap も破棄が必要なオブジェクト(IDisposable)なため、
使わなくなった時点で bmp.Dispose() を呼ぶ必要があります。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103794 ] / ▼[ 103796 ]
■103795 / 6階層)  Re[6]: Visual Basicで簡易CADを作成
□投稿者/ shiro (4回)-(2025/07/31(Thu) 07:34:24)
魔界の仮面弁士 様

ダブルクリックでの画像消去コード有難うございました。
非常に参考になりました。

Form1 上に
 Private element As String
は設定していました。

参考のListは「polygon」となっています。
これですと、グリッドの交点を掴むと、スパン幅(1m)に壁が配置されてしまいます。

私のwallはグリッドの2点の交点を結んで、線分の幅で厚さを表そうとしています。
これですと実際のCADに近くなり使いやすいです。
その場合、Pointになるのではと思い、下記のとおり簡単なListを作ってみました。

しかし、今度は次の壁を描こうとすると前のが消えてしまいます。

Paintで「For Each」で描かないとダメなのでしょうか。
その場合のコード記述が分かりませんでした。


       記

   Private startPoint As Point
    Private endPoint As Point
    Private isDragging As Boolean = False

     Private pointList As New List(Of Point)



    Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
        startPoint = SnapToGrid(e.Location)
        isDragging = True
    End Sub

    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        If isDragging Then
            endPoint = SnapToGrid(e.Location)
            PictureBox1.Invalidate()
        End If
    End Sub

    Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
        isDragging = False
         endPoint = SnapToGrid(e.Location)
        PictureBox1.Invalidate()
    End Sub

    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
        Dim pen As New Pen(Color.Black, 1)
        ' グリッド描画
        For i = 0 To PictureBox1.Width Step 10
            e.Graphics.DrawLine(pen, i, 0, i, PictureBox1.Height)
        Next
        For j = 0 To PictureBox1.Height Step 10
            e.Graphics.DrawLine(pen, 0, j, PictureBox1.Width, j)
        Next

        ' ラバーバンド線描画
        ' If isDragging Then
        Dim rubberPen As New Pen(Color.Blue, 5)
        rubberPen.DashStyle = Drawing2D.DashStyle.Solid

        e.Graphics.DrawLine(rubberPen, startPoint, endPoint)
        'End If
    End Sub


[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103795 ] / ▼[ 103797 ]
■103796 / 7階層)  Re[7]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3874回)-(2025/07/31(Thu) 10:48:38)
No103795 (shiro さん) に返信
> ダブルクリックでの画像消去コード有難うございました。

先の例では、polygons.RemoveAt によって、選択中の四角形を削除したわけですよね。
後はその応用です。

たとえば、polygons(i) と polygons(j) のインデックスを入れ替えれば、
重なり合った図形の上下関係を入れ替える(手前に表示、奥に移動)実装を作れます。

polygons(n).Rect の座標を差し替えるコードを書けば、
選択中の四角形をドラッグ移動で別の場所に再配置することもできるでしょう。


> 参考のListは「polygon」となっています。
> これですと、グリッドの交点を掴むと、スパン幅(1m)に壁が配置されてしまいます。

グリッドには実線と破線がありますが、スナップ先はどちらの交点でも良いのですかね。

グリッドの実線最外周部にオブジェクトを配置すると、柱や壁の厚みが
方眼の外にはみ出る形になりそうですが…再外周部には柱を建てない方が良いのかな。


> 参考のListは「polygon」となっています。
Private polygons As New List(Of Polygon)
のことですね。VB は大文字小文字を区別しませんが、意識して使い分けましょう。

クラス名は通常、先頭を大文字で表記します。
Private 変数やローカル変数は先頭小文字にすることが多いです。


さて。参考コードでは描こうとしているのが「四角形」であることから、座標情報の保持のために
『Recangle 構造体をメンバーに持つ Polygon クラス』というオブジェクトを作っておられます。

まぁ、Polygon は本来「多角形」を表す単語ですし、
Recangle も四角形ではなく「矩形(長方形)」を表すものですから、
役割と名前に軽微なズレがあるのですが……それはさておき。


> しかし、今度は次の壁を描こうとすると前のが消えてしまいます。
「次の壁だけ」を描こうとするからいけないのです。

先の回答で、私は
>> Paint イベントでは「方眼グリッドと、4 本の柱と、3 枚の壁」を描画します
と書いています。配置しようとしている壁だけを描くのではありません。

各回の Paint イベントでは、
『今から配置しようとしているオブジェクトだけ』を描画するのではなく
『今までに配置してきたオブジェクトすべて』を毎回再描画することになります。

そもそも Invalidate メソッドは、これまでの描画結果をまっさらに無効化して、
最初から描画しなおすことを求めるメソッドです。当然、以前の描画内容は毎回クリアされます。

もしもそうした全描画が手間なのであれば、前回の回答のように、
>> 永続的に表示し続けたい画像を Bitmap として用意する方法
を併用することで、差分だけを描画する仕組みにすることもできます。


> 私のwallはグリッドの2点の交点を結んで、線分の幅で厚さを表そうとしています。
> これですと実際のCADに近くなり使いやすいです。
> その場合、Pointになるのではと思い、下記のとおり簡単なListを作ってみました。
いやいや。単に宣言しただけで、一切使われていませんよね。その List 変数…。


御存知の通り、Point が表す情報は「点」だけです。

そして List(Of Point) で表せる情報とは、「0個以上の点」の集合です。
保持される点は 2 個かもしれないし 3 個かも知れません。

しかし Wall を管理するなら、「点A、点B、厚み」の 3 情報が必要ではありませんか?
厚みが固定なら 2 情報ですかね。「左上点、右下点、壁の回転角」などの持ち方もできますが。

もしも壁が 3 箇所に配置されていたら、三枚分の座標情報が必要になります。

柱を配置している座標と、壁を配置する座標はそれぞれ別物なのですから
 Private pointList As New List(Of Point)
という変数だけでは、管理情報が曖昧になってしまいます。

そこで、それぞれの位置関係を分かりやすくするためにも、
Point の集合で管理するのではなく、Visual Basic 中学校のサンプルの考え方に倣って
壁を管理するための「Wall クラス」
柱を管理するための「Pillar クラス」
など、配置オブジェクトごとの管理クラスを用意することをおすすめします。


Wall(壁) クラスにせよ、Pillar(柱) クラスにせよ、
Dot(点) クラスにせよ、Line(線分) クラスにせよ、
Polygon(多角形) クラスにせよ、Ellipse(楕円) クラスにせよ、
それぞれに「自身の位置やサイズを表すためのプロパティ」を持たせます。

そして、柱や壁といったそれぞれのオブジェクトを List(Of ) コレクションなどで管理します。

個々のオブジェクトには、元サンプルのように IsSelected プロパティを設けても良いでしょうし、
BackColor プロパティや SelectionColor プロパティなどを追加することもできるでしょう。


もちろん、描画対象のオブジェクトによって、保持するべき情報は異なります。
壁なら「点A、点B、厚み」の 3 情報で管理されますが、
柱なら、「中心点、柱の太さ」の 2 情報ですかね。
円柱とか角柱といった追加情報を与えることもできそう。

それぞれを List(Of Wall) や List(Of Pillar) といった個別のコレクションで管理することもできますが、
すべての描画オブジェクトを束ねられるよう、Interface あるいは MustInherit を用いて
ポリモーフィズムな実装にするという選択肢もあります。


普段使う Form1 には、Label や TextBox や Button や PictureBox といった、
様々なオブジェクトを配置できますよね。これらのコントロールは、すべて Control クラスを
継承したクラスとして実装されており、Me.Controls のコレクションに束ねられています。



それと同様、この CAD アプリでも描画オブジェクトをポリモーフィズムな実装にしておけば、
Wall や Pillar などを、単一のコレクション上に束ねることができます。

ポリモーフィズムの例として…たとえば、全ての描画オブジェクトの継承元となる
 Public MustInherit Class DrawingObjectBase
  Public Property IsSelected As Boolean
  Public MustOverride Sub Draw(g As Graphics)
 End Class
といったベースクラスを用意してみます。

Form1 上のコレクションも、As New List(Of Polygon) ではなく
 Private drawItems As New List(Of DrawingObjectBase)()
のように、ベースクラスのコレクションに変更します。

Polygon や Wall や Pillar は、このDrawingObjectBase を継承させます。
たとえば Polygon であればこう。

 Public Class Polygon
  Inherits DrawingObjectBase

  Public Property Rect As Rectangle
  Public Overrides Sub Draw(g As Graphics)
   g.FillRectangle(If(IsSelected, Brushes.Yellow, Brushes.Cyan), Rect)
   g.DrawRectangle(Pens.Blue, Rect)
  End Sub
 End Class

このようにしておくと、List(Of )に追加したアイテムのインスタンスが
「壁」であれ「柱」であれ「矩形」であれ、PictureBox1 の Paint イベントの描画処理は

 For Each drawItem In drawItems
  drawItem.Draw(e.Graphics)
 Next

だけで済みます。コレクションにAdd したオブジェクトが柱であれ壁であれ矩形であれ、
PictureBox1_Paint 側ではそれらを区別せず、単に Draw メソッドを呼ぶだけで済むので、
配置オブジェクトの種類を増やすことも容易になります。



> Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
> Dim pen As New Pen(Color.Black, 1)
> Dim rubberPen As New Pen(Color.Blue, 5)
Pen は IDisposable なオブジェクトです。
繰り返しになりますが、自身で生成するのであれば、Using ステートメントで囲む癖をつけましょう。

とはいえ、これらの Pen は何度も使い続ける物ですから、描画のたびにその都度生成しなおすのではなく、
Form の起動時に一度だけ生成し、それを Private 変数で保持して使いまわすという方法でも構いません。
Visual Basic 中学校のサンプルで言うところの「Private selectPen As Pen」のように。

※本来は、フォーム終了時に selectPen も Dispose するコードを書くのが望ましいですが、
 Form1 終了時にはアプリ自体が終了して、自動的に解放されるため、先のサンプルでは省略されています。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103796 ] / ▼[ 103798 ] ▼[ 103800 ]
■103797 / 8階層)  Re[8]: Visual Basicで簡易CADを作成
□投稿者/ shiro (5回)-(2025/08/01(Fri) 06:15:16)
魔界の仮面弁士 様


ご助言有難うございます。

「ポリモーフィズムな実装」など、画像処理を始めたばかりで難解で、
まずはListで処理をしようと思います。

下記のとおりListで試したところ、Invalidate メソッドで壁も柱も
残ったのですが、壁を配置するとき、次の壁を書くと、直前に書いた壁が消えます。
結局、最後に書いた壁だけが残ります。

全て配置した壁を残る場合はどうすれば良いか上手くいきません。

     記

Public Class Form1

    Private pointList As New List(Of Point)
    Private element As String
    Private startPoint As Point
    Private endPoint As Point
    Private isDragging As Boolean = False

   Private Sub PictureBox1_MouseDown(
          ByVal sender As System.Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseDown

        Select Case element

                Case "wall"

                    startPoint = SnapToGrid(e.Location)

                     isDragging = True

        End Select

    Private Sub PictureBox1_MouseMove(
          ByVal sender As Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseMove

        Select Case element

            Case "wall"
 
                If isDragging Then
                    endPoint = SnapToGrid(e.Location)
                    PictureBox1.Invalidate()
                End If

        End Select

    End Sub


    Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp

        isDragging = False
 
        pointList.Clear()
        pointList.Add(startPoint)
        pointList.Add(endPoint)

        PictureBox1.Invalidate()

    End Sub

    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

        Dim pen7 As New Pen(Color.Blue, 5)

        If isDragging Then
            e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
        ElseIf pointList.Count = 2 Then
            e.Graphics.DrawLine(Pen7, pointList(0), pointList(1))
        End If

    End Sub


[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103797 ] / ▼[ 103803 ]
■103798 / 9階層)  Re[9]: Visual Basicで簡易CADを作成
□投稿者/ kiku (484回)-(2025/08/01(Fri) 08:47:54)
No103797 (shiro さん) に返信
> Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
>
> isDragging = False
>
> pointList.Clear()
> pointList.Add(startPoint)
> pointList.Add(endPoint)
>
> PictureBox1.Invalidate()
>
> End Sub
pointListを毎回クリアしているので、
保持している線は1本のみで、
過去の線は保持していないのではないでしょうか?

> Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
>
> Dim pen7 As New Pen(Color.Blue, 5)
>
> If isDragging Then
> e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
> ElseIf pointList.Count = 2 Then
> e.Graphics.DrawLine(Pen7, pointList(0), pointList(1))
> End If
>
> End Sub

ドラック中は、ドラック中の線のみを描画し、
ドラックしていないときは、保持した1本のみを描画しています。
やはり、過去の線を描画するようにはなっていないようです。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103798 ] / 返信無し
■103803 / 10階層)  Re[10]: Visual Basicで簡易CADを作成
□投稿者/ shiro (7回)-(2025/08/02(Sat) 03:40:48)
No103798 (kiku さん) に返信
> ■No103797 (shiro さん) に返信

kiku 様

> pointListを毎回クリアしているので、
> 保持している線は1本のみで、
> 過去の線は保持していないのではないでしょうか?

クリアしているのが問題でした。
解決できました。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103797 ] / ▼[ 103801 ] ▼[ 103802 ]
■103800 / 9階層)  Re[9]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3876回)-(2025/08/01(Fri) 10:46:05)
2025/08/01(Fri) 14:32:02 編集(投稿者)

No103797 (shiro さん) に返信
> Dim pen7 As New Pen(Color.Blue, 5)
うーん。頑なに Dispose をサボってくれますねぇ…。
もう指摘するのは諦めました。


> 下記のとおりListで試したところ、Invalidate メソッドで壁も柱も

必要な手順を整理してみましょう。


A:PictureBox1 の MouseDown/MouseMove/MouseUp イベント群の仕事は主に 3 つ
 A1:配置しようとしている図形の位置情報(ドラッグ時などに描きたいラバーバンドの座標など)を更新すること
 A2:MouseUp などで配置が完了した際に、その図形(壁や柱)の情報を List(Of ) に追加していくこと
 A3:これらの位置情報を更新した場合、PictureBox1.Invalidate() を呼びだすこと

B:PictureBox1 の Paint イベントの仕事は主に 3 つ
 B1:方眼グリッドを描画すること
 B2:List(Of ) に保持させておいた図形群を、すべて描画していくこと
 B3:配置中図形(ドラッグ移動などによるラバーバンドなど)の状況を描画すること


A2・B2 で読み書きしている List コレクションに保持されている図形群とは、
「今までに配置してきた、『すべての』壁と柱の情報」のことを指します。
これは VB 中学校のサンプルで言うところの「Private polygons As New List(Of Polygon)」に当たります。

A1・B3 で読み書きしている配置中図形の情報とは、
「現在配置しようとしている、『いずれか 1 つ』のオブジェクト情報(壁 or 柱)」を指します。
VB 中学校のサンプルで言う「mouseDownPosition」「mouseDragPosition」などのことです。


ここまではよろしいですか?

※B1 は Paint イベントで毎回再描画する代わりに、方眼画像を描いた Bitmap を
 あらかじめ PictureBox1.BackgroundImage あるいは PictureBox1.Image に割り当てておく方が楽だと思います。


> 次の壁を書くと、直前に書いた壁が消えます。
「今の壁を描く時、それまで描いた壁を描いていないから」です。


まず、shiro さんが用意された pointList というのは、
「今までに配置してきた『すべての』壁の情報」ではなく
「現在配置しようとしている、『いずれか 1つ』の壁情報」に過ぎません。


VB中学校の「Private polygons As New List(Of Polygon)」は、配置済みのすべての矩形情報を保持していますが
shiro さんの「Private pointList As New List(Of Point)」が保持するのは、現在配置しようとしている element = "wall" の情報だけなのです。


もしも pointList を「配置済みのすべての壁情報」に変更したいのであれば、現在の MouseUp イベント内の
 pointList.Clear()
という行をコメントアウトしてください。そうすれば、過去の壁情報がすべて累積保持されます。

その場合、壁を 3 つ描いたとしたら、
 pointList(0) は、壁1の開始点
 pointList(1) は、壁1の終了点
 pointList(2) は、壁2の開始点
 pointList(3) は、壁2の終了点
 pointList(4) は、壁3の開始点
 pointList(5) は、壁3の終了点
のように管理されることになります。

すなわち、Paint イベントでは
 If isDragging Then
  e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
 ElseIf pointList.Count = 2 Then
  e.Graphics.DrawLine(Pen7, pointList(0), pointList(1))
 End If
ではなく、
 For n = 0 To pointList.Count - 1 Step 2
  e.Graphics.DrawLine(Pen7, pointList(n + 0), pointList(n + 1))
 Next
 If isDragging Then
  e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
 End If
にすれば良い、ということになります。
これにより、全ての壁が描かれるようになることでしょう。


正しく動作することを確認できたら、コメントアウトしていた pointList.Clear() していた行を復活させてみてください。
「最新の壁だけが描かれ、過去の壁は消えてしまう」状態に戻ってしまうことを体験できると思います。


そして先に述べた通り、pointList As List(Of Point) が保持しているのは
あくまで座標情報の集合にすぎません。

このままでは、蓄えていった座標が「壁」なのか「柱」なのかを区別できません。

壁の位置情報と、柱の位置情報を、それぞれ別々の List(Of Point) 変数で分けて管理することもできますが、
そのように個別の変数で管理していってしまうと、今後の機能拡張――たとえば、
それぞれの「壁の厚さ」や「柱の太さ」や「色」などをも指定・変更できるようにするとか、
新たに壁や柱以外のオブジェクト(扉など)を加えるなど――を実装する際、
管理が煩雑になり過ぎてしまいますし、デバッグも大変になってしまいます。


そのため、参考にしているサイト(Visual Basic 中学校)の例に倣って、
描画対象のオブジェクトごとに、管理用のクラス(Wall や Pillar など)を
用意されることを強くおすすめします。
その方が今後の拡張性が高くなり、メンテナンスも容易になりますよ。


そこからさらに、ポリモーフィズムな設計(基底クラスを Inherits するなど)を採用することで、
さらに管理が容易になるかと思いますが…そこまで組むにはオブジェクト指向設計の知識が
要求されますので、それは今後、経験をつんでからの課題ということで。



No103798 (kiku さん) に返信
> ドラック中は、ドラック中の線のみを描画し、
> ドラックしていないときは、保持した1本のみを描画しています。

drag (引きずる) にしても
drug (薬物) にしても、一般的には
「ドラック」ではなく
「ドラッグ」と表記します。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103800 ] / 返信無し
■103801 / 10階層)  Re[10]: Visual Basicで簡易CADを作成
□投稿者/ kiku (485回)-(2025/08/01(Fri) 10:50:02)
No103800 (魔界の仮面弁士 さん) に返信

> ■No103798 (kiku さん) に返信
>>ドラック中は、ドラック中の線のみを描画し、
>>ドラックしていないときは、保持した1本のみを描画しています。
>
> drag (引きずる) にしても
> drug (薬物) にしても、一般的には
> 「ドラック」ではなく
> 「ドラッグ」と表記します。

あうう。おっしゃる通りです。泣き
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103800 ] / ▼[ 103805 ]
■103802 / 10階層)  Re[10]: Visual Basicで簡易CADを作成
□投稿者/ shiro (6回)-(2025/08/02(Sat) 03:38:00)
No103800 (魔界の仮面弁士 さん) に返信
> 2025/08/01(Fri) 14:32:02 編集(投稿者)

魔界の仮面弁士 様


> もしも pointList を「配置済みのすべての壁情報」に変更したいのであれば、現在の MouseUp イベント内の
>  pointList.Clear()
> という行をコメントアウトしてください。そうすれば、過去の壁情報がすべて累積保持されます。
>
> その場合、壁を 3 つ描いたとしたら、
>  pointList(0) は、壁1の開始点
>  pointList(1) は、壁1の終了点
>  pointList(2) は、壁2の開始点
>  pointList(3) は、壁2の終了点
>  pointList(4) は、壁3の開始点
>  pointList(5) は、壁3の終了点
> のように管理されることになります。
>
> すなわち、Paint イベントでは
>  If isDragging Then
>   e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
>  ElseIf pointList.Count = 2 Then
>   e.Graphics.DrawLine(Pen7, pointList(0), pointList(1))
>  End If
> ではなく、
>  For n = 0 To pointList.Count - 1 Step 2
>   e.Graphics.DrawLine(Pen7, pointList(n + 0), pointList(n + 1))
>  Next
>  If isDragging Then
>   e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
>  End If
> にすれば良い、ということになります。
> これにより、全ての壁が描かれるようになることでしょう。
>
>
> 正しく動作することを確認できたら、コメントアウトしていた pointList.Clear() していた行を復活させてみてください。
> 「最新の壁だけが描かれ、過去の壁は消えてしまう」状態に戻ってしまうことを体験できると思います。

柱と壁が全て描けました。有難うございます。
かなり手こずったので嬉しかったです。


> そして先に述べた通り、pointList As List(Of Point) が保持しているのは
> あくまで座標情報の集合にすぎません。
>
> このままでは、蓄えていった座標が「壁」なのか「柱」なのかを区別できません。

柱は既に、

Private snappedRectangles As New List(Of Rectangle)

としてリストに分けていました。こちらは解決済みでした。

壁の位置情報と、柱の位置情報を、それぞれ別々の List(Of Point) 変数で分けて管理しています。
クラスという概念もまだ分からないことが多く、とりあえず計算が動くことをするための簡易ソフトです。

壁にはとりあえず、剛性5kN/mと仮定し、座標から長さを計算し、乗じることでX,Y方向の耐力を算出するものです。
柱は壁が取り付く場所を判別するために、座標情報だけを与えています。

図形の削除はPolygonではないので、紹介して頂いたような方法では出来ないのでしょうか。

また、計算結果はCSVやtxt形式で出力させますが、簡易CADで作成した情報を、
ファイルで保存や読み込みをして修正・追加入力をする機能も付与したいのですが、
この場合の保存方法は一般的にどうするのが妥当でしょうか。


[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103802 ] / ▼[ 103806 ]
■103805 / 11階層)  Re[11]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3877回)-(2025/08/03(Sun) 11:50:26)
No103802 (shiro さん) に返信
> 図形の削除はPolygonではないので、紹介して頂いたような方法では出来ないのでしょうか。

えぇと…。No103791 の
>> 選択された図形を管理できるようになっているのですが、その List(Of ) コレクションから
>> 不要な図形を RemoveAt (あるいは Remove) すれば、その図形を削除できることになります。
については理解されている、という事でよろしいでしょうか。
RemoveAt と Remove の違いについても、説明しなくて大丈夫ですね?


まず、『配置済みのすべての【矩形】情報』は、VB 中学校でいうところの
「Private polygons As New List(Of Polygon)」でしたよね。

shiro さんのケースでは、
『配置済みのすべての【柱】情報』が「Private snappedRectangles As New List(Of Rectangle)」
『配置済みのすべての【壁】情報』が「Private pointList As New List(Of Point)」
なわけですよね。ここまで理解いただけていると思います。

つまり、図形を保持しているのは、いずれも「List(Of 型パラメータ) クラス」です。

柱が3本立っていた場合、
 柱A = snappedRectangles(0)
 柱B = snappedRectangles(1)
 柱C = snappedRectangles(2)
なわけですが、このうち、柱Bを削除したければ、
 snappedRectangles.RemoveAt(1)
を呼び出せばよいことになります。これにより、残った柱は
 柱A = snappedRectangles(0)
 柱C = snappedRectangles(1)
と変化します。

削除するごとにインデックス番号がズレますので、もしも複数の要素をまとめて削除する場合は
For ループは 0 から最終番号に向けて昇順列挙しながら RemoveAt するのではなく、
For ループを最終番号から 0 にむけて降順列挙しながら RemoveAt するようにします。
No103794 のコードもそうなっていますよね?


削除部の手順は、上記の通りさほど難しくもありませんし、既に回答済みでもあります。
問題となるのは、その柱Bを「どうやってユーザーに選択させるか」という点でしょうか?


No103794 では、削除対象のオブジェクトを選択させる手段について
>> ダブルクリックで削除する方式では操作しにくいという場合は、別途、削除ボタンを用意し、
>> それを押したときに IsSelected = True なものを取り除く方式にしても良いでしょう。
>> あるいは、右クリックやコンテキストメニューなどで指示する手法を採用することもできますね。
という素案を述べています。

実際、VB中学校のサンプルをダブルクリックで削除する方法だと、
複数の矩形が重なりあっている場合、重なっている部分をダブルクリックすると
手前側の矩形が削除された瞬間、奥の矩形が選択状態になってしまい、使いにくいかと思います。

たとえば、Excel の図形の場合を考えてみましょう。
Excel のワークシート上に、矩形や楕円や直線やテキストボックスなどを、適当に 5 つほど貼り付けたうえで
[ホーム]リボン > [編集] グループにある[検索と選択]>[オブジェクトの選択と表示] もしくは
[ページ レイアウト]リボン > [配置] グループにある、[オブジェクトの選択と表示] を選択してみます。
https://hamachan.info/win10/office/object.html

そうすると、シート上に貼った画像アイテム群が列挙され、それを使って選択することができますよね。

これと同じものを作ることは、さほど難しくは無いでしょう。

たとえば、柱の一覧を扱うための ListBox を用意しておき、
snappedRectangles.Add する際には、それと同時に、柱用の ListBox にも .Items.Add しておくのです。
Add する内容は、それぞれの柱を識別できる情報になっていれば、ひとまずなんでも良いです。
"柱1"、"柱2" といった連番情報でも良いですし、
柱のグリッド行列番号でも良いですし、
柱の Rectangle を .ToString() した座標情報でも良いです。

そのうえで、ユーザーは ListBox から削除したい柱を選択してもらいます。

snappedRectangles と ListBox のアイテム番号を常に同期させてておくことで、
ListBox の .SelectedIndex プロパティから「消したい柱のインデックス」が得られますので、
それを snappedRectangles と ListBox の両方から RemoveAt すればよいのです。

これは、複数の柱をまとめて削除したい場合にも使えます。
 Do Until ListBox1.SelectedIndex = -1
  snappedRectangles.RemoveAt(ListBox1.SelectedIndex)
  ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
 Loop


あるいは ListBox に頼らず、描画済みの壁や柱を直接クリックして選択させたり、
周囲の背景をドラッグして囲むことで範囲内の図形を選択状態にするといった
ユーザーインターフェイスにしてもよいでしょう。

その場合、選択中のアイテムは色を変えて表現するなどの必要があるでしょうから、
Paint イベントで色を変えて描画するために、各アイテムが「選択中かどうか」を
管理するための変数が必要になってきます。これについては、現状の
 Private snappedRectangles As New List(Of Rectangle)()
 Private pointList As New List(Of Point)()
だけでは、それぞれのアイテムの「位置とサイズ」しか分からず、「選択状態かどうか」を管理できません。

上記の ListBox のように、「選択されているアイテムの番号」を別管理する方法でも良いですが、
お奨めされるのは、先に何度も提案している通り、「柱クラス」や「壁クラス」を自作して、
それぞれのクラスに、座標情報だけでなく VB中学校の例のような「IsSelected プロパティ」を設けることです。


> また、計算結果はCSVやtxt形式で出力させますが、簡易CADで作成した情報を、
> ファイルで保存や読み込みをして修正・追加入力をする機能も付与したいのですが、
> この場合の保存方法は一般的にどうするのが妥当でしょうか。

ファイルの読み書きの方法が分からない、ということでしょうか。
その場合、まずは読み書きの方法を習得することから始めましょう。
それを CAD に組み込むのはその後の話になりますね。

ファイルの読み書きの基礎となるのは、System.IO 名前空間のクラス群です。
たとえば、ファイル全体を一括読み取りするなら、System.IO.File.ReadAllLines など。

データ量が多い場合は、全体をまとめて読み取るとメモリ消費量が増えてしまうため、
一行ずつ読み取って順次変換していく方法が採用されることもあります。

ファイル操作については、ひとまず、この辺りを参考にしてみださい。
http://rucio.o.oo7.jp/main/dotnet/shokyu/standard30.htm
https://dobon.net/vb/dotnet/file/index.html


そのうえで、ファイルを扱う際の注意点を幾つか:

・読み書きするファイルのパスは固定にするのか、任意のパスにするのか?

任意のパスにするなら、ユーザーにパスを選択させるための OpenFileDialog / SaveFileDialog の使い方を学んだ方が良いです。

固定とする場合、そのパスは「読み書き可能な場所」でなければなりませんが。EXE と同じフォルダ―にあるファイルは
読み込みはできても書き込みや編集が許可されていないこと(例:Program Files フォルダー)が少なからずあるため、
%LocalAppData% 、デスクトップ、ドキュメント フォルダーなどの、読み書き可能なパスが使われたりします。

これらの特殊フォルダーのパスは、Environment.GetFolderPath メソッドや
My.Computer.FileSystem.SpecialDirectories などから得られます。


・例外処理の方法は習得済みか?

ファイルを保存しようとしたら、ディスク容量不足や書き込み権限不足でエラーになることがあります。
その CSV を Excel で開いていたら、排他ロックがかかっていて上書き保存できないこともあるでしょう。
読み込みも同様に、アクセスできずにエラーになってしまう可能性がありますし、実際に読み込んでみたら
「拡張子が .csv なだけで、中身は Excel ブックだった」といった異常データの可能性もあるかもしれません。

このため、外部ファイルの読み書きを行う際には、「例外処理(Try〜Catch)」を組み込むことがほぼ必須となります。


・ファイルの文字コードは?

保存したいデータが、座標情報の数値だけならば、ASCII 形式でも良いですが、
漢字やカナなどを含めるのであれば、Shift_JIS か UTF-8 を採用するのが良いでしょう。
.NET では、デフォルトで UTF-8 が採用されますが、処理系によっては Shift_JIS の方が望まれることもあります。

ファイルを読み書きする際の文字コードの指定については、System.Text.Encoding クラスについて調べてみてください。

なお、そのファイルを読み書きするのが自アプリだけに限定される場合は、CSV といったテキストデータにせず、
独自のバイナリファイルや、ローカルデータベースファイルを採用するといった選択肢もあります。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103805 ] / ▼[ 103808 ]
■103806 / 12階層)  Re[12]: Visual Basicで簡易CADを作成
□投稿者/ shiro (8回)-(2025/08/04(Mon) 05:10:00)
No103805 (魔界の仮面弁士 さん) に返信
> ■No103802 (shiro さん) に返信
>>図形の削除はPolygonではないので、紹介して頂いたような方法では出来ないのでしょうか。

魔界の仮面弁士 様


いつもご丁寧な回答有難うございます。
休日、酷暑の中感謝致します。

> つまり、図形を保持しているのは、いずれも「List(Of 型パラメータ) クラス」です。
>
> 柱が3本立っていた場合、
>  柱A = snappedRectangles(0)
>  柱B = snappedRectangles(1)
>  柱C = snappedRectangles(2)
> なわけですが、このうち、柱Bを削除したければ、
>  snappedRectangles.RemoveAt(1)
> を呼び出せばよいことになります。これにより、残った柱は
>  柱A = snappedRectangles(0)
>  柱C = snappedRectangles(1)
> と変化します。
>
> 削除するごとにインデックス番号がズレますので、もしも複数の要素をまとめて削除する場合は
> For ループは 0 から最終番号に向けて昇順列挙しながら RemoveAt するのではなく、
> For ループを最終番号から 0 にむけて降順列挙しながら RemoveAt するようにします。
> No103794 のコードもそうなっていますよね?
>
>
> 削除部の手順は、上記の通りさほど難しくもありませんし、既に回答済みでもあります。
> 問題となるのは、その柱Bを「どうやってユーザーに選択させるか」という点でしょうか?
>
>
> No103794 では、削除対象のオブジェクトを選択させる手段について
> >> ダブルクリックで削除する方式では操作しにくいという場合は、別途、削除ボタンを用意し、
> >> それを押したときに IsSelected = True なものを取り除く方式にしても良いでしょう。
> >> あるいは、右クリックやコンテキストメニューなどで指示する手法を採用することもできますね。
> という素案を述べています。

分かりやすい説明でした。番号がズレるのですね。

> たとえば、柱の一覧を扱うための ListBox を用意しておき、
> snappedRectangles.Add する際には、それと同時に、柱用の ListBox にも .Items.Add しておくのです。
> Add する内容は、それぞれの柱を識別できる情報になっていれば、ひとまずなんでも良いです。
> "柱1"、"柱2" といった連番情報でも良いですし、
> 柱のグリッド行列番号でも良いですし、
> 柱の Rectangle を .ToString() した座標情報でも良いです。
>
> そのうえで、ユーザーは ListBox から削除したい柱を選択してもらいます。
>
> snappedRectangles と ListBox のアイテム番号を常に同期させてておくことで、
> ListBox の .SelectedIndex プロパティから「消したい柱のインデックス」が得られますので、
> それを snappedRectangles と ListBox の両方から RemoveAt すればよいのです。
>
> これは、複数の柱をまとめて削除したい場合にも使えます。
>  Do Until ListBox1.SelectedIndex = -1
>   snappedRectangles.RemoveAt(ListBox1.SelectedIndex)
>   ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
>  Loop
>
>
> あるいは ListBox に頼らず、描画済みの壁や柱を直接クリックして選択させたり、
> 周囲の背景をドラッグして囲むことで範囲内の図形を選択状態にするといった
> ユーザーインターフェイスにしてもよいでしょう。
>
> その場合、選択中のアイテムは色を変えて表現するなどの必要があるでしょうから、
> Paint イベントで色を変えて描画するために、各アイテムが「選択中かどうか」を
> 管理するための変数が必要になってきます。これについては、現状の
>  Private snappedRectangles As New List(Of Rectangle)()
>  Private pointList As New List(Of Point)()
> だけでは、それぞれのアイテムの「位置とサイズ」しか分からず、「選択状態かどうか」を管理できません。
>
> 上記の ListBox のように、「選択されているアイテムの番号」を別管理する方法でも良いですが、
> お奨めされるのは、先に何度も提案している通り、「柱クラス」や「壁クラス」を自作して、
> それぞれのクラスに、座標情報だけでなく VB中学校の例のような「IsSelected プロパティ」を設けることです。

ここら辺の連動した処理が難しそうだと感じました。
座標情報も保存するとなると、やはりクラスを使用しないと難しいのでしょうね。
今まで、subだけで処理していましたが、少し勉強してみます。

> ファイルの読み書きの方法が分からない、ということでしょうか。
> その場合、まずは読み書きの方法を習得することから始めましょう。
> それを CAD に組み込むのはその後の話になりますね。
>
> ファイルの読み書きの基礎となるのは、System.IO 名前空間のクラス群です。
> たとえば、ファイル全体を一括読み取りするなら、System.IO.File.ReadAllLines など。
>
> データ量が多い場合は、全体をまとめて読み取るとメモリ消費量が増えてしまうため、
> 一行ずつ読み取って順次変換していく方法が採用されることもあります。
>
> ファイル操作については、ひとまず、この辺りを参考にしてみださい。
> http://rucio.o.oo7.jp/main/dotnet/shokyu/standard30.htm
> https://dobon.net/vb/dotnet/file/index.html

今までsubだけでデータの入出力はcsvやテキストで実施していました。
やはり保存や読み込みは同様に実施し、グリッド上に座標をスナップさせ、
柱は四角形で、壁はPointで線を繋ぐ逆の処理をし、再度、図形を描かせるということでしょうか。

図形の削除、ファイルへの入出力等、少し時間をかけて実施してみます。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103806 ] / ▼[ 103811 ]
■103808 / 13階層)  Re[13]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3879回)-(2025/08/04(Mon) 11:36:03)
No103806 (shiro さん) に返信
> 柱は四角形で、
「四角形」だと、「凹四角形」や「平行四辺形」も含まれてしまうので、
より正確には「長方形」という表現になりますね。


現状の柱は、Point 構造体ではなく Rectangle 構造体で管理されているわけですが、
広義の意味の「四角形」を表そうとしたら、Rectangle ではなく、
「4 つの Point」を用いる方が良いでしょう。
https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample279WinFormDrawPolygon.htm


一方、柱が長方形(正方形)に限定されており、斜めに配置する事も無いのであれば
一つの壁を「4 個の Point」で扱うより、「単一の Rectangle」の方が管理しやすいですよね?


であれば同様に壁についても「2 個の Point」ではなく単一のオブジェクトで扱った方が
分かりやすくなるとは思います、クラスや構造体を自作できるだけの経験を積み重ねれば。



> 壁はPointで線を繋ぐ逆の処理をし、
幾つか疑問が。

a)柱を配置しないと、壁を描けない仕様でしょうか?
 あるいは先に壁を描いた後、両端に柱が建つという手順もあるのでしょうか?

b)もしも壁の両端が柱と接触していなければならないという条件なのだとしたら
 柱を撤去した場合、それに付随する壁も一緒に削除されるべきということになりますか?

c)一本の柱に複数の壁が接触している可能性がありますか?
 一つの柱交点から複数の壁が伸びるイメージで(「╋」「┗」「┫」「*」など)


もしも壁の両端に柱が必須であるがゆえに、柱の撤去と共に壁も取り去る必要があるのだとしたら。

柱を削除する際には、削除する柱に接するすべての壁を列挙して、
柱のみならず、接する壁すべても併せて RemoveAt しないといけないでしょうね。

そうして、柱(snappedRectangles)やpointList(壁)の情報を変化させた後は、
再描画を依頼するために、いつもの『PictureBox1.Invalidate()』を呼び出します。

これによって、
> 再度、図形を描かせるということでしょうか。
のための Paint イベントが発生することになります。



あるいは、「柱に接していない壁も配置できる」ルールだが、本来は柱が必要であるというのなら、
接していない壁を別の色で描画したい、といったニーズも出てくるかもしれません。そうした場合も、
 ・位置算出処理(マウス操作あるいはCSV読み取り)で、壁ごとの状態や色情報を記録し、Invalidate を呼び出す
 ・Paint イベントで、既に算出済みの柱や壁の情報を読み取り、適切な位置と色で描いていく
という方針には変わりないでしょうね。


> 今までsubだけでデータの入出力はcsvやテキストで実施していました。
> 柱は四角形で、壁はPointで線を繋ぐ逆の処理をし、再度、図形を描かせるということでしょうか。

もちろん、Sub だけで開発することも可能ですが、
できれば Function や Property についても学んでいった方が良いでしょう。


今回の「壁や柱の情報を保持している CSV」の具体的な内容が分からないので
具体的なコードは書けませんが:

柱と壁を別々に管理するのではなく、No103796 のような統一的な継承実装にしておくと、
 Friend Function LoadFromCSV(csvPath As String) As List(Of DrawingObjectBase)
のようなメソッドを作り込むことができ、今後、
 ・剛性を5kN/m固定とせず、基準剛性の異なる厚壁/薄壁の追加
 ・壁や柱以外のアイテムを新設
など、管理情報が増えた場合にも保守しやすいコードになりますよ。


イメージコード:

'描画アイテム全てを管理しているコレクション
Private drawItems As New List(Of DrawingObjectBase)()

'CSV ファイル読み込みボタン
Private Sub Button1_Click(……
 If OpenFileDialog1.ShowDialog() = DialogResult.Cancel Then
  Return 'CSVファイルが選択されなかった
 End If

 'CSV を読み込み
 Dim newItems As List(Of DrawingObjectBase) = LoadFromCSV(OpenFileDialog1.FileName)
 If newItems IsNot Nothing Then
  '読み込んだデータに基づいて再描画
  Me.drawItems = newItems
  PictureBox1.Invalieda()
 End If
End Sub

Private Sub PictureBox1_Paint(……
 For Each drawItem In drawItems
  drawItem.Draw(e.Graphics) '柱も壁もすべて、このメソッドを呼ぶだけで描画される
 Next
 :
 :
End Sub
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103808 ] / ▼[ 103812 ]
■103811 / 14階層)  Re[14]: Visual Basicで簡易CADを作成
□投稿者/ shiro (9回)-(2025/08/04(Mon) 18:59:38)
No103808 (魔界の仮面弁士 さん) に返信
> ■No103806 (shiro さん) に返信

魔界の仮面弁士 様

> 幾つか疑問が。
>
> a)柱を配置しないと、壁を描けない仕様でしょうか?
>  あるいは先に壁を描いた後、両端に柱が建つという手順もあるのでしょうか?
>
> b)もしも壁の両端が柱と接触していなければならないという条件なのだとしたら
>  柱を撤去した場合、それに付随する壁も一緒に削除されるべきということになりますか?
>
> c)一本の柱に複数の壁が接触している可能性がありますか?
>  一つの柱交点から複数の壁が伸びるイメージで(「╋」「┗」「┫」「*」など)

壁は必ず柱間に配置するルールとなっています。
順番はどちらが先でも構いませんが、このようなルールのため、
先に柱を配置し、その間をラバーバンドのように壁を配置しました。

計算したい項目は3つあります。

1つ目は柱情報は関係なく、X,Y方向の壁の長さを座標から計算し、
その壁の剛性を乗じて方向別に耐力を出します。

2つ目は、1つ目の計算を上下左右の1/4の面積に存在する壁だけで計算します。
この際に柱の座標によって平面図の輪郭から1/4の面積を計算することになります。

3つ目は、柱の両側に接続する、壁の左右の剛性の差から引張力を計算します。
その際、2階建ての場合は、その柱の上の柱も同様に計算します。
X,Y方向別なので、最大でも両側に壁がある2つの剛性の差となります。

取り合えず、1つ目の簡単な計算を完成することを目標にしています。
間違って配置した柱・壁の削除、座標情報等のCSVファイルの入出力が成功すれば、
計算自体は方向別に集計するだけですのでうまくいくと思います。

まずは1階(平屋)だけをイメージし、PictureBoxも1つだけで1,2階を選択できません。

また、俄か仕込みですが、CLASSを試してみました。
コンパイルは成功したのですが、柱を選択しグリッドをクリックしても全く配置されなくなりました。
初心者にはクラスの使用は難しいかもしれません。

以下、変更した部分です。

Private Sub PictureBox1_MouseDown(
ByVal sender As System.Object,
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureBox1.MouseDown

Dim pillarList As New List(Of Pillar)

Dim newPillar As New Pillar With {
.Index = pillarList.Count + 1,
.X = SnapToGrid(e.Location).X,
.Y = SnapToGrid(e.Location).Y
}

pillarList.Add(newPillar)

PictureBox1.Invalidate() ' 再描画


Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

Dim pillarList As New List(Of Pillar)

For Each pillar In pillarList

g.FillRectangle(Brushes.Red, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
g.DrawRectangle(Pens.Black, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

Next


Public Class Pillar

Public Property Index As Integer ' 柱のインデックス
Public Property X As Integer ' X座標
Public Property Y As Integer ' Y座標

End Class

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103811 ] / ▼[ 103814 ]
■103812 / 15階層)  Re[15]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3881回)-(2025/08/04(Mon) 19:43:28)
No103811 (shiro さん) に返信
> Private Sub PictureBox1_MouseDown(
>  ByVal sender As System.Object,
>  ByVal e As System.Windows.Forms.MouseEventArgs) _
>  Handles PictureBox1.MouseDown
>
>  Dim pillarList As New List(Of Pillar)

Sub や Function の内側で Dim 宣言された変数を「ローカル変数」と呼びます。

そのデータ型が Integer であれ List であれ String であれ、
上記のローカル変数は、MouseDown イベントが発生するたびに初期化され
MouseDown イベントを抜けるたびに破棄されてしまいます。

※変数の有効期間のことを、専門用語で「スコープ」と呼びます。


> Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
>  Dim pillarList As New List(Of Pillar)

Paint イベントで宣言された pillarList 変数と
MouseDown イベントで宣言された pillarList 変数は
名前が同じというだけで、それぞれ別物です。


描画オブジェクトの List は、各イベント間で共有されるべきものですから、
ローカル変数として「Dim pillarList As New List(Of Pillar)」などと宣言するのではなく、
フィールド変数として宣言するようにしてください。

つまり、従来の
 Private pointList As New List(Of Point)
 Private snappedRectangles As New List(Of Rectangle)
と同様に、Form1 の直下にて
 Private pillarList As New List(Of Pillar)
として宣言されるべきである、ということです。


これは、クラスを自作していなかったとしても同じことです。

スコープの概念は、Visual Basic の言語仕様として重要な所なので、
もしも御存知なかったのであれば
 Visual Basic 中学校の 初級講座 [改訂版]
 第9回 変数のスコープと寿命
 https://www.umayadia.com/VBStandard2/VBStandard2Toc.htm
などで学んでおきましょう。


> Dim pillarList As New List(Of Pillar)
> Dim newPillar As New Pillar With {
>  .Index = pillarList.Count + 1,
>  .X = SnapToGrid(e.Location).X,
>  .Y = SnapToGrid(e.Location).Y
> }
> pillarList.Add(newPillar)

これだと、Index 管理に問題があります。
No103805 で 柱B を削除した時のことを覚えていますか?

たとえば、柱が 3 本あった場合、それぞれの Index は
 柱A ⇒ pillarList(0).Index は 1
 柱B ⇒ pillarList(1).Index は 2
 柱C ⇒ pillarList(2).Index は 3
と追加されていくことを想定されているのかと思います。

ではここで、柱A を削除したとすると
 柱B ⇒ pillarList(0).Index は 2
 柱C ⇒ pillarList(1).Index は 3
に変わるわけですよね。

ではその後、さらに柱D を建てるときに
 .Index = pillarList.Count + 1
が実行されたらどうなるでしょう? pillarList には柱が 2 本しかないので
 柱B ⇒ pillarList(0).Index は 2
 柱C ⇒ pillarList(1).Index は 3
 柱D ⇒ pillarList(2).Index は 3
になってしまうわけです。


柱に固定的な番号を付けるのであれば、同じ Index 番号が再利用されることは防ぐべきなので、
例えばこのような書き方もしてみてください。

 Public Class Pillar
  Private Shared LastIndex As Integer = 0
  Public ReadOnly Property Index As Integer
  Public Property X As Integer
  Public Property Y As Integer

  Public Sub New(location As Point)
   LastIndex += 1
   Me.Index = LastIndex
   Me.X = location.x
   Me.Y = location.y
  End Sub
 End Class


そして、柱を建てる際には
  Dim newPillar As New Pillar(SnapToGrid(e.Location))
  pillarList.Add(newPillar)
のようにします。こうすれば Index も自動採番されますし、
pillarList から柱が削除された後も、Index の重複が発生することがありません。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103812 ] / ▼[ 103815 ]
■103814 / 16階層)  Re[16]: Visual Basicで簡易CADを作成
□投稿者/ shiro (10回)-(2025/08/05(Tue) 12:42:22)
No103812 (魔界の仮面弁士 さん) に返信
> ■No103811 (shiro さん) に返信

魔界の仮面弁士 様

柱座標をcsvファイルに出力できました。
次のとおり、Indexや座標を正しく格納できました。

Type Index X Y
C 1 280 40
C 2 280 120
C 3 360 120
C 4 360 40

柱が上手く行ったので、今度は壁に挑戦してみました。
pointListからwallListに変えました。

Wallクラスでは剛性を2.5の壁で一応固定しています。
startPoint、endPointとWallクラスでエラーが出ます。

csvファイルへの出力は、壁の始点(X,Y)、終点(X,Y)、剛性と出力したいです。

Type Index X1 Y1 X2 Y2 Stiff
W 1 280 40   360 40  2.5
W 2 280 120   360 120  2.5


        記

Private wallList As New List(Of Wall)

Private isPlacing As Boolean = False


Private Sub PictureBox1_MouseDown(
ByVal sender As System.Object,
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureBox1.MouseDown


'耐力壁

Case "wall"

startPoint As New Wall(SnapToGrid(e.Location))

isPlacing = True


Private Sub PictureBox1_MouseMove(
ByVal sender As Object,
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureBox1.MouseMove


Case "wall"

If isPlacing Then

endPoint As New Wall(SnapToGrid(e.Location))

PictureBox1.Invalidate()
End If


Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp

isPlacing = False

wallList.Add(startPoint)
wallList.Add(endPoint)

PictureBox1.Invalidate()
End Sub


Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

For n = 0 To wallList.Count - 1 Step 2
e.Graphics.DrawLine(pen7, wallList(n + 0), wallList(n + 1))
Next

If isPlacing Then
e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
End If


Public Sub ExportToCSV(pillarList As List(Of Pillar), wallList As List(Of Wall))

Dim filePath As String = "export.csv"
Using writer As New StreamWriter(filePath)

writer.WriteLine("Type,Index,X,Y,Stiff")

' 柱データを書き込み
For Each pillar In pillarList
writer.WriteLine($"C,{pillar.Index},{pillar.X},{pillar.Y}")
Next

' 壁データを書き込み
For Each wall In wallList
writer.WriteLine($"W,{wall.Index},{wall.X},{wall.Y},{wall.Stiffness}")
Next
End Using

End Sub


Public Class Wall
Private Shared LastIndex As Integer = 0
Public ReadOnly Property Index As Integer
Public Property X As Integer
Public Property Y As Integer
Public Property Stiffness As Double

Public Sub New(location As Point)
LastIndex += 1
Me.Index = LastIndex
Me.X = location.X
Me.Y = location.Y

Me.Stiffness = 2.5

End Sub
End Class






[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103814 ] / ▼[ 103817 ]
■103815 / 17階層)  Re[17]: Visual Basicで簡易CADを作成
□投稿者/ shiro (11回)-(2025/08/05(Tue) 12:44:24)
No103814 (shiro さん) に返信
> ■No103812 (魔界の仮面弁士 さん) に返信
>>■No103811 (shiro さん) に返信

魔界の仮面弁士 様

柱座標をcsvファイルに出力できました。
次のとおり、Indexや座標を正しく格納できました。

Type	Index	X	Y
C	1	280	40
C	2	280	120
C	3	360	120
C	4	360	40

柱が上手く行ったので、今度は壁に挑戦してみました。
pointListからwallListに変えました。

Wallクラスでは剛性を2.5の壁で一応固定しています。
エラーが出るので間違っていると思います。

csvファイルへの出力は、壁の始点(X,Y)、終点(X,Y)、剛性と出力したいです。

Type	Index	X1	Y1	X2	Y2    Stiff
W	1	280	40   360	40  2.5
W	2	280	120   360	120  2.5


        記

    Private wallList As New List(Of Wall)

    Private isPlacing As Boolean = False


    Private Sub PictureBox1_MouseDown(
          ByVal sender As System.Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseDown


          '耐力壁

            Case "wall"

                startPoint As New Wall(SnapToGrid(e.Location))

                isPlacing = True


    Private Sub PictureBox1_MouseMove(
          ByVal sender As Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseMove


            Case "wall"

                If isPlacing Then

                    endPoint As New Wall(SnapToGrid(e.Location))

                    PictureBox1.Invalidate()
                End If


    Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp

        isPlacing = False 

        wallList.Add(startPoint)
        wallList.Add(endPoint)

        PictureBox1.Invalidate()
    End Sub


    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

        For n = 0 To wallList.Count - 1 Step 2
            e.Graphics.DrawLine(pen7, wallList(n + 0), wallList(n + 1))
        Next

        If isPlacing Then
            e.Graphics.DrawLine(Pens.Blue, startPoint, endPoint)
        End If


    Public Sub ExportToCSV(pillarList As List(Of Pillar), wallList As List(Of Wall))

        Dim filePath As String = "export.csv"
        Using writer As New StreamWriter(filePath)

            writer.WriteLine("Type,Index,X,Y,Stiff")

            ' 柱データを書き込み
            For Each pillar In pillarList
                writer.WriteLine($"C,{pillar.Index},{pillar.X},{pillar.Y}")
            Next

            ' 壁データを書き込み
            For Each wall In wallList
                writer.WriteLine($"W,{wall.Index},{wall.X},{wall.Y},{wall.Stiffness}")
            Next
        End Using

    End Sub


Public Class Wall
    Private Shared LastIndex As Integer = 0
    Public ReadOnly Property Index As Integer
    Public Property X As Integer
    Public Property Y As Integer
    Public Property Stiffness As Double

    Public Sub New(location As Point)
        LastIndex += 1
        Me.Index = LastIndex
        Me.X = location.X
        Me.Y = location.Y

        Me.Stiffness = 2.5

    End Sub
End Class






[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103815 ] / ▼[ 103819 ]
■103817 / 18階層)  Re[18]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3883回)-(2025/08/05(Tue) 18:02:32)
No103815 (shiro さん) に返信
> pointListからwallListに変えました。
> Private wallList As New List(Of Wall)
その方が分かりやすいので良いと思います。


> startPoint、endPointとWallクラスでエラーが出ます。
いわゆるコンパイルエラーというやつですね。

前回 ( No103812 ) でも指摘させていただきましたが、「変数のスコープと寿命」に関して
Visual Basic の言語機能として習得しきれていない印象を受けました。


> '耐力壁
> Case "wall"
>  startPoint As New Wall(SnapToGrid(e.Location))

As 句は、変数宣言時に型を明示するためのものです。

startPoint は、MouseDown イベントで書き込まれ、Paint イベントで読まれる値
endPoint は、MouseMove/MouseUp イベントで書き込まれ、Paint イベントで読まれる値
ですよね。そのほか、isPlacing もそうですね。

つまり、これらの変数は『複数のイベントで使われる変数』なのですから、
これらの変数は「ローカル変数」ではなく「フィールド変数」でなければなりません。
ローカル変数だと、変数の寿命がプロシージャー単位(Sub や Function ごと)になってしまいます。


さて、ローカル変数における
  Dim a As New Foo(b)
という記述は、Visual Basic では
  Dim a As Foo = New Foo(b)
と完全に同じです。
これらは、変数の宣言と同時に、その初期値を設定するためのコードですよね。

宣言済みのクラス変数に対して、そのインスタンスを割り当てる場合、
宣言済みの「Dim a As Foo」あるいは「Priavate a As Foo」への代入のために
  a = New Foo(b)
と書くことはできますが
  a As New Foo(b)
と書くことはできません。As は変数宣言時に型を指定するためのものだからです。

そのため、現在の
> startPoint As New Wall(SnapToGrid(e.Location))
> endPoint As New Wall(SnapToGrid(e.Location))
という記述は、コンパイルエラーになってしまっているということです。
複数のイベントで使われる変数は、Form1 直下にローカル変数として宣言しましょう。


ただ、startPoint や endPoint という名から想像するに、
それらは壁そのものではなく、
壁の端を表す位置情報に過ぎないはずです。つまり、
  Private startPoint As Wall
  Private endPoint As Wall
といった「壁オブジェクト」(Wall クラス)ではなく、
  Private startPoint As Point
  Private endPoint As Point
といった「位置オブジェクト」(Point 構造体)であるはずなのです。


MouseDown で決まるのは壁の始端位置なので、「startPoint = SnapToGrid(e.Location)」とします。
MouseMode/MouseUp では壁の終端位置なので、「endPoint = SnapToGrid(e.Location)」とします。
いずれの場合も、位置情報が更新されたので、「PictureBox1.Invalidate()」で再描画を依頼します。

また、MouseUp のタイミングでは、始端と終端の両方が揃ったことになりますので、
これでようやく「壁」を建てることできるようになります。両端が確定したので、
  Dim newWall As New Wall(startPoint, endPoint) '新しい壁を準備
  wallList.Add(newWall) 'それを壁一覧に加える
を行うことで、Paint イベントに壁情報を渡せるようになります。


……とはいえ、上記の改修を行うと、今度は New Wall(〜) を呼び出す部分で、
また別のコンパイルエラー(引数の数が違っているので)になってしまうはずです。


そもそも、壁の位置は (X,Y) が一つだけあっても決定できません。
当然、(X1,Y1)と(X2,Y2) が必要なわけです。そのための修正を行いましょう。

ついでに、剛性値も「既定値(2.5)」または「任意値」の両方に対応させちゃいましょうか。


'=== 案1 ===
Public Class Wall
    Private Shared LastIndex As Integer = 0
    Private Const DefaultStiffnessValue As Double = 2.5

    Public ReadOnly Property Index As Integer
    Public Property StartPoint As Point
    Public Property EndPoint As Point
    Public Property Stiffness As Double

    Public Sub New(startPoint As Point, endPoint As Point)
        Me.New(startPoint, endPoint, DefaultStiffnessValue)
    End Sub

    Public Sub New(startPoint As Point, endPoint As Point, stiffness As Double)
        LastIndex += 1
        Me.Index = LastIndex
        Me.StartPoint = startPoint
        Me.EndPoint = endPoint
        Me.Stiffness = stiffness
    End Sub
End Class


'=== 案2 ===
Public Class Wall
    Private Shared LastIndex As Integer = 0
    Private Const DefaultStiffnessValue As Double = 2.5

    Public ReadOnly Property Index As Integer
    Public Property StartPoint As Point
    Public Property EndPoint As Point
    Public Property Stiffness As Double

    Public Sub New(startPoint As Point, endPoint As Point, Optional stiffness As Double = DefaultStiffnessValue)
        LastIndex += 1
        Me.Index = LastIndex
        Me.StartPoint = startPoint
        Me.EndPoint = endPoint
        Me.Stiffness = stiffness
    End Sub
End Class


これにより、
  Dim newWall As New Wall(startPoint, endPoint)
  wallList.Add(newWall)
あるいは、両者をまとめて
  wallList.Add(New Wall(startPoint, endPoint))
と書けるようになります。また、剛性値を別の値とするために
  wallList.Add(New Wall(startPoint, endPoint, 2.2))
といった指定も可能となっています。


Paint イベントの描画については、
 For Each w In WallList
  e.Graphics.DrawLine(pen7, w.StartPoint, w.EndPoint)
 Next
になります。一つの Wall インスタンスで始点と終点の両方を管理しているため、
従来の Step 2 な For ループを使う必要がなく、処理が簡潔になります。


なお、上記では Wall クラスの座標情報を 2 つの Point で表していますが、
元のソースに従って、X1, Y1, X2, Y2 の 4 つの Integer で表現することもできるでしょう。
どちらが良いかはお好みで。


> csvファイルへの出力は、壁の始点(X,Y)、終点(X,Y)、剛性と出力したいです。
> Type	Index	 X1	 Y1	 X2	 Y2	Stiff
> W   	1    	280	 40	360	 40	  2.5
> W   	2    	280	120	360	120	  2.5

Type=W が壁を意味するデータで
Type=C が柱を意味する行データなのですよね。
柱用の CSV と壁用の CSV で別ファイルになるのでしょうか?

もしも一つのファイルにするのなら、壁と柱で列数が動的に変化するのは、ちょっと不自然かもしれませんね。
列数可変な CSV も世の中にはありますが、ヘッダー付き CSV で行ごとに列数が異なるケースは稀ですし。

案1) ヘッダー行(今回は、"Type,Index," で始まる行)が、ファイル先頭だけでなく文中にも現れ、そこから列数が切り替る
案2) どちらも7列構成にするが、柱の時は先頭4列だけを使い、未使用列には空のデータが入る(空列のためのカンマが続く形になる)
案3) CSV の代わりに、レコードごとに構造を混ぜられる形式(JSON や XML など)を採用する



<ひとりごと>

建築構造体としての柱というニュアンスであれば、Pillar クラスではなく Column クラスの方が単語的に望ましいかな…。
でも Column だと、グリッドや CSV などの「Row/Column」の意味に混同されそうで、命名的に悩んでしまった。

Column  ⇒ 柱、あるいは建築物の中で装飾的・構造的に並んでいる縦の柱
Pillar  ⇒ 建物の主要構造を支える支柱、記念碑や大黒柱などといった中心的存在あるいは意匠的に独立した支柱
Pier   ⇒ 建築・土木分野の専門用語で「柱状の構造物」「基礎・橋脚・大型の柱」を指す
Post   ⇒ 支柱・杭、小さい柱や看板の支え
Pole   ⇒ 細長い棒・支柱、電信柱や旗竿など
Stanchion⇒ 鉄パイプなどの支柱・枠、工事現場の柵や支柱
Support ⇒ 支えとしての支柱、構造体のサポート部材
Beam   ⇒ 横向きの梁、水平な構造体

方眼上に配置するための柱なので、GridColumn とか? こちらは WPF の Grid.Column を連想してしまいそう。

StructuralColumn クラスという名前にすれば明確にはなるけれど、それだと Wall に比べて名前が冗長すぎるか。

Pier であれば、Pillar と Column の中間的なニュアンスとなり、Row/Column との混同も避けられるけれど、
そもそも専門用語過ぎて伝わらないだろうなぁ…。(一般語としての Pier は、桟橋・埠頭の意味で使われる)


</ひとりごと>

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103817 ] / ▼[ 103821 ]
■103819 / 19階層)  Re[19]: Visual Basicで簡易CADを作成
□投稿者/ shiro (12回)-(2025/08/05(Tue) 19:28:33)
No103817 (魔界の仮面弁士 さん) に返信
> ■No103815 (shiro さん) に返信

魔界の仮面弁士 様

いつもアドバイス有難うございます。
早速次のように実施しました。


    Private startPoint As Point
    Private endPoint As Point

MouseDown

          '耐力壁

            Case "wall"

                startPoint = SnapToGrid(e.Location)

MouseMove

           Case "wall"

                If isPlacing Then

                    endPoint = SnapToGrid(e.Location)

                    PictureBox1.Invalidate()

MouseUp

        isPlacing = False

        Dim newWall As New Wall(startPoint, endPoint) '新しい壁を準備

        wallList.Add(newWall) 'それを壁一覧に加える

        'wallList.Add(New Wall(startPoint, endPoint))

        '剛性値を別の値とするために

        'wallList.Add(New Wall(startPoint, endPoint, 2.2))

        PictureBox1.Invalidate()

Paint

        For Each w In wallList
            e.Graphics.DrawLine(pen7, w.StartPoint, w.EndPoint)
        Next

    Public Sub ExportToCSV(pillarList As List(Of Pillar), wallList As List(Of Wall))

        ' ファイルパスを指定
        Dim filePath As String = "export.csv"
        Using writer As New StreamWriter(filePath)
 
            writer.WriteLine("Type,Index,X,Y,Stiff")
 
            ' 柱データを書き込み
            For Each pillar In pillarList
                writer.WriteLine($"C,{pillar.Index},{pillar.X},{pillar.Y}")
             Next

            ' 壁データを書き込み
            For Each wall In wallList
 
                writer.WriteLine($"W,{wall.Index},{wall.StartPoint},{wall.EndPoint},{wall.Stiffness}")

            Next
        End Using

その結果、コンパイルエラーもなく、次のとおり出力されました。
気になるのがWの1〜4が出力されていることでした。
関係ない出力だと思うのですが。

W5~8は柱のCと座標が整合していました。


Type	Index	X	Y	Stiff		
C	1	40	40			
C	2	40	80			
C	3	80	80			
C	4	80	40			
W	1	{X=0	Y=0}	{X=0	Y=0}	2.5
W	2	{X=0	Y=0}	{X=0	Y=0}	2.5
W	3	{X=0	Y=0}	{X=0	Y=0}	2.5
W	4	{X=0	Y=0}	{X=0	Y=0}	2.5
W	5	{X=40	Y=40}	{X=40	Y=80}	2.5
W	6	{X=40	Y=80}	{X=80	Y=80}	2.5
W	7	{X=80	Y=80}	{X=80	Y=40}	2.5
W	8	{X=80	Y=40}	{X=40	Y=40}	2.5


> <ひとりごと>
> 
> 建築構造体としての柱というニュアンスであれば、Pillar クラスではなく Column クラスの方が単語的に望ましいかな…。
> でも Column だと、グリッドや CSV などの「Row/Column」の意味に混同されそうで、命名的に悩んでしまった。
> 
> Column  ⇒ 柱、あるいは建築物の中で装飾的・構造的に並んでいる縦の柱
> Pillar  ⇒ 建物の主要構造を支える支柱、記念碑や大黒柱などといった中心的存在あるいは意匠的に独立した支柱
> Pier   ⇒ 建築・土木分野の専門用語で「柱状の構造物」「基礎・橋脚・大型の柱」を指す
> Post   ⇒ 支柱・杭、小さい柱や看板の支え
> Pole   ⇒ 細長い棒・支柱、電信柱や旗竿など
> Stanchion⇒ 鉄パイプなどの支柱・枠、工事現場の柵や支柱
> Support ⇒ 支えとしての支柱、構造体のサポート部材
> Beam   ⇒ 横向きの梁、水平な構造体
> 
> 方眼上に配置するための柱なので、GridColumn とか? こちらは WPF の Grid.Column を連想してしまいそう。
> 
> StructuralColumn クラスという名前にすれば明確にはなるけれど、それだと Wall に比べて名前が冗長すぎるか。
> 
> Pier であれば、Pillar と Column の中間的なニュアンスとなり、Row/Column との混同も避けられるけれど、
> そもそも専門用語過ぎて伝わらないだろうなぁ…。(一般語としての Pier は、桟橋・埠頭の意味で使われる)


Pillarは地下の杭などに使われます。
上部構造の柱はcolumnが使われます。

今回は動かせることが目的でしたので、そのまま使用していました。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103819 ] / ▼[ 103822 ]
■103821 / 20階層)  Re[20]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3886回)-(2025/08/05(Tue) 20:39:45)
No103819 (shiro さん) に返信
> Pillarは地下の杭などに使われます。
> 上部構造の柱はcolumnが使われます。

勉強になります! ( ..)φ


> Type	Index	X          	Y          	Stiff		
> C   	    1	40         	40         	     		
> C   	    2	40         	80         	     		
> C   	    3	80         	80         	     		
> C   	    4	80         	40         	     		
> W   	    1	{X=0,Y=0}  	{X=0,Y=0}  	2.5  
> W   	    2	{X=0,Y=0}  	{X=0,Y=0}  	2.5  
> W   	    3	{X=0,Y=0}  	{X=0,Y=0}  	2.5  
> W   	    4	{X=0,Y=0}  	{X=0,Y=0}  	2.5  
> W   	    5	{X=40,Y=40}	{X=40,Y=80}	2.5  
> W   	    6	{X=40,Y=80}	{X=80,Y=80}	2.5  
> W   	    7	{X=80,Y=80}	{X=80,Y=40}	2.5  
> W   	    8	{X=80,Y=40}	{X=40,Y=40}	2.5  

Point 構造体のまま出力すると、「{x,y}」形式のカンマ付き文字列が生成されるので、
タブ区切りテキストならまだしも、CSV(カンマ区切りテキスト)とは相性が悪いかと思います。

このフォーマットにするなら、ヘッダー行は
 writer.WriteLine("Type,Index,X,Y,Stiff")
ではなく、
 writer.WriteLine("Type,Index,X1,Y1,X2,Y2,Stiff")
に変更することを提案します。


データ部については
  For Each pillar In pillarList
    writer.WriteLine($"C,{pillar.Index},{pillar.X},{pillar.Y}")
  Next
  For Each wall In wallList
    writer.WriteLine($"W,{wall.Index},{wall.StartPoint},{wall.EndPoint},{wall.Stiffness}")
  Next
ではなく、
  For Each pillar In pillarList
    writer.WriteLine($"C,{pillar.Index},{pillar.X},{pillar.Y},,,")
  Next
  For Each wall In wallList
    writer.WriteLine($"W,{wall.Index},{wall.StartPoint.X},{wall.StartPoint.Y},{wall.EndPoint.X},{wall.EndPoint.Y},{wall.Stiffness}")
  Next
のように空値の列を追加することで、すべての行で列数を同一に揃えます。


Type=W の方は列数が多いので、まとめて一行で示す代わりに
  For Each wall In wallList
    writer.Write($"W,{wall.Index}")
    writer.Write($",{wall.StartPoint.X},{wall.StartPoint.X}")
    writer.Write($",{wall.EndPoint.X},{wall.EndPoint.X}")
    writer.Write($",{wall.Stiffness}")
    writer.WriteLine()
  Next
のように分けて記述すると言う手もあるかと。



> ' ファイルパスを指定
> Dim filePath As String = "export.csv"
> Using writer As New StreamWriter(filePath)
カレントディレクトリによって、出力先がぶれることになるので、
実際の運用では、フルパスが渡されるようにした方が良いかも。


> 気になるのがWの1〜4が出力されていることでした。
> 関係ない出力だと思うのですが。
恐らく、wallList の中身が 4 件ではなく、8 件あったというだけのことだと思います。

その処理において、wallList.Add(newWall) は何回呼び出されていますか?

たとえば、MouseUp は「壁」モードだけでなく「柱」モードでも発生しますが、
壁モードでは無い時にまで、wallList に Add していないか確認してみてください。

また、ExportToCSV の処理にブレークポイントを貼って一時停止させ、
出力時点での wallList 変数の中身をデバッガで確認してみましょう。
(ローカルウィンドウ、ウォッチ、イミディエイトなど)



<蛇足情報>
Double 値を文字列として出力する場合、書式指定子を付けておかないと
 ・OS の地域設定によって、小数点記号などが異なる
という問題があります。

たとえば欧州圏では、小数に「.」ではなく「,」を使う国が多いため(fr-FR など)、
CSV との相性が悪く、そのままだとトラブルの元になりかねません。

OS 設定によって、ファイル処理が変化してしまうことを避けるため、
地域依存の無い書式指定として
  'writer.Write($",{wall.Stiffness}") '標準書式(地域設定に依存)
  writer.Write($",{wall.Stiffness:R}") 'ラウンドトリップ書式
を使うか、あるいはアプリの既定のカルチャを、明示的に InvariantCulture に
設定すると言ったことが行われます。

ただし R 書式は、.NET Core 3.0 以下や .NET Framework で用いた場合、
正確な復元に失敗する可能性があることが知られています。(変換効率も比較的低め)

そのため、値の厳密性を求めるアプリケーションにおいては、
「Double 型では、代わりに G17 書式指定子を使う」
「Single 型では、代わりに G9 書式指定子を使う」
ことが、より望ましいとされています。
https://learn.microsoft.com/ja-jp/dotnet/standard/base-types/standard-numeric-format-strings#RFormatString

ただし、小数点記号や負数記号などといった、カルチャ依存性は残ってしまうため、入出力時のカルチャを固定化する
(InvariantCulture、あるいはアプリで定めた特定のカルチャ(ja-JP など)にする)ことが必要となります。

値によっては指数表記で出力されるという問題もありますが、
このあたりの仕様をどうすべきかは、案件次第といったところ。
</蛇足情報>

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103821 ] / ▼[ 103825 ]
■103822 / 21階層)  Re[21]: Visual Basicで簡易CADを作成
□投稿者/ shiro (13回)-(2025/08/06(Wed) 12:22:08)
No103821 (魔界の仮面弁士 さん) に返信
> ■No103819 (shiro さん) に返信

魔界の仮面弁士 様

お陰様で、次のとおりIndexや座標を正しく格納できました。

Type Index X1 Y1 X2 Y2 Stiff
C 1 40 40
C 2 40 80
C 3 80 80
C 4 80 40
W 1 40 40 40 80 2.5
W 2 40 80 80 80 2.5
W 3 80 80 80 40 2.5
W 4 80 40 40 40 2.5

Wで0が4カ所出来ていた原因は、MouseUpでSelect case文でwallを設定していないためでした。
そのため、柱を最初に4カ所配置した結果が0として出力されたようです。
上述のとおり綺麗に解決しました。有難うございます。

WのCSV結果から、計算したい1つ目の方法は解決出来そうです。
そのため計算の実装は一番最後にし、以下の内容に進みたいと考えています。

1.間違って配置した柱・壁を削除する

 これは以前質問していた内容です。そのため、初めてクラス化を試みました。
 Indexを付与させた後、どうやって実装するかを考えないといけません。

 イメージとしては、削除したい柱や壁を選択すると色が変わり、右クリックや
 ダブルクリック、或いは削除ボタンで実施出来れば良いと考えています。

2.簡易CADで出力したcsvファイルから柱・壁情報を読み取って再壁画

 変更等で柱・壁が増減したり面積の増減対応のためには、最初から配置するのではなく、
 当初の図面に修正をした方が現実的です。また保存や管理上も有益なためです。

3.2階建てに対応させる

 今回、初めてGUI操作に取り組んだため、1階だけの図面となっています。
 そのため、PictureBox1だけの1画面となっています。

 現実には2階建てが多く、フロア別(1階/2階など)に PictureBox もしくは TabControl を使って
 柱や壁配置を切り替え表示したいです。

 ComboBoxで階切り替え(1階/2階)をし、PictureBox に表示するか、
 TabPage を階ごとに追加(例:1F, 2F)などが想像ですが考えられます。

 2階建てを想定した場合、2階用に「pillarList2F、wallList2F」などとして別に設け、
 Pillar、Wallクラスにも階ごとにも設定可能な「階数、階高」を追加した方が良いのかなど。



[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103822 ] / ▼[ 103828 ] ▼[ 103847 ]
■103825 / 22階層)  Re[22]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3888回)-(2025/08/06(Wed) 14:42:11)
No103822 (shiro さん) に返信
>  タ゛フ゛ルクリック、或いは削除ボタンで実施出来れば良いと考えています。

掲示板投稿時の注意事項に、
>> 半角カナは使用しないでください。文字化けの原因になります。
とありますし、利用上の注意点としても明示されております。
http://bbs.wankuma.com/index.cgi?mode=man

掲示板(cgi)側の都合によるものではありますが、次回以降ご留意ください。


> 1.間違って配置した柱・壁を削除する
既にいくつかの案を提示していたかと思いますが、まずは Visual Basic 中学校にあったように、
それぞれに IsSelected プロパティを追加してみてはいかがでしょうか?

そうすれば、Paint 時の「For Each wall In wallList」ループの中で、
 If wall.IsSelected Then
で判定して、選択されているものとそうでない物とを、別の色で描画させることもできますね。

ついでに、削除処理によって「柱と接していない壁」が生じた場合に、
柱と接している壁と接していない壁を配置エラー扱いとし、別色で塗り分けるといったこともできそう。


>  イメージとしては、削除したい柱や壁を選択すると色が変わり、右クリックや
>  タ゛フ゛ルクリック、或いは削除ボタンで実施出来れば良いと考えています。
既存の element モードについて、"wall" / "col" に加えて
"sel" あるいは "del" を加えれば処理できませんか?

アイテムを配置するモードと、アイテムを選択するモードは分けた方が良いと思うのですよ。
Form1 に Button や Label を配置するモードと、それらを削除するモードでは
ツールボックスで選択するアイコンがそれぞれ異なりますよね。それと同じで。


自分が作るなら、設置済みのアイテム(柱や壁)にマウスをホバーさせた場合、
アイテムの塗色はそのままで枠線色が変わるようにし、
選択すると、アイテムの塗色が変わるようにするかな…。

でもって、キーボードから [Delete] キーを押すと抹消されるイメージ。

ホバー判定は MouseMove 、選択は MouseDown で。

Ctrl キーを押しながら MouseDown だと複数のアイテムを選択できるとか、
Esc キーを押すと選択状態が解除されるといった作り込みもできそう。


> 2.簡易CADで出力したcsvファイルから柱・壁情報を読み取って再壁画
>
>  変更等で柱・壁が増減したり面積の増減対応のためには、最初から配置するのではなく、
>  当初の図面に修正をした方が現実的です。また保存や管理上も有益なためです。

これらは、柱と壁の List(wallList 等のこと)をいったん Clear してから、
そこに、CSV から読み取った情報を Add していくだけで済みますね。
リストの更新が終わったら、Invalid すればできあがり。


CSV データは手動で編集される可能性があるので、読み取りに関しては、
データ異常に関する制御(ファイル形式が違いますエラーとか)も必要ですね。

・列数チェックは行った方が良い(別の CSV が指定されるなど、列数が異なっていた場合に対処するため)
・壁のStiffが未指定だった場合、エラーとするのか既定値として扱うのか
・Type 列が C,W 以外の未知の値だった場合、単に読み飛ばすのか、データエラーとして扱うのか
・Index の重複時にどうするか(そもそも、Index を CSV で保持する必要はあるのか)
・同じ座標に柱が複数配置されていた場合に、無視するか警告にとどめるかエラーとするか
・目標エリア外の座標が指定された場合はどうするか
・柱無しの単独壁を許可するか許容警告とするとか不許可エラーとするか


なお、文字列から数値への変換には、CInt や CDbl ではなく、
Integer.TryParse や Double.TryParse を使うのが良いでしょう。

また、現在は Index を自動採番としていますが、CSV 読み込み時は自動採番せずに CSV の Index を採用したい場合は
Index を明示指定できるよう、Sub New の引数に Index も指定できるオーバーロードを追加するべきでしょう。
その場合、CSV 読み込み後に追加されるアイテムの Index 値も調整可能な設計にしないといけませんね。



> 3.2階建てに対応させる
>
>  今回、初めてGUI操作に取り組んだため、1階だけの図面となっています。
>  そのため、PictureBox1だけの1画面となっています。
>
>  現実には2階建てが多く、フロア別(1階/2階など)に PictureBox もしくは TabControl を使って
>  柱や壁配置を切り替え表示したいです。

こちらですが、描画内容は List(Of ) コレクションで管理されているので、
PictureBox は一つだけで充分では無いでしょうか。
1階と2階を同時に表示する必要はなく、切り替えられれば良いのですよね?

たとえば、階層選択用の ComboBox を DropDownList スタイルで一つ用意しておき、
そこに階数を入れておきます。ComboBox の選択が変更された時に Invalidate を呼べば、
自動的にその階が描画されます。

このため、たとえば wallList を一次元配列あるいは Dictionary で管理してみます。
 Private wallList As New Dictionary(Of String, List(Of Wall))() '複数フロア版

フロアを追加する場合は、
 wallList("3F") = New List(Of Wall)()
のように書きます。フロアに壁を追加する処理は
 wallList("3F").Add( newWall )
という構文に変わります。

管理イメージとしては
 wallList("1F")(0) ならば 1 階 の壁A
 wallList("1F")(1) ならば 1 階 の壁B
 wallList("1F")(2) ならば 1 階 の壁C
 wallList("2F")(0) ならば 2 階 の壁A
 wallList("B1F")(0) ならば 地下1階 の壁A
といった具合です。
階数情報は、先ほどの ComboBox から得られます。

このように、Dictionary コレクションを使って管理することで
>  2階建てを想定した場合、2階用に「pillarList2F、wallList2F」などとして別に設け、
といった、個別の変数やクラスを作り直すことなく、任意数のフロアを制作できるでしょう。


>  Pillar、Wallクラスにも階ごとにも設定可能な「階数、階高」を追加した方が良いのかなど。
管理方法によっては

Public Class Floor
 Public Property Name As String
 Public ReadOnly Property Walls As List(Of Wall)
 Public ReadOnly Property Columns As List(Of Column)
 Public Sub New(name As String)
  Me.Name = name
  Me.Walls = New List(Of Wall)()
  Me.Columns = New List(Of Column)()
 End Sub
End Class

のようなクラスを作ってみるという手もあります。

この場合、Form1 の wallList 変数は廃止して
 Private floors As New List(Of Floor)()
あるいは
 Private floors As New Dictionary(Of String, Floor)()
などで管理するということです。


今までは、Form1 が直接、柱や壁を管理していましたが、
このパターンでは、Form1 が管理するのは Floor だけです。
そしてそれぞれの Floor が、その階にある壁や柱を管理するイメージ。

このような構造にしておくと、たとえば、
 柱や壁だけでなく、階段(Stairs)も管理できるようにする
 Floor ごとに、その階の床から天井までの高さを管理するプロパティを持たせる
 階によって基準剛性値を変更したいような場合、Floor ごとに DefaultStiffValue を持たせる
など、階固有の管理情報を加えたい場合にも拡張しやすくなります。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103825 ] / ▼[ 103830 ]
■103828 / 23階層)  Re[23]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3889回)-(2025/08/06(Wed) 17:53:37)
No103825 (魔界の仮面弁士) に追記
> それぞれに IsSelected プロパティを追加してみてはいかがでしょうか?
> ホバー判定は MouseMove 、選択は MouseDown で。

座標判定については、たとえば「柱」ならば、
Rectangle の Contains メソッドを使ってクリック座標が柱内かどうかを調べられます。
(Visual Basic 中学校の例でも、このメソッドが使われていました)


壁については、同様の Contains メソッドが無いので、
2点間の距離の公式を使ってヒットテスト判定を自作します。

点が幅のある直線上にあるかどうかで判定するわけですが、
今回は pen7 で示された幅 5 の直線で描かれているようなので、
線分とその点との距離(ユークリッド距離)を計算し、
その距離が線幅の半分以下(2.5)かどうかで判定してやります。
いわゆる三平方の定理(ピタゴラスの定理)ってやつですね。


こうしたオブジェクトごとの座標判定は、Form1 の PictureBox 内に書き連ねると
コードが複雑化して分かりにくくなるので、座標判定は壁や柱のクラス自身に任せた方が良いです。

たとえば Wall クラスに、Function Contains(p As Point) As Boolean メソッドを用意し、
引数から受け取ったマウス座標に対して、自壁の上の座標であれば True を返すように実装します。

Form1 側からこの判定メソッドを呼び出すようにすれば、PictureBox 側のコードを複雑化させずに済みます。


そのようにして、選択モードの際にマウス座標位置にアイテムがあるかどうかを調べ、
MouseDown 位置にアイテム(壁や柱)があれば、そのアイテムの IsSelected プロパティを反転させます。

 w.IsSelected = Not w.IsSelected

Not で反転させているので、未選択をクリックすれば選択状態になりますし、
選択済みのものを再度クリックすれば選択状態が解除されます。


この時、複数選択を許可せず単一選択モードとする場合は、
それ以外のすべてのアイテムの選択を解除する処理もあわせて必要となります。
Visual Basic 中学校のサンプルでもそうなっていましたね。


また、壁は柱に比べるとクリックしにくそうなので、可能であれば、
クリック以外での選択方法も一緒に用意しておくと便利かもしれません。
No103805 で述べた ListBox 案のように。

これはたとえば、Visual Studio のフォームデザイナーでいうところの
 [表示]-[その他のウィンドウ]-[ドキュメント アウトライン]

 プロパティ ウィンドウ上部のコントロール名一覧ドロップダウン
のような選択画面をイメージしています。

あるいは、右クリックでコンテキストメニューを表示するようにして、
そこから壁や柱の一覧を選べるようにしても良いかと。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103828 ] / ▼[ 103832 ] ▼[ 103833 ]
■103830 / 24階層)  Re[24]: Visual Basicで簡易CADを作成
□投稿者/ shiro (14回)-(2025/08/06(Wed) 19:13:25)
No103828 (魔界の仮面弁士 さん) に返信
> ■No103825 (魔界の仮面弁士) に追記

魔界の仮面弁士 様

早速ですが、下記のとおり作成してみました。

それぞれのクラスに、

    Public Property IsSelected As Boolean

を設定しました。

右クリックで

   IsSelected = True

としてみました。

Button5で一気に削除することにしました。

また、柱は楕円で、壁は太さを3で被らないようにして見やすくしました。

コンパイルは成功するのですが、実行すると綺麗に順番に削除されませんでした。
柱や壁が黄色で反転されて削除されるのは確認できましたが。

           記

    Private selectedPillars As New List(Of Pillar)
    Private selectedWalls As New List(Of Wall)



   Private Sub PictureBox1_MouseDown(
          ByVal sender As System.Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseDown


      If e.Button.HasFlag(MouseButtons.Right) Then

            For Each pillar In pillarList
                pillar.IsSelected = True
            Next

            For Each wall In wallList
                wall.IsSelected = True
            Next

        End If

       '削除する柱や壁をクリックすることで選択できるようにする

        For Each pillar In pillarList

                If pillar.IsSelected Then
                    If Math.Abs(e.X - pillar.X) < 10 AndAlso Math.Abs(e.Y - pillar.Y) < 10 Then
                        If Not selectedPillars.Contains(pillar) Then
                            selectedPillars.Add(pillar)
                        End If
                    End If
                End If

            Next

        For Each wall In wallList

                If wall.IsSelected Then

                    If Math.Abs(e.X - wall.startPoint.X) < 10 AndAlso Math.Abs(e.Y - wall.endPoint.Y) < 10 Then
                        If Not selectedWalls.Contains(wall) Then
                            selectedWalls.Add(wall)
                        End If
                    End If

                End If

            Next



    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
  
        For Each pillar In pillarList

                If pillar.IsSelected Then

                    If selectedPillars.Contains(pillar) Then
                         e.Graphics.FillEllipse(Brushes.Yellow, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
                    Else
                        e.Graphics.FillRectangle(Brushes.Red, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
                    End If

                End If

            Next

        For Each wall In wallList

                If wall.IsSelected Then

                If selectedWalls.Contains(wall) Then
 
                    e.Graphics.DrawLine(pen9, wall.startPoint, wall.endPoint)
                Else
 
                    e.Graphics.DrawLine(pen7, wall.startPoint, wall.endPoint)
                End If

                End If

        Next


   Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click

        '3. 削除ボタンを追加する

        '選択された柱や壁を削除する操作を追加。

         For Each pillar In selectedPillars

                If pillar.IsSelected Then

                    pillarList.Remove(pillar)

                End If

            Next
            selectedPillars.Clear()

        For Each wall In selectedWalls

                If wall.IsSelected Then

                    wallList.Remove(wall)

                End If

            Next
            selectedWalls.Clear()


        PictureBox1.Invalidate() ' 再描画

    End Sub


Public Class Pillar
    Private Shared LastIndex As Integer = 0
    Public ReadOnly Property Index As Integer
    Public Property X As Integer
    Public Property Y As Integer

    Public Property IsSelected As Boolean

    Public Sub New(location As Point)
        LastIndex += 1
        Me.Index = LastIndex
        Me.X = location.X
        Me.Y = location.Y

        'Me.IsSelected = False

    End Sub
End Class




'=== 案1 ===
Public Class Wall
    Private Shared LastIndex As Integer = 0
    Private Const DefaultStiffnessValue As Double = 2.5

    Public ReadOnly Property Index As Integer
    Public Property startPoint As Point
    Public Property endPoint As Point
    Public Property Stiffness As Double

    Public Property IsSelected As Boolean

    Public Sub New(startPoint As Point, endPoint As Point)
        Me.New(startPoint, endPoint, DefaultStiffnessValue)
    End Sub

    Public Sub New(startPoint As Point, endPoint As Point, stiffness As Double)
        LastIndex += 1
        Me.Index = LastIndex
        Me.StartPoint = startPoint
        Me.EndPoint = endPoint
        Me.Stiffness = stiffness

        'Me.IsSelected = IsSelected

    End Sub
End Class

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103830 ] / ▼[ 103834 ]
■103832 / 25階層)  Re[25]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3891回)-(2025/08/06(Wed) 19:44:38)
No103830 (shiro さん) に返信
> If selectedPillars.Contains(pillar) Then

IsSelected = True なアイテムは選択済み、
IsSelected = False なアイテムは未選択なのですから
わざわざ selectedWalls といった、別のコレクションを用意する必要は無いと思います。

No103794 のダブルクリック案の時も、
selectedPolygons As List(Of Polygon) なんて作らずに処理できていましたよね。

なので
> Private selectedPillars As New List(Of Pillar)
> Private selectedWalls As New List(Of Wall)
は不要かと思います。



> If e.Button.HasFlag(MouseButtons.Right) Then
>  For Each pillar In pillarList
>   pillar.IsSelected = True
> Next
>  For Each wall In wallList
>   wall.IsSelected = True
>  Next
> End If

この処理は、「右クリックされたら、すべてのアイテムを選択状態にする」
という処理になってしまっています。

すべてのアイテムではなく、右クリックした位置のアイテムだけを選択したいなら
For Each で列挙して、それぞれのアイテムの位置を確認して
それと e.Location (あるいは e.X, e.Y) の位置を比較して
該当したものだけを True に置き換えるようにします。


イメージとしては、こんな感じ。selectedWalls コレクションは使いません。

 If e.Button.HasFlag(MouseButtons.Right) Then
  For Each wall In wallList
   wall.IsSelected = wall.Contains(e.Location)
  Next
  For Each pillar In pillarList
   pillar.IsSelected = pillar.Contains(e.Location)
  Next
 End If


上記の wall.Contains というのは、Wall クラスに追加した
 Public Function Contains(p As Point) As Boolean
なメソッドであり、その中身は No103830 でいうところの
 If Math.Abs(e.X - wall.startPoint.X) < 10 AndAlso Math.Abs(e.Y - wall.endPoint.Y) < 10 Then
の部分に相当する座標判定コードが書かれています。
※Wall クラス内で呼ばれるので、上記の "wall.startPoint.X" の部分は "Me.startPoint.X" になる


MouseDown の処理は上記のように変化した上で、Button5_Click は
No103794 の PictureBox1_DoubleClick と完全に同一の実装で大丈夫です。
変数 polygons が pillarList と wallList に変わるだけ。
Clear メソッドも不要です。



その上で、Paint イベントは
 For Each wall In wallList
  Dim p = If(wall.IsSelected, pen9, pen7) '選択状態に合わせて Pen を切り替える
  e.Graphics.DrawLine(p, wall.startPoint, wall.endPoint)
 Next
のようにします。色が変わるだけで座標情報は変わらないのですから、
この方が処理としても短く書けますよね。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103832 ] / ▼[ 103835 ]
■103834 / 26階層)  Re[26]: Visual Basicで簡易CADを作成
□投稿者/ shiro (15回)-(2025/08/07(Thu) 07:06:59)
No103832 (魔界の仮面弁士 さん) に返信
> ■No103830 (shiro さん) に返信

魔界の仮面弁士 様

下記のとおり修正してコンパイルすると次のワーニングが出ましたが実行ファイルは出来ました。
新しくクラスに追加したFunction部分です。

右クリックして柱や壁を選択しても黄色表示がされず、削除もできませんでした。


1>Form1.vb(1510,5): warning BC42353: 関数 'Contains' には値を返さないコード パスがあります。'Return' ステートメントが不足していないかどうかを確認してください。
1>Form1.vb(1480,5): warning BC42353: 関数 'Contains' には値を返さないコード パスがあります。'Return' ステートメントが不足していないかどうかを確認してください。


           記

    'Private selectedPillars As New List(Of Pillar)
    'Private selectedWalls As New List(Of Wall)


   Private Sub PictureBox1_MouseDown(
          ByVal sender As System.Object,
          ByVal e As System.Windows.Forms.MouseEventArgs) _
          Handles PictureBox1.MouseDown

       If e.Button.HasFlag(MouseButtons.Right) Then

            For Each pillar In pillarList
                pillar.IsSelected = pillar.Contains(e.Location)
            Next

            For Each wall In wallList
                wall.IsSelected = wall.Contains(e.Location)
            Next

        End If

       '削除する柱や壁をクリックすることで選択できるようにする

        For Each pillar In pillarList
            If pillar.IsSelected Then
                If Math.Abs(e.X - pillar.X) < 10 AndAlso Math.Abs(e.Y - pillar.Y) < 10 Then
                    'If Not selectedPillars.Contains(pillar) Then
                    'selectedPillars.Add(pillar)
                    'End If
                End If
            End If
        Next

       For Each wall In wallList
            If wall.IsSelected Then

                If Math.Abs(e.X - Me.startPoint.X) < 10 AndAlso Math.Abs(e.Y - Me.endPoint.Y) < 10 Then
                    'If Math.Abs(e.X - wall.startPoint.X) < 10 AndAlso Math.Abs(e.Y - wall.endPoint.Y) < 10 Then

                    'If Not selectedWalls.Contains(wall) Then
                    'selectedWalls.Add(wall)
                    'End If
                End If
            End If
        Next


    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
  
       For Each pillar In pillarList
            If pillar.IsSelected Then
                'If selectedPillars.Contains(pillar) Then
                'e.Graphics.FillEllipse(Brushes.Yellow, pillar.X - 5, pillar.Y - 5, 10, 10)
                e.Graphics.FillEllipse(Brushes.Yellow, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
            Else
                'e.Graphics.FillEllipse(Brushes.Red, pillar.X - 5, pillar.Y - 5, 10, 10)
                e.Graphics.FillRectangle(Brushes.Red, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
                'End If
            End If
        Next

       For Each wall In wallList
            Dim p = If(wall.IsSelected, pen9, pen7) '選択状態に合わせて Pen を切り替える
            e.Graphics.DrawLine(p, wall.startPoint, wall.endPoint)
        Next


   Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click

        Dim selectedPillar As Pillar = pillarList.FirstOrDefault(Function(p) p.IsSelected)
        If selectedPillar IsNot Nothing Then
            '選択済みの矩形があれば削除
            pillarList.Remove(selectedPillar)
            PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
        End If

        Dim selectedWall As Wall = wallList.FirstOrDefault(Function(p) p.IsSelected)
        If selectedWall IsNot Nothing Then
            '選択済みの矩形があれば削除
            wallList.Remove(selectedWall)
            PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
        End If

    End Sub

Public Class Pillar
    Private Shared LastIndex As Integer = 0
    Public ReadOnly Property Index As Integer
    Public Property X As Integer
    Public Property Y As Integer

    Public Property IsSelected As Boolean

    Public Function Contains(p As Point) As Boolean

    End Function

    Public Sub New(location As Point)
        LastIndex += 1
        Me.Index = LastIndex
        Me.X = location.X
        Me.Y = location.Y

        'Me.IsSelected = False

    End Sub
End Class


'=== 案1 ===
Public Class Wall
    Private Shared LastIndex As Integer = 0
    Private Const DefaultStiffnessValue As Double = 2.5

    Public ReadOnly Property Index As Integer
    Public Property startPoint As Point
    Public Property endPoint As Point
    Public Property Stiffness As Double

    Public Property IsSelected As Boolean

    Public Function Contains(p As Point) As Boolean

    End Function

    Public Sub New(startPoint As Point, endPoint As Point)
        Me.New(startPoint, endPoint, DefaultStiffnessValue)
    End Sub

    Public Sub New(startPoint As Point, endPoint As Point, stiffness As Double)
        LastIndex += 1
        Me.Index = LastIndex
        Me.StartPoint = startPoint
        Me.EndPoint = endPoint
        Me.Stiffness = stiffness

        'Me.IsSelected = IsSelected

    End Sub
End Class

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103834 ] / ▼[ 103836 ]
■103835 / 27階層)  Re[27]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3893回)-(2025/08/07(Thu) 12:18:14)
No103834 (shiro さん) に返信
> 下記のとおり修正してコンパイルすると次のワーニングが出ましたが実行ファイルは出来ました。
> 新しくクラスに追加したFunction部分です。
> 1>Form1.vb(1510,5): warning BC42353: 関数 'Contains' には値を返さないコード パスがあります。'Return' ステートメントが不足していないかどうかを確認してください。
> 1>Form1.vb(1480,5): warning BC42353: 関数 'Contains' には値を返さないコード パスがあります。'Return' ステートメントが不足していないかどうかを確認してください。

Function の「メソッド定義」しか追加していないですよね。肝心の「中身」の実装はどうしたのでしょうか?
戻り値が As Boolean だというのに、True も False も Return していないではありませんか。


No103832 の回答は、用意した Contains メソッドの中に、座標判定のコードを自身で用意せよ、という意味です。

>>> イメージとしては、こんな感じ。
>>>  (中略)
>>> 上記の wall.Contains というのは、Wall クラスに追加した
>>>  Public Function Contains(p As Point) As Boolean
>>> なメソッドであり、その中身は No103830 でいうところの
>>>  If Math.Abs(e.X - wall.startPoint.X) < 10 AndAlso Math.Abs(e.Y - wall.endPoint.Y) < 10 Then
>>> の部分に相当する座標判定コードが書かれています。
>>> ※Wall クラス内で呼ばれるので、上記の "wall.startPoint.X" の部分は "Me.startPoint.X" になる


contain とは、「含有する」「含む」を意味する動詞であり、
contains は、それの三人称単数現在形(-s)。

VB 中学校でも使われていた、標準実装の「Rectangle 構造体の Contains メソッド」もまた、
引数指定の座標が矩形内の座標であれば True、矩形外なら False を返すメソッドです。
いわゆる hit-test 判定のメソッドにあたります。

今回作成する「壁クラス/柱クラスの Contains メソッド」についても、
引数指定の座標(マウス位置)が、それらのオブジェクトの中であるかどうかを
True/False で返すものとして実装する想定としています。

そのための判定方法は、既にご自身が No103830 で書かれたものがあるので、
まずはそれらを移植してみてください。これは No10832 で述べた通り、
今まで、For Each 内で「wall.startPoint.X」や「pillar.X」と記していたものを、
壁/柱クラスの Contains 内で「Me.startPoint.X」や「Me.X」に変更する作業です。


> '削除する柱や壁をクリックすることで選択できるようにする
MouseDown イベント内における、上記コメントから下にあるコードですが、
For ループと If 判定が行われているものの、その中身は、すべてがコメントアウトされていますよね。

条件判定していても、その後で実質的に何もしていないのですから、
この部分のコードは現状、まったく何の役にも立っていません。すべて削除してしまいましょう。
もちろん、投稿時に省略されたであろう「描画位置を決めるための endPoint や isDragging 関連のコード」は残します。


MouseDown 右クリックによる選択処理というのは、実質的にはそのコメントの上にある
.IsSelect プロパティに True/False を代入するコードのところだけです。

True が代入されれば、そのオブジェクト(壁/柱)が選択され、False なら未選択を意味します。
今は Contains メソッドの実装をサボったせいで、常に False しか代入されていませんけれどね。


あと、「柱の rectangleSize」や「壁の厚さ」は Form1 側で管理させるのではなく、
個々の柱側に持たせておいた方が扱いやすいかもしれません。保持のさせ方は、壁の Stiffness と同様です。


選択のためのヒットテストを行う際にも、描画処理を行う際にも、こうしたサイズ情報が必要になってきますが、
「壁関連の座標計算は Wall クラス自身」「柱関連の座標計算は Pillar クラス自身」にと
それぞれ分担させておくことで、コードの保守性を高めることができるかと思うのです。

もちろん、各オブジェクトに自身の座標を算出させるのではなく、現状のように
「あらやるオブジェクトの座標判定を Form1 自身に担当させる」というコーディングも可能なのですが、
全部を Form1 に任せてしまうとコードが読みにくくなるので、各クラスに役割分担させるということです。


しかし現状、そのあたりの座標データの管理方法に、どうにも統一性がとれていないように見えるのです。
たとえば、提示頂いたコードの柱関連の座標処理を見てみましょう。

[Paint]イベントでは、マジックナンバー「10」の代わりに rectangleSize 表記に改良されているにもかかわらず、
> 'e.Graphics.FillEllipse(Brushes.Yellow, pillar.X - 5, pillar.Y - 5, 10, 10)
> e.Graphics.FillEllipse(Brushes.Yellow, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)
柱の hit-test 判定部のコードは、マジックナンバー「10」がそのまま使われており、処理に統一性がありません。
> If Math.Abs(e.X - pillar.X) < 10 AndAlso Math.Abs(e.Y - pillar.Y) < 10 Then


ということで、この rectangleSize 値の管理を柱クラス内に移動させておきます。
そうすると、先ほどの柱クラスの Contains 実装は

 Public Function Contains(p As Point) As Boolean
  If Math.Abs(p.X - Me.X) < Me.rectangleSize AndAlso Math.Abs(p.Y - pillar.Y) < Me.rectangleSize Then
   Return True
  Else
   Return False
  End If
 End Function

のように書けるということになります。

なお、If 判定式というのはそれ自体が True/False を表すものなので、上記をより簡単にして
 Public Function Contains(p As Point) As Boolean
  Return Math.Abs(p.X - Me.X) < Me.rectangleSize AndAlso Math.Abs(p.Y - pillar.Y) < Me.rectangleSize
 End Function
と単純化することもできます。



ただ……ちょっと気になるのは、選択時と非選択時で柱の形状が異なる事。
No103830 以降では、柱の形は「普段は赤い角柱(FillRectangle)」「選択時だけ黄色の円柱(FillEllipse)」なのですよね。

円柱と角柱ではヒットテスト領域が異なってしまいますが、今のヒットテストは正方形前提なので、
操作時に違和感が生じたりしないかな、と。

円柱モードになっているときは、ヒットテストのエリア判定(Contains メソッド)が変化するはずですよね。
まぁ、判定エリアが正方形のままだとしても、大きな問題にはならないかも知れませんが。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103835 ] / ▼[ 103837 ]
■103836 / 28階層)  Re[28]: Visual Basicで簡易CADを作成
□投稿者/ shiro (16回)-(2025/08/07(Thu) 19:14:49)
No103835 (魔界の仮面弁士 さん) に返信
> ■No103834 (shiro さん) に返信

魔界の仮面弁士 様

> ただ……ちょっと気になるのは、選択時と非選択時で柱の形状が異なる事。
> No103830 以降では、柱の形は「普段は赤い角柱(FillRectangle)」「選択時だけ黄色の円柱(FillEllipse)」なのですよね。
> 
> 円柱と角柱ではヒットテスト領域が異なってしまいますが、今のヒットテストは正方形前提なので、
> 操作時に違和感が生じたりしないかな、と。
> 
> 円柱モードになっているときは、ヒットテストのエリア判定(Contains メソッド)が変化するはずですよね。
> まぁ、判定エリアが正方形のままだとしても、大きな問題にはならないかも知れませんが。

動作確認のため、柱は円形にして赤色が見えるように、壁は太さを細くして青色部分が見えるようにしました。
領域判定でミスがあるかもしれないため、同じ大きさに修正しました。

クラスのFunction部分で下記のとおり修正しました。
柱・壁とも10の距離だと同時に黄色を選択してしまうためでした。

少し改善しましたが、それでも次のような不具合がありました。

・柱だけを先に配置し、右クリックで柱を選択しようとしても黄色にならない。
 壁も配置し終わった後でないと有効にならない。

・壁を選択しようとすると柱も一緒に黄色に選択してしまったり、
 新たに青色の壁を新たな方向に配置してしまう。

VB中学生のように、一回で綺麗に削除出来なく立ち止まってしまいました。


          記

Pillar Class

        If Math.Abs(p.X - Me.X) < 5 AndAlso Math.Abs(p.Y - Me.Y) < 5 Then
  
Wall Class

        If Math.Abs(p.X - Me.startPoint.X) < 15 Or Math.Abs(p.Y - Me.endPoint.Y) < 15 Then

Paint

      For Each pillar In pillarList

            If pillar.IsSelected Then

                e.Graphics.FillRectangle(Brushes.Yellow, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            g.DrawRectangle(Pens.Black, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            Else

                e.Graphics.FillRectangle(Brushes.Red, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            g.DrawRectangle(Pens.Black, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            End If

        Next


        For Each wall In wallList
            Dim p = If(wall.IsSelected, pen9, pen7) '選択状態に合わせて Pen を切り替える
            e.Graphics.DrawLine(p, wall.startPoint, wall.endPoint)
        Next

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103836 ] / ▼[ 103838 ] ▼[ 103839 ]
■103837 / 29階層)  Re[29]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3894回)-(2025/08/07(Thu) 20:25:02)
No103836 (shiro さん) に返信
> 柱・壁とも10の距離だと同時に黄色を選択してしまうためでした。

現状は、右クリックした時に選択判定が起きますが、その操作だと
近くに複数のアイテムがあった場合、どれが選択されるかユーザーに分かりにくいですね。

こういう時には、
 「アイテム上にマウスがホバーしていた時に色を変える」
 「アイテムを選択(右クリック)したら、さらに別の色にする」
な画面操作にすることで、ユーザーの誤選択を防げるようになるかと思います。

この掲示板の HTML リンクでも、
 普段は青文字
 ホバー時は下線+赤文字
として区別されていますが、それと同じようなイメージです。


また、クリック選択する際には「円柱なら円の中だけ」「壁なら線分の中だけ」のように
ヒットテスト範囲を厳密にすることで、近接するアイテムが誤って選択されることを防げそうです。


あるいは、右クリックした時に複数の選択候補がある場合、
どれを選択させるか、ユーザーに選ばせるようにしてみるのも便利かもしれません。

WinForms でも、TabControl などの上に Button を配置し、
デザイン時にそのボタンを右クリックすると、
 TabPage1 の選択
 TabControl1 の選択
 Form1 の選択
という選択肢が現れ、それを選ぶことで正しいアイテムを選べるので、それと同じイメージ。


> ・柱だけを先に配置し、右クリックで柱を選択しようとしても黄色にならない。
>  壁も配置し終わった後でないと有効にならない。
状況を整理しましょう。
「黄色にならない」のではなく、「選択状態にできていない」のではありませんか?
もう一度デバッグしてみてください。

IsSelcted が True なのに黄色にならないのなら、
Paint イベントの処理に不具合があることになります。

しかし、右クリックしても IsSelected が False のままになっているだとか、
あるいは別アイテムが選択されてしまうといった状況なのであれば、それは
Paint イベントではなくマウス系イベント(MouseDown など)に問題があるということです。


> ・壁を選択しようとすると柱も一緒に黄色に選択してしまったり、
>  新たに青色の壁を新たな方向に配置してしまう。

座標を決めるための各種のフィールド変数(element, isPlacing, startPoint, endPoint など)の値が、
それぞれのタイミングでどういう値になっているか、デバッグ時に確認するようにしてみましょう。

値の確認には、Debug.WriteLine メソッドを使うことができます。
https://www.umayadia.com/Note/Note021DebugWriteLine.htm
https://www.umayadia.com/Note/Note001ConsoleWriteLine.htm


一時停止中なら、ローカルウィンドウ/自動変数ウィンドウ/ウォッチウィンドウなどでも確認できますが、
マウス操作など随時発生する処理の値を追跡するには、Debug.WriteLine を使うのが良いかと思います。

値の変化をデバッグしてみると、たとえば新規に壁を作る前に、以前のクリック座標の情報をリセットし忘れているとか、
あるいは判定式の書き漏れがあるとか、そういったミスを発見できるかもしれません。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103837 ] / 返信無し
■103838 / 30階層)  Re[30]: Visual Basicで簡易CADを作成
□投稿者/ shiro (17回)-(2025/08/08(Fri) 03:34:56)
No103837 (魔界の仮面弁士 さん) に返信
> ■No103836 (shiro さん) に返信

魔界の仮面弁士 様

> 座標を決めるための各種のフィールド変数(element, isPlacing, startPoint, endPoint など)の値が、
> それぞれのタイミングでどういう値になっているか、デバッグ時に確認するようにしてみましょう。
> 
> 一時停止中なら、ローカルウィンドウ/自動変数ウィンドウ/ウォッチウィンドウなどでも確認できますが、
> マウス操作など随時発生する処理の値を追跡するには、Debug.WriteLine を使うのが良いかと思います。
> 
> 値の変化をデバッグしてみると、たとえば新規に壁を作る前に、以前のクリック座標の情報をリセットし忘れているとか、
> あるいは判定式の書き漏れがあるとか、そういったミスを発見できるかもしれません。

デバッグで確認します。
動くことは確認できましたので改善をしてみます。

ファイルの入力にも取り組んでみました。下記に示します。
出力したファイルを読み込んでPAINTで配置しようとしましたがコンパイルエラーになります。
IndexはReadOnlyというエラーも。

        記

    Dim filePath As String = "export.csv"

    Using reader As New StreamReader(filePath)
        Dim headerLine As String = reader.ReadLine() ' ヘッダーを読み飛ばす
        While Not reader.EndOfStream
            Dim line As String = reader.ReadLine()
            Dim data As String() = line.Split(",")

            If data(0) = "C" Then
                pillarList.Add(New Pillar With {
                    .Index = Integer.Parse(data(1)),
                    .X = Integer.Parse(data(2)),
                    .Y = Integer.Parse(data(3))
                })
            ElseIf data(0) = "W" Then
                wallList.Add(New Wall With {
                    .Index = Integer.Parse(data(1)),
                    .StartPoint = New Point(Integer.Parse(data(2)), Integer.Parse(data(3))),
                    .EndPoint = New Point(Integer.Parse(data(4)), Integer.Parse(data(5))),
                    .Stiffness = Double.Parse(data(6))
                })
            End If
        End While
    End Using

    PictureBox1.Invalidate() ' 再描画


[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103837 ] / ▼[ 103841 ]
■103839 / 30階層)  Re[30]: Visual Basicで簡易CADを作成
□投稿者/ shiro (18回)-(2025/08/08(Fri) 07:29:36)
No103837 (魔界の仮面弁士 さん) に返信
> ■No103836 (shiro さん) に返信

>>・柱だけを先に配置し、右クリックで柱を選択しようとしても黄色にならない。
>> 壁も配置し終わった後でないと有効にならない。

この原因が分かりました。
柱・壁をSelect Case文で処理していましたが、その前に右クリック判定をしていました。
そのため、pillarListに柱が入力されませんでした。
修正したところ、削除ボタンで消えましたが、2回押さないと消えませんでした。
空欄に右クリックすると、黄色の柱が配置されました。
よって、右クリックでも柱が配置されるため、2つの柱が重なり2回処理を繰り返したようです。

壁の方はMouseDownでstartpointが設定され(左右どちらのクリックでも)、upでwalllistに記録されるため、
右クリックで削除しようとすると可笑しな挙動になっていました。

壁も柱も左クリックだけで配置し、削除は右クリックと設定すれば改善するかもしれません。
それでも、柱・壁が混在すると柱と壁が同時に選択されてしまいます。
壁の方を上手く柱の選択に被らないようにしたいのですが、良い解決法が浮かびませんでした。





[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103839 ] / ▼[ 103842 ]
■103841 / 31階層)  Re[31]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3896回)-(2025/08/08(Fri) 09:40:56)

No103838 (shiro さん) に返信
> IndexはReadOnlyというエラーも。

はい。既に No103825 で指摘しているように、それについては
「Index を指定可能なコンストラクタ」を設けることで回避できます。
>>> また、現在は Index を自動採番としていますが、CSV 読み込み時は自動採番せずに CSV の Index を採用したい場合は
>>> Index を明示指定できるよう、Sub New の引数に Index も指定できるオーバーロードを追加するべきでしょう。


また、Index は自動採番としているのですから、CSV から読み込んだ後で
LastIndex を「CSV 内の最終番号(あるいはそれ以上の値)」にする必要もありますので
その点も考慮してみてください。これも既に指摘済みです。
>>> その場合、CSV 読み込み後に追加されるアイテムの Index 値も調整可能な設計にしないといけませんね。


No103839 (shiro さん) に返信
> この原因が分かりました。
> 柱・壁をSelect Case文で処理していましたが、その前に右クリック判定をしていました。

このあたり、掲示板に投稿いただいた範囲のコードでは無いため、
具体的な修正箇所を提示できず、前回の回答のように、
>>> もう一度デバッグしてみてください。
としか答えられませんでした。でも、解決できてよかったです。


> 修正したところ、削除ボタンで消えましたが、2回押さないと消えませんでした。
配置処理に問題があるのか、選択処理に問題があるのか、削除ボタンの処理に問題があるのか、でしょうね。

まずは削除ボタンの Click イベントの先頭に、各種変数の値を Debug.WriteLine するコードを書いておき、
1回目の Click と 2回目の Click とで、それぞれの変数の状態にどのような差異があるのか確認してみましょう。


> 空欄に右クリックすると、黄色の柱が配置されました。
> よって、右クリックでも柱が配置されるため、2つの柱が重なり2回処理を繰り返したようです。
柱と壁の List(Of ) 内に、重複した内容が含まれていないかどうか、Debug.WriteLine やウォッチウィンドウにて確認しましょう。
その上で重複しているようであれば、次にやることは重複した原因の調査です。

コーディングミスで誤って配置されているのであれば、要因を調べて直さねばならないでしょう。

あるいはユーザー操作によって同一座標に配置されたものであれば、
 案1) 重複位置に配置させようとしたら、単に無視して何もしない(List に Add しない)
 案2) 重複位置に配置させようとしたら警告メッセージを出し、本当に重複配置させるかどうかを Yes/No 選択させる
 案3) 重複配置をそのまま認めるが、重なっていることが分かるように描画を工夫する(重複数を柱の上に数字で表現するなど)
など、重複を認めるのかどうか、認めないならどうあるべきか、認めるのであれば見やすく改善するといったルールを定めます。

> 壁の方はMouseDownでstartpointが設定され(左右どちらのクリックでも)、upでwalllistに記録されるため、
> 右クリックで削除しようとすると可笑しな挙動になっていました。
それを避けるため、 No103825 では、壁配置モードや柱配置モードとは別に
>>> 既存の element モードについて、"wall" / "col" に加えて
>>> "sel" あるいは "del" を加えれば処理できませんか?
という指摘をさせていただいています。

もちろん、そうした専用モードを付けずとも、左クリックと右クリックを区別して処理することで、
この問題は防げるわけなので、そこはコーディング次第ですね。


> 壁も柱も左クリックだけで配置し、削除は右クリックと設定すれば改善するかもしれません。
ですね。


> それでも、柱・壁が混在すると柱と壁が同時に選択されてしまいます。
はい。ゆえにそうした点に対処するために、ヒットテスト領域をより厳密にするなど
 ・柱なら、円柱や角柱の領域をヒットテストエリアにする ( No103835 )
 ・壁なら、線分をヒットテストエリアにする ( No103828 )
 ・交点や重複配置点などで複数のアイテムが候補にあった場合の仕組みを検討する ( No103837 )
 ・クリック以外でも選択できる機能を検討する ( No103805 )
などといった、さまざまな提案をしてきたつもりです。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103841 ] / ▼[ 103843 ]
■103842 / 32階層)  Re[32]: Visual Basicで簡易CADを作成
□投稿者/ shiro (19回)-(2025/08/09(Sat) 05:05:04)
No103841 (魔界の仮面弁士 さん) に返信
> 

魔界の仮面弁士 様

>>それでも、柱・壁が混在すると柱と壁が同時に選択されてしまいます。
> はい。ゆえにそうした点に対処するために、ヒットテスト領域をより厳密にするなど
>  ・柱なら、円柱や角柱の領域をヒットテストエリアにする ( No103835 )
>  ・壁なら、線分をヒットテストエリアにする ( No103828 )
>  ・交点や重複配置点などで複数のアイテムが候補にあった場合の仕組みを検討する ( No103837 )
>  ・クリック以外でも選択できる機能を検討する ( No103805 )
> などといった、さまざまな提案をしてきたつもりです。

線分との距離でヒットすることにさせ柱との重複問題が解消されました。
下記のFunctionのとおりです。

また、左クリック時のみ柱・壁を作図させ、右クリックのみ黄色にし、削除ボタンで削除させました。

この時、柱を1本配置し、右クリックをしても黄色になりません。
2本目の柱を配置すると、最初の1本目の柱が黄色になり1回で削除できます。
壁も同様に、2回目に配置すると1回目が黄色になり削除できます。

つまり1回遅れて黄色になり削除できます。
重複は避けれたのですが、なぜか1回遅れる原因が分かりません。

          記

MouseDown


        If e.Button.HasFlag(MouseButtons.Left) Then


            Select Case element

                Case "col"

                    Dim newPillar As New Pillar(SnapToGrid(e.Location))

                    pillarList.Add(newPillar)

                    PictureBox1.Invalidate() ' 再描画

              Case "wall"

                    startPoint = SnapToGrid(e.Location)

                   isPlacing = True

            End Select

        End If


        If e.Button.HasFlag(MouseButtons.Right) Then

            For Each pillar In pillarList
                pillar.IsSelected = pillar.Contains(e.Location)
            Next

        End If


MouseMove

      If e.Button.HasFlag(MouseButtons.Left) Then

            Select Case element

                Case "wall"
 
                    If isPlacing Then

                        endPoint = SnapToGrid(e.Location)

                        PictureBox1.Invalidate()

                    End If

            End Select

        End If

MouseUp

        If e.Button.HasFlag(MouseButtons.Left) Then

            Select Case element

            Case "wall"

                isPlacing = False

                    Dim newWall As New Wall(startPoint, endPoint) '新しい壁を準備

                    wallList.Add(newWall) 'それを壁一覧に加える

                    PictureBox1.Invalidate()

            End Select

        ElseIf e.Button.HasFlag(MouseButtons.Right) Then

            For Each wall In wallList
                wall.IsSelected = wall.Contains(e.Location)
            Next

        End If

 
Paint

        For Each pillar In pillarList

            If pillar.IsSelected Then

                e.Graphics.FillRectangle(Brushes.Yellow, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

                g.DrawRectangle(Pens.Black, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            Else
 
                e.Graphics.FillRectangle(Brushes.Red, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            g.DrawRectangle(Pens.Black, pillar.X - rectangleSize \ 2, pillar.Y - rectangleSize \ 2, rectangleSize, rectangleSize)

            End If

        Next

 
        For Each wall In wallList
            Dim p = If(wall.IsSelected, pen9, pen7) '選択状態に合わせて Pen を切り替える
            e.Graphics.DrawLine(p, wall.startPoint, wall.endPoint)
        Next

削除ボタン

        Dim selectedPillar As Pillar = pillarList.FirstOrDefault(Function(p) p.IsSelected)
        If selectedPillar IsNot Nothing Then
            '選択済みの矩形があれば削除
            pillarList.Remove(selectedPillar)
            PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
        End If

        Dim selectedWall As Wall = wallList.FirstOrDefault(Function(p) p.IsSelected)
        If selectedWall IsNot Nothing Then
            '選択済みの矩形があれば削除
            wallList.Remove(selectedWall)
            PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
        End If


Wall Class

Function


       If Math.Abs((Me.endPoint.Y - Me.startPoint.Y) * p.X - (Me.endPoint.X - Me.startPoint.X) * p.Y + Me.endPoint.X * Me.startPoint.Y - Me.endPoint.Y * Me.startPoint.X) / Math.Sqrt((Me.endPoint.X - Me.startPoint.X) ^ 2 + (Me.endPoint.Y - Me.startPoint.Y) ^ 2) < 5 Then

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103842 ] / ▼[ 103844 ]
■103843 / 33階層)  Re[33]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3897回)-(2025/08/09(Sat) 14:24:11)
2025/08/09(Sat) 14:34:39 編集(投稿者)

No103842 (shiro さん) に返信
> この時、柱を1本配置し、右クリックをしても黄色になりません。

IsSelected を変更した後の再描画依頼を投げ忘れているからですね。



> PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
ちょっと補足。

Invalidate は強制再描画ではありません。再描画を依頼するだけです。

VB 中学校のサンプルを見ると
> PictureBox1.Invalidate() 'PictureBox1の再描画を促す
> PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する
という二種類のコメントがありましたが、正しいのは前者の「再描画を促す」です。


今回必要なのは、強制再描画ではなく再描画依頼ですので、
呼び出すのは Invalidate で良いのですが、
コメント記述が間違っているので直しておきましょう。

私の例示したコードでも、VB 中学校のサンプルをそのまま写したため、
間違ったコメントをそのまま掲載してしまっていました。m(_ _)m


なお、強制再描画との違いについては下記のページで解説されています。
https://dobon.net/vb/dotnet/control/refreshupdateinvalidate.html
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103843 ] / 返信無し
■103844 / 34階層)  Re[34]: Visual Basicで簡易CADを作成
□投稿者/ shiro (20回)-(2025/08/09(Sat) 19:18:17)
No103843 (魔界の仮面弁士 さん) に返信
> 2025/08/09(Sat) 14:34:39 編集(投稿者)
>
> ■No103842 (shiro さん) に返信

魔界の仮面弁士 様

>>この時、柱を1本配置し、右クリックをしても黄色になりません。
>
> IsSelected を変更した後の再描画依頼を投げ忘れているからですね。

Invalidateを実施したところ正しく動きました。
有難うございます。

削除機能も追加され平屋建てとしては簡易CADとして取り合えず動かせますが、
2階建ても対応できるように進めたいと思います。

まずはcomboboxで1,2階を選択できる機能を追加させてみます。

初めてのGUI操作でしたが実際に動かせて勉強になりました。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103830 ] / 返信無し
■103833 / 25階層)  Re[25]: Visual Basicで簡易CADを作成
□投稿者/ 魔界の仮面弁士 (3892回)-(2025/08/06(Wed) 19:53:54)
No103830 (shiro さん) に返信
> If Math.Abs(e.X - wall.startPoint.X) < 10 AndAlso Math.Abs(e.Y - wall.endPoint.Y) < 10 Then

ところで壁って、水平「─」または垂直「│」にしか配置されないのでしょうか。
斜め方向「/」「\」の柱を繋ぐ壁だと、この判定式だと都合が悪い様な。

同じ交点から複数の壁が生える(「<」とか「へ」とか「└」みたいに)パターンがありえるなら、
選択判定で困りそうなので、交点だけじゃなくて線分もクリックできた方が良いかもしれない。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103825 ] / ▼[ 103848 ]
■103847 / 23階層)  Re[23]: Visual Basicで簡易CADを作成
□投稿者/ shiro (21回)-(2025/08/18(Mon) 12:41:14)
No103825 (魔界の仮面弁士 さん) に返信
> ■No103822 (shiro さん) に返信

魔界の仮面弁士 様


Dictionaryや新しいFloorクラスを設定するなど、混乱しそうでしたので次のように処理してみました。

Form1でコンボボックスで階を切り替えるようにし、1,2階用にListを分けました。
また、下記の計算のため、braceListも追加しました。Wallクラスを使用してみました。
新たにBraceクラスを設けるか悩みました。

    '1階用

    Private pillarList As New List(Of Pillar)
    Private wallList As New List(Of Wall)
    Private braceList As New List(Of Wall)

    '2階用

    Private pillarList2 As New List(Of Pillar)
    Private wallList2 As New List(Of Wall)
    Private braceList2 As New List(Of Wall)

elementにbraceを追加し、MouseUPでWallと違うStiff値2.0を設定しました。

                        Dim newWall As New Wall(startPoint, endPoint, 2.0) '新しい壁を準備
                        braceList.Add(newWall) 'それを壁一覧に加える

                        Dim newWall2 As New Wall(startPoint, endPoint, 2.0) '新しい壁を準備
                        braceList2.Add(newWall2) 'それを壁一覧に加える

出力でWallとBraceのStiffの違いに応じた結果を出力できました。

W	1	40	40	80	40	2.5
W	2	120	40	200	40	2.5
W	3	200	40	240	40	2.5
W	4	240	40	320	40	2.5
B	5	320	120	280	120	2
B	6	40	40	40	80	2
B	7	40	240	40	200	2
B	8	200	240	200	200	2

以上により、初歩的ですが階毎に図面が切り替わり、また1つ目の計算したい内容も正しく計算出来ました。

2つめの計算したい内容について、どのように処理すれば上手く計算できるかご知見を頂ければ有難いです。
実施したい内容は下記のとおりです。

A1,A2の係数で、筋かいの場合は補正した数値としなければならず、そのため、Wallとは別にBraceを設けました。
先の事例では、Stiff=2とした場合(筋交い45*90)です。
対象とする柱の片側だけに筋交いが付く場合、柱頭なら+0.5,柱脚なら-0.5を2に加算します。

処理的には次のような方法が考えられます。

 @各階で壁倍率(Stiff)の差Aの算出:検討する柱の両側に取り付く耐力壁の倍率の差Aを求める。
 A補正値の決定(筋かいの場合)
 B係数B、Lの決定:柱が出隅かどうかを確認し、係数B、Lを決定する。
 C1階の耐力壁を持つ柱の上に、2階の耐力壁を持つ柱があるか探す。
 D2階がある場合、1階のみの場合に分けて計算式に当てはめてN値を算出。:一つの柱についてX・Y両方向のN値を求め、大きい方を選ぶ。
 E柱頭・柱脚の接合金物の選択:Dで求めたN値以上の許容耐力を持つ接合金物等を選ぶ。

下記の「計算事例」にあるとおり計算自体は簡単です。
ExcelなどではVlookupなどを使用して計算を実施していました。

しかしVBで実施するとなると処理が大変そうだと感じました。
最初に、Pillarクラスに「出隅かその他」を判別するために、「sumi」を設け、
出隅なら1,その他なら0としてelementで設定出来るようにした方が良いのかなど。

そして、

 For Each pillar In pillarList ループで、

    pillarListの座標と、wallListのstartPoint座標が合致すれば、柱の右側にStiffが存在、
              endPoint座標が合致すれば、柱の左側にStiffが存在、とするなど。
          同様に、braceListのstartPoint座標が合致すれば、柱脚に接続。
              endPoint座標が合致すれば、柱頭に接続とするなど。
              ただこの場合、向きが左側と右側の2通りあるため処理が難しそうです。
            
     そして、2階部分の柱も検索し、計算を実施しないといけません。

ボタンをクリックすると計算が実行されるようにしたいです。

手計算では分かり易いですが柱の数が多く煩雑です。
Excelでは計算可能ですがテキスト入力のため操作性が乏しいです。

VBの簡易CADだと、柱や壁を配置するだけで、ボタンを押すと計算が実行され便利です。
さらに、削除や追加による検討が容易で便利です。


        記

・N値計算

  https://kakunin-shinsei.com/n-value-calculation-method/

・計算事例

https://stmaki.com/2025kaberyou-005/#:~:text=%E5%80%A4%E8%A8%88%E7%AE%97%E5%81%8F%EF%BC%89-,%EF%BC%AE%E5%80%A4%E8%A8%88%E7%AE%97%E3%81%AE%E6%89%8B%E9%A0%86,%E5%8F%B7%E3%81%8B%E3%82%89%E9%81%B8%E6%8A%9E%E3%81%97%E3%81%BE%E3%81%99%E3%80%82



[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103847 ] / ▼[ 103849 ]
■103848 / 24階層)  Re[24]: Visual Basicで簡易CADを作成
□投稿者/ kiku (487回)-(2025/08/20(Wed) 15:09:27)
No103847 (shiro さん) に返信
> ■No103825 (魔界の仮面弁士 さん) に返信
>>■No103822 (shiro さん) に返信

> 処理的には次のような方法が考えられます。
>
>  @各階で壁倍率(Stiff)の差Aの算出:検討する柱の両側に取り付く耐力壁の倍率の差Aを求める。
>  A補正値の決定(筋かいの場合)
>  B係数B、Lの決定:柱が出隅かどうかを確認し、係数B、Lを決定する。
>  C1階の耐力壁を持つ柱の上に、2階の耐力壁を持つ柱があるか探す。
>  D2階がある場合、1階のみの場合に分けて計算式に当てはめてN値を算出。:一つの柱についてX・Y両方向のN値を求め、大きい方を選ぶ。
>  E柱頭・柱脚の接合金物の選択:Dで求めたN値以上の許容耐力を持つ接合金物等を選ぶ。
>
> 下記の「計算事例」にあるとおり計算自体は簡単です。
> ExcelなどではVlookupなどを使用して計算を実施していました。
>
> しかしVBで実施するとなると処理が大変そうだと感じました。
> 最初に、Pillarクラスに「出隅かその他」を判別するために、「sumi」を設け、
> 出隅なら1,その他なら0としてelementで設定出来るようにした方が良いのかなど。

計算例の部分を見てみましたが
正直に申し上げまして、素人には理解不能でした。
用語の定義から理解を深めないと厳しそうです。

よって、業務経験が必要な部分はご自身で判断し、
プログラム部分に特化した部分のみをご質問されると
回答が付きやすいと思いました。

具体的には、どういうことかと申しましと、
「出隅」か「その他」を判別するという記述がありますが、
これは既存の情報のみで、プログラムを駆使すれば、判定が可能であれば、その判定方法を
どうすれば良いかという話になりますし、
既存の情報のみで判定できないのであるならば、情報が足りていないということになると
思うため、GUIとしてその情報を入力してあげる必要が生じます。
この場合、どのように入力するのかという話になると思います。

例えば、GUIとして追加の情報を入力する方法として
書いた柱を選択すると、画面の別部分に、その柱のプロパティ情報を
表示できるようにし、そのプロパティを変更できるようにする機能をつくれば
できそうです。

例えば、既存情報で計算可能であるならば、
具体的に、既存情報をどのように判定するかのロジックを提示いただければ
回答が付きやすいのではと考えます。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103848 ] / ▼[ 103850 ]
■103849 / 25階層)  Re[25]: Visual Basicで簡易CADを作成
□投稿者/ shiro (22回)-(2025/08/20(Wed) 18:35:48)
No103848 (kiku さん) に返信
> ■No103847 (shiro さん) に返信
>>■No103825 (魔界の仮面弁士 さん) に返信
> >>■No103822 (shiro さん) に返信

kiku 様

> 計算例の部分を見てみましたが
> 正直に申し上げまして、素人には理解不能でした。
> 用語の定義から理解を深めないと厳しそうです。
>
> よって、業務経験が必要な部分はご自身で判断し、
> プログラム部分に特化した部分のみをご質問されると
> 回答が付きやすいと思いました。
>
> 具体的には、どういうことかと申しましと、
> 「出隅」か「その他」を判別するという記述がありますが、
> これは既存の情報のみで、プログラムを駆使すれば、判定が可能であれば、その判定方法を
> どうすれば良いかという話になりますし、
> 既存の情報のみで判定できないのであるならば、情報が足りていないということになると
> 思うため、GUIとしてその情報を入力してあげる必要が生じます。
> この場合、どのように入力するのかという話になると思います。

ご指摘のとおりです。

この間、テキストデーターからconsole版での計算を実装して、事例通り正しく計算されることを確認できました。
ですが、やはりCAD入力からの一貫計算を実装したいです。

出隅については、ToolStripMenuItemで、

'柱

Case "colsumi"

を追加し、出隅の場合は1とする設定を、Pillar Classに追加したいと考えています。
長方形の平面図ですと、4隅の4点のみが出隅になります。

コンソール版で成功した概要は、wallの合板2.5倍と、筋交いの2.0倍を、
bai1、Bai2として、同じ座標に存在するならば、bai3=bai1+Bai2として格納しました。

よって、FORM1上でcsvファイルにWall Classのsutiffを止めて、

bai1の場合、menzaiに該当する構造材の番号を、
bai2には筋交いのINDEXとして、jiku、jikusの2つを追加したいです。

よって、Wallクラスに、INDEX startPoint, endPoint、 menzai、 jiku、 jikus
となるようにしたいです。

ただ、ToolStripMenuItemで壁と筋交いを設定した場合、同じ座標の場合に(MouseDown,move,UP)、
どうすればWallクラスにADDして格納できるかが分かりませんでした。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103849 ] / ▼[ 103851 ]
■103850 / 26階層)  Re[26]: Visual Basicで簡易CADを作成
□投稿者/ kiku (488回)-(2025/08/21(Thu) 09:03:52)
> ただ、ToolStripMenuItemで壁と筋交いを設定した場合、同じ座標の場合に(MouseDown,move,UP)、
> どうすればWallクラスにADDして格納できるかが分かりませんでした。

質問したいことは上記の部分でしょうか?

操作が良くわかりませんでした。
・オブジェクトを1つ選択する
・メニューから設定する
・WallクラスにADDされる

上記の流れだと仮定すると、
なぜ同じ座標となるのかがわかりませんでした。
なぜならば、オブジェクトは1つのみ選択していると思われるからです。

もし同じ座標に2つのオブジェクトが重なっていたとした場合、
それでも選択できるのは、上位のオブジェクト1つのみのはずで、
やはり1つしか選択できないのではないでしょうか?

このあたりもご説明頂けないと、質問そのものが伝わらないと思います。
[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103850 ] / ▼[ 103852 ]
■103851 / 27階層)  Re[27]: Visual Basicで簡易CADを作成
□投稿者/ shiro (23回)-(2025/08/21(Thu) 22:01:16)
No103850 (kiku さん) に返信

kiku 様

1スパンに壁とブレース材を配置することはあります。
その場合、壁はwalllistに、ブレースはbracelistに格納しました。

次のように、W1とB2、W3とB4が同じ場所に配置されます。
この時、同じ座標ならば、座標を同じとし、W1とB2なら、

40 40 80 40 3 2 4

と、wallbai sujiku sujiikusを1つにまとめたいという意味でした。



Type Index X1 Y1 X2 Y2 wallbai sujiku sujiikus Stiff
C 1 40 40 0
C 2 40 80 0
C 3 80 80 0
C 4 80 40 0
W 1 40 40 80 40 3 2.5
W 3 40 40 40 80 3 2.5
B 2 40 40 80 40 2 4 2
B 4 40 40 40 80 4 4 2

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103851 ] / ▼[ 103853 ]
■103852 / 28階層)  Re[28]: Visual Basicで簡易CADを作成
□投稿者/ kiku (489回)-(2025/08/22(Fri) 08:45:51)
No103851 (shiro さん) に返信
> ■No103850 (kiku さん) に返信

> Type	Index	X1	Y1	X2	Y2	wallbai	sujiku	sujiikus	Stiff
> WA	1A	40	40	80	40	3	@	@	2.5A
> B	2	40	40	80	40	@	2	4	2

上記の場合

> Type	Index	X1	Y1	X2	Y2	wallbai	sujiku	sujiikus	Stiff
> WA	1A	40	40	80	40	3	2	4	2.5A

上記の結果になって欲しいということでしょうか?

なるぼど、ファイル出力するときの処理を変更したいということなのでしょうか?
それともやはり、メニューから操作したときの処理を変更したいということなのでしょうか?
どちらにしても、変更したい内容を明確にすれば
プログラムできることになります。

・不明点1
 @は必ず空白か?
 空白でない場合、どうしたいのか?
・不明点2
 合算する場合、AはWの行を優先するのか?


[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103852 ] / ▼[ 103854 ]
■103853 / 29階層)  Re[29]: Visual Basicで簡易CADを作成
□投稿者/ shiro (24回)-(2025/08/22(Fri) 13:20:22)
No103852 (kiku さん) に返信
> ■No103851 (shiro さん) に返信
>>■No103850 (kiku さん) に返信

kiku 様


>>Type Index X1 Y1 X2 Y2 wallbai sujiku sujiikus Stiff
>>WA 1A 40 40 80 40 3 @ @ 2.5A
>>B 2 40 40 80 40 @ 2 4 2
>
> 上記の場合
>
>>Type Index X1 Y1 X2 Y2 wallbai sujiku sujiikus Stiff
>>WA 1A 40 40 80 40 3 2 4 2.5A
>
> 上記の結果になって欲しいということでしょうか?

その通りです。空欄が詰まってしまいました。

> なるぼど、ファイル出力するときの処理を変更したいということなのでしょうか?
> それともやはり、メニューから操作したときの処理を変更したいということなのでしょうか?
> どちらにしても、変更したい内容を明確にすれば
> プログラムできることになります。

コマンドプロンプトで動くコードは、上記の仕様で計算しました。
よって、一度CSVに出力させ、それをオープンして計算する方法を考えています。
ファイル出力でそのようになれば良いのですが。

> ・不明点1
>  @は必ず空白か?
>  空白でない場合、どうしたいのか?

0としていました。空白ではあれません。

> ・不明点2
>  合算する場合、AはWの行を優先するのか?

最後のSTIFFの列はその後の計算では使用しないため、どちらになってもかいませんし、
未出力でも問題ないです。



[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103853 ] / ▼[ 103855 ]
■103854 / 30階層)  Re[30]: Visual Basicで簡易CADを作成
□投稿者/ kiku (490回)-(2025/08/22(Fri) 16:45:05)
2025/08/22(Fri) 16:45:59 編集(投稿者)

No103853 (shiro さん) に返信
>>・不明点1
>> @は必ず空白か?
>> 空白でない場合、どうしたいのか?
> 0としていました。空白ではあれません。
空白はあり得ないと理解しました。
では下記の場合はどのように動作するのですか?

パターン1
Type wallbai sujiku sujiikus
W 3 0 0
B 0 2 4
Type wallbai sujiku sujiikus
W 3 2 4

パターン2
Type wallbai sujiku sujiikus
W 3 5 0
B 0 2 4
Type wallbai sujiku sujiikus
W 3 @ 4
 上記@は、どうなるべきか?

パターン3
Type wallbai sujiku sujiikus
W 3 0 0
B 0 0 4
Type wallbai sujiku sujiikus
W 3 A 4
 上記Aは、どうなるべきか?

>>・不明点2
>> 合算する場合、AはWの行を優先するのか?
> 最後のSTIFFの列はその後の計算では使用しないため、どちらになってもかいませんし、
> 未出力でも問題ないです。

どちらでも良いということなので、
Wを優先すると理解しました。
※合算する場合、WへBを合算するイメージ

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103854 ] / ▼[ 103857 ]
■103855 / 31階層)  Re[31]: Visual Basicで簡易CADを作成
□投稿者/ shiro (25回)-(2025/08/22(Fri) 20:33:17)
No103854 (kiku さん) に返信
> 2025/08/22(Fri) 16:45:59 編集(投稿者)
>
> ■No103853 (shiro さん) に返信

kiku 様

> 空白はあり得ないと理解しました。
> では下記の場合はどのように動作するのですか?
>
> パターン1
> Type wallbai sujiku sujiikus
> W 3 0 0
> B 0 2 4
> Type wallbai sujiku sujiikus
> W 3 2 4
>
> パターン2
> Type wallbai sujiku sujiikus
> W 3 5 0
> B 0 2 4
> Type wallbai sujiku sujiikus
> W 3 @ 4
>  上記@は、どうなるべきか?
>
> パターン3
> Type wallbai sujiku sujiikus
> W 3 0 0
> B 0 0 4
> Type wallbai sujiku sujiikus
> W 3 A 4
>  上記Aは、どうなるべきか?

パターン1のとおり、Wはwallbai列のみ1以上の数字をとり、
Bの場合は隣2列だけの数字となります。

よって、列どうしの足し算による増減はありません。
位置の重複がない場合は、そのまま出力させます。

パターン2の5は常に0です。
パターンAは0となります。

> どちらでも良いということなので、
> Wを優先すると理解しました。
> ※合算する場合、WへBを合算するイメージ

そのようなイメージです。

[ 親 103783 / □ Tree ] 返信 編集キー/

▲[ 103855 ] / 返信無し
■103857 / 32階層)  Re[32]: Visual Basicで簡易CADを作成
□投稿者/ kiku (491回)-(2025/08/25(Mon) 08:42:21)
No103855 (shiro さん) に返信

> パターン2の5は常に0です。

0でない値で重複した場合、
常に0をセットするという意味ですか?
それとも5という数字に特別な意味があり
その場合のみ0にセットするという意味ですか?
5に特別な意味がある場合、その他のルールはどんなルールがありますか?

※過不足なくルールを決まっていないと
 プログラムできないので聞いています。
 自身で作成するプログラムなので、
 その意識で回答してください。
[ 親 103783 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -