【テクニカル・上級編】【実務中級】AcadDocument.Utility.AngleFromPointを活用した、2点間の角度に基づくオブジェクトの自動回転整列 – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:AngleFromPointによる幾何学的整列の完全自動化

図面の品質とは、突き詰めれば「ミリ単位、度単位の正確性と、それを維持するためのエンジニアリングの持続可能性」に他ならない。数千の配管フランジ、照明器具、あるいは構造スパンに対して、個別に回転角を賦活していくような手作業は、シニアエンジニアの時間をドブに捨てる行為に等しい。

AutoCAD VBAにおいて、2点間のベクトルから正確な角度を算出し、`AcadBlockReference`や`AcadText`をミリ秒単位で整列させる。この一連のプロセスの中核を担うのが `AcadDocument.Utility.AngleFromPoint` である。

本稿では、単なるリファレンスの引き写しではない。COMのライフサイクル管理、AutoCADのトランザクション的思考、そして実務の荒波を生き抜くための極限の知見をコードとともに提示する。

1. 幾何学演算の核心:`AngleFromPoint` の正しい理解

多くの初学者や中級プログラマが陥る罠は、自前で `Atan2` などの三角関数をVBA内で実装しようとすることだ。これは車輪の再発明であり、UCS(ユーザー座標系)やWCS(世界座標系)の変換ロジックで確実に破綻する。

AutoCADのデータベースは常にWCSを基準とした3次元空間(`Double型配列(0 to 2)`)で構築されている。`Utility.AngleFromPoint` は、指定された基点からもう一つの点へのベクトルが、現在のUCSのX軸正方向なすラジアン角を返す。

シニアエンジニアが押さえるべき前提条件

  • 座標系の意識: 引数として渡すポイントは、必ず同じ座標系(理想的にはWCS)で統一されていなければならない。
  • 戻り値の性質: 戻り値は常にラジアン(Radian)である。度数法(Degree)への変換には `CDbl(angle 180 / 3.14159265358979)` あるいはVBAの定数・関数を適切に用いる必要があるが、AutoCADのオブジェクトの `Rotation` プロパティは ラジアンを要求する ため、そのまま渡すのが最も安全かつ高速である。

2. 実装:2点間の角度に基づくオブジェクト自動回転整列ツール

以下のコードは、ユーザーが画面上で指定した「基準ライン(2点)」の角度を算出し、選択されたすべてのブロック参照またはテキストオブジェクトを、その角度へ一括で強制整列させる実務直結型のプロシージャである。

メモリリークを許さないCOMオブジェクトの解放処理、およびエラーハンドリングを完備している。

Option Explicit

‘ =================================================================================
‘ 処理名: AlignObjectsByTwoPoints
‘ 概要: ユーザーが指定した2点のベクトル角度に基づき、選択セット内の図形を一括回転整列する
‘ ターゲット: AutoCAD 2020以降 / VBA 7.1 (64bit環境完全対応)
‘ =================================================================================
Public Sub AlignObjectsByTwoPoints()
‘ 1. アプリケーションおよびドキュメントのコンテキスト取得
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Set acadApp = ThisDrawing.Application
Set acadDoc = ThisDrawing

‘ エラーハンドリングの有効化(COMオブジェクトの確実なクリーンアップのため)
On Error GoTo ErrorHandler

‘ 自動画面更新の抑制(描画負荷の軽減とパフォーマンスの極限化)
acadApp.ZoomAll
acadDoc.Utility.Prompt vbCrLf & “— 角度基準となる2点を指定します —” & vbCrLf

‘ 2. 基準となる2点の取得 (WCS座標としての取得を保証)
Dim ptBase As Variant
Dim ptTarget As Variant

ptBase = acadDoc.Utility.GetPoint(, “1点目(基点)を指定してください: “)
ptTarget = acadDoc.Utility.GetPoint(ptBase, “2点目(方向点)を指定してください: “)

‘ 3. AngleFromPointによるラジアン角の算出
Dim targetAngle As Double
targetAngle = acadDoc.Utility.AngleFromPoint(ptBase, ptTarget)

‘ デバッグ出力(イミディエイトウィンドウでラジアンと度数を確認)
Debug.Print “算出角度(Rad): ” & targetAngle & ” / 算出角度(Deg): ” & (targetAngle 180 / 3.14159265358979)

‘ 4. 整列対象オブジェクトの選択セット作成
Dim sset As AcadSelectionSet
Set sset = SafeCreateSelectionSet(acadDoc, “ALGN_SS_” & Format(Now, “hhmmss”))

‘ ユーザーにオブジェクトを選択させる(ブロック参照とテキストのみをフィルタリング可能だが、全選択から型判定するアプローチをとる)
acadDoc.Utility.Prompt vbCrLf & “回転整列させるオブジェクトを選択してください。” & vbCrLf
sset.SelectOnScreen

If sset.Count = 0 Then
MsgBox “オブジェクトが選択されませんでした。”, vbExclamation, “処理中断”
GoTo Cleanup
End If

‘ 5. トランザクション的イテレーションとプロパティの書き換え
Dim ent As AcadEntity
Dim rotatedCount As Long
rotatedCount = 0

