縮尺の不整合を断罪せよ:Visioにおける次元的整合性を保証する極限の監査アルゴリズム
設計図面における「縮尺(Scale)」は、単なる表示上の設定ではない。それは物理世界とデジタル空間を繋ぐ、数学的な契約である。
しかし、実務の現場では、設計者が図形を誤ってリサイズしたり、ページ設定を後から変更したりすることで、この「契約」が密かに、かつ致命的に破綻しているケースが多々ある。図面上では100mmに見える線が、実際には1:10の縮尺設定下で1000mmを意図しているはずが、何らかの操作ミスで95mmになっている――。このような「次元の乖離」を見逃すことは、製造・建設工程における致命的なコスト増大を招く。
本稿では、Visio VBAを用いて、ページ設定の縮尺と、個々のシェイプが保持する「実寸(True Dimension)」の整合性を厳密に検証する、プロフェッショナル向けの監査ツールを構築する。単なる比較スクリプトではない。浮動小数点誤差の制御、メモリ管理、そしてパフォーマンスの最適化まで、エンジニアリングの真髄を実装に込める。
1. 理論的背景:浮動小数点誤差とEpsilonの概念
コンピュータにおける数値計算、特に `Double` 型を用いた計算において、我々は常に「誤差」という敵と対峙しなければならない。`1/10` という数値は、バイナリの世界では無限小数となる。
縮尺の計算において `If ActualWidth = ExpectedWidth Then` という単純な比較を行うのは、素人の仕事だ。我々アーキテクトは、Epsilon(エプシロン)、すなわち「許容可能な最小誤差」を定義し、比較を行う。
$$| \text{Actual} – \text{Expected} | < \epsilon$$ この数学的アプローチこそが、監査ツールの信頼性を担保する。
2. 実装:高精度縮尺監査エンジン
以下に示すコードは、ページ内の全シェイプを走査し、ユーザー定義セル(`TrueDimension`)に格納された「設計上の実寸」と、現在のページ縮尺およびシェイプの描画サイズを照合する。
Option Explicit
' Windows API: 実行時間の高精度計測用
' パフォーマンス・プロファイリングはアーキテクトの嗜みである
If VBA7 Then
Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Else
Declare Function GetTickCount Lib "kernel32" () As Long
End If
''' <summary>
''' 縮尺整合性監査メインルーチン
''' </summary>
Public Sub AuditScaleIntegrity()
Dim vsoPage As Visio.Page
Dim vsoShape As Visio.Shape
Dim delta As Double
Dim expectedWidth As Double
Dim actualWidth As Double
Dim epsilon As Double
Dim errorCount As Long
Dim startTime As Long
' 許容誤差の定義 (例: 0.001インチ)
' 物理的な精度要求に応じて調整すること
epsilon = 0.0001
Set vsoPage = ActivePage
errorCount = 0
startTime = GetTickCount()
' パフォーマンス最適化: 画面更新の停止
' これを行わないと、大規模図面ではUIスレッドがロックされる
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
Debug.Print "--- Audit Started at " & Now & " ---"
' 全シェイプの走査
' 階層構造を持つ複雑な図面の場合、Page.Shapes.All を使用するが、
' メモリ負荷を考慮し、ループ内でのオブジェクト参照は最小限に留める
For Each vsoShape In vsoPage.Shapes
' ユーザー定義セル 'TrueWidth' が存在するかチェック
' これがないシェイプは監査対象外とする(設計思想に基づくフィルタリング)
If HasUserCell(vsoShape, "TrueWidth") Then
' 1. 設計上の実寸を取得 (単位: インチ)
' ※あらかじめ設計者が TrueWidth セルに実寸を書き込んでいる前提
expectedWidth = vsoShape.CellsU("TrueWidth").ResultIU
' 2. ページ縮尺に基づいた「あるべき描画サイズ」を計算
' Page.Scale は (1/Scale) の形式で保持されるため注意が必要
' 描画サイズ = 実寸 * (1 / Scale)
' ただし、VisioのPage.Scaleプロパティは「1:n」の形式を扱うため
' Scale値を適切に取得する必要がある
actualWidth = vsoShape.CellsU("Width").ResultIU
' 3. 整合性検証のロジック
' 期待される描画サイズ = expectedWidth * (1 / ページ縮尺)
' ここでは、Page.Scale プロパティが 1:10 なら 10 となることを前提とする
' (Visioの内部実装に依存するため、厳密には Page.Scale を直接計算に使用)
' 簡略化のため、シェイプの Width が (実寸 / Scale) と一致するかを検証
' Scale値の取得(Page.Scaleは比率を返す)
Dim scaleFactor As Double
scaleFactor = 1 / vsoPage.Scale
' 期待値の再計算
Dim targetDrawWidth As Double
targetDrawWidth = expectedWidth * scaleFactor
' 差分計算
delta = Abs(actualWidth - targetDrawWidth)
If delta > epsilon Then
' 乖離を検出
errorCount = errorCount + 1
ReportError vsoShape, expectedWidth, actualWidth, scaleFactor
End If
End If
' メモリリーク防止のため、巨大なオブジェクトを扱う際は
' ループ内で明示的に参照を解除する検討が必要だが、
' Shapeオブジェクトの走査においては、Iteratorが管理するため
' 基本的には DoEvents で制御する
If errorCount Mod 50 = 0 Then DoEvents
Next vsoShape
' クリーンアップ
Application.ScreenUpdating = True
Debug.Print "--- Audit Completed ---"
Debug.Print "Errors Found: " & errorCount
Debug.Print "Elapsed Time: " & (GetTickCount() - startTime) & " ms"
MsgBox "監査完了。" & vbCrLf & "検出された不整合: " & errorCount & " 件", vbInformation
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
End Sub
''' <summary>
''' 指定したユーザー定義セルが存在するか判定
''' </summary>
Private Function HasUserCell(ByRef vsoShape As Visio.Shape, ByVal cellName As String) As Boolean
On Error Resume Next
Dim dummy As Double
dummy = vsoShape.CellsU(cellName).ResultIU
HasUserCell = (Err.Number = 0)
On Error GoTo 0
End Function
''' <summary>
''' エラーの詳細をログ出力
''' </summary>
Private Sub ReportError(ByRef vsoShape As Visio.Shape, ByVal expected As Double, ByVal actual As Double, ByVal scale As Double)
' 本来はExcelやDBへ書き出すべきだが、ここではDebug窓へ出力
' 現場では、このログをCSV出力してシステム間連携させるのが定石
Debug.Print "[ERROR] ShapeID: " & vsoShape.ID & " | Name: " & vsoShape.Name
Debug.Print " Expected Draw Width: " & expected * scale & " in"
Debug.Print " Actual Draw Width: " & actual & " in"
Debug.Print " Deviation: " & Abs(actual - (expected * scale))
End Sub
3. アーキテクトの視点:システムの堅牢性を高める極限の知見
このスクリプトを、単なる「動くコード」から「エンタープライズ・グレードのツール」へ昇華させるためには、以下の3点に留意せよ。
I. オブジェクト・ライフサイクルの管理
VBAはCOM(Component Object Model)の上で動作している。`For Each` ループによるオブジェクトの走査は非常に強力だが、大規模な図面(数万個のシェイプ)を扱う場合、メモリ消費が指数関数的に増大し、Visioが「応答なし」に陥る。
コード内で `DoEvents` を戦略的に配置しているのは、Windowsのメッセージキューを処理させ、UIスレッドのデッドロックを防ぐためだ。これは、レガシーな環境で長時間実行されるバッチ処理を設計する際の鉄則である。
II. 精度と型選択
`Single` 型(単精度浮動小数点)を使用することは、この種の監査ツールにおいては「禁忌」である。図面の寸法精度は、設計要求によっては小数点以下4桁、5桁に及ぶ。必ず `Double` 型(倍精度浮動小数点)を使用し、比較時には必ず前述した `epsilon` による閾値判定を行え。
III. システム間連携への拡張性
実務における監査ツールは、単体で完結してはならない。
`ReportError` サブルーチンを拡張し、結果を XML または JSON 形式で出力、あるいは SQL Server 等のデータベースへ直接インサートする構成にせよ。これにより、設計部門の不備を「統計データ」として集計し、品質管理(QC)プロセスへ自動的にフィードバックするパイプラインを構築することが可能となる。
結語
自動化エンジニアの真価は、コードの短さではなく、その「決定論的な正確さ」に宿る。
この監査ツールは、図面の「見た目」ではなく、「数学的真実」を追求するためのものである。設計の品質を、個人の注意力が頼る曖昧なものから、アルゴリズムが保証する堅牢なものへと変革せよ。
