Visioの境界判定を極める:回転・グループを掌握するGetBoundingBoxの真実
Visio VBAにおける座標計算は、多くの開発者が「なんとなく動く」レベルで放置している地雷原だ。特に `Shape.BoundingBox` プロパティを盲信して、回転した図形やグループ化した複雑なオブジェクトのレイアウト計算に挑む者は、必ず「微妙なズレ」という名の技術的負債に直面する。
本稿では、Visioの描画エンジンが内部的に保持する「真の境界」を、`GetBoundingBox` メソッドと `VisBBoxFlags` を駆使して引きずり出す、極限のアルゴリズムを解説する。
—
なぜ `Shape.BoundingBox` ではいけないのか
多くの開発者は `Shape.BoundingBox` を使うが、これは「図形の回転を無視した、現在の座標系における最小外接矩形(AABB: Axis-Aligned Bounding Box)」しか返さない。図形を45度回転させれば、当然ながら意図しない広大な範囲が返される。
我々が求めるのは、「図形そのものが持つローカル座標系に基づいた、回転後の精密な矩形情報」だ。これには `Shape.GetBoundingBox` メソッドが不可欠となる。
—
GetBoundingBoxとVisBBoxFlagsの制約を突破する
`GetBoundingBox` は、引数に `Flags` を取る。ここを制御しなければ、Visioの「見えない余白(影や接続ポイントの装飾)」まで計算に含まれてしまい、自動レイアウト計算が破綻する。
特に重要なのは以下のフラグの組み合わせだ。
- `visBBoxUpright`: 回転を無視した外接矩形(基本)
- `visBBoxIncludeOutlines`: 線幅を考慮する
- `visBBoxIncludeHidden`: 非表示オブジェクトを含めるか(通常は除外)
実装:高精度なバウンディングボックス取得関数
以下のコードは、回転を考慮した座標算出において、メモリーリークを排し、かつ `Double` 型の精度を最大限に活かすアーキテクチャだ。
‘ @brief 回転を考慮した図形の精密外接矩形を取得する
‘ @param shp 対象のShapeオブジェクト
‘ @param left, top, right, bottom 戻り値用引数
Public Sub GetPreciseBoundingBox(ByVal shp As Visio.Shape, _
ByRef left As Double, ByRef top As Double, _
ByRef right As Double, ByRef bottom As Double)
‘ 精度を担保するため、必ずVisioの内部単位(インチ/内部ポイント)で処理する
‘ visBBoxExtents: 外形線を含み、かつ回転を考慮した境界を取得するフラグ
‘ 複雑なグループ図形の場合、グループ内の最外縁を再帰的に特定する
On Error GoTo Err_Handler
‘ Flags:
‘ visBBoxExtents (1) + visBBoxIncludeOutlines (2)
‘ これにより、線の太さを含めた実質的な占有領域を特定できる
shp.GetBoundingBox 1 + 2, left, bottom, right, top
Exit Sub
Err_Handler:
‘ Visioオブジェクトの解放は明示的に行うのがアーキテクトの矜持
Debug.Print “Error in GetPreciseBoundingBox: ” & Err.Description
End Sub
—
メモリ最適化とパフォーマンスの極意
Visio VBAはオブジェクトの生成・破棄が非常に重い。特に `Page.Shapes` をループで回す際、安易に `Set shp = …` を繰り返すと、ガベージコレクションのタイミングでUIスレッドが停止する。
1. オブジェクトの明示的解放: ループ内のローカル変数として `Shape` を定義せず、必要に応じて `Nothing` を代入する習慣を徹底せよ。
2. イベントの抑制: 大規模なレイアウト計算を行う際は、`Application.ScreenUpdating = False` および `Application.DeferRecalc = True` を適用せよ。これを怠ることは、戦闘中に弾薬を浪費するのと同じだ。
3. Windows APIの活用: もし画面上のピクセル座標との相互変換が必要なら、`ClientToScreen` APIを呼び出す。VBAの変換関数に頼らず、ネイティブな値を計算せよ。
—
現場で生き残るための「境界」哲学
システム間連携(例えばVisioからSVGやDXFへの変換)を行う際、この境界計算のミスは致命的なデータ不整合を招く。
- グループ化の罠: グループ内の図形が回転している場合、`GetBoundingBox` はグループ全体の領域を返す。個別のパーツの挙動を追うには、`GroupItems` を再帰的に走査し、各パーツの `Transform` マトリクスを積算する必要がある。
- レガシー保守: 古いVisioファイル(.vsd)では、座標系の基準点(PinX, PinY)が中央にないものが散見される。常に `Shape.Cells(“LocPinX”).ResultIU` を確認し、図形中心を補正値として加味することを忘れるな。
結びに代えて
Visio VBAはレガシーと言われるが、そのオブジェクトモデルは極めて数学的で美しい。境界計算を制する者は、Visioの描画エンジンを制する。
「なんとなく動く」コードを書く時間は終わった。フラグの意味を定義まで遡り、メモリの消費を意識し、計算の根拠を物理単位で証明できる。これこそが、我々エンジニアが真に追求すべき「自動化」の神髄である。
この知見が、貴殿の自動化システムをより堅牢なものに変えることを期待する。
