図面アノテーションの干渉回避:空間幾何学を用いたレイアウト最適化の極致
図面自動生成の自動化において、多くの開発者が「アノテーションを正しい位置に配置すること」をゴールだと誤解している。しかし、実務における真の地獄は、配置した後に訪れる。
幾何公差(GD&T)や表面粗さ記号が、モデルの輪郭線や他の注釈と重なり、視認性を損なう瞬間。それは設計品質を疑わせる致命的な欠陥となる。これを「手動で直す」のは、自動化を導入した意味を根底から否定する行為だ。
本稿では、SolidWorks APIを極限まで使いこなし、図面内のアノテーション同士の衝突を検出し、数学的アプローチによって自動的に配置を最適化する、高度なレイアウト制御モジュールの設計思想を詳解する。
1. 座標系の罠:正規化された空間への変換
アノテーションの干渉判定を行う際、初心者が必ず陥る罠がある。それは、「座標系の不一致」だ。
SolidWorksの図面において、アノテーションの座標は以下のいずれかに依存している。
1. Sheet(図面シート)座標系
2. View(配置ビュー)座標系
3. Model(モデル空間)座標系
ビュー内に配置された幾何公差の座標を、そのままシート座標として比較することは不可能だ。衝突判定を行うには、すべてのオブジェクトを「シート上の絶対座標(Global Sheet Coordinates)」へと射影(Projection)する必要がある。
これには、`View::GetCoordinateSystem` を用いてビューの変換行列を取得し、各アノテーションの相対座標に対して行列演算を行うプロセスが不可欠だ。
2. 衝突判定アルゴリズム:AABB(Axis-Aligned Bounding Box)の採用
計算コストと精度のトレードオフを考慮したとき、アノテーションの干渉判定には AABB (Axis-Aligned Bounding Box) アルゴリズムが最も合理的である。
アノテーションは複雑な形状をしているが、干渉判定の目的は「重なっているか否か」の高速な識別だ。各アノテーションの `GetBoundingBox` メソッドから取得した最小・最大座標を用いて、矩形領域として定義する。
$O(n^2)$ の計算量は避けられないが、図面内のアノテーション数 $n$ が数百程度であれば、現代のプロセッサにおいてこの計算は瞬時である。もし数千規模を扱う場合は、空間分割(Quadtree等)を導入する検討が必要だが、図面自動化の文脈ではAABBで十分だ。
3. 実装:干渉検出および自動位置調整モジュール
以下に、メモリ管理とパフォーマンスを最適化した実装例を示す。ここでは、オブジェクトの明示的な解放(`Set = Nothing`)を行い、COM参照のリークを防ぐ設計を徹底している。
'**
' Module: AnnotationOptimizer
' Description: 図面内アノテーションの干渉を検出し、位置を微調整する
' Author: Chief Architect
'**
Option Explicit
' アノテーションの境界情報を保持するための構造体
Private Type AnnotationNode
Ann As SldWorks.Annotation
MinX As Double
MaxX As Double
MinY As Double
MaxY As Double
IsAdjusted As Boolean
End Type
' Windows API: 高精度なパフォーマンス計測用
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Public Sub OptimizeAnnotationLayout(swDraw As SldWorks.DrawingDoc)
Dim swSheet As SldWorks.Sheet
Dim swView As SldWorks.View
Dim swAnn As SldWorks.Annotation
Dim nodes() As AnnotationNode
Dim nodeCount As Long
Dim i As Long, j As Long
Dim startTime As Long
If swDraw Is Nothing Then Exit Sub
startTime = GetTickCount()
nodeCount = 0
Set swSheet = swDraw.GetCurrentSheet
' 1. 全アノテーションの走査と境界ボックスの取得
' 座標系をシート座標に正規化して格納する
Set swView = swDraw.GetFirstView ' Sheet自体が最初のView
Do While Not swView Is Nothing
Set swAnn = swView.GetFirstAnnotation
Do While Not swAnn Is Nothing
' 幾何公差や表面粗さなどの特定タイプのみを対象とする(必要に応じて条件追加)
' swAnnotationType_e.swAnnotationType_Note 等
nodeCount = nodeCount + 1
ReDim Preserve nodes(1 To nodeCount)
Set nodes(nodeCount).Ann = swAnn
' GetBoundingBoxを使用して矩形領域を取得
' 注意: 戻り値の座標系はViewに依存するため、ここでは概念的に正規化が必要
' 実際の実装では、swView.GetCoordinateSystem を用いた変換を行う
Call GetNormalizedBoundingBox(swAnn, swView, nodes(nodeCount))
nodes(nodeCount).IsAdjusted = False
Set swAnn = swAnn.GetNext
Loop
Set swView = swView.GetNext
Loop
' 2. 衝突判定と位置調整(Repulsion Algorithm)
' 簡易的な斥力モデルを用いて、重なった場合にY軸方向にオフセットさせる
For i = 1 To nodeCount
For j = i + 1 To nodeCount
If CheckCollision(nodes(i), nodes(j)) Then
' 干渉検知:重なりがある場合、ノード(i)を上に押し上げる
' 実際のプロダクションコードでは、移動方向をベクトル計算で決定すべき
Dim offsetValue As Double
offsetValue = 0.005 ' 5mmのオフセット(単位系に注意)
' アノテーションの位置を更新
Dim currentPos As Variant
currentPos = nodes(i).Ann.GetPosition
nodes(i).Ann.SetPosition currentPos(0), currentPos(1) + offsetValue
' 座標情報の再計算(簡易化のためここでは再計算しないが、厳密には必要)
nodes(i).MaxY = nodes(i).MaxY + offsetValue
nodes(i).MinY = nodes(i).MinY + offsetValue
nodes(i).IsAdjusted = True
End If
Next j
Next i
' 3. メモリの明示的解放(COMオブジェクトのライフサイクル管理)
' 配列内の各Annotationオブジェクトを解放するプロセスは、
' 大規模なループ内では極めて重要である
For i = 1 To nodeCount
Set nodes(i).Ann = Nothing
Next i
Debug.Print "Optimization completed in " & (GetTickCount() - startTime) & " ms"
End Sub
' 座標系を正規化してBoundingBoxを取得する内部関数
Private Sub GetNormalizedBoundingBox(ByRef swAnn As SldWorks.Annotation, ByRef swView As SldWorks.View, ByRef node As AnnotationNode)
Dim vBox As Variant
vBox = swAnn.GetBoundingBox
' ここで swView の変換行列を適用し、Sheet座標系へ変換するロジックを実装
' 今回は簡略化のため、取得した値をそのまま代入
node.MinX = vBox(0)
node.MaxX = vBox(3)
node.MinY = vBox(1)
node.MaxY = vBox(2)
End Sub
' AABBによる衝突判定
Private Function CheckCollision(ByRef n1 As AnnotationNode, ByRef n2 As AnnotationNode) As Boolean
CheckCollision = Not (n1.MaxX < n2.MinX Or n1.MinX > n2.MaxX Or _
n1.MaxY < n2.MinY Or n1.MinY > n2.MaxY)
End Function
4. 伝説的アーキテクトの知見:実運用における極意
このコードを単に動かすだけでは、シニアエンジニアとしては失格だ。実運用に耐えうるシステムにするためには、以下の3点を考慮せよ。
メモリリークの徹底排除
VBAはCOM(Component Object Model)の上で動作している。`swAnn.GetNext` で取得したオブジェクトを適切に `Set = Nothing` しない限り、SolidWorksのプロセスはメモリを食いつぶし、最終的にはクラッシュする。特に、図面生成のような「ループ内で大量のオブジェクトを生成・破棄する」処理では、オブジェクトのライフサイクル管理がシステムの寿命を決定する。
座標変換の精度
上記のコードでは簡略化しているが、実務では `swView.GetCoordinateSystem` で取得した `MathTransform` オブジェクトを使用し、各アノテーションの座標に対して `Transform` を適用しなければならない。これを行わないと、ビューを回転させた際や、斜めに配置されたビューにおいて、判定が完全に無意味なものとなる。
「押し出し」のアルゴリズム(Repulsion Strategy)
単に $Y$ 方向にずらすだけでは、移動した先で別のオブジェクトと衝突する「連鎖衝突」が発生する。
高度なシステムでは、「Force-directed Graph」 の考え方を導入すべきだ。各アノテーションを「粒子」と見なし、重なりを「斥力(反発力)」として計算し、システム全体のエネルギーが最小になる位置へ反復計算(Iteration)によって収束させる。これにより、極めて自然で視認性の高いレイアウトが実現する。
結びに代えて
自動化の本質は、単なる「作業の短縮」ではない。「人間が本来集中すべき思考プロセスから、低次な調整作業を解放すること」にある。
図面内の微細な重なりを計算機に解決させる。その一歩が、エンジニアリングの精度を次の次元へと引き上げるのだ。
