【実務・中級編】【実務中級】AcadDocument.Utility.GetOrientationによる「回転角」の取得:ANGBASE設定に左右されない正確な角度入力 – AutoCAD VBA解析バイブル

スポンサーリンク

【実務中級】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配列) ”’ 取得された絶対ラジアン(参照引数) ”’ ユーザーが正常に入力した場合はTrue、Esc等でキャンセルした場合はFalse
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)を安全に処理するラッパー関数を共通モジュール化し、チーム全体のコード資産の品質を高める。

「動くだけのコード」から、「いかなる顧客図面でも破綻しない防弾コード」へ。
この差こそが、プロフェッショナルな自動化エンジニアを定義づける一線です。

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