こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押して出てきたコードを眺めるだけの日々は、今日で終わりにしましょう。
今回は、Visio VBAの真骨頂とも言える「空間認識」のテーマに切り込みます。テーマは「図形同士の接触・重なりを正確に検知する `HitTest` メソッド」です。
ネットワーク図、フローチャート、組織図……自動で図形を配置していくマクロを書いたとき、「うわっ、図形同士が重なって文字が読めない!」と頭を抱えた経験はありませんか?
「ここをクリアすれば、Visio VBAの基本はバッチリですよ!」
今回は、Visio VBAが持つ強力な幾何学エンジンを叩き起こし、図形の干渉をスマートに検知して、安全な空き領域へ自動退避させるアルゴリズムの極意を伝授します。
—
1. なぜ「重なり検知」でマクロ初心者は挫折するのか?
多くの人がやりがちな失敗は、図形の座標(`PinX` や `PinY`)と幅・高さ(`Width` / `Height`)を数学的に計算して、「X座標がここからここまでで……」と自前で判定ロジックを書こうとすることです。
ちょっと待ってください。図形が「回転」していたらどうなりますか? 角丸四角形や、複雑なマスターシェイプだったら?
自前の四角形判定ロジックは、回転が入った瞬間に破綻します。
ここで登場するのが、Visioが裏で持っている最強の幾何学メソッド`Shape.HitTest`です。
HitTestメソッドの本質とは?
`HitTest` は、指定した図形が、別の図形(あるいは点や領域)と「接触しているか(ヒットしているか)」をVisioのエンジンに直接判定させるメソッドです。
人間が目で見て「あ、重なってるな」と判断する処理を、VBAから一瞬で正確に実行してくれます。しかも、図形の回転や形状の複雑さを完全に無視して(Visioが勝算してくれて)判定してくれます。これを使わない手はありません。
—
2. HitTestメソッドの基本文法
まずは、`HitTest` の基本の形を押さえましょう。
targetShape.HitTest(otherShape)
これだけです。非常にシンプルですね。
このメソッドは、戻り値として整数(Integer)を返します。返ってくる値の意味は以下の通りです。
- `0` (visHitOutside): 接触していない(外側にある)
- 非`0` (visHitInside / visHitTarget): 接触している、または重なっている
つまり、「戻り値が `0` 以外なら、干渉している!」と判定すれば良いのです。
—
3. 実践!干渉チェック&自動退避アルゴリズム
それでは、現場で即戦力として使えるコードを書き下ろしましょう。
このコードは、「移動させたい図形(`shpMovable`)」が、既存の図形グループと重なっていないかを `HitTest` でチェックし、もし重なっていたら、重ならなくなるまで右方向へ少しずつ自動で移動(退避)させるアルゴリズムです。
実装コード
Sub AutoAvoidanceMacro()
Dim vsoPage As Visio.Page
Dim shpTarget As Visio.Shape
Dim shpObstacle As Visio.Shape
Dim isColliding As Boolean
Dim safetyCount As Integer
‘ アクティブなページを取得
Set vsoPage = ActivePage
‘ 【前提】事前に配置されている「障害物となる図形」と「動かしたい図形」があると仮定
‘ ※動作確認のため、あらかじめページ上に適当な図形を2つ以上置いておいてください
On Error GoTo ErrorHandler
Set shpTarget = vsoPage.Shapes(1) — 動かしたい図形
Set shpObstacle = vsoPage.Shapes(2) — 障害物となる図形
‘ 無限ループを防ぐための安全カウンター
safetyCount = 0
Do
‘ HitTestメソッドで接触判定を行う
‘ 戻り値が 0 以外であれば「接触・重なりあり」とみなす
If shpTarget.HitTest(shpObstacle) <> 0 Then
isColliding = True
Else
isColliding = False
End If
‘ もし重なっていたら、右へ 0.5インチずつ移動させる
If isColliding Then
shpTarget.Cells(“PinX”).ResultIU = shpTarget.Cells(“PinX”).ResultIU + 0.5
‘ 無限ループ防止(50回動いてもダメなら強制脱出)
safetyCount = safetyCount + 1
If safetyCount > 50 Then
MsgBox “安全な退避場所が見つかりませんでした。”, vbExclamation
Exit Do
End If
End If
Loop While isColliding
If Not isColliding Then
MsgBox “図形の干渉を回避し、安全な位置に配置しました!”, vbInformation
End If
Exit Sub
ErrorHandler:
MsgBox “エラーが発生しました。ページ上に2つ以上の図形が存在するか確認してください。”, vbCritical
End Sub
—
4. チーフアーキテクトが教える「現場の知見」と注意点
このコードをそのまま実務のシステムに組み込む際、プロとして知っておくべき「重み」と「罠」をいくつか共有しておきます。
① 単位系(IU: Internal Units)の罠
Visioの内部データは、すべて「インチ(Inches)」で処理されています。コード内の `0.5` という数値は、ミリメートルではなく0.5インチを意味します。
もし図面の設定がセンチメートルであっても、APIを叩くときは基本的に内部単位(IU)で計算されるため、単位の換算ミス(`Visio.VisUnitCodes` の扱いなど)には注意してください。
② パフォーマンスへの配慮(多重ループの呪縛)
ページ上に数百個の図形がある場合、すべての図形に対して `HitTest` を総当たり($N^2$ の計算量)で行うと、Visioがフリーズしたような状態になります。
- 対策: 判定対象を「同じレイヤーの図形」に絞る、あるいは大まかなBounding Box(外接矩形)でスクリーニングしてから、ピンポイントで `HitTest` を呼ぶといった工夫が、プロのエンジニアとしての腕の見所です。
③ 図形のライフサイクルとエラーハンドリング
VBAで図形を削除したり追加したりする際、オブジェクト変数(`Visio.Shape`)がメモリ上で宙ぶらりんになる(参照切れを起こす)ことがあります。ループ処理の中では、必ず図形が存在しているか、エラーが起きないかを監視するセーフティネット(`On Error`)を張る習慣をつけましょう。
—
まとめ
今回は、Visio VBAの `HitTest` メソッドを使った図形間の干渉検知と、自動退避の基本アルゴリズムを解説しました。
- 自前の座標計算で苦しまず、`Shape.HitTest` というVisioの幾何学エンジンを信頼して呼び出すこと。
- 戻り値(`0` かそれ以外か)で判定し、条件を満たすまでループや退避処理を回すこと。
ここをクリアすれば、単なる「お絵描きマクロ」から、知的で自律的に動く「Visioレイアウト・エンジニアリング」へと、あなたのスキルは確実にワンランクアップします。
明日からの開発現場で、ぜひこの知見を役立ててください。それでは、また次回の極限の知見でお会いしましょう!
