AutoCAD VBAを掌握する極限の知見:幾何情報抽出の極意と堅牢な数量算出演算
開発現場のエンジニア諸君。日々の図面管理や設計検証で、図面上の線分長やポリラインの面積を一つひとつ「プロパティウィンドウで確認してExcelに転記する」という、前世紀のような泥臭い作業に時間を奪われてはいないか?
もし君が、数千オブジェクトを含むマスター図面から物量(数量)を算出しなければならない立場なら、手作業など論外だ。AutoCAD VBAを駆使し、瞬時に幾何情報を吸い上げる自動化ロジックを構築しなければならない。
今回は、AutoCADの根幹である `AcadDocument` と、知られざる罠が潜む幾何情報の取得メソッドについて、我々プロのアーキテクトが実践している「バグを踏まないための極限の設計思想」を授けよう。
—
1. なぜ「泥臭いコード」は現場で爆発するのか?
多くの初学者が書く図面解析マクロは、大抵こういう構造をしている。
‘ 【アンチパターン】絶対に真似してはならない脆弱なコード
Sub BadExample()
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
If ent.ObjectName = “AcDbLine” Then
Debug.Print ent.Length
End If
Next
End Sub
一見、動くように見えるだろう。しかし、このコードは大規模図面や実務の現場ではゴミ同然だ。理由は3つある。
1. トランザクションとコレクションの評価コスト: `ModelSpace` を直接イテレートするのは重い。さらに `ObjectName` の文字列比較は実行速度の観点から褒められたものではない。
2. レイヤや画層フィルターの欠落: 実務の図面には、非表示画層(Frozen/Off)のオブジェクトや、ビューポート内の不要な線分も混ざっている。これらを無視して計算すると、検図で致命的なミスを誘発する。
3. エラーハンドリングの欠如: 閉じていないポリラインに対して `.Area` プロパティを叩いた瞬間、AutoCADは容赦なく実行時エラーを吐いて沈黙する。
プロのエンジニアが目指すべきは、「高速」「正確」「例外に強い」の3拍子が揃ったプロダクションコードだ。
—
2. 幾何情報抽出のアーキテクチャ設計
線分長(Length)と面積(Area)を安全に抽出するためには、以下の要件を満たす設計が必要となる。
- Typeof 演算子による厳密な型安全: 文字列比較ではなく、VBAの `TypeOf` を用いてメモリ上で安全に型を判定する。
- 図形ごとの特性の把握:
- `AcadLine`: `.Length` が直接取得可能。
- `AcadLWPolyline` / `AcadPolyline`: 線分長は `.Length`、閉じた図形(`.Closed = True`)であれば `.Area` が取得可能。
- `AcadCircle` / `AcadHatch`: 面積や周囲長を特殊なプロパティで保持。
これらを綺麗にカプセル化し、実務でそのまま使える堅牢なモジュールを提示しよう。
—
3. 【コピペ即実戦投入可】全自動・高精度数量算出エンジン
以下のコードは、アクティブ図面のモデル空間からすべての「線分(Line)」と「閉じたポリライン(LWPolyline)」を走査し、それぞれの総延長と総面積をイミディエイトウィンドウに出力、さらにCSV出力やExcel連携の基礎となるデータ集計を行う実用コードだ。
Option Explicit
‘ ==============================================================================
‘ 模块名: MdlGeometryExtractor
‘ 概要 : 図面内の線分長および閉じた領域の面積を安全かつ高速に算出する
‘ 著者 : チーフアーキテクト
‘ ==============================================================================
Public Sub ExtractDrawingMetrics()
‘ 1. 高速化のための環境退避
Dim oldAccel As Boolean
Dim oldOsmode As Long
oldAccel = ThisDrawing.Application.Preferences.System.DisplayRasterImage ‘ 画面描画制御の代用として
‘ 画面更新を停止してパフォーマンスを極限まで引き上げる
ThisDrawing.Application.Update
On Error GoTo ErrorHandler
Dim ent As AcadEntity
Dim totalLineLength As Double
Dim totalPolyArea As Double
Dim totalPolyLength As Double
Dim lineCount As Long
Dim polyCount As Long
totalLineLength = 0#
totalPolyArea = 0#
totalPolyLength = 0#
lineCount = 0
polyCount = 0
‘ 2. モデル空間の走査
Dim ws As AcadModelSpace
Set ws = ThisDrawing.ModelSpace
For Each ent In ws
‘ 画層がロックされている、または非表示の場合はスキップする実務的配慮
If IsLayerActive(ent.Layer) Then
‘ A. 線分 (Line) の処理
If TypeOf ent Is AcadLine Then
totalLineLength = totalLineLength + ent.Length
lineCount = lineCount + 1
‘ B. 軽量ポリライン (LWPolyline) の処理
ElseIf TypeOf ent Is AcadLWPolyline Then
‘ 閉じたポリラインのみ面積を計算対象にする
If ent.Closed Then
totalPolyArea = totalPolyArea + ent.Area
totalPolyLength = totalPolyLength + ent.Length
polyCount = polyCount + 1
Else
‘ 閉じていない場合は長さのみ加算する場合の処理
totalPolyLength = totalPolyLength + ent.Length
End If
‘ C. 3Dポリライン (Polyline) の処理
ElseIf TypeOf ent Is AcadPolyline Then
If ent.Closed Then
totalPolyArea = totalPolyArea + ent.Area
totalPolyLength = totalPolyLength + ent.Length
polyCount = polyCount + 1
Else
totalPolyLength = totalPolyLength + ent.Length
End If
End If
End If
Next ent
‘ 3. 結果の出力 (実務ではここでExcelへの書き出しやCSV保存を行う)
Debug.Print “==========================================”
Debug.Print ” 図面数量算出レポート ”
Debug.Print “==========================================”
Debug.Print “対象図面 : ” & ThisDrawing.Name
Debug.Print “——————————————”
Debug.Print “【線分 (Line)】”
Debug.Print ” 総本数 : ” & lineCount & ” 本”
Debug.Print ” 総延長 : ” & Format(totalLineLength, “#,
0.00″) & ” mm”
Debug.Print “——————————————”
Debug.Print “【閉じたポリライン (Closed Polyline)】”
Debug.Print ” 総図形数 : ” & polyCount & ” 面”
Debug.Print ” 総周囲長 : ” & Format(totalPolyLength, “#,
0.00″) & ” mm”
Debug.Print ” 総面積 : ” & Format(totalPolyArea, “#,
0.00″) & ” mm² (” & Format(totalPolyArea / 1000000#, “#,##0.04″) & ” m²)”
Debug.Print “==========================================”
MsgBox “数量算出し、イミディエイトウィンドウに出力しました。”, vbInformation, “処理完了”
CleanUp:
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub
‘ ==============================================================================
‘ 補助関数: 指定された画層が有効(表示・フリーズ解除)か判定する
‘ ==============================================================================
Private Function IsLayerActive(layerName As String) As Boolean
Dim lyr As AcadLayer
On Error GoTo Catch
Set lyr = ThisDrawing.Layers(layerName)
‘ フリーズしている、または非表示の場合はFalse
If lyr.Freeze = True Or lyr.LayerOn = False Then
IsLayerActive = False
Else
IsLayerActive = True
End If
Exit Function
Catch:
‘ 画層が存在しないなどの例外時は安全側に倒してFalse
IsLayerActive = False
End Function
—
4. チーフアーキテクトからの実務アドバイス
このコードを現場に導入するにあたり、以下の2点を心に留めておいてほしい。
1. 単位系の意識(図面単位の罠):
AutoCADの図面は「mm」で描かれているとは限らない(「inch」や「m」の場合もある)。面積や長さを集計する際、外部データベースやExcelと連携するならば、`ThisDrawing.GetVariable(“MEASUREMENT”)` や図面単位の係数を考慮したスケーリングロジックを前段に噛ませるべきだ。
2. 大規模図面におけるパフォーマンス:
オブジェクト数が10万を超えるような極めて重い図面の場合、`For Each` による総なめ走査は数秒のフリーズを生む。その場合は、`SelectionSet`(選択セット)を用いてあらかじめ画層やオブジェクトタイプでフィルタリングした上で取得する設計にブラッシュアップせよ。
理論と実務を繋ぐのは、こうした「細部へのこだわり」だ。コピペで終わらせず、自社の図面規約に合わせてこのコードを深化させ、君のチームの生産性を圧倒的な高みへと引き上げてくれ。
