【テクニカル・上級編】【上級プロ向け】図面内アノテーション(幾何公差・表面粗さ記号)の座標解析と干渉自動検出モジュール – SolidWorks VBA解析バイブル

スポンサーリンク
スポンサーリンク

図面アノテーションの干渉回避:空間幾何学を用いたレイアウト最適化の極致

図面自動生成の自動化において、多くの開発者が「アノテーションを正しい位置に配置すること」をゴールだと誤解している。しかし、実務における真の地獄は、配置した後に訪れる。

幾何公差(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)によって収束させる。これにより、極めて自然で視認性の高いレイアウトが実現する。

結びに代えて

自動化の本質は、単なる「作業の短縮」ではない。「人間が本来集中すべき思考プロセスから、低次な調整作業を解放すること」にある。

図面内の微細な重なりを計算機に解決させる。その一歩が、エンジニアリングの精度を次の次元へと引き上げるのだ。

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