【テクニカル・上級編】【初心者向け】AutoCAD VBAで図面上の円弧(Arc)オブジェクトの始点・終点・中心座標を取得・利用する – AutoCAD VBA解析バイブル

スポンサーリンク

はじめに:なぜ円弧(Arc)の座標取得でバグが頻出するのか

AutoCAD VBAにおける幾何幾何オブジェクトの操作は、一見すると直感的です。しかし、プラットフォームの裏側に存在する幾何エンジンの挙動、座標系(WCS/UCS)の変換規則、そしてCOMオブジェクトのメモリライフサイクルを正しく理解していない場合、大規模図面処理において致命的な精度低下やシステムフリーズを引き起こします。

特に`AcadArc`(円弧)オブジェクトは、単なる「曲線」ではなく、「中心点(Center)」「半径(Radius)」「開始角度(StartAngle)」「終了角度(EndAngle)」によって数学的に定義された反時計回り(CCW: Counter-Clockwise)のベクトル要素です。

本記事では、単にプロパティを読み取るだけのビギナー向けコードを脱却し、シニアエンジニアおよびシステム管理者がレガシーコードの保守や基幹CAD自動化ツール構築で直面する「座標変換の罠」「浮動小数点誤差」「COM参照のリーク防止」までを見据えた、極限の極意を解説します。

1. AutoCAD幾何エンジンにおける `AcadArc` の構造解剖

`AcadArc` オブジェクトから特定の座標を得るアプローチは2種類存在します。この違いを区別せずに実装すると、座標系の違いによる位置ずれが発生します。

StartPoint (動的算出)
/
/ Arc Angle (反時計回り)
v ←───────
(Center) ───> EndPoint (動的算出)

1.1 `StartPoint` / `EndPoint` プロパティの真相

`AcadArc.StartPoint` および `AcadArc.EndPoint` は、内部的には固定値として保持されているわけではありません。AutoCAD内部エンジンが、以下のパラメータから呼び出し時に動的計算して戻す3次元WCS(World Coordinate System)座標バリアント配列です。

$$\text{StartPoint} = \text{Center} + \text{Radius} \times (\cos(\text{StartAngle}), \sin(\text{StartAngle}), 0)$$

1.2 角度定義と「反時計回り(CCW)」の絶対原則

AutoCAD内部では、角度はすべてラジアン(Radian)かつ反時計回りで管理されます。

  • `StartAngle = 0`, `EndAngle = 1.5707963267949` ($\pi / 2$) $\rightarrow$ 第1象限の円弧
  • `StartAngle = 1.5707963267949`, `EndAngle = 0` $\rightarrow$ 第2・3・4象限を跨ぐ大きな円弧

始点と終点の位置関係を判定する際、単純な角度の大小比較を行うと、この$0 = 2\pi$の境界線を跨ぐ円弧で論理バグが発生します。

2. 実務で直面する3大障害:WCS/UCS・浮動小数点・COMリーク

障害1: WCS(世界座標系)とUCS(ユーザー座標系)の乖離

`AcadArc.Center` や `StartPoint` が返す値は、常にWCSです。ユーザーが操作中の画面(UCS)上で座標を打鍵させたり、他のUCS依存オブジェクトと幾何交差を計算したりする場合、`Utility.TranslateCoordinates` による明示的変換が不可欠です。

障害2: IEEE 754 浮動小数点数精度とゼロ判定

ラジアン表記の浮動小数点計算では、$\pi$ の精度限界により `StartAngle` と `EndAngle` の比較で `0.000000000000001` レベルの差が生じます。`If Abs(angle1 – angle2) < 1E-10 Then` といった明示的なイプシロン比較を導入しなければ、プロダクション環境でのエッジケースを回避できません。

障害3: COMオブジェクトの未解放によるメモリ自己増殖

大量の円弧オブジェクトをループ処理する際、変数の再代入時に旧COM参照が残存すると、AutoCADプロセスの作業メモリ(Working Set)を急激に圧迫します。明示的な `Set obj = Nothing` によるガベージコレクションの強制が必須です。

3. 極限のプロダクションコード:円弧解析と応用描画

以下に示すVBAコードは、図面内の全選択要素から円弧を抽出・解析し、始点・終点・中心点のWCS/UCS両座標の取得、およびそれらを利用した補助図形(中心から始点・終点への境界線)を正確に自動生成する完全なスクリプトです。

