【実務・中級編】【中級者向け】図面内に配置された不要なスケッチエンティティや構築線を一括削除するクリーンアップVBA – SolidWorks VBA解析バイブル

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

【極限のクリーンアップ】図面データの「澱(よどみ)」を排除せよ:堅牢なエンティティ削除ロジックの設計思想

CADデータの自動化において、最も「美しく、かつ恐ろしい」領域は何か?
それは、モデルの整合性を保つことではなく、「不要なゴミ(残留データ)をいかにスマートに、かつ破壊せずに排除するか」というクリーンアップの領域だ。

実務において、インポートされたSTEPデータや、過去の編集の残骸として図面に残った「構築線」や「孤立したスケッチエンティティ」は、単なる視覚的なノイズではない。これらはファイルの再生成(Regeneration)時間を増大させ、ファイルサイズを肥大化させ、最悪の場合、アセンブリの更新時に予期せぬエラーを引き起こす「技術的負債」そのものである。

本記事では、中級者レベルのエンジニアが陥りがちな「動くが、脆いコード」を脱し、プロダクション環境に投入できるレベルの「堅牢な図面クリーンアップ・マクロ」の設計思想と実装を伝授する。

1. 初級者が陥る「インデックスの罠」と設計の失敗

まず、多くの開発者が犯す致命的なミスを指摘しておこう。それは、コレクション(集合)をループしながら、その要素を直接削除しようとすることだ。

非効率で危険なコードの例(アンチパターン)

' ❌ 絶対にやってはいけない書き方
For i = 1 To sketch.GetSketchSegments.Count
    Set segment = sketch.GetSketchSegments.Item(i)
    If segment.IsConstruction Then
        segment.Delete ' ここでインデックスがズレる!
    End If
Next i

なぜこれが致命的なのか?
SolidWorksのAPIにおけるコレクションは、要素が削除された瞬間に動的に再インデックスされる。
例えば、要素3を削除した瞬間、元々要素4だったものは「新しい要素3」へと繰り上がる。しかし、ループのカウンタ `i` は次のステップで `4` を参照するため、本来処理すべき要素をスキップしてしまうのだ。これはバグではなく、数学的な必然である。

プロフェッショナルの設計思想:逆順走査(Reverse Iteration)

堅牢なツールを作るなら、設計の鉄則は「末尾から先頭へ向かって走査すること」だ。
インデックスが後ろから前に向かって動けば、要素を削除しても「次に処理すべき要素のインデックス」には影響を与えない。これはデータ構造を扱うエンジニアとしての最低限の作法である。

2. 実装:プロダクション・グレードのクリーンアップ・エンジン

以下に、図面内の全シート、全ビューを走査し、指定した条件(構築線や不要なスケッチ)を安全に一括削除するコードを示す。
単に削除するだけでなく、エラーハンドリングとオブジェクトの解放を考慮した、実戦的な構成にしている。

Option Explicit

