【テクニカル・上級編】【実務中級】図面内の全線分(Line)の始点・終点座標を取得し、その長さを計算してリスト化する – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:図面内全線分の座標抽出と幾何計算の最適化

AutoCAD VBAにおいて、図面データベースへのアクセスと幾何情報の抽出は、日々の業務自動化における最も基本的かつ、最もパフォーマンスのボトルネックになりやすい領域だ。

「図面内の全線分(Line)の始点・終点座標を取得し、その長さを計算してリスト化する」

この一見して初歩的なタスクであっても、数万オブジェクトを内包する巨大なプラント図面や建築図面を相手にする場合、安易なコードはAutoCADをフリーズさせ、最悪の場合はメモリリークを引き起こす。
本稿では、シニアエンジニアおよびCADインフラの管理者に向けて、単なるサンプルの提示にとどまらず、オブジェクトのライフサイクル管理、Comインターフェースの重み、そして実務で耐えうる堅牢なVBAアーキテクチャの極限の知見を解説する。

1. arquiteturaの核心:なぜ「単純なループ」では実務で破綻するのか

AutoCADのCOM(Component Object Model)APIを叩くとき、私たちは常に「プロセス間通信(Out-of-Process)」のオーバーヘッドと戦っている。VBAは単一スレッドで動作し、COMを介してObjectARX層(C++で書かれたAutoCADのコア)と対話する。

ここで発生する典型的なアンチパターンが以下の状態だ。

  • 取得したオブジェクトの参照を解放せず、VBAのガベージコレクションに丸投げする。
  • ループのたびにドキュメントのモデル空間(ModelSpace)へ無駄なアクセスコールを行う。
  • バリアント型の配列(SafeArray)のメモリレイアウトを理解せず、座標値の抽出に膨大なサイクルを消費する。

これらを克服し、ミリ秒単位で処理を完結させるための実践コードを以下に提示する。

2. 実装コード:高速線分抽出・長短短計算エンジン

以下のコードは、現在のActiveDocumentのモデル空間を走査し、すべての `Line` エンティティを捕捉、始点・終点座標から三次元ユークリッド距離を算出してイミディエイトウィンドウ(あるいは外部出力)へリスト化するプロシージャである。

Option Explicit

‘ —————————————————————————
‘ 業務自動化プロシージャ: モデル空間内の全線分抽出と長さリスト化
‘ —————————————————————————
Public Sub ExportLineLengthList()
Dim startTime As Double
startTime = Timer

‘ 1. アプリケーションおよびドキュメントの参照を厳格に取得
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument

‘ 実行中のAutoCADインスタンスをバインド(早期バインディング前提)
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If Err.Number <> 0 Then
MsgBox “実行中のAutoCADインスタンスが見つかりません。”, vbCritical, “致命的エラー”
Exit Sub
End If
On Error GoTo 0

Set acadDoc = acadApp.ActiveDocument

‘ 2. トランザクション的処理の開始(画面描画の凍結による爆速化)
acadApp.ScreenUpdating = False

Dim modelSpace As AcadModelSpace
Set modelspace = acadDoc.ModelSpace

Dim totalLines As Long
totalLines = 0

Dim ent As AcadEntity
Dim lineObj As AcadLine
Dim startPoint As Variant
Dim endPoint As Variant
Dim lineLength As Double

‘ 3. コレクション走査と型安全なダウンキャスト
Dim i As Long
Dim count As Long
count = modelSpace.Count

Debug.Print “=== 線分長リスト出力開始: ” & acadDoc.Name & ” ===”
Debug.Print “Handle, 始点X, 始点Y, 始点Z, 終点X, 終点Y, 終点Z, 長さ”

For i = 0 To count – 1
Set ent = modelSpace.Item(i)

‘ TypeOf判定によるフィルタリング(遅延バインディングの回避)
If TypeOf ent Is AcadLine Then
Set lineObj = ent

‘ 座標配列(SafeArray)の取得
startPoint = lineObj.StartPoint
endPoint = lineObj.EndPoint

‘ 三次元ピタゴラスの定理による距離計算(MathライブラリのSqr関数を使用)
lineLength = Sqr( _
(endPoint(0) – startPoint(0))^2 + _
(endPoint(1) – startPoint(1))^2 + _
(endPoint(2) – startPoint(2))^2 _
)