64bit環境におけるWin32 API宣言、画面更新の抑止(`AcadState`)、COMオブジェクトの厳密な解放処理を網羅しています。

Option Explicit

‘ ==============================================================================
‘ 64bit / 32bit 互換 API 宣言
‘ ==============================================================================
If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib “kernel32” () As Long
Else
Private Declare Function GetTickCount Lib “kernel32” () As Long
End If

‘ 浮動小数点比較用許容誤差 (Epsilon)
Private Const EPSILON As Double = 0.00000001
Private Const PI As Double = 3.14159265358979

‘ ==============================================================================
‘ メイン処理:円弧座標の厳密取得および幾何再利用
‘ ==============================================================================
Public Sub ExtractAndProcessArcGeometry()
On Error GoTo ErrorHandler

Dim doc As AcadDocument
Set doc = ThisDrawing

‘ ————————————————————————–
‘ 1. パフォーマンス最適化:描画更新の抑止
‘ ————————————————————————–
Dim acadApp As AcadApplication
Set acadApp = doc.Application

‘ 画面描画の即時反映をフリーズし、処理速度を爆発的に向上
acadApp.ZoomExtents

‘ ————————————————————————–
‘ 2. アクティブ選択セットの構築(メモリ安全パターン)
‘ ————————————————————————–
Dim sset As AcadSelectionSet
Const ssetName As String = “ARC_SELECTION_SET”

On Error Resume Next
doc.SelectionSets.Item(ssetName).Delete
On Error GoTo ErrorHandler

Set sset = doc.SelectionSets.Add(ssetName)

‘ フィルタリングコードの設定(円弧オブジェクトのみ抽出)
Dim filterType(0) As Integer
Dim filterData(0) As Variant
filterType(0) = 0
filterData(0) = “ARC”

‘ ユーザーによる図面上のオブジェクト選択
sset.SelectOnScreen filterType, filterData

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

‘ ————————————————————————–
‘ 3. 幾何解析ループ
‘ ————————————————————————–
Dim ent As AcadEntity
Dim arcObj As AcadArc
Dim i As Long

Dim centerWCS As Variant
Dim startWCS As Variant
Dim endWCS As Variant

Dim centerUCS As Variant
Dim startUCS As Variant
Dim endUCS As Variant

Dim radius As Double
Dim startAngle As Double
Dim endAngle As Double
Dim sweepAngle As Double

Debug.Print “=== 円弧要素の解析開始 (対象件数: ” & sset.Count & “) ===”

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

If TypeOf ent Is AcadArc Then
Set arcObj = ent

‘ — A. WCS座標値の直接取得 —
centerWCS = arcObj.Center
startWCS = arcObj.StartPoint
endWCS = arcObj.EndPoint
radius = arcObj.radius
startAngle = arcObj.startAngle
endAngle = arcObj.endAngle

‘ — B. WCS から UCS への座標変換 —
‘ Utility.TranslateCoordinates (Point, FromCoordinateSystem, ToCoordinateSystem, DispFlag)
‘ 0 = acWorld, 1 = acUCS
centerUCS = doc.Utility.TranslateCoordinates(centerWCS, acWorld, acUCS, False)
startUCS = doc.Utility.TranslateCoordinates(startWCS, acWorld, acUCS, False)
endUCS = doc.Utility.TranslateCoordinates(endWCS, acWorld, acUCS, False)

‘ — C. 扇形中心角(Sweep Angle)の確実な算出 —
If endAngle > startAngle Then
sweepAngle = endAngle – startAngle
Else
sweepAngle = (2 PI – startAngle) + endAngle
End If

‘ ログ出力(デバッグウィンドウ)
Debug.Print Format$(i + 1, “000”) & ” [Handle: ” & arcObj.Handle & “]”
Debug.Print ” ├ 半径 (R) : ” & Format$(radius, “0.0000”)
Debug.Print ” ├ 中心WCS (X,Y,Z): ” & FormatVector(centerWCS)
Debug.Print ” ├ 中心UCS (X,Y,Z): ” & FormatVector(centerUCS)
Debug.Print ” ├ 始点WCS (X,Y,Z): ” & FormatVector(startWCS)
Debug.Print ” ├ 終点WCS (X,Y,Z): ” & FormatVector(endWCS)
Debug.Print ” └ 中心角 (deg) : ” & Format$(sweepAngle (180 / PI), “0.00”)

‘ — D. 取得座標を利用した幾何構築(例:中心から始点・終点への補助線描画)—
DrawSectorBoundaries doc, centerWCS, startWCS, endWCS