'' <summary>
''' 図面内の不要なスケッチエンティティ(構築線等)をクリーンアップするメインルーチン
''' </summary>
Public Sub CleanDrawingEntities()
    Dim swApp As SldWorks.SldWorks
    Dim swModel As SldWorks.ModelDoc2
    Dim swDraw As SldWorks.DrawingDoc
    Dim swSheet As SldWorks.Sheet
    Dim swView As SldWorks.View
    Dim swSketch As SldWorks.Sketch
    Dim vSheetNames As Variant
    Dim i As Long, j As Long, k As Long
    
    On Error GoTo ErrorHandler

    Set swApp = Application.SldWorks
    Set swModel = swApp.ActiveDoc

    ' 1. 文脈チェック:ドキュメントが図面であるか?
    If swModel Is Nothing Then Exit Sub
    If swModel.GetType <> swDocDRAWING Then
        MsgBox "図面ファイルを開いた状態で実行してください。", vbCritical
        Exit Sub
    End If

    Set swDraw = swModel
    vSheetNames = swDraw.GetSheetNames

    ' 描画更新を一時停止(パフォーマンス最適化の定石)
    swModel.ActiveView.EnableGraphicsUpdate = False
    
    Debug.Print "--- クリーンアップ開始 ---"

    ' 2. シート単位の走査
    For i = 0 To UBound(vSheetNames)
        swDraw.ActivateSheet vSheetNames(i)
        Set swSheet = swDraw.GetCurrentSheet
        Debug.Print "Processing Sheet: " & vSheetNames(i)

        ' 3. ビュー単位の走査
        ' GetFirstViewはシートそのものを指すため、最初のループはスキップ
        Set swView = swDraw.GetFirstView 
        
        Do While Not swView Is Nothing
            ' シート上の「配置されたビュー」のみを対象とする
            ' (注: swView.IsSheetView は false になる)
            If Not swView.IsSheetView Then
                
                ' 4. ビューに紐づくスケッチの取得
                Set swSketch = swView.GetSketch
                
                If Not swSketch Is Nothing Then
                    ' 5. 【重要】エンティティの逆順走査
                    Dim vSegments As Variant
                    vSegments = swSketch.GetSketchSegments
                    
                    If Not IsEmpty(vSegments) Then
                        ' 逆順(Step -1)でループを回すことで削除時のインデックスズレを防止
                        For k = UBound(vSegments) To 0 Step -1
                            Dim swSeg As SldWorks.SketchSegment
                            Set swSeg = vSegments(k)
                            
                            ' 判定ロジック:構築線(Construction)を削除対象とする
                            ' 必要に応じて、特定のレイヤーや属性による条件分岐を追加可能
                            If swSeg.IsConstruction Then
                                swSeg.Delete
                                Debug.Print "  [Deleted] Construction Line in View: " & swView.Name
                            End If
                        Next k
                    End If
                End If
            End If
            
            ' 次のビューへ
            Set swView = swView.GetNextView
        Loop
    Next i

    ' 6. 仕上げ:再生成と更新
    swModel.ForceRebuild3 True
    swModel.ActiveView.EnableGraphicsUpdate = True
    
    MsgBox "クリーンアップが完了しました。", vbInformation
    Debug.Print "--- クリーンアップ終了 ---"
    Exit Sub

ErrorHandler:
    ' エラー発生時のリカバリ
    swModel.ActiveView.EnableGraphicsUpdate = True
    MsgBox "予期せぬエラーが発生しました。" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical
    Debug.Print "Error: " & Err.Description
End Sub

3. アーキテクトの視点:なぜこのコードは「強い」のか

このコードが単なるスクリプトと一線を画す理由は、以下の3点にある。

① パフォーマンスへの配慮 (`EnableGraphicsUpdate`)

大量のエンティティを削除する際、SolidWorksは一回一回の削除に対して画面の再描画を試みる。これは極めて重い処理だ。
`swModel.ActiveView.EnableGraphicsUpdate = False` によって描画更新を抑制し、全工程が終了した後に一括で `ForceRebuild3` を叩く。この「バッチ処理」の考え方が、数分かかる処理を数秒に短縮する。

② 堅牢な走査ロジック (`Step -1`)

前述の通り、`For k = UBound(vSegments) To 0 Step -1` を採用している。これにより、削除によるコレクションの変化に影響されず、すべてのエンティティを漏れなく、かつ二重に処理することなく走査できる。これは、大規模な図面ファイルを扱う上で必須のテクニックだ。

③ コンテキストの分離

`swView.IsSheetView` の判定を入れている点が肝だ。
図面には「シートそのもの」というビューが存在し、その上に「モデルの投影ビュー」が乗っている。シート自体に付随するスケッチ(図枠など)を誤って削除することは、業務上致命的なミスにつながる。この「対象範囲の厳密な定義」こそが、設計者の責任である。

4. さらなる高みへ:実務での拡張ガイダンス

このコードをベースに、実際の現場では以下のような拡張を検討せよ。

1. ホワイトリスト方式の導入:
「特定のレイヤーにある構築線は残す」といった要件がある場合、`swSeg.Layer` を参照して条件分岐を追加せよ。
2. インポートデータの特定:
`swSeg.GetEntityName` 等を用いて、特定のCADソフトからインポートされた際特有の名称を持つエンティティのみを狙い撃ちするロジックを組み込め。
3. ログ出力の外部化:
`Debug.Print` ではなく、CSVやテキストファイルへ削除ログを書き出す機能を持たせれば、監査ログ(Audit Log)として業務プロセスに組み込むことが可能になる。

結論

自動化エンジニアの仕事は、単に「自動で動くもの」を作ることではない。「誰が、いつ、どのような環境で実行しても、予測可能な結果を返すもの」を作ることだ。

インデックスの操作、オブジェクトのライフサイクル、そしてAPIの挙動を深く理解した上で、細部にまでロジックを詰め込む。その積み重ねが、信頼されるアーキテクトへの唯一の道である。

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