【テクニカル・上級編】図面内のジオメトリ情報(頂点、中心点など)を抽出!AcadEntityのCoordinatesプロパティ – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:`Coordinates`プロパティの深層とジオメトリ抽出の極意

AutoCAD VBAにおけるオブジェクトモデルの最深部に踏り込むとき、我々は避けて通れない壁に直面する。それが、図形が内包する生(生)の幾何学データ、すなわち`Coordinates`プロパティの挙動の非対称性である。

世の初級チュートリアルでは、「線分は始点と終点、ポリラインは頂点配列を返す」といった表層的な説明で終わる。しかし、シニアエンジニアや社内システムアーキテクトが対峙する現場――数百万ポリゴン規模の巨大プラント図面、外部BIM/CIMシステムとのリアルタイム連携、あるいはレガシーなCADデータからの一括解析――において、この`Coordinates`の仕様を誤解することは、メモリリーク、COMラッパーの肥大化、そして致命的なパフォーマンス低下を意味する。

本稿では、`AcadEntity`のジオメトリ抽出における真のメカニズムと、極限まで最適化されたVBAコードの実装パターンを詳解する。

1. `Coordinates`プロパティの非対称性とメモリ構造の真実

`Coordinates`プロパティは、一見すると「すべての図形座標を返す便利なプロパティ」に見える。しかし、AutoCADのCOM(Component Object Model)アーキテクチャの内部において、このプロパティが返すVariant型配列の構造は、対象とする図形(Entity)の型によって完全にその性格を変える

型によるデータの変容

  • `AcadLine`: 単なる2点(始点・終点)の3次元座標の連続であり、`Double型配列(要素数6: X1, Y1, Z1, X2, Y2, Z2)`を返す。
  • `AcadCircle` / `AcadArc`: `Coordinates`は中心点を返す(厳密には`Center`プロパティと同等だが、APIのインターフェース都合でバリアント配列として振る舞うことがある)。
  • `AcadLWPolyline` (軽量ポリライン): 2次元座標の連続値(X1, Y1, X2, Y2…)を返す。Z座標は含まれない。
  • `AcadPolyline` (3Dポリライン/旧型): 3次元座標の連続値(X1, Y1, Z1, X2, Y2, Z2…)を返す。

この「2Dと3Dの混在」「Variant配列のアンマーシャリングコスト」を意識せずループを回すと、AutoCADのプロセス(`acad.exe`)とVBAホスト間のCOMプロキシ通信がボトルネックとなり、処理速度が数分単位で悪化する。

2. 実装:高速ジオメトリ抽出エンジン

以下のコードは、図面内の全エンティティを走査し、`Coordinates`および関連プロパティから安全かつ高速に幾何学情報を抽出する実用的なクラスモジュール、あるいはプロシージャの設計パターンである。

COMオブジェクトの参照解放(Garbage Collectionの明示的制御)と、Variant配列のメモリバッファへのダイレクトアクセスを意識した実装に注目してほしい。

Option Explicit

‘ =========================================================================
‘ 模範的ジオメトリ抽出プロシージャ
‘ 対象: AcadLine, AcadLWPolyline, AcadCircle
‘ =========================================================================
Public Sub ExtractGeometryData()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim ent As AcadEntity
Dim sset As AcadSelectionSet
Dim i As Long

‘ エラーハンドリングによるCOMオブジェクトリーク防止
On Error GoTo ErrorHandler

‘ アクティブなAutoCADセッションへのバインド
Set acadApp = ThisDrawing.Application ‘ または GetObject(, “AutoCAD.Application”)
Set acadDoc = acadApp.ActiveDocument

‘ 効率的なセレクションセットの生成と管理(既存名義の衝突回避)
On Error Resume Next
acadDoc.SelectionSets.Item(“GEOMETRY_EXTRACTOR”).Delete
On Error GoTo ErrorHandler

Set sset = acadDoc.SelectionSets.Add(“GEOMETRY_EXTRACTOR”)

‘ 図面内の全図形を一括選択(必要に応じてフィルターを拡張)
sset.Select acSelectionSetAll

If sset.Count = 0 Then
MsgBox “抽出対象の図形が存在しません。”, vbExclamation
GoTo Cleanup
End If

‘ ログ出力用(イミディエイトウインドウ)
Debug.Print “— ジオメトリ抽出開始 (総数: ” & sset.Count & “件) —”

For i = 0 To sset.Count – 1
Set ent = sset.Item(i)

Select Case UCase$(ent.ObjectName)
Case “ACDBLINE”
ProcessLine ent

Case “ACDBPOLYLINE”, “ACDBLWPOLYLINE”
ProcessPolyline ent

Case “ACDBCIRCLE”
ProcessCircle ent

Case Else
‘ 未知または対象外のエンティティ
End Select

‘ 毎ループのオブジェクト参照破棄はVBAではオーバーヘッドだが、
‘ 大規模図面ではメモリ断片化を防ぐために適宜変数をリセット
Set ent = Nothing
Next i

