Visio VBAを掌握する極限の知見:SpatialRelationによる超高速・空間判定エンジンの実装
Visio VBAの領域において、多くの開発者が陥る悪夢がある。それは、2つの図形(Shape)の位置関係を判定しようとして、自前で`.Result`プロパティから座標(PinX, PinY, Width, Height)を取得し、複雑な幾何学計算(バウンディングボックスの交差判定や回転を考慮した座標変換)をVBAのループ内で実装してしまう愚行だ。
断言しよう。VBAのインタープリターで三角関数や行列計算を回すのは、鈍ら刀で丸太を斬るようなものであり、パフォーマンスの観点から万死に値する。
Visioには、C++のネイティブ層で最適化された最強の空間演算エンジンが標準で備わっている。それが `Shape.SpatialRelation` プロパティ である。今回は、このAPIを極限まで引き出し、図形の「包含」「重なり」「近接」をノーコストで瞬時に判定するプロダクション・グレードの空間判定エンジンの全貌を公開する。
—
1. SpatialRelationの真髄とアーキテクチャの理解
`SpatialRelation` は、指定した2つの図形の間で、一方がもう一方に対してどのような幾何学的位相にあるかをビットフラッグとして返す。
Long = targetShape.SpatialRelation(otherShape, Tolerance, Flags)
このAPIの恐るべき点は、図形の回転(Angle)や、グループ化のネスト、さらには頂点数が多い複雑なパス(Path)を持つ図形であっても、Visioのコアエンジンがミリ秒単位で判定を完了する点にある。
押さすべき3つのコア・フラグ
引数に渡す `Flags` と返り値の評価には、以下のVisio内部定数が絡み合う。
- `visSpatialContain` (8): 完全に対象図形の内側に包含されている。
- `visSpatialOverlap` (16): 図形の一部が重なり合っている。
- `visSpatialTouch` (4): 境界線(ピンチ・接点)が接触している。
これらを組み合わせることで、自前の数学的アルゴリズムを一切書くことなく、CADやGIS(地理情報システム)並みの空間クエリをVBAから実行できる。
—
2. 実装:プロダクション仕様の空間判定エンジン
以下に、実業務の図面自動化パイプラインに即座に組み込める、堅牢かつ洗練された空間判定クラス・モジュールのコードを示す。
メモリリークを完全に防ぐためのオブジェクトの明示的解放(`Set … = Nothing`)、および予期せぬエラーに対する防御的プログラミングを徹底している。
クラスモジュール名: `SpatialEngine`
Option Explicit
‘ ==============================================================================
‘ 伝説のチーフアーキテクトが贈る: Visio 空間判定エンジン
‘ 概要: Shape.SpatialRelationを活用した高速空間クエリ
‘ ==============================================================================
‘ 許容誤差(デフォルト値: 0 インチ = 完全一致を狙う場合。必要に応じて変更)
Private m_Tolerance As Double
Private Sub Class_Initialize()
‘ 内部許容誤差の初期化 (Visioの内部単位はインチ。必要に応じミリメートル等に換算)
m_Tolerancenud = 0.0001
End Sub
‘ ——————————————————————————
‘ 判定メソッド: sourceShape が targetShape に対して指定の関係にあるか判定
‘ ——————————————————————————
Public Function EvaluateRelation(ByVal sourceShape As Visio.Shape, _
ByVal targetShape As Visio.Shape, _
ByVal relationFlag As Integer) As Boolean
On Error GoTo ErrorHandler
‘ ヌルポインタおよびオブジェクトの生存確認(防御的プログラミング)
If sourceShape Is Nothing Or targetShape Is Nothing Then
EvaluateRelation = False
Exit Function
End If
‘ 同一オブジェクトの比較は除外
If sourceShape.ID = targetShape.ID Then
EvaluateRelation = False
Exit Function
End If
Dim result As Long
‘ SpatialRelationの呼び出し
‘ 第2引数: 許容誤差, 第3引数: 0 (デフォルトの精密判定)
result = sourceShape.SpatialRelation(targetShape, m_Tolerance, 0)
‘ ビット演算によるフラグ判定
If (result And relationFlag) <> 0 Then
EvaluateRelation = True
Else
EvaluateRelation = False
End If
Exit Function
ErrorHandler:
‘ 異常終了時のログ出力やフェイルセーフ
Debug.Print “SpatialEngine Error: ” & Err.Description
EvaluateRelation = False
End Function
‘ ——————————————————————————
‘ 応用メソッド: 指定ページ内の特定図形を「包含」している子図形群を列挙する
‘ ——————————————————————————
Public Function GetContainedShapes(ByVal containerShape As Visio.Shape, _
ByVal targetPage As Visio.Page) As Visio.Selection
On Error GoTo ErrorHandler
Dim targetSelection As Visio.Selection
Set targetSelection = targetPage.CreateSelection(visSelTypeEmpty)
Dim shp As Visio.Shape
For Each shp in targetPage.Shapes
‘ 自分自身はスキップ
If shp.ID <> containerShape.ID Then
‘ 包含関係(visSpatialContain)をチェック
If shp.SpatialRelation(containerShape, 0, 0) And visSpatialContain Then
targetSelection.Select shp, visSelect
End If
End If
Next shp
Set GetContainedShapes = targetSelection
‘ クリーンアップ
Set shp = Nothing
Exit Function
ErrorHandler:
Debug.Print “GetContainedShapes Error: ” & Err.Description
Set GetContainedShapes = Nothing
End Function
—
3. 実務での活用:システム間連携とメモリ最適化の極意
このエンジンを数千個の図形が配置されるプラント図やネットワーク構成図の自動生成・検証プロセスに投入する場合、以下の「プロの知見」を忘れてはならない。
1. イテレーションにおけるオブジェクトのキャッシュと解放
VBAにおいて、`For Each shp In Page.Shapes` のようなループを回す際、COMオブジェクトの参照が暗黙的にスタックに残り続けることがある。大規模図面でこれを放置すると、メモリリークを起こし、最悪の場合はVisio本体がクラッシュする。
ループ変数は必ず最後に `Set shp = Nothing` で解放し、マクロ終了時にはすべてのオブジェクト参照を断ち切る設計にすること。
2. レガシー環境(Visio 2010〜2016等)における挙動の差異
古いVisioのバージョンでは、CAD図面(DWG/DXF)をインポートしたグループ図形に対して `SpatialRelation` を実行すると、内部のベクターデータの複雑さゆえに正しく判定できない(あるいはスタックオーバーフローを起こす)ケースがある。
これを回避するため、実務の現場では、インポート図形やCADブロックに対しては事前に `Shape.Ungroup` を行うか、あるいはバウンディングボックス(`BoundingBox` プロパティ)による簡易フィルターを一手前に挟むハイブリッドな設計が求められる。
3. 他システム(C# / .NET API)への移行パス
もし将来的にこのVBAマクロを、COMアドインや外置きのC# (.NET 8) アプリケーションに移行する場合でも、このコードの思想はそのまま流用できる。C#の `Visio.Shape.SpatialRelation` も完全に同一のインターフェースを持つため、VBAで構築したこの空間クエリのロジックは、そのままエンタープライズなシステムアーキテクチャへとスケールアップさせることが可能だ。
—
総括
VBAは、単なる「おまけの自動化スクリプト言語」ではない。Visioのオブジェクトモデルの深淵を理解し、COMのライフサイクルとネイティブAPIを正しく手なづければ、C/C++製アプリケーションに匹敵する高速な幾何学処理エンジンを構築できる。
感覚的な位置合わせや、泥臭い座標計算のコードは今日で捨て去ろう。`SpatialRelation` を制する者が、Visio自動化の領域を制覇する。
