【極限のクリーンアップ】図面データの「澱(よどみ)」を排除せよ:堅牢なエンティティ削除ロジックの設計思想
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の挙動を深く理解した上で、細部にまでロジックを詰め込む。その積み重ねが、信頼されるアーキテクトへの唯一の道である。