Debug.Print “— ジオメトリ抽出完了 —”

Cleanup:
‘ 厳格なオブジェクトの解放(COM参照カウンタのデクリメント)
If Not sset Is Nothing Then
sset.Delete
Set sset = Nothing
End If
Set acadDoc = Nothing
Set acadApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
Resume Cleanup
End Sub

‘ ————————————————————————-
‘ 個別エンティティハンドラ:Line
‘ ————————————————————————-
Private Sub ProcessLine(ByRef ent As AcadEntity)
Dim lineObj As AcadLine
Dim coords As Variant

Set lineObj = ent
coords = lineObj.Coordinates ‘ Variant型配列(X1, Y1, Z1, X2, Y2, Z2)

‘ 外部システム連携用フォーマット出力
Debug.Print “Line -> 始点:(” & coords(0) & “, ” & coords(1) & “, ” & coords(2) & _
“) 終点:(” & coords(3) & “, ” & coords(4) & “, ” & coords(5) & “)”

Set lineObj = Nothing
End Sub

‘ ————————————————————————-
‘ 個別エンティティハンドラ:Polyline (LWPolyline含む)
‘ ————————————————————————-
Private Sub ProcessPolyline(ByRef ent As AcadEntity)
Dim plineObj As AcadLWPolyline
Dim coords As Variant
Dim vertexCount As Long
Dim j As Long

‘ ※AcadPolylineの場合は AcadPolyline にキャストを変更すること
Set plineObj = ent
coords = plineObj.Coordinates

‘ 2次元配列の場合、要素数は 2 頂点数 となる
vertexCount = (UBound(coords) – LBound(coords) + 1) \ 2

Debug.Print “Polyline (Handle: ” & plineObj.Handle & “) 頂点数: ” & vertexCount

For j = 0 To UBound(coords) Step 2
‘ coords(j) = X座標, coords(j+1) = Y座標
‘ Debug.Print ” Vertex[” & (j \ 2) & “] = X:” & coords(j) & “, Y:” & coords(j+1)
Next j

Set plineObj = Nothing
End Sub

‘ ————————————————————————-
‘ 個別エンティティハンドラ:Circle
‘ ————————————————————————-
Private Sub ProcessCircle(ByRef ent As AcadEntity)
Dim circleObj As AcadCircle
Dim centerPt As Variant

Set circleObj = ent
centerPt = circleObj.Center

Debug.Print “Circle -> 中心:(” & centerPt(0) & “, ” & centerPt(1) & “, ” & centerPt(2) & _
“) 半径:” & circleObj.Radius

Set circleObj = Nothing
End Sub

3. シニアエンジニアが知るべき「罠」と最適化の極意

実務において上記のコードをさらにスケールさせる場合、以下のアーキテクチャ上の制約と対策を考慮しなければならない。

① Variant配列の境界(`LBound` / `UBound`)の保証

AutoCADのCOMインターフェースから返される `Coordinates` 配列は、言語仕様上 0ベース(`LBound`が常に0) であることが保証されているが、プロセスの言語環境やバインド方法によっては予期せぬオフセットを持つケースが稀にある。配列を走査する際は、必ず `LBound(coords)` と `UBound(coords)` を動的に評価し、ハードコーディングを避けること。

② 大規模図面におけるガベージコレクションとメモリ肥大化

VBAのランタイムは、COMオブジェクトのラッパーが解放されるタイミングが曖昧である。数万件のエンティティをループ内で処理すると、VBA内部のメモリリークによりAutoCAD本体がクラッシュすることがある。
これを防ぐため、1000件処理するごとに `DoEvents` を挟み、明示的にローカル変数のオブジェクト参照を `Nothing` にクリアする防衛的プログラミングが必須となる。

③ 外部システム(C# / Database)へのバルク転送

抽出したジオメトリデータをCSVやJSON、あるいはRDBにインサートする場合、VBAのループ内で1件ずつI/Oを行うのは悪手である。
配列データを一度メモリ上の2次元バッファ(あるいはScripting.Dictionary)に蓄積し、ADO(ActiveX Data Objects)を用いてトランザクション一括(Bulk Insert)で外部DBへ流し込むアーキテクチャを採用すべきだ。

総括

AutoCAD VBAは、レガシーと揶揄されることもあるが、CAD内部の生データに最もダイレクトかつ軽量にアクセスできる極めて強力なインターフェースである。
`Coordinates`プロポティの仕様の裏側にあるメモリ構造、型ごとの差異、そしてCOMのライフサイクルを完全に掌握したエンジニアにとって、AutoCADは単なる製図ソフトではなく、「高度な空間データを内包する巨大なデータベース」へと変貌する。

この知見をあなたの自動化パイプラインに組み込み、システム全体の堅牢性とパフォーマンスを極限まで高めてほしい。

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