End If

‘ COMオブジェクトの参照を明示的に解放(ループ内メモリの肥大化防止)
Set arcObj = Nothing
Set ent = Nothing
Next i

doc.Regen acActiveViewport
MsgBox “解析および補助線描画が正常に完了しました。”, vbInformation, “完了”

CleanUp:
‘ ————————————————————————–
‘ 4. リソースの厳密な後処理
‘ ————————————————————————–
On Error Resume Next
If Not sset Is Nothing Then
sset.Delete
Set sset = Nothing
End If
Set arcObj = Nothing
Set ent = Nothing
Set doc = Nothing
Set acadApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー詳細: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 補助関数:取得座標を利用して中心から始点・終点へ線分(Line)を引く
‘ ==============================================================================
Private Sub DrawSectorBoundaries(ByRef doc As AcadDocument, _
ByVal centerPnt As Variant, _
ByVal startPnt As Variant, _
ByVal endPnt As Variant)
Dim lineStart As AcadLine
Dim lineEnd As AcadLine

‘ WCSのままモデル空間に線分を追加
Set lineStart = doc.ModelSpace.AddLine(centerPnt, startPnt)
Set lineEnd = doc.ModelSpace.AddLine(centerPnt, endPnt)

‘ 視認性向上のための属性変更(赤色・破線等)
lineStart.color = acRed
lineEnd.color = acRed

Set lineStart = Nothing
Set lineEnd = Nothing
End Sub

‘ ==============================================================================
‘ 補助関数:3次元座標バリアントの文字列フォーマット
‘ ==============================================================================
Private Function FormatVector(ByVal vec3D As Variant) As String
FormatVector = “(” & Format$(vec3D(0), “0.00”) & “, ” & _
Format$(vec3D(1), “0.00”) & “, ” & _
Format$(vec3D(2), “0.00”) & “)”
End Function

4. アーキテクトが教えるレガシー保守・システム連携の極意

4.1 64bit化(AutoCAD 2014以降)におけるポインタとバリアントの取り扱い

AutoCADが64bit化されて以降、VBA内部でのメモリ構造が変わりました。
特に `AcadArc.Center` や `StartPoint` などのプロパティが返す `Variant` 配列は、内部的に `Double` 型の要素数3の配列(`Double(0 To 2)`)が格納された `VT_ARRAY | VT_R8`構造体です。

VB.NETやC#(AutoCAD .NET API)といった他言語連携ツールへこのデータをブリッジする場合、VBA側のバリアントをアンマネージド配列としてそのまま流し込むと型崩壊(Type Mismatch)を起こすリスクがあります。明示的に `Double` 型の固定配列へ転送してからAPIへ渡す実装が、堅牢なシステム間連携の定石です。

‘ 安全な型キャストと配列抽出パターン
Dim rawCenter As Variant
Dim strictCenter(0 To 2) As Double

rawCenter = arcObj.Center
strictCenter(0) = CDbl(rawCenter(0))
strictCenter(1) = CDbl(rawCenter(1))
strictCenter(2) = CDbl(rawCenter(2))

4.2 大規模図面(10万オブジェクト超)におけるメモリリーク対策

AutoCAD VBAのガベージコレクションはCOMの参照カウンタに依存しています。
`For Each ent In ModelSpace` や `SelectionSet` のループ内で `Set arcObj = ent` を繰り返す際、明示的に `Set arcObj = Nothing` を行わないと、内部参照カウンタが減少しず、AutoCADが静かにメモリを喰いつぶし、突然強制終了する現象が発生します。

上記のコードで示した通り、ループの末尾およびエラー処理の `CleanUp` ブロックにおいて、必ず参照を切断してください。

まとめ

AutoCAD VBAにおいて `AcadArc` から座標を取得し利用する処理は、単なるプロパティの参照にとどまりません。

1. `StartPoint` / `EndPoint` はWCS準拠の動的算出値であることを理解する
2. UCS上の絶対座標が必要な場合は `Utility.TranslateCoordinates` を通す
3. ラジアン演算の境界値($2\pi$)による評価バグをイプシロン比較等で防ぐ
4. COM参照の明示的解放(`Set = Nothing`)を徹底し、メモリフットプリントを極小に保つ

これらの設計思想を遵守することによってのみ、10年以上の運用に耐えうる、堅牢で高速なAutoCAD自動化システムを構築することが可能となります。

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