【極限の図面自動化】バウンディングボックス幾何学による「非干渉オートレイアウト」エンジンの設計思想
図面作成の自動化において、多くの開発者が最初に突き当たる壁がある。それは「ビューの配置」だ。
初心者は、`DrawingView.Position` に固定の座標を代入する。中級者は、ビューのサイズを取得して、単純なオフセットを加算して配置しようとする。しかし、これらはすべて「設計の敗北」だ。複雑なアセンブリ図面において、ビューの大きさは投影スケールやモデルの形状によって動的に変化し、さらに寸法線(Dimensions)が描画されることで、その「占有領域」は予測不能な広がりを見せる。
本稿では、単なる座標制御を超え、幾何学的な「占有領域(Bounding Box)」に基づいた、ビュー同士の干渉を数学的に回避するオートレイアウト・エンジンの設計思想と実装方法を伝授する。
1. 「点」ではなく「領域」で捉える:AABB(Axis-Aligned Bounding Box)の概念
図面上のビューを「中心点(X, Y)」として扱うのは、設計上の致命的なミスである。プロフェッショナルは、ビューを「占有領域を持つ2次元の矩形」として定義する。
ここで採用すべきアルゴリズムは、計算負荷が極めて低く、かつ堅牢な AABB (Axis-Aligned Bounding Box) である。各ビューが持つ `Xmin, Xmax, Ymin, Ymax` の4つの境界値を用い、2つの矩形が重なっているかどうかを判定する。
干渉判定の論理式
2つのビュー(View A と View B)が干渉している条件は、以下のすべてを満たす時である:
1. `A.Xmin < B.Xmax`
2. `A.Xmax > B.Xmin`
3. `A.Ymin < B.Ymax`
4. `A.Ymax > B.Ymin`
この論理式を反転させることで、「干渉していない(安全な)配置」を瞬時に判定できる。
2. 「幾何学的シャドウ」:寸法線の広がりをどう計算するか
実務上の最大の難所は、「ビューそのものの大きさ」と「寸法線の広がり」の乖離だ。
SolidWorks APIの `View.GetBox` で取得できる矩形は、あくまで投影されたモデルの範囲である。しかし、設計図面において重要なのは、その周囲に展開される寸法線や注釈(Annotation)が占めるスペースだ。
これを解決するために、我々は「幾何学的シャドウ(Geometric Shadow)」という概念を導入する。
- Buffer Factor(バッファ係数): ビューの矩形に対し、設計ルールに基づいたマージン(例: 20mm)を外周に付加する。
- Scale-Dependent Margin: スケールが小さいほど、寸法線の影響力が増大することを考慮し、マージンを動的に計算する。
3. プロダクション級の実装:クラス設計によるカプセル化
スパゲッティコードを避けるため、API操作と幾何学演算を分離する。以下に、保守性と再利用性を極限まで高めたVBAクラスの構造を示す。
クラス名:`clsLayoutEngine` (概念実装)
' ==========================================================================
' Class: clsViewGeometry
' Purpose: ビューの幾何学情報をカプセル化し、干渉判定を行う
' ==========================================================================
Public XMin As Double
Public XMax As Double
Public YMin As Double
Public YMax As Double
Public ViewObject As SldWorks.DrawingView
' ビューの境界情報を取得し、バッファを適用する
Public Sub UpdateGeometry(ByVal buffer As Double)
Dim vBox As Variant
' GetBoxは [Xmin, Xmax, Ymin, Ymax] の配列を返す
vBox = ViewObject.GetBox
' バッファ(幾何学的シャドウ)を適用して境界を拡張
XMin = vBox(0) - buffer
XMax = vBox(1) + buffer
YMin = vBox(2) - buffer
YMax = vBox(3) + buffer
End Sub
' AABBアルゴリズムによる干渉判定
Public Function Intersects(ByRef other As clsViewGeometry) As Boolean
Intersects = Not (XMax < other.XMin Or _
XMin > other.XMax Or _
YMax < other.YMin Or _
YMin > other.YMax)
End Function
メインロジック:非干渉配置エンジン
' ==========================================================================
' Module: modAutoLayout
' Description: 衝突回避アルゴリズムを用いたビュー配置プロセス
' ==========================================================================
Sub ExecuteSmartLayout()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swView As SldWorks.DrawingView
Dim viewList As Collection
Set viewList = New Collection
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swDraw = swModel
' 1. 全ビューのジオメトリ情報を収集
Set swView = swDraw.GetFirstView ' Sheet自体が最初のViewとして取得されるため注意
Set swView = swView.GetNextView
Dim vGeo As clsViewGeometry
Dim buffer As Double: buffer = 0.02 ' 単位はメートル (20mm)
Do While Not swView Is Nothing
Set vGeo = New clsViewGeometry
Set vGeo.ViewObject = swView
vGeo.UpdateGeometry buffer
viewList.Add vGeo
Set swView = swView.GetNextView
Loop
' 2. 配置計算(Greedy Placement Algorithm)
' ここでは単純化のため、既存のビューを避けてグリッド状に配置するロジックを想定
Dim i As Integer, j As Integer
Dim targetX As Double, targetY As Double
Dim attemptX As Double, attemptY As Double
Dim isCollision As Boolean
' 配置のステップ(解像度)
Dim stepSize As Double: stepSize = 0.05 ' 50mm刻み
For i = 1 To viewList.Count
Set vGeo = viewList(i)
isCollision = True
' 衝突しない座標が見つかるまでスキャン(簡易的な探索)
' 本来はより高度なパッキングアルゴリズムを用いるべき
attemptX = 0: attemptY = 0
Do While isCollision
' 新しい候補座標を決定
' ここでは左下から順にスキャンするロジック
' (実務ではSheetの境界条件に基づいたループ設計が必要)
' --- 衝突判定ループ ---
isCollision = False
Dim k As Integer
For k = 1 To viewList.Count
' 自分自身との判定はスキップ
If viewList(k) IsNot vGeo Then
' 暫定的な位置での衝突をシミュレーション
' (実際にはView.Positionを一時的に変更して判定するか、
' 数学的に計算した仮の境界矩形で判定する)
If CheckVirtualCollision(vGeo, attemptX, attemptY, viewList, k) Then
isCollision = True
Exit For
End If
End If
Next k
If isCollision Then
attemptX = attemptX + stepSize
If attemptX > 0.5 Then ' Sheetの端に達したら
attemptX = 0
attemptY = attemptY + stepSize
End If
End If
' 無限ループ防止(デバッグ用)
If attemptY > 0.5 Then Exit Do
Loop
' 3. 確定した座標への適用
vGeo.ViewObject.Position = Array(attemptX, attemptY)
Next i
swModel.EditRebuild3
End Sub
' 仮想的な位置での衝突を判定する関数(メモリ上の計算のみ)
Private Function CheckVirtualCollision(target As clsViewGeometry, tx As Double, ty As Double, _
list As Collection, currentIndex As Integer) As Boolean
' targetの現在の境界に、新しい(tx, ty)のオフセットを適用して
' 他のビュー(list(k))と重なるかを判定する
' ※実装の詳細は省略するが、数学的な矩形比較を行う
End Function
4. 堅牢なシステムのための「アーキテクトの視点」
このエンジンをプロダクション環境(実務ツール)として稼働させるには、コードの書き方以上に、以下の3点に留意せよ。
① データベース/外部設定との連携
配置ルール(バッファ量、配置順序、優先度)をVBAコード内にハードコードしてはならない。
- 設計指針: 配置パラメータは外部の `JSON` または `XML` ファイル、あるいは `Excel` のマスターシートから読み込む設計にせよ。これにより、図面規格の変更時にプログラムを再コンパイルすることなく、設定値の変更だけで対応可能になる。
② エラーハンドリングと例外処理
自動配置アルゴリズムは、解が見つからない(配置スペースが足りない)場合に「無限ループ」や「計算爆発」を起こすリスクがある。
- 設計指針: `Do While` ループには必ず `MaxIterations`(最大試行回数)を設け、解が見つからない場合は「手動配置モード」へフォールバックするか、エラーログを出力して停止させる設計にせよ。
③ パフォーマンスの最適化
ビューの数が増えると、衝突判定の計算量は $O(n^2)$ で増加する。
- 設計指針: 大規模アセンブリを扱う場合、すべてのビューを総当たりで比較するのは非効率だ。空間分割法(Quadtreeなど)を用いて、近傍にあるビューのみを判定対象とするアルゴリズムを検討せよ。
結論
「ビューを配置する」という行為を、単なる座標の操作から「幾何学的な領域の管理」へと昇華させること。これこそが、プロフェッショナルな自動化エンジニアと、単なるスクリプターを分かつ境界線である。
このエンジンを基盤に、君のツールは「予測不能な複雑さ」を「制御可能な秩序」へと変える力を持つはずだ。
