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

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

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

Visual Basicで簡易CADを作成 [1]

[トピック内 51 記事 (21 - 40 表示)]  << 0 | 1 | 2 >>

■103814 / inTopicNo.21)  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






引用返信 編集キー/
■103815 / inTopicNo.22)  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






引用返信 編集キー/
■103817 / inTopicNo.23)  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 は、桟橋・埠頭の意味で使われる)


</ひとりごと>

引用返信 編集キー/
■103819 / inTopicNo.24)  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が使われます。

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

引用返信 編集キー/
■103821 / inTopicNo.25)  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 など)にする)ことが必要となります。

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

引用返信 編集キー/
■103822 / inTopicNo.26)  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クラスにも階ごとにも設定可能な「階数、階高」を追加した方が良いのかなど。



引用返信 編集キー/
■103825 / inTopicNo.27)  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 を持たせる
など、階固有の管理情報を加えたい場合にも拡張しやすくなります。
引用返信 編集キー/
■103828 / inTopicNo.28)  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 のフォームデザイナーでいうところの
 [表示]-[その他のウィンドウ]-[ドキュメント アウトライン]

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

あるいは、右クリックでコンテキストメニューを表示するようにして、
そこから壁や柱の一覧を選べるようにしても良いかと。
引用返信 編集キー/
■103830 / inTopicNo.29)  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

引用返信 編集キー/
■103832 / inTopicNo.30)  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
のようにします。色が変わるだけで座標情報は変わらないのですから、
この方が処理としても短く書けますよね。
引用返信 編集キー/
■103833 / inTopicNo.31)  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

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

同じ交点から複数の壁が生える(「<」とか「へ」とか「└」みたいに)パターンがありえるなら、
選択判定で困りそうなので、交点だけじゃなくて線分もクリックできた方が良いかもしれない。
引用返信 編集キー/
■103834 / inTopicNo.32)  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

引用返信 編集キー/
■103835 / inTopicNo.33)  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 メソッド)が変化するはずですよね。
まぁ、判定エリアが正方形のままだとしても、大きな問題にはならないかも知れませんが。
引用返信 編集キー/
■103836 / inTopicNo.34)  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

引用返信 編集キー/
■103837 / inTopicNo.35)  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 を使うのが良いかと思います。

値の変化をデバッグしてみると、たとえば新規に壁を作る前に、以前のクリック座標の情報をリセットし忘れているとか、
あるいは判定式の書き漏れがあるとか、そういったミスを発見できるかもしれません。
引用返信 編集キー/
■103838 / inTopicNo.36)  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() ' 再描画


引用返信 編集キー/
■103839 / inTopicNo.37)  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に記録されるため、
右クリックで削除しようとすると可笑しな挙動になっていました。

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





引用返信 編集キー/
■103841 / inTopicNo.38)  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 )
などといった、さまざまな提案をしてきたつもりです。
引用返信 編集キー/
■103842 / inTopicNo.39)  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

引用返信 編集キー/
■103843 / inTopicNo.40)  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
引用返信 編集キー/

このトピックをツリーで一括表示

<前の20件 | 次の20件>
トピック内ページ移動 / << 0 | 1 | 2 >>

管理者用

- Child Tree -