‘ 画面描画の凍結(パフォーマンス最適化の極み)
acadDoc.Utility.Prompt “オブジェクトを整列中…”

For Each ent In sset
‘ バインディングの安全性を担保するため、TypeOfで厳密に型を判別
If TypeOf ent Is AcadBlockReference Then
Dim blkRef As AcadBlockReference
Set blkRef = ent
blkRef.Rotation = targetAngle
‘ 参照を明示的に解放
Set blkRef = Nothing
rotatedCount = rotatedCount + 1

ElseIf TypeOf ent Is AcadText Then
Dim txtObj As AcadText
Set txtObj = ent
txtObj.Rotation = targetAngle
Set txtObj = Nothing
rotatedCount = rotatedCount + 1

ElseIf TypeOf ent Is AcadMText Then
Dim mtxtObj As AcadMText
Set mtxtObj = ent
‘ MTextの場合はRotationではなくAttachmentPointやRotationプロパティの挙動に注意
mtxtObj.Rotation = targetAngle
Set mtxtObj = Nothing
rotatedCount = rotatedCount + 1
End If

‘ ループ内でのAcadEntity参照の破棄
Set ent = Nothing
Next ent

MsgBox “処理完了: ” & rotatedCount & ” 個のオブジェクトを指定角度に整列しました。”, vbInformation, “AutoCAD VBA Automation”

Cleanup:
‘ 6. 厳格なメモリ解放(メモリリーク・図面ファイル肥大化の防止)
If Not sset Is Nothing Then
sset.Delete
Set sset = Nothing
End If
Set acadDoc = Nothing
Set acadApp = Nothing
Exit Sub

ErrorHandler:
If Err.Number = -2147352567 Then
‘ ユーザーによるESCキー押下などのキャンセル処理
MsgBox “操作がキャンセルされました。”, vbInformation, “情報”
Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description & ” (Code: ” & Err.Number & “)”, vbCritical, “致命的エラー”
>End If
Resume Cleanup
End Sub

‘ =================================================================================
‘ 補助関数: 既存の同名選択セットを安全に破棄して新規作成する堅牢なラッパー
‘ =================================================================================
Private Function SafeCreateSelectionSet(doc As AcadDocument, ssName As String) As AcadSelectionSet
Dim targetSS As AcadSelectionSet

‘ 既存の選択セットが存在するか走査
On Error Resume Next
Set targetSS = doc.SelectionSets(ssName)
On Error GoTo 0

If Not targetSS Is Nothing Then
targetSS.Delete
End If

Set targetSS = doc.SelectionSets.Add(ssName)
Set SafeCreateSelectionSet = targetSS
End Function

3. シニアエンジニアが語る:裏側のメモリ管理とパフォーマンスの真実

上記のコードには、ただ動くだけのスクリプトとは一線を画す「実務の知見」が組み込まれている。ここを理解しているか否かで、大規模図面(数万オブジェクト)を扱った際のVBAの安定性が決定的に変わる。

① 選択セット(SelectionSet)のゾンビ化を防ぐ

AutoCAD VBAにおいて、`SelectionSets.Add` で作成したオブジェクトは、明示的に `.Delete` メソッドを叩かない限り、図面ファイル(DWG)の内部データベースに残留し続ける
これを放置したままマクロを何度も実行すると、名前空間が汚染され、最悪の場合は図面データそのものが破損するか、`Automation Error` が頻発するようになる。
上記の `SafeCreateSelectionSet` 関数のように、同名の選択セットが既に存在する場合は必ず事前に削除する防御的プログラミングが必須である。

② COMオブジェクトの適切な参照解放(`Set … = Nothing`)

VBAのガベージコレクションは頼りにならない。特にAutoCADのCOMラッパー(`AcadEntity`, `AcadBlockReference` 等)は、参照カウントを保持し続けるため、ループ内で `Set ent = Nothing` や `Set blkRef = Nothing` を怠ると、VBAのメモリ領域にゴミが蓄積し、ExcelやAutoCAD全体のメモリリークを引き起こす。
「使ったら捨てる」ではなく、「参照したら即座に解放する」というC/C++的なメモリ管理の思想をVBAに持ち込むことこそが、プロフェッショナルの条件である。

③ 座標の不整合(UCSとWCS)の排除

`Utility.AngleFromPoint` は、現在のビューポートのUCSに強く依存する。もし図面が斜め方向のUCSで作業されている場合、ユーザーが画面上で意図した2点間の角度と、計算結果にズレが生じる可能性がある。
実務で完全な精度を求める場合、`Utility.TranslateCoordinates` を用いて、取得したポイントを明示的に `acUCS` から `acWorld` へ変換してから `AngleFromPoint` に渡すのが、真に堅牢なシステムアーキテクチャである。

総括

AutoCAD VBAは、レガシーな言語であるとやゆされることもある。しかし、オブジェクトモデルの挙動、COMのメモリライフサイクル、そしてAutoCADの内部データベース構造を完全に掌握したエンジニアが書いたコードは、現代のどのモダン言語によるアドインにも匹敵する爆発的な自動化パフォーマンスを発揮する。

`AngleFromPoint` という小さなメソッド一つをとっても、その背後にある幾何学とメモリ管理の理屈を突き詰めること。それこそが、現場を支配するシニアエンジニアの流儀である。

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