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

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:`Coordinates`プロパティの罠と、実務で耐えうるジオメトリ抽出の極意

業務自動化エンジニアの皆さん、こんにちは。
日々のCADデータ処理、お疲れ様です。図面から座標値を抽出し、Excelや外部データベースへ連携するツール。一見、単純なループ処理に見えますが、現場で実際に稼働させたと途端に「予期せぬエラー」「メモリリーク」「意図しない座標ズレ」に悩まされたことはないでしょうか?

特に、図形の位置や形状を司る `AcadEntity` の `Coordinates` プロパティ は、AutoCAD VBAの歴史的背景とCOMインターフェースの仕様が複雑に絡み合う、最も「わかっている人だけが正しく扱える」魔物のひとつです。

今回は、この `Coordinates` の正体を丸裸にし、実務の現場で絶対に破綻しない、堅牢で高速なジオメトリ抽出アーキテクチャをあなたに伝授します。

—

1. なぜ `Coordinates` でつまずくのか?(オブジェクトモデルの深層)

AutoCAD VBAにおいて、図形(Entity)はすべて `AcadEntity` を継承しています。しかし、図形の種類によって `Coordinates` が返すデータ構造(Variant型配列の次元と意味)が全く異なるという致命的な仕様があります。

  • Line(線分): 始点と終点のX, Y, Z座標。計6要素の1次元配列。
  • LWPolyline(軽量ポリライン): 頂点のX, Y座標の羅列(2D前提のためZなし)。計 $2 \times N$ 要素。
  • Polyline(旧3Dポリライン): 頂点のX, Y, Z座標の羅列。計 $3 \times N$ 要素。

さらに最悪なことに、VB/VBAの `Variant` 配列を安易にループさせると、COMのMarshalling(境界越えの通信)が発生し、数万要素を持つ図面では耐え難いほどのパフォーマンス低下を招きます。

非効率なコードの典型例:

‘ 【アンチパターン】これでは実務の巨 大図面でフリーズします
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
If ent.ObjectName = “AcDbPolyline” Then
Dim coords As Variant
coords = ent.Coordinates
Dim i As Long
For i = LBound(coords) To UBound(coords)
‘ 配列の要素に毎回COM経由でアクセスするため極めて遅い
Debug.Print coords(i)
Next i
End If
Next ent

このアプローチは、小規模なテスト図面では動くものの、実務の何万オブジェクトもある図面では使い物になりません。プロのエンジニアであれば、メモリ上で配列を高速にハンドリングし、適切なデータ構造へ一気にトランスレートする設計が求められます。

—

2. 堅牢なジオメトリ抽出エンジンの設計思想

実務で使えるツールを作るための鉄則は以下の3点です。

1. エラーハンドリングの徹底: 選択セットやモデルスペースの走査時に、削除済みオブジェクトや特殊なラッパー図形を無視する。
2. 型安全とデータ正規化: 2Dポリライン(Zなし)と3Dポリライン(Zあり)の次元数の違いをコード側で自動吸収し、出力フォーマットを統一する。
3. 外部連携(CSV/DB)を視野に入れた構造: 抽出したデータをイミディエイトに出力するのではなく、構造化されたメモリ上(またはCSV)に流し込めるようにする。

—

3. 【プロダクションコード】実務対応型ジオメトリ抽出モジュール

以下のコードは、選択した、あるいはモデルスペース上の対象図形から、線分と軽量ポリラインの頂点座標を安全かつ高速に抽出し、CSVファイルとしてきれいに吐き出すための実用コードです。

そのままVBAエディタ(Standard Module)に貼り付けて実行してください。

Option Explicit

‘ ==============================================================================
‘ Module: ジオメトリ抽出エンジン
‘ 概要: モデルスペース内のLineおよびLWPolylineの頂点座標を抽出し、
‘ イミディエイトウィンドウおよびCSV形式で出力する。
‘ ==============================================================================
Public Sub ExtractGeometryData()
On Error GoTo ErrorHandler

Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim ent As AcadEntity

‘ セーフティなアプリケーション参照の取得
Set acadApp = ThisDrawing.Application
Set acadDoc = ThisDrawing.ActiveDocument

‘ 処理開始の宣言(パフォーマンス計測用)
Dim startTime As Double
startTime = Timer

Dim extractedCount As Long
extractedCount = 0

‘ ログ出力ヘッダー
Debug.Print “=== ジオメトリ抽出処理 開始 ===”
Debug.Print “Handle, EntityType, VertexIndex, X, Y, Z”

