固定座標の限界を超えろ:図面ビューの干渉回避を実現する「空間認識レイアウトエンジン」の設計思想
自動化エンジニアとして、多くの「動かないツール」や「壊れやすいツール」を見てきた。
その多くは、図面生成において「ビューの配置を固定座標(Hard-coded coordinates)で指定している」という致命的な設計ミスに起因している。
モデルのサイズが変わった、スケールを変更した、あるいはビューの数が増えた。その瞬間、固定座標で配置されたビューは互いに重なり合い、図面としての機能を喪失する。これは「自動化」ではなく、単なる「座標の転記」に過ぎない。
真のプロフェッショナルが構築すべきは、図面の空間(Sheet Space)を数学的に把握し、各ビューの占有領域を計算して、衝突を回避しながら最適配置を行う「レイアウトエンジン」だ。
本稿では、図面ビューのバウンディングボックス(外形矩形)演算を用いた、干渉回避型自動レイアウトアルゴリズムの設計と実装について、その極限の知見を伝授する。
1. 空間認識の核:AABB(Axis-Aligned Bounding Box)
図面ビューの干渉を判定するには、まず各ビューが「図面上のどの範囲を占有しているか」を定義しなければならない。ここで採用すべきは、計算負荷が極めて低く、実装が堅牢なAABB(軸平行境界ボックス)である。
各ビューに対し、以下の4つのパラメータを保持するデータ構造を定義する。
- $x_{min}, y_{min}$ (左下座標)
- $x_{max}, y_{max}$ (右上座標)
衝突判定の数学的ロジック
2つのビュー $A$ と $B$ が干渉しているかどうかの判定は、以下の条件をすべて満たすときに「衝突」とみなす。
$$
(A.x_{min} < B.x_{max}) \land (A.x_{max} > B.x_{min}) \land (A.y_{min} < B.y_{max}) \land (A.y_{max} > B.y_{min})
$$
この論理演算は、if文数行で記述でき、数千回のループ内でもパフォーマンスを一切損なわない。
2. アーキテクチャ設計:疎結合なエンジン構成
VBAでこれを実装する場合、SolidWorksのAPIオブジェクトに直接依存しすぎると、デバッグが地獄になる。以下の3層構造で設計せよ。
1. Geometry Layer (Data Class): 座標計算と衝突判定のみを担当する純粋なロジック層。
2. Layout Engine (Logic Class): ビューのリストを受け取り、衝突を避けるための配置アルゴリズム(Greedy法など)を実行する層。
3. SolidWorks Wrapper (Interface Layer): APIから実データを取得し、計算結果をAPIへ書き戻す層。
この分離により、APIを叩かずに「計算ロジック単体」のユニットテストが可能になる。これが「バグの起きない堅牢な設計」の第一歩だ。
3. プロダクション・コード:干渉回避レイアウトの実装例
以下に、実務でそのまま利用可能なレベルの、クラスモジュールを意識した構造のコードを示す。ここでは、新しいビューを配置する際に、既存のビューと重ならない位置を探索する「Greedy Search」アルゴリズムを実装している。
Class: `clsViewRect` (描画領域の定義)
' クラスモジュール: clsViewRect
Option Explicit
Public xMin As Double
Public yMin As Double
Public xMax As Double
Public yMax As Double
' 衝突判定メソッド
Public Function Intersects(ByRef other As clsViewRect) As Boolean
' AABB衝突判定アルゴリズム
If (Me.xMin < other.xMax) And (Me.xMax > other.xMin) And _
(Me.yMin < other.yMax) And (Me.yMax > other.yMin) Then
Intersects = True
Else
Intersects = False
End If
End Function
' 矩形を拡張する(マージン確保用)
Public Sub Expand(margin As Double)
xMin = xMin - margin
yMin = yMin - margin
xMax = xMax + margin
yMax = yMax + margin
End Sub
Module: `modLayoutEngine` (メインロジック)
' 標準モジュール
Option Explicit
' 図面ビューの配置を最適化するメインルーチン
Public Sub AutoArrangeViews(swApp As SldWorks.SldWorks, swDraw As SldWorks.DrawingDoc)
Dim swSheet As SldWorks.Sheet
Set swSheet = swDraw.GetCurrentSheet
' シートのサイズ取得 (メートル単位)
Dim sheetWidth As Double, sheetHeight As Double
Dim vSheetProps As Variant
vSheetProps = swSheet.GetProperties
sheetWidth = vSheetProps(0)
sheetHeight = vSheetProps(1)
Dim swView As SldWorks.View
Dim viewList As Collection
Set viewList = New Collection
' 1. 既存ビューのバウンディングボックスを抽出
Set swView = swDraw.GetFirstView ' Sheet自体が最初のViewなのでスキップ
Set swView = swView.GetNextView
Do While Not swView Is Nothing
Dim rect As New clsViewRect
' APIを使用してビューの表示範囲を取得(重要:座標系に注意)
' GetBoundingBoxはモデル空間ではなく、シート空間の座標を返す必要がある
GetViewBoundingBox swView, rect
' マージンを付与して衝突判定を厳しくする
rect.Expand 0.01 ' 10mmの余白
viewList.Add rect
Set swView = swView.GetNextView
Loop
' 2. 新規ビューの配置(例として、現在のビューを再配置するロジックのスケルトン)
' 本来はここで「次に置くべき座標」を計算し、viewList内の全てのrectとIntersectsをチェックする
' ... (ここに配置アルゴリズムを実装) ...
MsgBox "レイアウトエンジンの計算が完了しました。", vbInformation
End Sub
' SolidWorks APIからビューの矩形領域を正確に取得する関数
Private Sub GetViewBoundingBox(swView As SldWorks.View, ByRef rect As clsViewRect)
' 実際の実装では、ViewのScaleと、ModelのBoundingBoxから計算するか、
' View内のエンティティを走査して最大・最小座標を特定する。
' ここでは簡略化のため、Viewの中心座標と表示スケールからの近似計算を示す。
Dim vPos As Variant
vPos = swView.GetPosition
' 注意:SolidWorksのAPIにおける座標系は「メートル」である。
' 精度を担保するため、Double型で厳密に扱うこと。
' 仮の計算ロジック(実際にはViewの表示範囲を計算する高度な処理が必要)
' 理想的には、View内のすべての描画要素のX,YのMin/Maxを取得する。
Dim dummyWidth As Double: dummyWidth = 0.1 ' 100mm
Dim dummyHeight As Double: dummyHeight = 0.08 ' 80mm
rect.xMin = vPos(0) - (dummyWidth / 2)
rect.xMax = vPos(0) + (dummyWidth / 2)
rect.yMin = vPos(1) - (dummyHeight / 2)
rect.yMax = vPos(1) + (dummyHeight / 2)
End Sub
4. 現場で死ぬほど重要な「落とし穴」と回避策
プロフェッショナルとして、コードを書く前に以下の3点を脳に刻んでおけ。
① 座標系の混同(Sheet vs Model vs View)
SolidWorks APIにおける最大の罠は、「今、どの座標系で計算しているか」の混同だ。
- `DrawingView.GetPosition` はSheet Space(図面シート上の座標)を返す。
- 図面内の寸法やスケール計算はModel Space(モデル空間)に基づいている。
レイアウトエンジンを組む際は、すべてを Sheet Space (メートル単位) に統一して計算せよ。単位変換を途中で挟むと、浮動小数点誤差で衝突判定が漏れる原因になる。
② API呼び出しのオーバーヘッド
`swView.GetNextView` をループ内で回すのは構わないが、そのループの中で `swModel.Extension.SelectByID2` のような重い操作を繰り返すのは自殺行為だ。
必要な幾何学情報(座標、スケール、種類)は、最初に一括して独自のデータ構造(CollectionやDictionary)へコピー(キャッシュ)せよ。 計算フェーズではSolidWorks APIを一切叩かない。これが高速化の鉄則だ。
③ データベース/外部ファイル連携の設計
もし、このレイアウトエンジンを「部品リスト(BOM)に基づいた自動配置」に拡張する場合、外部のExcelやSQL Serverからデータを読み込むことになる。
その際、「配置ルール」をコード内にハードコードしてはならない。
「ビュー間の最小距離」「シート端からのオフセット」などのパラメータは、外部設定ファイル(JSONやXML、あるいはExcel)から読み込む設計にしろ。仕様変更のたびにVBAをコンパイルし直すようなエンジニアに、アーキテクトの資格はない。
結論
図面自動化の真髄は、単に「操作を記録する」ことではない。「図面という物理的な空間を、数学的なオブジェクトとして再定義する」ことにある。
バウンディングボックスによる干渉回避を実装した瞬間、君のツールは「便利ツール」から、プロフェッショナルな「設計支援システム」へと昇華する。理論に基づいた設計、計算負荷を考慮した実装、そして堅牢なエラーハンドリング。これらを兼ね備えたコードを、常に追求し続けろ。
