【Visio VBA極限解説】数学的計算を一切排除する!SpatialRelationによる超高速・空間判定エンジンの実装
開発プロジェクトでVisio図面を自動解析するツールを作るとき、こんな壁にぶつかったことはないか?
- 「このノード(図形)の中に、別の小さなアイコンが含まれているか判定したい」
- 「配管と機器が正しく接続(接触)しているかを自動チェックしたい」
- 「2つの図形がどれくらい近くに配置されているか検知したい」
これを愚直にやろうとすると、図形の`PinX`, `PinY`, `Width`, `Height`を取り出して、自前でバウンディングボックスの交差判定や幾何学的計算(三角関数や不等式)をVBAで実装し始めるエンジニアが出てくる。
今すぐその手を止めろ。 それは車輪の再発明であり、非効率の極みだ。回転(Angle)が入った図形や、複雑な頂点を持つベクター図形の当たり判定をVBAの自前計算でやろうとすれば、必ず計算漏れやバグの温床になり、何より処理が重くて使い物にならなくなる。
Visioには、C++のコアエンジンが瞬時に弾き出す、図形間の幾何学的関係を完璧に暴く公式APIが標準で備わっている。それが今回解説する `Shape.SpatialRelation` プロパティだ。
今回は、このネイティブAPIを極限まで使い倒し、実務の図面検証や自動レイアウトで即座に使える「堅牢な空間判定エンジン」の設計と実装を伝授する。
—
1. SpatialRelationの正体と、プロが知るべき「重み」
`SpatialRelation` は、ある図形(Source)から見て、別の図形(Target)がどのような空間的関係にあるかを、特定のフラグ(定数)を組み合わせて一撃で判定するメソッドだ。
lngResult = shpSource.SpatialRelation(shpTarget, Tolerance, Flags)
このAPIの恐ろしいところは、Visioの図形が持つ「回転・非表示・グルーピング・ピン位置」の概念をすべてネイティブで解決した上で、純粋な幾何学的トポロジー関係を返してくれる点にある。自前で座標計算をする必要など1ミリもない。
陥りがちな罠:パフォーマンスとイベントの嵐
何百、何千という図形を持つ図面(P&IDやネットワーク図など)で全対全(N×N)の空間判定を行うと、VBAのイミディエイトウィンドウが固まったような挙動を示すことがある。
これを回避するため、プロダクションコードでは以下の鉄則を厳守する。
1. 画面描画の完全停止 (`ScreenUpdating`)
2. イベントの無効化 (`EnableEvents`)
3. 無駄なループの排除(レイヤーやマスター名による事前フィルタリング)
—
2. 【プロダクションコード】実用・空間判定エンジン
以下のコードは、選択した親図形(コンテナやエリア枠)の中に「完全に含まれている(Contained)」、または「接触している(Intersecting)」子図形を自動的にスキャンし、結果をイミディエイトに出力、さらに該当図形の色を変更して視覚化する実用マクロだ。
コピペしてそのままVisioの標準モジュールに貼り付け、実務で検証してほしい。
Option Explicit
‘ =================================================================================
‘ 圧倒的なパフォーマンスと保守性を誇る空間判定エンジン
‘ =================================================================================
Public Sub ExecuteSpatialAnalysisEngine()
‘ 1. 環境構築:パフォーマンス最大化のための定石
Dim currentScreenUpdating As Boolean
Dim currentEnableEvents As Boolean
currentScreenUpdating = Application.ScreenUpdating
currentEnableEvents = Application.EnableEvents
Application.ScreenUpdating = False
Application.EnableEvents = False
On Error GoTo ErrorHandler
Dim vsoPage As Visio.Page
Set vsoPage = ActivePage
Dim vsoSelection As Visio.Selection
Set vsoSelection = vsoPage.CreateSelection
Set vsoSelection = ActiveWindow.Selection
‘ バリデーション:判定の起点となる親図形が1つ選択されているか
If vsoSelection.Count <> 1 Then
MsgBox “空間判定の基準となる『親図形(エリア等)』を正確に1つだけ選択してください。”, vbExclamation, “空間判定エンジン”
GoTo Finally
End If
Dim shpContainer As Visio.Shape
Set shpContainer = vsoSelection(1)
Debug.Print “=== 空間判定開始: 基準図形 = ” & shpContainer.Name & ” (ID: ” & shpContainer.ID & “) ===”
Dim shpTarget As Visio.Shape
Dim lngRelation As Long
Dim intCountContained As Integer
Dim intCountIntersect As Integer
intCountContained = 0
intCountIntersect = 0
‘ 2. ページ内の全図形をスキャン(N対Nの総当たりを効率的に処理)
For Each shpTarget in vsoPage.Shapes
‘ 自分自身との比較はスキップ
If shpTarget.ID <> shpContainer.ID Then
‘ SpatialRelationの実行
‘ Tolerance: 0 (許容誤差なし。厳密な判定を行う場合は数ミリを指定)
‘ VisSpatialRelationFlags.visSpatialContainedInside: 完全に内部に含まれているか
‘ VisSpatialRelationFlags.visSpatialTouching: 接触しているか
‘ ※複数の条件をOR演算子(+)で結合可能
lngRelation = shpContainer.SpatialRelation(shpTarget, 0, _
VisSpatialRelationFlags.visSpatialContainedInside + _
VisSpatialRelationFlags.visSpatialTouching)
‘ 判定結果の解析
‘ visSpatialContainedInside (8) が含まれているかビット演算で確認
If (lngRelation And VisSpatialRelationFlags.visSpatialContainedInside) <> 0 Then
intCountContained = intCountContained + 1
Debug.Print ” [包含] 図形 ‘” & shpTarget.Name & “‘ はエリア内に完全に含まれています。”
‘ 実務でのアクション例:該当図形の色をシアンに変更
shpTarget.CellsU(“LineColor”).FormulaU = “RGB(0, 150, 255)”
‘ visSpatialTouching (16) が含まれているか確認
ElseIf (lngRelation And VisSpatialRelationFlags.visSpatialTouching) <> 0 Then
intCountIntersect = intCountIntersect + 1
Debug.Print ” [接触] 図形 ‘” & shpTarget.Name & “‘ はエリアの境界線に接触しています。”
‘ 実務でのアクション例:該当図形の色をオレンジに変更
shpTarget.CellsU(“LineColor”).FormulaU = “RGB(255, 128, 0)”
End If
End If
Next shpTarget
Debug.Print “=== 判定終了 ===”
Debug.Print ” 包含された図形数: ” & intCountContained
Debug.Print ” 接触している図形数: ” & intCountIntersect
MsgBox “空間判定が完了しました。” & vbCrLf & _
“・内部包含: ” & intCountContained & ” 件” & vbCrLf & _
“・境界接触: ” & intCountIntersect & ” 件”, vbInformation, “処理完了”
Finally:
‘ 3. 環境の確実な復元(エラーが発生しても必ず実行させる)
Application.ScreenUpdating = currentScreenUpdating
Application.EnableEvents = currentEnableEvents
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume Finally
End Sub
—
3. 現場で役立つ!SpatialRelationFlagsの主要な定数
今回のコードでも使用している `VisSpatialRelationFlags` は、空間判定の精度や条件をコントロールする心臓部だ。実務でよく使うフラグを以下に整理する。
| 定数名 | 値 | 意味・用途 |
| :— | :— | :— |
| `visSpatialContainedInside` | 8 | ターゲットがソースの完全に内部に収まっている。 |
| `visSpatialContain` | 4 | ソースがターゲットの完全に内部に含まれている(包含の逆)。 |
| `visSpatialTouching` | 16 | 2つの図形の輪郭線が接触(接して)している。 |
| `visSpatialOverlap` | 2 | 2つの図形が重なり合っている(部分的にクロスしている)。 |
※これらのフラグは、加算(`+`)またはビット演算(`OR`)で複数同時に指定し、返り値とのAND演算で判定することが鉄則となる。
—
4. データベース連携・外部システム連携への応用
この `SpatialRelation` エンジンを実務の自動化パイプラインに組み込む場合、次のようなアーキテクチャが考えられる。
1. 設計図書の自動バリデーション(BIM/CIMやプラント設計)
- エリア枠(親図形)の中に、指定されたマスターIDを持つ機器図形が正しく配置されているかを夜間バッチやボタン一つで全自動チェック。漏れがあればデータベース(SQL ServerやSQLite)にログを吐き出す。
2. 帳票・レイアウト自動生成
- 自動配置アルゴリズムの中で、部品同士が重なっていないか(Overlap)をこのAPIでリアルタイムに検知し、重なりがなくなるまで座標を自動微調整するループ組込。
—
チーフアーキテクトからの総括
VBAによる図形操作の善し悪しは、「Visioが標準で持っている賢い機能(ネイティブAPI)をどれだけ信用して使い倒せるか」で決まる。車輪の再発明をして自前で幾何学計算を書くコードは、保守性が低く、拡張性もない「負債」でしかない。
`SpatialRelation` をマスターすれば、Visioは単なる「お絵描きツール」から、「空間構造を理解する高度なCAD・インテリジェントエンジン」へと変貌を遂げる。
あなたの開発現場の自動化ツールにも、ぜひこの知見を取り入れ、圧倒的なパフォーマンスとエレガントなコードベースを実現してほしい。
