AutoCAD VBAを掌握する極限の知見:図面内の全線分解析と高精度リスト化の全技術
業務自動化エンジニアの私から、現場で戦う君たちへ問いたい。
「図面内にあるすべての線分(Line)の長さを集計し、リスト化してくれ」と言われた時、君はどのようなコードを書くだろうか?
ネットの海を漂うサンプルコードをコピペし、`For Each` でモデル空間をしらみつぶしに舐めるようなコードを書いて満足していないか? もしそうなら、そのコードは中規模以上の実務図面(要素数が数十万を超えるようなデータ)にブチ当たった瞬間、メモリリークを起こすか、永遠に終わらないフリーズの海に沈むことになる。
AutoCAD VBAにおいて、オブジェクトモデルの挙動、特に「コレクションの走査コスト」と「座標計算の数学的アプローチ」を理解していないコードは、実務で使い物にならない。
今回は、実務の現場で絶対に破綻しない、堅牢性・高速性・拡張性を極限まで高めた「全線分座標取得・長さ計算エンジン」の設計思想と実装を、私の知見のすべてを込めて伝授する。
—
1. なぜ素朴な実装では実務で破綻するのか?
多くの初級・中級プログラマが陥る罠は、以下のような構造のコードを平然と書いてしまうことだ。
‘ 【アンチパターン】絶対にやってはいけない例
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
If ent.ObjectName = “AcDbLine” Then
‘ 処理…
End If
Next ent
このアプローチがなぜ「悪」なのか。理由は3つある。
1. `ObjectName` による文字列比較のオーバーヘッド:
文字列の比較はCPUコストが高い。これを数万回のループ内で実行すると、それだけで甚大なロスになる。
2. COMオブジェクトのラップコスト:
`For Each` による遅延バインディング的な走査は、AutoCADの内部COMレイヤーとVBA間で無駄な通信が発生し、実行速度を著しく殺す。
3. エラーハンドリングの欠如:
画層(Layer)がロックされている、あるいはプロキシオブジェクトが混入している図面では、このループは容赦なく実行時エラーでクラッシュする。
プロのエンジニアであれば、「型フィルター(SelectionSets)」を活用し、最初から目的のオブジェクトだけをメモリ上に高速抽出する設計にすべきだ。
—
2. 堅牢なプロダクションコード:全線分抽出&長さ計算エンジン
ここに示すのは、実務の現場でそのままデプロイできるプロダクションコードだ。
選択セットの動的生成・確実なクリーンアップ(メモリ管理)、そして三次元空間におけるユークリッド距離の算出を完璧に網羅している。
Option Explicit
‘ =================================================================itten
‘ モジュール名: modLineAnalyzer
‘ 概要: モデル空間内の全線分(Line)の始点・終点座標と長さを取得し、イミディエイト窓に出力する
‘ 著者: チーフアーキテクト
‘ =================================================================
Public Sub ExportAllLineLengths()
Dim sset As AcadSelectionSet
Dim ssetName As String
ssetName = “LineAnalyzerSet_” & Format(Now, “hhmmss”)
‘ 1. 堅牢な選択セットの生成(既存重複のパージ処理を含む)
On Error Resume Next
Set sset = ThisDrawing.SelectionSets.Add(ssetName)
If Err.Number <> 0 Then
‘ 既に同名のセットが存在する場合は一度削除して再作成
ThisDrawing.SelectionSets.Item(ssetName).Delete
Set sset = ThisDrawing.SelectionSets.Add(ssetName)
End If
On Error GoTo 0
‘ 2. フィルターの設定(AcDbLineのみをターゲットにする)
Dim filterType(0) As Integer
Dim filterData(0) As Variant
filterType(0) = 0 DXFグループコード:オブジェクトタイプ
filterData(0) = “LINE”
‘ 3. 高速一括選択(SelectionSet.Select 全体スキャン)
sset.Select acSelectionSetAll, , , filterType, filterData
If sset.Count = 0 Then
MsgBox “対象となる線分(Line)は見つかりませんでした。”, vbInformation, “処理終了”
sset.Delete
Exit Sub
End If
‘ 4. データ抽出と解析処理
Dim ent As AcadEntity
Dim lineObj As AcadLine
Idm startPoint As Variant
Dim endPoint As Variant
Dim length3D As Double
Dim totalLength As Double
totalLength = 0#
Debug.Print “— 線分解析レポート —”
Debug.Print “ハンドル, 始点X, 始点Y, 始点Z, 終点X, 終点Y, 終点Z, 長さ”
Dim i As Long
For i = 0 To sset.Count – 1
Set ent = sset.Item(i)
‘ 念のため型安全なキャストを確認
If TypeOf ent Is AcadLine Then
Set lineObj = ent
‘ 座標の取得(Variant型配列が返る)
startPoint = lineObj.StartPoint
endPoint = lineObj.EndPoint
‘ 長さの取得(AutoCADのネイティブプロパティを利用するのが最も正確)
length3D = lineObj.Length
totalLength = totalLength + length3D
‘ ログ出力(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(length3D, “0.000”)
End If
Next i
Debug.Print “————————”
Debug.Print “総線分数: ” & sset.Count & ” / 総延長: ” & Format(totalLength, “0.000”)
‘ 5. メモリの解放(非常に重要:AutoCAD VBAの基本作法)
sset.Delete
MsgBox “解析が完了しました。イミディエイト窓を確認してください。” & vbCrLf & _
“総延長: ” & Format(totalLength, “0.000”), vbInformation, “完了”
End Sub
—
3. コードの急所:プロが押さえるべきアーキテクチャの解説
上記のコードには、実務で生き残るための「技術的布石」がいくつも埋め込まれている。その核心を解説しよう。
① 選択セット(SelectionSet)のライフサイクル管理
AutoCAD VBAにおいて、`SelectionSets` は明示的に削除(`Delete`)しないと、図面ファイル内にゴミとして残り続ける。次に同じマクロを実行した際に「既に存在します」という致命的なエラー(Runtime Error 457)を引き起こす原因になる。
コード内で行っているように、生成前のトラップと、処理終了後の確実な `Delete` は、VBA開発における絶対の鉄則である。
② 距離計算のスマートなアプローチ
「始点と終点の三平方の定理(ピタゴラスの定理)を使って自分で計算すべきか?」という疑問を持つ者がいるが、答えは「NO」だ。
`AcadLine.Length` プロパティが存在する以上、それを素直に利用すべきである。AutoCADの内部エンジンが計算した値をそのまま取得する方が、丸め誤差の観点からも、処理速度の観点からも圧倒的に有利である。
※もし手動で二点間の距離を求める必要がある場合は、以下のように `Sqr` 関数を用いる(Z軸を含む3次元空間を想定すること)。
‘ 参考:数学的に二点間距離を計算する場合のロジック
length3D = Sqr((endPoint(0) – startPoint(0))^2 + _
(endPoint(1) – startPoint(1))^2 + _
(endPoint(2) – startPoint(2))^2)
③ Variant型の配列座標
AutoCADの `StartPoint` および `EndPoint` は、`Double` 型の3要素配列(X, Y, Z)として返される。VBAではこれを `Variant` 型で受けることになる。
`startPoint(0)` が X座標、`startPoint(1)` が Y座標、`startPoint(2)` が Z座標となる。2次元図面であっても Z軸の値(通常は 0 または現在の標高)が含まれるため、インデックスの指定ミスには十分に注意してほしい。
—
4. 実務への拡張:CSVファイル出力へのトランスフォーメーション
イミディエイト窓に出力するだけでは、設計部隊や生産管理部隊への納品物としては不十分だ。これを実務のデータベース連携やExcel集計に直結させるため、CSVファイルへの直接書き出しへと拡張する知見を与えよう。
ファイルシステムオブジェクト(FSO: `Scripting.FileSystemObject`)を使用し、図面と同階層に自動でCSVを生成するロジックを組み込む。
‘ — ファイル出力の追加スニペット —
Dim fso As Object
Dim ts As Object
Dim csvPath As String
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 図面と同じフォルダに “LineList_図面名.csv” を吐き出す
csvPath = ThisDrawing.Path & “\LineList_” & fso.GetBaseName(ThisDrawing.Name) & “.csv”
Set ts = fso.CreateTextFile(csvPath, True)
ts.WriteLine “Handle,StartX,StartY,StartZ,EndX,EndY,EndZ,Length”
‘ (ループ内で `ts.WriteLine` を実行してデータを流し込む)
ts.Close
Set ts = Nothing
Set fso = Nothing
このひと手間で、VBAマクロは単なる「お遊びの自動化」から、「業務プロセスを根底から変える基幹ツール」へと昇華する。
—
総括
AutoCAD VBAは、レガシーな言語であるとやゆされることもある。しかし、オブジェクトモデルの挙動を完全に掌握し、メモリ管理とアルゴリズムの最適化を行えば、今日のモダンな開発言語に引けを取らない圧倒的な処理速度と実用性を叩き出すことができる。
今回伝授した「選択セットによる一括フィルタリング」と「厳格なオブジェクト解放」の作法を、君たちの開発現場のスタンダードにしてほしい。プロフェッショナルとしての誇りを持ったコードこそが、君の仕事を真に価値あるものに変えるのだ。
