【テクニカル・上級編】グループ・コンテナ内シェイプの真の座標を特定:Shape.XYToPageメソッドによる絶対位置計算 – Visio VBA解析バイブル

スポンサーリンク

Visioの座標系を支配せよ:Shape.XYToPageで紐解く「入れ子構造」の絶対位置算出

Visioの座標系は、一見すると単純な2次元のグリッドに見える。しかし、一度「グループ化」や「コンテナ」という概念が介在した瞬間、その座標空間は多重構造の迷宮へと変貌する。

多くのVBAエンジニアが遭遇する「子シェイプの座標が期待した値にならない」という悲劇。これは、彼らが`PinX`や`PinY`といったローカルプロパティを盲信していることに起因する。

本稿では、Visioのオブジェクトモデルを掌握し、入れ子構造の深淵から「絶対位置」を正確に引き出すための技術論を説く。

1. なぜ「PinX/PinY」だけでは死ぬのか

Visioにおける座標は「親(Parent)」のローカル空間に依存する。グループ化されたシェイプの`PinX`は、親グループの座標系におけるオフセットであり、ページ全体の絶対座標ではない。

この不整合を解消するため、我々は`Shape.XYToPage`メソッドを召喚する。これはVisioの描画エンジンが保持する座標変換行列を直接叩くものであり、再帰的な計算を自前で実装する無駄な努力を不要にする「唯一の正解」である。

2. 実装:Shape.XYToPageによる絶対座標算出アルゴリズム

以下のコードは、単なる座標取得の関数ではない。オブジェクトの明示的解放、エラーハンドリング、そして将来的な拡張性を考慮した設計だ。

Option Explicit

‘ @description 指定シェイプのページ内絶対座標を取得する
‘ @param shp 対象のVisio.Shapeオブジェクト
‘ @param absX 出力用:絶対X座標
‘ @param absY 出力用:絶対Y座標
Public Sub GetAbsolutePosition(ByVal shp As Visio.Shape, ByRef absX As Double, ByRef absY As Double)
If shp Is Nothing Then Exit Sub

‘ Shape.XYToPageは、シェイプのピン位置(通常は中心点)を基点とする
‘ 第1、第2引数にローカルの(0, 0)を指定することで、
‘ そのシェイプの親空間における中心座標をページ座標へ変換する

‘ ここで重要なのは、PinX/PinYを直接渡すのではなく、
‘ 「シェイプのローカル中心点(0,0)」を変換させることである
shp.XYToPage 0, 0, absX, absY
End Sub

‘ 使用例
Public Sub Example_GetAbsolutePosition()
Dim shp As Visio.Shape
Dim absX As Double, absY As Double

Set shp = ActivePage.Shapes(“Group.1”).Shapes(“SubShape.1”)

Call GetAbsolutePosition(shp, absX, absY)

Debug.Print “Absolute Position: X=” & absX & “, Y=” & absY

‘ オブジェクトの明示的解放(VBAメモリ管理の鉄則)
Set shp = Nothing
End Sub

技術的な深掘り

  • 第1・第2引数の意味: `XYToPage`の引数はシェイプのローカル座標だ。`0, 0`を渡せばシェイプの`Pin`位置が返る。もしシェイプの左下角が必要なら、`LocPinX`と`LocPinY`の値を計算(負の値にする等)して渡す必要がある。
  • パフォーマンスの重み: このメソッドは内部的に行列演算を行っている。数千個のシェイプを走査する際は、`ActivePage.Shapes`のコレクションを何度も呼び出すのではなく、一度変数に格納してから処理するなどのキャッシュ戦略が不可欠だ。

3. レガシー環境とメモリ管理への極限の知見

VBAは、一見すると「書いて終わり」の言語に見えるが、COM(Component Object Model)のラッパーである以上、メモリリークは常に隣り合わせだ。

1. オブジェクトの解放: `Set shp = Nothing` は単なる儀式ではない。特に大規模なループ処理において、参照カウンターを適切にデクリメントしないと、Visioのプロセスが肥大化し、最悪の場合、OSレベルでメモリ不足を招く。
2. Windows APIとの共存: もし、Visioの描画外(外部データベースやC#で組んだバックエンドシステム)との同期が必要な場合、VBAから直接Win32 API(`User32.dll`など)を呼び出し、メモリマップドファイルや名前付きパイプでデータを飛ばす手法も考慮すべきだ。
3. コンテナ操作の罠: Visio 2010以降の「コンテナ」機能は、内部的に`ContainerProperties`を保持している。グループとコンテナを混同すると、座標計算で致命的なバグを生む。`If shp.ContainingShape.Type = visTypePage` で親がページか否かを判定し、再帰的に上位の座標を追跡するアーキテクチャこそが、堅牢なシステムを作る。

4. 最後に:アーキテクトとしての助言

Visio VBAを触るということは、「図面という非構造化データ」を「ビジネスロジックという構造化データ」に変換するという極めて高度な抽象化作業を行っているということだ。

座標計算一つ取っても、場当たり的な修正は避けろ。常に「このコードが10年後にメンテナンスされるとしたら?」という視点を持つこと。今回紹介した`XYToPage`による変換こそが、将来の仕様変更にも耐えうる、最も信頼のおける座標特定の手法である。

コードは、ただ動けばいいのではない。正確で、かつ美しいものであるべきだ。それが、真のエンジニアが守るべき矜持である。

タイトルとURLをコピーしました