【テクニカル・上級編】図形レイアウトの自動整列:複数シェイプの位置座標(PinX/PinY)をVBAで一斉計算する – Visio VBA解析バイブル

スポンサーリンク

Visio自動整列の極致:PinX/PinYを数学的に制圧し、メモリを極限まで最適化せよ

Visioのオートメーションは、多くのエンジニアにとって「泥沼」になりがちだ。特に図形の自動整列を甘く見て、無計画なループ処理を組めば、描画の再計算(Re-calculation)がスタックし、パフォーマンスは瞬く間に崩壊する。

シニアエンジニアとして諸君に問う。「なぜシェイプを動かすだけで、Visioがフリーズするのか?」 その答えは、オブジェクトの生存期間と、Visioが裏側で実行するShapeSheetの依存関係更新(Dependency Update)を制御できていないからだ。

今回は、数千のシェイプを瞬時に整列させるための、アーキテクチャレベルの知見を授ける。

1. 座標計算の定石:PinX/PinYとLocPinX/LocPinYの欺瞞

Visioにおいて座標を操作する際、初心者は単純に `PinX` を代入するが、これは大きな誤りだ。シェイプの回転軸(LocPin)が中央でない場合、期待した位置からズレが生じる。

真の自動整列を目指すなら、以下の原則を厳守せよ。

  • LocPinを左上(0,0)に固定する: 計算の複雑さを排除するため、整列処理の前に `LocPinX/Y` をシェイプの左上隅に強制的に設定する。
  • ScreenUpdatingの無効化: `Application.ScreenUpdating = False` は必須だが、さらに `Application.DeferRecalc = True` を併用せよ。これが最も劇的にパフォーマンスを向上させる。

2. 実装:高速グリッド整列アルゴリズム

以下は、選択したシェイプをグリッド状に整列させるためのプロトタイプだ。メモリリークを防ぐため、オブジェクトの参照は徹底して `Nothing` で開放する。

Option Explicit

‘ 伝説的なパフォーマンスのための最適化ルーチン
Public Sub AutoLayoutShapes(ByVal gridCols As Integer, ByVal offsetX As Double, ByVal offsetY As Double)
Dim vsoSelection As Visio.Selection
Dim vsoShape As Visio.Shape
Dim i As Long, row As Long, col As Long
Dim posX As Double, posY As Double

‘ 描画更新と計算更新を停止(これが劇薬となる)
Application.ScreenUpdating = False
Application.DeferRecalc = True

Set vsoSelection = ActiveWindow.Selection

‘ 選択がない場合は即座に抜ける
If vsoSelection.Count = 0 Then GoTo Cleanup

For i = 1 To vsoSelection.Count
Set vsoShape = vsoSelection(i)

‘ 計算処理
row = (i – 1) \ gridCols
col = (i – 1) Mod gridCols

posX = col offsetX
posY = -(row offsetY) ‘ VisioのY軸は上がプラス

‘ LocPinを左上に固定して計算誤差を排除
vsoShape.Cells(“LocPinX”).Formula = “Width0”
vsoShape.Cells(“LocPinY”).Formula = “Height1”

‘ 座標適用
vsoShape.Cells(“PinX”).Result(“mm”) = posX
vsoShape.Cells(“PinY”).Result(“mm”) = posY

‘ オブジェクトの明示的解放(VBAでも重要な習慣)
Set vsoShape = Nothing
Next i

Cleanup:
‘ 状態を戻す
Application.DeferRecalc = False
Application.ScreenUpdating = True
Set vsoSelection = Nothing
End Sub

3. チーフアーキテクトの視点:なぜこれが「極限」なのか

このコードの肝は、計算ロジックそのものではなく、「ShapeSheetの再計算を抑制する」 という設計思想にある。

メモリ最適化とライフサイクル

VBAのガベージコレクションは非力だ。大規模な図面で数千のオブジェクトを回す場合、`Set vsoShape = Nothing` をループ内で書くことは、単なる作法ではなく、メモリの断片化(Fragmenting)を防ぐ防波堤となる。

レガシー環境とWindows APIの連携

もし、さらに複雑なフローチャートの自動整列(木構造の再帰的な配置など)を行う場合、VBAの単一スレッドでは限界が来る。その際は、`User32.dll` の `Sleep` 関数をループの適所に挿入し、OSのメッセージキューを解放することで、Visioが「応答なし」になるのを防ぐという「荒技」も検討すべきだ。

4. 最後に:エンジニアへの提言

VisioのVBAは、「マクロ」という言葉で侮られることが多い。しかし、内部オブジェクトモデルの挙動を深く理解し、ShapeSheetの依存関係を計算量オーダー(Big O)の観点から制御すれば、それは立派な「システム基盤」へと昇華する。

諸君が書くコードは、単に図形を動かすだけの記述ではない。Visioという巨大なエンジンを、計算アルゴリズムという剣でコントロールする儀式なのだ。

次回のブログでは、「ShapeSheetの `User.x` セルを動的に書き換えることで、Visio標準のコネクタを完全掌握する方法」について深掘りしようと思う。期待して待っていてくれ。

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