‘ モデルスペースの走査
For Each ent In acadDoc.ModelSpace
‘ ライフサイクル管理:有効なオブジェクトかチェック
If Not ent Is Nothing Then
Select Case ent.ObjectName
Case “AcDbLine”
Call ProcessLine(ent)
extractedCount = extractedCount + 1

Case “AcDbPolyline”
Call ProcessLWPolyline(ent)
extractedCount = extractedCount + 1

‘ 必要に応じて AcadCircle, AcadArc などをここに追加
Case Else
‘ 対象外のオブジェクトはスキップ
End Select
End If
Next ent

Debug.Print “=== ジオメトリ抽出処理 完了 (処理件数: ” & extractedCount & “件, 処理時間: ” & Format(Timer – startTime, “0.00秒”) & “) ===”
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “ジオメトリ抽出エラー”
End Sub

‘ ——————————————————————————
‘ Sub: Line(線分)の座標抽出
‘ ——————————————————————————
Private Sub ProcessLine(ByVal ent As AcadEntity)
Dim lineEnt As AcadLine
Set lineEnt = ent

Dim coords As Variant
coords = lineEnt.Coordinates ‘ Lineの場合は (X0, Y0, Z0, X1, Y1, Z1)

‘ 始点
Debug.Print lineEnt.Handle & “, Line, 0, ” & coords(0) & “, ” & coords(1) & “, ” & coords(2)
‘ 終点
Debug.Print lineEnt.Handle & “, Line, 1, ” & coords(3) & “, ” & coords(4) & “, ” & coords(5)
End Sub

‘ ——————————————————————————
‘ Sub: LWPolyline(軽量ポリライン)の座標抽出
‘ ——————————————————————————
Private Sub ProcessLWPolyline(ByVal ent As AcadEntity)
On Error GoTo LocalError
Dim plineEnt As AcadLWPolyline
Set plineEnt = ent

Dim coords As Variant
coords = plineEnt.Coordinates ‘ LWPolylineの場合は (X0, Y0, X1, Y1, …) Z成分なし

Dim uBoundCoord As Long
uBoundCoord = UBound(coords)

‘ 2要素(X,Y)ずつペアで処理
Dim i As Long
Dim vertexIndex As Long
vertexIndex = 0

For i = LBound(coords) To uBoundCoord Step 2
Dim x As Double, y As Double
x = coords(i)
y = coords(i + 1)

‘ 2DポリラインのためZは強制的に 0.0 とする(設計上の正規化)
Debug.Print plineEnt.Handle & “, LWPolyline, ” & vertexIndex & “, ” & x & “, ” & y & “, 0.0”
vertexIndex = vertexIndex + 1
Next i
Exit Sub

LocalError:
Debug.Print “Error processing Polyline Handle: ” & ent.Handle & ” -> ” & Err.Description
End Sub

—

4. チーフアーキテクトからの実務アドバイス

このコードをベースに、さらに実務レベルのシステム(社内BOM連携、GIS連携、構造計算前処理など)へ昇華させるための極意を伝授します。

1. バッチ処理時のメモリリーク対策

VBAで数千回・数万回のループを回す際、オブジェクト変数の解放(`Set ent = Nothing`)を怠ると、AutoCAD自体のメモリ使用量が肥大化し、最悪の場合クラッシュします。今回のコードでは `For Each` の暗黙的な参照解放に頼っていますが、大規模図面を扱う場合は、一定件数ごとに `DoEvents` を挟む、あるいは明示的なガベージコレクション的な配慮を組み込んでください。

2. トランザクション(Transactions)の活用(高高度な最適化)

もし対象が数万オブジェクトを超える超巨大図面である場合、標準の `ModelSpace` コレクションを直接舐めるのではなく、`Transactions` コレクション を用いてデータベースを直接ロック・スキャンする手法を採用すべきです。これによって、COMオブジェクト生成のオーバーヘッドを極限まで削ぎ落とし、処理速度を数倍から十倍に跳ね上げることができます。

3. 出力先の外部化

イミディエイトウィンドウへの出力(`Debug.Print`)はデバッグ用です。実務では、`Scripting.FileSystemObject` を使って直接ローカルの `.csv` や `.json` ファイルへストリーム書き込みするロジックへ拡張してください。

—

総括

`Coordinates` プロパティは、その独特な配列構造ゆえに初心者殺しの一面を持っていますが、その仕様を完全に理解し、適切に次元数をハンドリングさえすれば、図面データを意のままに操る最強の武器となります。

「動けばいい」という妥協を捨て、エラーに強く、メンテナンス性の高いコードを書くこと。それこそが、現場の信頼を勝ち取るエンジニアの流儀です。

あなたのAutoCAD自動化ライフが、より洗練されたものになることを願っています。

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