幾何学的衝突の回避:SolidWorks APIによる寸法レイアウトの動的最適化とメモリ管理の極意
自動化エンジニアとして長年設計現場の最前線に立っていると、ある種の「絶望」に直面することがある。それは、APIによって寸法の生成自体には成功したものの、生成された寸法線が互いに重なり合い、図面としての可読性を完全に喪失している状態だ。
「自動生成」は入り口に過ぎない。真の自動化、すなわち「設計者の思考を代替するシステム」には、幾何学的な衝突検知と、それに基づくレイアウトの動的再配置アルゴリズムが不可欠である。
本稿では、SolidWorks VBAを用いて、図面内の寸法線の重なりを検知し、適切なオフセットを計算して配置を最適化する、実戦レベルのアーキテクチャについて詳説する。
1. 幾何学的衝突検知の理論的アプローチ
寸法線の重なりを検知するためには、各寸法オブジェクトを単なる「データ」としてではなく、「空間を占有する境界ボックス(Bounding Box)」として捉える必要がある。
SolidWorks APIにおける `DisplayDimension` オブジェクトは、その表示位置を特定するための座標情報を提供しているが、単一の点ではなく、寸法テキストや引き出し線を包含する「矩形領域」として評価しなければならない。
検知アルゴリズムのフロー
1. オブジェクトの走査: `DrawingDoc` 内の全ての `DisplayDimension` を走査。
2. 境界領域の取得: `GetBoundingBox` メソッド(または座標プロパティの組み合わせ)を用いて、各寸法の占有領域 $R_i$ を定義。
3. 衝突判定: 2つの領域 $R_1, R_2$ において、以下の条件を満たす場合に「衝突」と定義する。
$$\text{Intersection}(R_1, R_2) \neq \emptyset$$
4. オフセット計算: 衝突した次元(X軸またはY軸)に基づき、重なりを解消する最小の移動量を算出。
2. 実装における極限の知見:メモリとパフォーマンスの管理
大規模なアセンブリ図面において、数千の寸法を扱う場合、不適切なコーディングは即座にメモリリークやSolidWorksのフリーズを招く。我々アーキテクトが遵守すべき鉄則は以下の3点だ。
I. COMオブジェクトの明示的解放
VBAはガベージコレクションに依存しがちだが、SolidWorksのような巨大なCOMサーバーを操作する場合、ループ内でのオブジェクト生成・破棄は極めて重い。
`Set obj = Nothing` をループの末尾で確実に実行し、参照カウントを適切に管理せよ。
II. 再構築(Rebuild)のコスト制御
寸法位置を変更するたびに `swModel.ForceRebuild3` を呼び出すのは愚策である。これは計算コストが極めて高い。
すべての配置計算をメモリ上(配列またはコレクション)で完了させ、最後に一括して座標を適用し、一度だけ Rebuild を実行するのがプロフェッショナルの作法である。
III. Windows APIによる高精度タイマーの活用
処理時間の計測には `Timer` 関数ではなく、`GetTickCount` や `QueryPerformanceCounter` を用いた Windows API を推奨する。マイクロ秒単位のボトルネック特定こそが、最適化の第一歩だ。
3. 実践的実装例:寸法レイアウト最適化マクロ
以下に、水平方向の寸法線が重なっている場合に、Y軸方向にオフセットを適用するコアロジックを示す。これは実務における「雛形」として機能する。
Option Explicit
' Windows APIの宣言 (パフォーマンス計測用)
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
''' <summary>
''' 図面内の寸法線の重なりを検知し、自動整列を行うメインルーチン
''' </summary>
Public Sub OptimizeDimensionLayout()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swDispDim As SldWorks.DisplayDimension
Dim swAnn As SldWorks.Annotation
Dim startTime As Long
startTime = GetTickCount()
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
' ドキュメント型のチェック
If swModel Is Nothing Or swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いてください。", vbCritical
Exit Sub
End If
Set swDraw = swModel
' --- 1. 寸法情報の収集フェーズ ---
' ここでは単純化のため、コレクションに格納する。
' 大規模図面では、空間分割(Quadtree等)を用いるのが理想的。
Dim dimList As Collection
Set dimList = New Collection
Set swDispDim = swDraw.GetFirstDisplayDimension
Do While Not swDispDim Is Nothing
' 寸法オブジェクトをラップするカスタムクラス、または構造体として保持することを推奨
' ここでは簡易的にAnnotationの座標を取得
Set swAnn = swDispDim.GetAnnotation
dimList.Add swDispDim
Set swDispDim = swDispDim.GetNext
Loop
' --- 2. 衝突判定とオフセット計算フェーズ ---
Dim i As Long, j As Long
Dim offsetAmount As Double
offsetAmount = 0.005 ' 5mm (単位系に注意: メートル)
' 二重ループによる衝突検知 (O(n^2) だが、小規模〜中規模なら許容範囲)
For i = 1 To dimList.Count
For j = i + 1 To dimList.Count
Dim d1 As SldWorks.DisplayDimension: Set d1 = dimList(i)
Dim d2 As SldWorks.DisplayDimension: Set d2 = dimList(j)
' 境界ボックスの取得 (簡易的な実装)
Dim box1 As Variant: box1 = d1.GetBoundingBox
Dim box2 As Variant: box2 = d2.GetBoundingBox
' 矩形の重なり判定 (AABB: Axis-Aligned Bounding Box)
If Not IsDisjoint(box1, box2) Then
' 衝突検知:Y軸方向へオフセットを適用
' 実際には寸法の向き(Horizontal/Vertical)を判定すべき
AdjustDimensionPosition d1, offsetAmount
AdjustDimensionPosition d2, offsetAmount
End If
Next j
Next i
' --- 3. 確定フェーズ ---
swModel.ForceRebuild3 True
Dim endTime As Long
endTime = GetTickCount()
Debug.Print "Processing Time: " & (endTime - startTime) & " ms"
' メモリ解放の徹底
Set swDraw = Nothing
Set swModel = Nothing
Set swApp = Nothing
Set dimList = Nothing
End Sub
''' <summary>
''' 2つの境界ボックスが重なっているかを判定
''' </summary>
Private Function IsDisjoint(box1 As Variant, box2 As Variant) As Boolean
' box(0,1,2) = MinX, MinY, MinZ | box(3,4,5) = MaxX, MaxY, MaxZ
' 重なっていない条件を返す
If box1(0) > box2(3) Or box2(0) > box1(3) Or _
box1(1) > box2(4) Or box2(1) > box1(4) Then
IsDisjoint = True
Else
IsDisjoint = False
End If
End Function
''' <summary>
''' 寸法の位置を物理的に移動させる
''' </summary>
Private Sub AdjustDimensionPosition(dim As SldWorks.DisplayDimension, offset As Double)
Dim swAnn As SldWorks.Annotation
Set swAnn = dim.GetAnnotation
' 現在の座標を取得
Dim coords As Variant
coords = swAnn.GetPosition
' Y軸方向にオフセット (水平寸法のケース)
swAnn.SetPosition coords(0), coords(1) + offset, coords(2)
End Sub
4. レガシー環境の保守とシステム間連携への提言
この種の自動化ツールを社内標準として展開する場合、単なるVBAコード以上の設計思想が求められる。
1. 単位系の抽象化: SolidWorks APIは内部的に常にメートル(SI単位系)を使用する。ユーザーが「5mm動かしたい」と言った際に、コード内で `0.005` とハードコーディングすることは、保守性を著しく低下させる。必ず単位変換関数を介せ。
2. エラーハンドリングの堅牢化: `On Error Resume Next` は、アーキテクトの敗北を意味する。API呼び出しが失敗した際(例:ロックされたレイヤー、読み取り専用の図面)、その原因を特定し、ユーザーに適切なコンテキストを提供できるエラーハンドリングを実装せよ。
3. 外部構成ファイルとの連携: オフセット量や検知しきい値などのパラメータは、VBA内に閉じ込めず、外部の `.ini` ファイルや `XML`、あるいは社内のデータベースから取得するように設計せよ。これにより、設計ルールが変更された際に、ソースコードを再コンパイルすることなく、システム全体を即座にアップデートできる。
結論
図面の自動生成は、単に「値を書き込む」作業ではない。それは「空間を整理し、情報を伝える」という、極めて幾何学的なプロセスである。
APIの背後にあるオブジェクトのライフサイクルを理解し、計算量とメモリ管理に細心の注意を払うこと。それこそが、現場のエンジニアから「道具」として信頼される、真の自動化システムを構築するための唯一の道である。