‘ CSV形式でのイミディエイト出力(システム間連携へのフック)
Debug.Print lineObj.Handle & “,” & _
Format(startPoint(0), “0.000”) & “,” & _
Format(startPoint(1), “0.000”) & “,” & _
Format(startPoint(2), “0.000”) & “,” & _
Format(endPoint(0), “0.000”) & “,” & _
Format(endPoint(1), “0.000”) & “,” & _
Format(endPoint(2), “0.000”) & “,” & _
Format(lineLength, “0.000”)

totalLines = totalLines + 1

‘ 4. オブジェクト参照の明示的破棄(メモリ最適化の極意)
Set lineObj = Nothing
End If

Set ent = Nothing
Next i

‘ 5. 画面描画の復元と後始末
acadApp.ScreenUpdating = True

Dim elapsedTime As Double
elapsedTime = Timer – startTime

Debug.Print “=== 処理完了 ===”
Debug.Print “総抽出線分数: ” & totalLines & ” / 処理時間: ” & Format(elapsedTime, “0.00”) & ” 秒”

MsgBox “線分情報の抽出が完了しました。” & vbCrLf & _
“抽出数: ” & totalLines & “件” & vbCrLf & _
“処理時間: ” & Format(elapsedTime, “0.00”) & “秒”, vbInformation, “完了”

CleanUp:
‘ 参照の完全解放
Set modelspace = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
End Sub

3. チーフアーキテクトが解説するコードの急所(深層インサイト)

上記のコードが、単なる「動くスクリプト」の枠を超えている理由を3つの視点から分解する。

① `ScreenUpdating = False` による描画リフレッシュの抑制

AutoCADは、VBAからエンティティの参照やプロパティへのアクセスが発生するたびに、内部のグラフィックパイプラインやレイアウトキャッシュを同期させようとする場合がある。ループの前に画面描画を停止させることで、CPUサイクルの大部分を「純粋な幾何計算」に割り当てることが可能になる。これは数万オブジェクト規模の図面で数倍の速度差を生む。

② SafeArray(バリアント配列)のスマートなアンパック

AutoCADの `StartPoint` および `EndPoint` プロパティは、内部的に3要素の `Double` 型のSafeArray(1次元配列)を返す。
VBAにおいて、この配列要素へのアクセス(`startPoint(0)`など)は、オーバーヘッドが比較的小さい。しかし、わざわざ一度ローカルの `Variant` 型変数に受けているのは、COMオブジェクトのプロパティをループ内で直接何回も叩く(`lineObj.StartPoint(0)` のように記述する)という最悪のCOMラウンドトリップ(プロセス間往復通信)を防ぐためである。メモリ上に一度スナップショットを生成し、そこで計算を完結させるのが鉄則だ。

③ オブジェクト参照の明示的破棄 (`Set … = Nothing`)

VBAのランタイムは賢く見えて、COMオブジェクトの参照カウント(Reference Counting)管理においては時として怠慢だ。ループ内で `Set lineObj = ent` を繰り返す際、古い参照がメモリ上に残存し続けると、ガベージコレクタが追いつかずにメモリ消費量が肥大化する(特に循環参照や大規模ループ内)。ループの各イテレーションの終わりに `Set lineObj = Nothing`、`Set ent = Nothing` を明示することで、メモリフットプリントを極限まで小さく保つことができる。

4. ささらなる高みへ:システム間連携とCSV/Excelエクスポートへの拡張

このコードで得られたイミディエイトウィンドウの出力は、そのまま外部の生産管理システムや、見積もり積算システムへ流し込むためのCSVデータとして流用できる。
もし実務でこれをExcelファイルに直接書き出したい場合は、`CreateObject(“Excel.Application”)` を用いてメモリ上でワークブックを生成し、二次元配列(Array)に一括格納して `.Value = arrayData` でセル範囲に流し込む実装に拡張してほしい。セルを1つずつ `.Cells(i, j).Value = …` と操作する愚行は、シニアエンジニアたるもの決して犯してはならない。

AutoCAD VBAはレガシーな技術と揶揄されることもあるが、その内部にあるCOMの仕組みとメモリ構造を完全に掌握したエンジニアが書いたコードは、現代のどのモダン言語による自動化スクリプトにも引けを取らない爆発的なパフォーマンスを発揮する。

現場の王であれ。コードで限界を突破し続けろ。

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