【実務中級】AcadDocument.Utility.GetOrientation による「回転角」の完全制御:図面依存の罠(ANGBASE/ANGDIR)を排した堅牢な自動化設計
AutoCAD VBAで図面自動化ツールを開発している際、自席のテスト環境では完璧に動作していたプログラムが、現場の運用図面や土木系の図面で実行した途端、「挿入したブロックや文字が意図しない方向(90度や180度オフセット)を向く」という致命的なバグに遭遇したことはないでしょうか。
その原因の99%は、AutoCADのシステム変数 `ANGBASE`(角度基準)および `ANGDIR`(角度方向)の考慮漏れ、そして入力メソッドである `GetAngle` と `GetOrientation` の使い分けに対する無知に起因します。
本稿では、AutoCAD VBAのオブジェクトモデルにおける `AcadUtility` の挙動をロジカルに解剖し、ユーザーの図面環境に依存せず、常に絶対的な回転角を正確に取得・適用するためのプログラミング手法を詳解します。
—
1. なぜ「回転角のバグ」は潜伏するのか?
AutoCADには、角度の「ゼロ」がどこを指し、どちら回りを正とするかを制御するシステム変数が存在します。
- `ANGBASE`: 0度の基準方向(デフォルト:0 = 東/3時方向)。土木・測量図面では「90 = 北/12時方向」に設定されることが多い。
- `ANGDIR`: 角度の増加方向(デフォルト:0 = 反時計回り)。1 に設定すると「時計回り」になる。
一般的な開発者は、無意識に `AcadUtility.GetAngle` を使用してユーザーから回転角を取得します。しかし、`GetAngle` が返すラジアン値は `ANGBASE` と `ANGDIR` の設定に依存してシフトされます。
一方で、`AcadBlockReference.Rotation` や `AcadText.Rotation` といったCADオブジェクトのプロパティは、「東向き(X軸正方向)を0度とし、反時計回りを正とする絶対ラジアン」で値を要求します。
ここに設計上の致命的なミスマッチ(構造的欠陥)が発生します。
【 GetAngle を使用した場合の危険なデータフロー 】
ユーザーの入力 (例: 北を指示)
↓
GetAngle() が ANGBASE=90° を考慮して 「0ラジアン」 を返却(ユーザーから見た相対角)
↓
BlockReference.Rotation に 「0ラジアン」 を代入
↓
AutoCAD内部では0ラジアンは「東向き」なので、ブロックが東(右)を向く!
⇒ ユーザーが指定した「北」から90度ずれる「サイレントバグ」の完成
この「図面の設定によって挙動が変わるコード」は、プログラミングにおいて最も排除すべき脆弱性です。
—
2. メカニズムの徹底解剖:GetAngle vs GetOrientation
この問題を根本から解決するのが `AcadUtility.GetOrientation` メソッドです。両者の挙動の違いを正確に把握してください。
| メソッド | `ANGBASE`(基準角)の影響 | `ANGDIR`(回転方向)の影響 | 戻り値の数学的意味 | 主な用途 |
| :— | :— | :— | :— | :— |
| `GetAngle` | 受ける(ANGBASEが0度になる) | 受ける(1なら時計回りが正) | 現在の図面設定に対する「相対ラジアン」 | オフセット角度、相対的な回転量の取得 |
| `GetOrientation` | 無視する(常に東=0度) | 無視する(常に反時計回りが正) | WCS/UCSにおける「絶対ラジアン」 | ブロック挿入、テキスト配置、オブジェクトの角度設定 |
結論:どのメソッドを使うべきか?
オブジェクトの `Rotation` プロパティ(絶対ラジアンを要求するプロパティ)に直接代入する値をユーザーに入力させる場合、選択肢は `GetOrientation` の一択です。`GetAngle` を使ってよいのは、ユーザーに対して「現在のオブジェクトから何度回転させますか?」といった、相対的な増分値を問う場合のみです。
—
3. プロダクション環境に耐えうる堅牢な実装設計
実際の業務ツールでは、単に `GetOrientation` を呼び出すだけでは不十分です。
以下のエッジケースを考慮したカプセル化(モジュール化)が必要です。
1. ユーザーによるキャンセル(Escキー)のハンドリング: VBAのエラーハンドリング (`On Error Resume Next` とエラーコード `-2145320928 / 0x80040000` 付近のキャッチ) を適切に行う。
2. ベースポイント(起点)の指定有無: 1点目を渡して動的に角度線をプレビューさせるか、画面上の任意2点クリックを許容するか。
3. UCS(ユーザー座標系)とWCS(ワールド座標系)の変換: `GetOrientation` が返す角度は現在のUCS平面上の角度であるため、必要に応じてWCS基準の絶対角として扱う設計。
—
4. 完全防弾仕様のプロダクションVBAコード
以下に、実務の現場でそのままコピペして運用に投入できる、安全かつ保守性の高いコード例を示します。
Option Explicit
‘ ==============================================================================
‘ クラス/モジュール名: Mod_AngleUtility
‘ 概要: 図面設定(ANGBASE/ANGDIR)に依存せず、安全に絶対角度を取得する堅牢なモジュール
‘ ==============================================================================
‘ AutoCAD入力キャンセル時のエラーコード定義
Private Const ERR_USER_CANCELLED As Long = -2145320928
”’
”’ ANGBASE / ANGDIR の設定に関わらず、東=0度・反時計回りの絶対ラジアンを返します。
”’
”’ 対象のAcadDocumentオブジェクト
”’ ユーザーへのプロンプトメッセージ
”’ 基点座標(オプション。3要素のDouble配列)
”’ 取得された絶対ラジアン(参照引数)
”’
Public Function TryGetAbsoluteOrientation( _
ByVal doc As AcadDocument, _
ByVal promptMsg As String, _
ByRef basePoint As Variant, _
ByRef outRadians As Double _
) As Boolean
On Error Resume Next
Dim selectedAngle As Double
‘ 基点(basePoint)が渡されているかどうかで呼び出しを分岐
If IsArray(basePoint) Then
selectedAngle = doc.Utility.GetOrientation(basePoint, promptMsg)
Else
selectedAngle = doc.Utility.GetOrientation(, promptMsg)
End If
‘ エラー判定
If Err.Number <> 0 Then
If Err.Number = ERR_USER_CANCELLED Then
‘ ユーザーがEscキー等で入力をキャンセルした場合
TryGetAbsoluteOrientation = False
Else
‘ その他の予期せぬエラー
AppActivate doc.Application.Caption
MsgBox “角度取得時にエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
TryGetAbsoluteOrientation = False
End If
Err.Clear
Exit Function
End If
On Error GoTo 0
‘ 成功時
outRadians = selectedAngle
TryGetAbsoluteOrientation = True
End Function
‘ ==============================================================================
‘ 実行サンプル:ユーザー指定の角度でブロック(または文字)を安全に挿入する
‘ ==============================================================================
Public Sub Executive_InsertTextWithCorrectAngle()
Dim doc As AcadDocument
Set doc = ThisDrawing
‘ 1. 挿入位置の取得
Dim insertPoint As Variant
On Error Resume Next
insertPoint = doc.Utility.GetPoint(, vbCrLf & “テキストの挿入位置をクリック:”)
If Err.Number <> 0 Then
Exit Sub ‘ キャンセル終了
End If
On Error GoTo 0
‘ 2. 絶対回転角の取得(GetOrientationを内部で使用する安全な関数を呼び出し)
Dim textRotation As Double
Dim success As Boolean
‘ 挿入位置を基点として動的に角度線を表示させながら角度を取得
success = TryGetAbsoluteOrientation(doc, vbCrLf & “配置角度を指定:”, insertPoint, textRotation)
If Not success Then
doc.Utility.Prompt vbCrLf & “処理がキャンセルされました。”
Exit Sub
End If
‘ 3. オブジェクトの生成と角度設定
Dim textObj As AcadText
Dim textString As String
Dim textHeight As Double
textString = “ANGLE_TEST: ” & Format(textRotation (180 / 3.14159265358979), “0.0”) & ” deg”
textHeight = doc.GetVariable(“TEXTSIZE”) ‘ 現在の文字高を取得
‘ モデル空間にテキストを作成
Set textObj = doc.ModelSpace.AddText(textString, insertPoint, textHeight)
‘ 【重要】GetOrientationで得た絶対ラジアンを直接セットする
‘ ANGBASEが90度(北)にセットされている図面であっても、意図通り指定した方向を正しく向く
textObj.Rotation = textRotation
textObj.Update
doc.Utility.Prompt vbCrLf & “正常にオブジェクトを配置しました。”
End Sub
—
5. チーフアーキテクトが教える、一歩先の設計知見
コードをコピペして満足するレベルを超え、エンタープライズレベルのツール構築を目指すなら、以下の2点も設計思想に組み込んでください。
① データベース(外部ファイル)連携時の単位変換の厳格化
CSVやSQL Server、JSON等の外部ソースから「回転角(例:90.0度)」を読み込んで自動作図する場合、度数法(Degree)から弧度法(Radian)への変換式は次のようにカプセル化し、精度の崩れ(丸め誤差)を防ぎます。
$$\text{Radian} = \text{Degree} \times \left( \frac{\pi}{180} \right)$$
VBA内部で扱う `Pi` は定数として高精度に定義するか、`4 Atn(1)` から動的に計算させて保持するのが鉄則です。
② Multi-UCS(ユーザー座標系)環境下における考慮
`GetOrientation` が返す値は、「現在のUCSにおけるXY平面上の角度」です。
もしツールがWCS(ワールド座標系)基準の空間計算(ベクトル演算)を内部で行い、直接 `WCS` 座標を構成してオブジェクトを生成するアーキテクチャになっている場合は、`doc.Utility.TranslateCoordinates` を用いて座標軸の整合性を取るか、処理の開始時に一時的にUCSをWCSにリセットし、`Finally` ブロック(VBAでは適切なエラーハンドラ経由の復元処理)で元のUCSに戻すガードコードを組むのがプロの設計です。
—
6. まとめ
- 図面ごとに設定が異なる `ANGBASE`(角度基準)や `ANGDIR`(回転方向)は、`GetAngle` を使うことでサイレントバグの温床となる。
- ブロックの回転角(`BlockReference.Rotation`)や文字角度(`Text.Rotation`)を取得する際は、必ず `GetOrientation` を使用する。
- `GetOrientation` は、図面の設定を無視して常に「東=0度」「反時計回り=正」の絶対ラジアンを返すため、オブジェクトのRotationプロパティと100%の親和性を持つ。
- 入力キャンセル(Esc)を安全に処理するラッパー関数を共通モジュール化し、チーム全体のコード資産の品質を高める。
「動くだけのコード」から、「いかなる顧客図面でも破綻しない防弾コード」へ。
この差こそが、プロフェッショナルな自動化エンジニアを定義づける一線です。
