オートシェイプの位置関係をコネクタに自動で反映させる
昨日は、複数選択したオートシェイプを、選択順にコネクタで接続してみました。
今日は、オートシェイプの位置関係を自動で認識し、コネクタへ反映させることに挑戦です。
今日やりたいこと
昨日行ったのは、オートシェイプの位置関係に関わらず、とにかく下から上への接続でした。しかしこれでは、オートシェイプとコネクタが不自然に重なってしまい、多くの修正を要することになります。
そこで少しでも修正を減らすべく、コネクタがどこから出てどこに入るかを、ある程度コントロールしてみました。手順は、以下の通りです。
- 二つのオートシェイプについて、位置関係を把握する
- 1.を踏まえたうえで、オートシェイプを描画する
- 1.を踏まえたうえで、オートシェイプを接続する
必要なこと
- 二つのオートシェイプについて位置関係を把握して数値化
- 1.を踏まえたうえで、オートシェイプを描画・接続
二つのオートシェイプを引数として与え、戻り値として位置関係を表す数値を返す関数があれば、便利そうです。
1.二つのオートシェイプについて位置関係を把握して数値化
今回は、位置関係を9つに分類し、それぞれを数で表すことにしました。
八つの方位を、1~8の数字で表しています。また0は、オートシェイプ同士が重なっている場合を示すことにしました。
位置関係については、このように考えます。例えば中心から見て左にあるオートシェイプは、右端の座標が自身の左端の座標より小さいはずです。
上下方向についても、同じことが言えます。
- 上か、下か、真ん中かの3通り
- 左か、右か、真ん中かの3通り
で、3×3で9通りとしたわけです。
【クラスモジュール】(ShapeClass)
' Shape1 から見た Shape2 の位置を数値化 ' ' 5 1 8 ' 2 shape1 4 ' 6 3 7 Public Function GetPositionRelation(Shape1 As Shape, Shape2 As Shape) As Long Dim val As Long If Shape2.Left + Shape2.Width < Shape1.Left Then If Shape2.Top + Shape2.Height < Shape1.Top Then val = 5 ElseIf Shape1.Top + Shape1.Height < Shape2.Top Then val = 6 Else val = 2 End If ElseIf Shape1.Left + Shape1.Width < Shape2.Left Then If Shape2.Top + Shape2.Height < Shape1.Top Then val = 8 ElseIf Shape1.Top + Shape1.Height < Shape2.Top Then val = 7 Else val = 4 End If Else If Shape2.Top + Shape2.Height < Shape1.Top Then val = 1 ElseIf Shape1.Top + Shape1.Height < Shape2.Top Then val = 3 Else val = 0 End If End If GetPositionRelation = val End Function
2.1.を踏まえたうえで、オートシェイプを描画・接続
私のかつての上司は制御設計出身で、フローチャートの書き方を指南してくださいました。分野によって書き方は変わるのかもしれませんが、そのとき教わった基本ルールは以下の通りです。
- コネクタは下または右から出る
- コネクタは上または左から入る
例えば上から出て下から入る、といのはNGです。
そこで、位置関係によって出口と入口を一つずつ設定し、描画して接続するまでを一つの関数にまとめました。
※方向によっては同じ設定のものもありますが、のちに変更する場合を考慮して、方位毎に設定してあります。
Public Sub ConnectShape(start_shape As Shape, end_shape As Shape) Dim SC As ShapeClass Set SC = New ShapeClass Dim PositionRelationIndex As Long PositionRelationIndex = SC.GetPositionRelation(start_shape, end_shape) Dim myArrow As Shape Dim StartPosition As Long Dim EndPosition As Long Select Case PositionRelationIndex ' 真上:下 ⇒ 上:カギ Case 1 Set myArrow = SC.DrawConnector(2) StartPosition = 3 EndPosition = 1 ' 左横:下 ⇒ 上:カギ Case 2 Set myArrow = SC.DrawConnector(2) StartPosition = 3 EndPosition = 1 ' 真下:下 ⇒ 上:直 Case 3 Set myArrow = SC.DrawConnector StartPosition = 3 EndPosition = 1 ' 右横:左 ⇒ 右:直 Case 4 Set myArrow = SC.DrawConnector StartPosition = 4 EndPosition = 2 ' 左上:下 ⇒ 上:カギ Case 5 Set myArrow = SC.DrawConnector(2) StartPosition = 3 EndPosition = 1 ' 左下:下 ⇒ 上:カギ Case 6 Set myArrow = SC.DrawConnector(2) StartPosition = 3 EndPosition = 1 ' 右下:下 ⇒ 上:カギ Case 7 Set myArrow = SC.DrawConnector(2) StartPosition = 3 EndPosition = 1 ' 右上:右 ⇒ 左:カギ Case 8 Set myArrow = SC.DrawConnector(2) StartPosition = 4 EndPosition = 2 Case Else Exit Sub End Select With myArrow .ConnectorFormat.BeginConnect start_shape, StartPosition .ConnectorFormat.EndConnect end_shape, EndPosition End With End Sub
こちらで描画まで済ませたので、併せてユーザーフォーム側のマクロも変更です。
【ユーザーフォームのモジュール】
Private Sub ConnectButton_Click() If iMax = 2 Then Exit Sub Dim SC() As ShapeClass ReDim SC(1 To iMax - 1) Dim i As Long For i = 0 To ListBox1.ListCount - 1 If ListBox1.Selected(i) = True Then Dim myID As Long myID = ListBox1.List(i, 1) Dim myIndex As Long myIndex = ListBox1.List(i, 2) Set SC(myIndex) = New ShapeClass Set SC(myIndex).myShape = TargetShape(myID) End If Next ' --------------↓↓前回からの変更箇所↓↓-------------- For i = 1 To iMax - 2 Call ConnectShape(SC(i).myShape, SC(i + 1).myShape) Next ' --------------↑↑前回からの変更箇所↑↑-------------- End Sub
結果
オートシェイプの位置関係に合わせて自動でコネクタの種類を選択し、設定した位置で接続できるようになりました。
次回に続きます。
参考まで。