【実務中級】AcadDocument.Utility.GetDistanceの向こう側:図面スケールを完全制圧する実寸値算出エンジンの構築
図面管理システムや自動集計マクロの開発現場において、ジュニア層とシニア層の決定的な違いは「APIが返す数値をそのまま信じるか、その裏にあるコンテキストを疑うか」にある。
AutoCAD VBAにおける `AcadDocument.Utility.GetDistance` メソッドは、指定した2点間の「図面上の作図単位(Drawing Units)」を返す。しかし、実務の現場において、CAD上の1.0ユニットが常に「1.0ミリメートル」であるとは限らない。詳細図における拡大率、異尺度混在図面、あるいはメートル・ミリ・インチの単位混迷。これらを無視した自動化コードは、現場に致命的な手戻りを引き起こす。
今回は、`GetDistance` の基本を押さえた上であえて一歩踏み込み、「現在のビューポート尺度」「図面単位」「Dimscale(寸法尺度)」を動的に解決し、現実世界の「実寸法(実寸値)」をミリ秒単位のオーバーヘッドですくい上げるプロダクション品質の計測支援ツールを実装する。
—
1. 座標系と尺度の乖離:なぜ `GetDistance` 単体では通用しないのか
AutoCADの内部データベースにおいて、オブジェクトは常に「アンディメンション(無次元)な浮動小数点数(Double)」で保持されている。`GetDistance` が返す値は、UCS(ユーザー座標系)上でのユークリッド距離に過ぎない。
実務で遭遇する課題は以下の3点に集約される。
1. ビューポートの尺度(Annotation Scale / Viewport Scale): レイアウト空間(ペーパー空間)からのビューポート投影における縮尺。
2. 図面単位(INSUNITS): 基底データがミリメートルなのか、メートルなのか、インチなのか。
3. 寸法補助設定(DIMSCALE / DIMLFAC): 局部的な倍率補正。
これらを統合し、「画面上でクリックした2点から、構造物の現実のサイズ(mm単位)」を算出し、ログ出力およびシステム連携を行うモジュールを構築する。
—
2. 実装コード:実寸値算出エンジン(Production Grade)
以下のコードは、エラーハンドリング、オブジェクトのライフサイクル管理、そしてCOMのマーシャリングコストを意識した、実務投入可能なクラスモジュール設計のVBAコードである。
クラスモジュール: `clsScaleMeasurer.cls`
Option Explicit
‘ =====================================================================
‘ 模範的アーキテクチャ: 実寸値算出・計測エンジン
‘ 概要: 空間コンテキストを自動判定し、GetDistanceの生値を物理実寸値に変換する
‘ =====================================================================
Private m_Doc As AcadDocument
Private m_ScaleFactor As Double
‘ 初期化時にドキュメント参照を安全にバインド
Public Sub Initialize(ByVal targetDoc As AcadDocument)
If targetDoc Is Nothing Then
Err.Raise 91, “clsScaleMeasurer”, “ドキュメントオブジェクトが無効です。”
End If
Set m_Doc = targetDoc
Me.RefreshScaleContext
End Sub
‘ 尺度コンテキストの動的再計算(メモリリーク防止と最新状態の保証)
Public Sub RefreshScaleContext()
Dim insUnits As Integer
Dim currentSpace As Integer
Dim vpScale As Double
On Error GoTo ErrorHandler
‘ 1. 図面単位系(INSUNITS)の取得
insUnits = m_Doc.GetVariable(“INSUNITS”)
‘ 2. 空間判定 (Model = 0, Paper = 1)
currentSpace = m_Doc.ActiveSpace
If currentSpace = acModelSpace Then
‘ モデル空間の場合:INSUNITSをミリメートルベースに換算する係数
m_ScaleFactor = GetUnitsToMillimeterFactor(insUnits)
Else
‘ ペーパー空間の場合:アクティブビューポートのカスタムスケールを解決する
‘ ※実務ではActivePViewportまたはVportTableRecordからの取得が必要
vpScale = GetActiveViewportScale()
m_ScaleFactor = GetUnitsToMillimeterFactor(insUnits) vpScale
End If
Exit Sub
ErrorHandler:
‘ フォールバック:デフォルトはスケール1.0(ミリメートル等価)
m_ScaleFactor = 1.0
End Sub
‘ 2点間の計測を実行し、実寸値(mm)を返す
Public Sub MeasureRealDistance(ByRef outRealDistance As Double, ByRef outDisplayString As String)
Dim util As AcadUtility
Dim rawDist As Double
Dim basePoint As Variant
Dim secondPoint As Variant
Set util = m_Doc.Utility
On Error GoTo UserCanceled
‘ ユーザーインタラクション:2点取得
basePoint = util.GetPoint(, vbCrLf & “【実寸計測】基点を指定してください: “)
secondPoint = util.GetPoint(basePoint, vbCrLf & “【実寸計測】2点目を指定してください: “)
‘ 生のユークリッド距離を取得
rawDist = util.GetDistance(basePoint, secondPoint)
‘ 実寸値の算出 (生値 × 尺度ファクター)
outRealDistance = rawDist m_ScaleFactor
‘ フォーマット済み文字列の生成
outDisplayString = Format$(outRealDistance, “#,
0.00″) & ” mm”
‘ オブジェクトの明示的解放(VBAのCOM参照解放の鉄則)
Set util = Nothing
Exit Sub
UserCanceled:
‘ ESCキー等によるキャンセル処理
Set util = Nothing
outRealDistance = 0
outDisplayString = “計測がキャンセルされました。”
End Sub
‘ — 内部ヘルパー関数 —
Private Function GetUnitsToMillimeterFactor(ByVal units As Integer) As Double
‘ AutoCAD INSUNITS 定数に基づくミリメートル換算係数
‘ 4: mm, 5: cm, 6: m, 1: inches, etc.
Select Case units
Case 4: GetUnitsToMillimeterFactor = 1.0 ‘ ミリメートル
Case 5: GetUnitsToMillimeterFactor = 10.0 ‘ センチメートル
Case 6: GetUnitsToMillimeterFactor = 1000.0 ‘ メートル
Case 1: GetUnitsToMillimeterFactor = 25.4 ‘ インチ
Case 2: GetUnitsToMillimeterFactor = 304.8 ‘ フィート
Case Else: GetUnitsToMillimeterFactor = 1.0 ‘ 未定義・無単位の場合は1.0
End Select
End Function
Private Function GetActiveViewportScale() As Double
‘ ペーパー空間におけるビューポート尺度の解決
‘ 厳密なプロダクションコードでは、ActivePViewportのCustomScaleプロパティを評価する
Dim activeVP As AcadPViewport
On Error Resume Next
Set activeVP = m_Doc.ActivePViewport
If Err.Number <> 0 Or activeVP Is Nothing Then
GetActiveViewportScale = 1.0
Else
GetActiveViewportScale = activeVP.CustomScale
End If
Set activeVP = Nothing
End Function
—
標準モジュール: `modExecution.bas`(エントリーポイント)
Option Explicit
‘ ユーザーが呼び出すエントリポイント
Public Sub RunRealDistanceMeasurement()
Dim measurer As clsScaleMeasurer
Dim realDist As Double
Dim msg As String
‘ COMオブジェクトの初期化とエラー監視
On Error GoTo ErrorHandler
Set measurer = New clsScaleMeasurer
measurer.Initialize ThisDrawing
‘ 実行
measurer.MeasureRealDistance realDist, msg
‘ 結果の出力(メッセージボックスおよびコマンドラインへのログ出力)
If realDist > 0 Then
MsgBox “【計測結果】” & vbCrLf & msg, vbInformation, “AutoCAD 実寸計測システム”
ThisDrawing.Utility.PrintString vbCrLf & “>> 計測された実寸値: ” & msg & vbCrLf
‘ TODO: ここで外部DBやCSV、APIへのシステム間連携データを渡す処理を実装
Else
ThisDrawing.Utility.PrintString vbCrLf & “>> ” & msg & vbCrLf
End If
CleanUp:
Set measurer = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub
—
3. シニアアーキテクトが解説する設計の急所
上記のコード群は、単なる「ラッパー」ではない。実務で必ず直面する問題に対する備えが組み込まれている。
1. メモリ管理とCOMのライフサイクル
AutoCAD VBAにおいて、`ThisDrawing.Utility` や `ActivePViewport` などのCOMオブジェクトを頻繁に呼び出すと、内部の参照カウンタ(Reference Counter)が肥大化し、最悪の場合はAutoCAD自体のクラッシュ(Access Violation)を引き起こす。
今回のコードでは、ローカル変数として取得したオブジェクト(`util`, `activeVP`)をプロシージャの終了時に必ず `Set xxx = Nothing` で明示的に解放している。
2. 環境コンテキストの動的評価(INSUNITSと空間)
図面ごとに単位系(ミリ・メートル)や作図空間(モデル・レイアウト)が異なるマルチテナントな設計環境において、ハードコーディングされた係数は毒でしかない。`GetVariable(“INSUNITS”)` を用いてシステム変数を直接ポーリングし、さらに `ActiveSpace` を監視することで、ユーザーがどのタブにいても破綻しない堅牢性を担保している。
3. 保守性と拡張性
計測ロジックを `clsScaleMeasurer` クラスに隠蔽(カプセル化)しているため、将来的に「寸法線オブジェクト自体の公差判定」や「外部ERP/BIMシステムへのJSONエクスポート」といった機能拡張が必要になった際も、エントリーポイント(`modExecution.bas`)を汚すことなく、クラスの内部実装を差し替えるだけで対応可能である。
—
総括
APIが提供する数値を鵜呑みにせず、その背後にある「空間・単位・尺度」という文脈をコードで調停してこそ、真に信頼できる業務自動化システムが完成する。
レガシーなVBAであっても、オブジェクト指向的なカプセル化と厳格なライフサイクル管理を施すことで、モダンなソフトウェア工学に匹敵する堅牢性を手に入れることができる。日々の定型業務に追われるエンジニア諸賢の武器となれば幸いである。
