【極限の設計】Visioの「縮尺不整合」を根絶する:高精度監査エンジンの構築術
設計現場において、Visioの図面は単なる「絵」ではない。それは、実寸に基づいた「設計データ」であるべきだ。
しかし、現場では悲劇が日常的に起きている。ページ設定で「1:50」と謳いながら、シェイプのサイズが実寸計算に基づかず、適当なドラッグ&ドロップで配置されている。この「縮尺の乖離」は、後続の製造工程や施工工程において致命的なミスを誘発する。
多くの「ツール作成担当者」は、単にシェイプの幅を読み取るだけの稚拙なスクリプトを書く。だが、それではプロフェッショナルとは呼べない。我々が構築すべきは、Visioのオブジェクトモデルの深淵を理解し、数学的精度と堅牢なエラーハンドリングを備えた「監査エンジン」である。
本稿では、ページ設定の縮尺とシェイプの実寸を照合し、不整合を炙り出す、プロダクションレベルの監査ツールの設計思想を伝授する。
1. 陥りやすい「素人の設計」と、アーキテクトの視点
まず、避けるべき設計パターンを提示しよう。多くの開発者が以下のようなコードを書く。
' --- 避けるべき、脆弱な設計例 ---
Sub BadAudit()
Dim shp As Visio.Shape
For Each shp In ActivePage.Shapes
' ページスケールを無視して、単にサイズを表示するだけ
Debug.Print shp.Name & " Width: " & shp.Width
Next shp
End Sub
なぜこれが「ゴミ」なのか。理由は3つある。
1. 単位(Unit)の混同: Visioの`Shape.Width`が返す値は、ドキュメント単位(インチやミリメートル)である。ページ設定の「縮尺」が適用された後の「実寸(Real World Dimension)」を計算に含めていない。
2. 浮動小数点誤差の無視: 縮尺計算(1/50など)には割り算が伴う。`Double`型の比較において、`If a = b Then` といった単純比較を行うと、微小な誤差によって正解が「不整合」と判定される。
3. スケーラビリティの欠如: `MsgBox`でエラーを出す設計では、数百のシェイプがある図面を監査した瞬間にユーザーの作業が停止する。
プロフェッショナルは、「計算ロジック」「データ収集」「レポート出力」を完全に分離して設計する。
2. 監査エンジンの設計思想:数学的整合性の確保
高精度な監査ツールを作るためには、以下の数式を厳密に実装する必要がある。
$$ \text{RealWorldDimension} = \text{DocumentDimension} \times \text{PageScaleRatio} $$
ここで重要なのは、Visioの `Page.ScaleWidth` プロパティの扱いに注意することだ。これは「ドキュメントの1単位が、現実世界の何単位に相当するか」を示す係数である。
設計の3原則
1. Tolerance(許容誤差)の導入: `Abs(Calculated – Expected) < Tolerance` という形式で比較を行う。
2. Result Objectの構築: エラーを単に表示するのではなく、エラー内容(シェイプ名、期待値、実測値、乖離率)を構造化して保持する。
3. Decoupling(非結合): 監査ロジックはVisioのUIに依存させず、純粋な計算ロジックとして切り出す。
3. プロダクションコード:高精度監査エンジン
以下に、そのまま業務に投入可能なレベルの、堅牢な実装例を示す。このコードは、エラーをコレクションとして蓄積し、最後に一括して報告する設計思想に基づいている。
Option Explicit
' --- 監査結果を保持するための構造体 ---
Private Type AuditError
ShapeName As String
ExpectedWidth As Double
ActualWidth As Double
Deviation As Double
End Type
' --- 定数定義 ---
Private Const TOLERANCE As Double = 0.001 ' 許容誤差 (mm単位を想定)
''' <summary>
''' ページ内のシェイプの縮尺不整合を監査するメインルーチン
''' </summary>
Public Sub RunScaleAuditEngine()
Dim pg As Visio.Page
Set pg = ActivePage
Dim errorList() As AuditError
Dim errorCount As Long
errorCount = 0
' ページスケール係数の取得
' ScaleWidthは「1ドキュメント単位あたりの現実世界の単位数」
Dim scaleRatio As Double
scaleRatio = pg.ScaleWidth
Dim shp As Visio.Shape
Dim expectedRealWidth As Double
Dim actualRealWidth As Double
Dim deviation As Double
On Error GoTo ErrorHandler
' 高速化のための最適化
Application.ScreenUpdating = False
For Each shp In pg.Shapes
' ユーザー定義の「実寸属性」を持っているシェイプのみを対象とする設計
' (全てのシェイプを対象にすると、装飾用ライン等でノイズが出るため)
If shp.CellExists("Prop.RealWorldWidth", 0) Then
' 1. 属性から「本来あるべき実寸」を取得
expectedRealWidth = shp.Cells("Prop.RealWorldWidth").ResultIU
' 2. 現在のドキュメント上の幅から「現在の実寸」を計算
' Shape.Width はドキュメント単位。これにScaleWidthを掛けて実寸に変換
actualRealWidth = shp.Width * scaleRatio
' 3. 乖離の計算
deviation = Abs(expectedRealWidth - actualRealWidth)
' 4. 許容誤差判定
If deviation > TOLERANCE Then
errorCount = errorCount + 1
ReDim Preserve errorList(1 To errorCount)
With errorList(errorCount)
.ShapeName = shp.Name
.ExpectedWidth = expectedRealWidth
.ActualWidth = actualRealWidth
.Deviation = deviation
End With
End If
End If
Next shp
Application.ScreenUpdating = True
' --- 結果のレポート出力 ---
If errorCount > 0 Then
ReportErrors errorList, errorCount
Else
MsgBox "監査完了: 縮尺の不整合は見つかりませんでした。", vbInformation, "Audit Success"
End If
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "System Error"
End Sub
''' <summary>
''' 監査結果をログ出力(実務ではExcel出力やDB保存へ拡張可能)
''' </summary>
Private Sub ReportErrors(ByRef errors() As AuditError, count As Long)
Dim i As Long
Dim reportMsg As String
reportMsg = "【警告】" & count & " 件の縮尺不整合を検出しました。" & vbCrLf & vbCrLf
' 簡易的なメッセージ表示(件数が多い場合はExcel出力に切り替える設計にする)
If count > 10 Then
' 10件を超える場合はExcelへ書き出すロジックへ分岐させるのがプロの設計
ExportToExcel errors, count
Else
For i = 1 To count
reportMsg = reportMsg & "■ " & errors(i).ShapeName & vbCrLf & _
" 期待値: " & Round(errors(i).ExpectedWidth, 2) & " mm" & vbCrLf & _
" 現在値: " & Round(errors(i).ActualWidth, 2) & " mm" & vbCrLf & _
" 誤差 : " & Round(errors(i).Deviation, 3) & " mm" & vbCrLf & vbCrLf
Next i
MsgBox reportMsg, vbExclamation, "Audit Alert"
End If
End Sub
''' <summary>
''' 監査結果をExcelへエクスポートする(実務用拡張メソッド)
''' </summary>
Private Sub ExportToExcel(ByRef errors() As AuditError, count As Long)
Dim xlApp As Object
Dim xlWB As Object
Dim i As Long
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If xlApp Is Nothing Then
Set xlApp = CreateObject("Excel.Application")
End If
On Error GoTo 0
Set xlWB = xlApp.Workbooks.Add
xlApp.Visible = True
With xlWB.Sheets(1)
.Cells(1, 1).Value = "Shape Name"
.Cells(1, 2).Value = "Expected (mm)"
.Cells(1, 3).Value = "Actual (mm)"
.Cells(1, 4).Value = "Deviation (mm)"
For i = 1 To count
.Cells(i + 1, 1).Value = errors(i).ShapeName
.Cells(i + 1, 2).Value = errors(i).ExpectedWidth
.Cells(i + 1, 3).Value = errors(i).ActualWidth
.Cells(i + 1, 4).Value = errors(i).Deviation
Next i
End With
MsgBox "詳細な監査レポートをExcelで生成しました。", vbInformation
End Sub
4. 実務への統合:データベース・外部連携の注意点
このツールを単なるスクリプトから「業務基盤」へと昇華させるには、以下の点に留意せよ。
シェイプへのメタデータ埋め込み
上記のコードでは `shp.CellExists(“Prop.RealWorldWidth”, 0)` を使用している。これは、シェイプの「ShapeSheet」に、設計上の正しい実寸を保持するユーザー定義セル(`Prop.RealWorldWidth`)を持たせることを前提としている。
単に「図面の見た目」をチェックするのではなく、「正しい設計値」というマスターデータをシェイプ自体に持たせる。これが、データの一貫性を保つための鉄則である。
外部連携(Excel/DB)のパフォーマンス
大量の図面を監査する場合、Excelへの書き出しは「一セルずつ」行ってはならない。
1. メモリ上の配列(`errorList`)に一度すべて格納する。
2. Excelの `Range.Value = Array` を用いて、一括でセルへ流し込む。
この「バルク・インサート」の概念がないコードは、大規模プロジェクトではパフォーマンス・ボトルネックとなり、使い物にならない。
結論
Visio VBAにおける自動化とは、単に「操作を代行すること」ではない。「設計の品質を数学的に担保すること」である。
オブジェクトのライフサイクル、単位変換の精度、そしてエラーの構造化。これらを意識して設計されたコードこそが、エンジニアリングの現場で真に信頼される「ツール」となる。
君が次に書くコードは、単なるスクリプトか? それとも、設計品質を守るための堅牢なエンジンか? その答えは、君の設計思想に委ねられている。
