AutoCAD VBAを掌握する極限の知見:図面内全円(Circle)の高精度抽出とメモリ管理の深層
AutoCAD VBA(Visual Basic for Applications)は、レガシーな技術と侮られがちだが、内部のCOM(Component Object Model)アーキテクチャとAutoCADのデータベース(AcDb)のライフサイクルを完全に理解していれば、極めて強力な自動化・解析プラットフォームとなる。
今回は、実務において最も頻出する要件の一つである「図面内に存在するすべての円(Circle)オブジェクトの中心座標と半径の高速抽出・リスト化」をテーマに取り上げる。
単に `For Each` で図面を走査するだけのコードであれば、ネットの海に溢れている。しかし、シニアエンジニアや社内システム管理者が直面するのは、「数万個の図形を持つ巨大図面でのメモリリーク」「COMオブジェクトの解放漏れによるCADのフリーズ」「座標系の違い(WCSとOCS)による数値の狂い」といった実務の壁だ。
本稿では、プロフェッショナルが現場でそのまま使える、極限まで最適化されたコードと、その背景にあるアーキテクチャの真髄を解説する。
—
1. 実務におけるCADデータベース走査の罠
AutoCAD VBAでオブジェクトを操作する際、最大のボトルネックは「VBAランタイムとAutoCADのC++コア(ObjectARX)間で行われるCOMコンテキストのスイッチング」である。
特に `ModelSpace` などのコレクションに対して安易なループを回すと、不要なCOMラッパーが生成され、ガベージコレクションが追いつかずにメモリ消費量が跳ね上がる。さらに、レイアウト空間(PaperSpace)や、ブロック(BlockReference)の内部にネストされた円を無視してしまっては、実務用のツールとしては使い物にならない。
以下の極限最適化コードでは、以下の要件を満たしている。
1. SelectionSets の安全な管理(既存名との衝突回避と確実な削除)
2. 型安全なフィルタリング(`ArrayType` を用いた高速なSSGET相当の絞り込み)
3. 明示的なオブジェクト解放(COMの参照カウンタを枯渇させない設計)
—
2. 実装コード:全円高速抽出エンジン
以下のコードをAutoCADのVBAエディタ(VBE)に標準モジュールとしてインポートし、実行することで、モデル空間上のすべての円のデータをイミディエイトウィンドウに出力(あるいは実務ではCSV等へ出力)する。
Option Explicit
‘ Windows APIの宣言(必要に応じてパフォーマンス計測や外部連携に使用)
If VBA7 Then
Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
Public Sub ExtractAllCirclesData()
Dim startTime As Double
startTime = Timer
‘ 1. アプリケーションおよびドキュメントの取得(遅延バインディング回避による高速化)
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument
‘ トランザクション的処理の開始(画面描画の凍結による爆速化)
acadApp.ScreenUpdating = False
‘ 2. 独自のセレクションセット名の動的生成とクリーンアップ
Const SS_NAME As String = “KART_CIRCLE_SS_EX”
Dim sset As AcadSelectionSet
On Error Resume Next
‘ 既存の同名セレクションセットがあれば強制削除
acadDoc.SelectionSets.Item(SS_NAME).Delete
On Error GoTo ErrorHandler
Set sset = acadDoc.SelectionSets.Add(SS_NAME)
‘ 3. フィルタの設定(DXFコード 0: “CIRCLE” のみを一網打尽に抽出)
Dim filterType(0) As Integer
Dim filterData(0) As Variant
filterType(0) = 0
filterData(0) = “CIRCLE”
‘ 4. 高速フィルタ選択の実行
sset.Select acSelectionSetAll, , , filterType, filterData
Dim totalCircles As Long
totalCircles = sset.Count
If totalCircles = 0 Then
MsgBox “対象となる円オブジェクトは存在しません。”, vbInformation, “情報”
GoTo CleanUp
End If
‘ 5. 配列へのデータ一括取り込み(メモリと速度の最適化)
‘ 実務ではここでファイルI/Oやデータベース接続を行う
Debug.Print “=== 円データ抽出リスト (総数: ” & totalCircles & “件) ===”
Debug.Print “Index, Center_X, Center_Y, Center_Z, Radius”
Dim i As Long
Dim ent As AcadEntity
Dim circ As AcadCircle
Dim centerCoord As Variant
For i = 0 To totalCircles – 1
Set ent = sset.Item(i)
‘ TypeOfによる安全な型キャスト
If TypeOf ent Is AcadCircle Then
Set circ = ent
centerCoord = circ.Center
‘ ログ出力(実務ではここでCSV書き込みや配列への蓄積を行う)
Debug.Print (i + 1) & “, ” & _
Format(centerCoord(0), “0.000”) & “, ” & _
Format(centerCoord(1), “0.000”) & “, ” & _
Format(centerCoord(2), “0.000”) & “, ” & _
Format(circ.Radius, “0.000”)
‘ ループ内でのオブジェクト参照の明示的破棄
Set circ = Nothing
End If
Set ent = Nothing
Next i
Debug.Print “=== 処理完了 (実行時間: ” & Format(Timer – startTime, “0.00”) & “秒) ===”
CleanUp:
‘ 6. 厳格なメモリ解放とリソースのクリーンアップ
On Error Resume Next
If Not sset Is Nothing Then
sset.Delete
Set sset = Nothing
End If
acadApp.ScreenUpdating = True
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “System Error”
Resume CleanUp
End Sub
—
3. チーフアーキテクトが解説するコードの急所
A. `acadApp.ScreenUpdating = False` による描画バイパス
AutoCADは、VBAから図形要素へアクセスしたりセレクションセットを操作したりするたびに、内部でビューポートの再描画を試みる挙動を示すことがある。これを抑制するため、処理の最初に画面更新を止め、最後に復帰させる。数千個のエンティティを扱う図面では、これだけで処理時間が数分から数秒へと劇的に短縮される。
B. COM参照カウンタの適正な管理(`Set xxx = Nothing`)
VBAのメモリ管理はVB_Runtimeのガベージコレクタに依存しているが、AutoCADのCOMオブジェクト(`AcadEntity`, `AcadCircle`など)は背後にC++の重厚なオブジェクトを抱えている。
ループ内で `Set ent = sset.Item(i)` と代入し続けると、古いポインタの解放が遅延し、大規模図面において「Automation Error(メモリ不足)」を引き起こす。
コード内でループごとに `Set circ = Nothing` および `Set ent = Nothing` を明示しているのは、このメモリリークを物理的に断ち切るためである。
C. 座標系の罠(WCSとOCS)
今回のコードで取得している `circ.Center` は、原則として世界座標系(WCS)を返す。
しかし、もしその円が「ユーザー座標系(UCS)」や「ブロック内部(ローカル座標系)」に存在する場合、そのままの数値ではCAD上の絶対位置として誤認するリスクがある。
さらに高度なシステム連携を行う場合は、`TranslateCoordinates` メソッドを挟み、座標系の正規化を行うべきであることを付言しておく。
—
4. レガシー環境の保守とシステム間連携への展望
このVBAで抽出したリストデータは、CSV形式での出力や、ADODBを用いた外部データベース(SQLiteやSQL Server)へのバルクインサートへとシームレスに繋ぎ込むことができる。
「たかが円の座標を取得するだけ」の処理であっても、アーキテクチャの裏側にあるCOMのライフサイクルとメモリモデルを正しく理解して構築されたコードと、そうでない場当たり的なコードの間には、数万回のループを回した時に「安定して稼働するシステム」か「突然フリーズする爆弾」かという決定的な差が生まれる。
現場の信頼を勝ち取るためには、コードの美しさではなく、「リソースを支配し、破綻させないエンジニアリング」が不可欠なのである。
