デジタル・ハイジーン:SolidWorks図面における「ゴースト・エンティティ」の殲滅とメモリ管理の極致
大規模な設計プロジェクトにおいて、CADデータの「エントロピー(無秩序さ)」は静かに、しかし確実に進行する。
モデルから図面を生成する際、あるいは外部のSTEP/IGESファイルをインポートして図面化した際、図面ファイルには「意図しないスケッチエンティティ」が残留する。これらは、目には見えない、あるいは構築線として薄く表示されるだけの「ゴースト(幽霊)」だ。一見、設計に影響を与えない無害な存在に見えるが、数千枚の図面を管理するシステム管理者や、自動化パイプラインを構築するシニアエンジニアにとって、これらは致命的な毒となる。
ファイルサイズの肥大化、再構築(Rebuild)時間の増大、そして何より、後続のAPI処理やPLM連携における予期せぬエラーの温床となるからだ。
本稿では、単なる「削除マクロ」の提供はしない。SolidWorksのCOMオブジェクトモデルの深淵に触れ、メモリリークを回避し、実行速度を極限まで高めた「図面クリーンアップ・エンジン」の設計思想を詳解する。
1. 図面構造の解剖:なぜ単純な削除では不十分なのか
図面(`DrawingDoc`)の構造は、単なる平面ではない。それは `Sheet`、`View`、そして各ビューに紐付いた `Sketch` という階層構造を持つ複雑なデータベースである。
多くの開発者が陥る罠は、`SelectionMgr` を用いて画面上のオブジェクトを操作しようとすることだ。これはUI操作の自動化としては成立するが、大量のエンティティを処理する際には、選択操作に伴うグラフィックス更新のオーバーヘッドが指数関数的に増大し、システムをフリーズさせる。
真にプロフェッショナルなアプローチは、「階層的なオブジェクトトラバーサル(走査)」である。
1. `DrawingDoc` から全 `Sheet` を列挙。
2. 各 `Sheet` 内の全 `View` を列挙。
3. 各 `View` が持つ `Sketch` コンテキストへアクセス。
4. 条件に合致する `SketchEntity` を特定し、直接削除。
このプロセスにおいて、最も重要なのは「どのエンティティが『不要』であるか」の定義を厳格にすることだ。構築線(Construction lines)や、インポートされた際に生成される孤立した点(Points)を対象とするが、ユーザーが意図的に配置した注釈用スケッチまで破壊してはならない。
2. 実装:高パフォーマンス・クリーンアップ・エンジン
以下に、メモリ最適化とエラーハンドリングを極限まで突き詰めた実装例を示す。このコードは、64bit環境を想定し、Windows APIを用いた描画停止制御を組み込んでいる。
'==============================================================================
' Module: DrawingCleanupEngine
' Description: 図面内の不要なスケッチエンティティ(構築線・孤立点)を一括削除する
' Author: Chief Architect
'==============================================================================
Option Explicit
' Windows API: 描画の更新を抑制し、処理速度を向上させるための準備
' (VBAの標準機能だけでは不十分な場合があるため、システムレベルでの制御を意識する)
If VBA7 Then
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Else
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
End If
Public Sub ExecuteCleanSweep()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swSheet As SldWorks.Sheet
Dim vSheets As Variant
Dim i As Long
Dim swView As SldWorks.View
Dim swSketch As SldWorks.Sketch
Dim vEntities As Variant
Dim swEnt As SldWorks.SketchEntity
Dim deleteCount As Long
Dim startTime As Double
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
startTime = Timer
' 2. パフォーマンス最適化:グラフィックス更新の停止
' これを行わないと、1要素削除するごとに画面が再描画され、処理が壊滅的に遅くなる
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swDisplayGraphicsUpdate, False
' 3. 処理開始
vSheets = swDraw.GetSheetNames
deleteCount = 0
For i = 0 To UBound(vSheets)
swDraw.ActivateSheet vSheets(i)
Set swSheet = swDraw.GetCurrentSheet
' シート内の全ビューを走査
' GetFirstView はシート自体(Sheet View)を返すため、その次の View からが実質的なビュー
Set swView = swDraw.GetFirstView
Do While Not swView Is Nothing
' ビューに紐付くスケッチを取得
Set swSketch = swView.GetSketch
If Not swSketch Is Nothing Then
' スケッチ内のエンティティを列挙
vEntities = swSketch.GetSketchEntities
If Not IsEmpty(vEntities) Then
Dim j As Long
' 後ろから走査するのが鉄則(削除によるインデックスのズレを防ぐ)
For j = UBound(vEntities) To LBound(vEntities) Step -1
Set swEnt = vEntities(j)
' 【判定ロジック】
' ここでは「構築線」または「点」を削除対象とする
' 必要に応じて、特定のレイヤーや名前によるフィルタリングを追加せよ
If IsUnwantedEntity(swEnt) Then
swEnt.Delete
deleteCount = deleteCount + 1
End If
Next j
End If
End If
' 次のビューへ
Set swView = swView.GetNextView
Loop
Next i
' 4. 後処理と復元
swModel.ForceRebuild3 True
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swDisplayGraphicsUpdate, True
MsgBox "クリーンアップ完了。" & vbCrLf & _
"削除されたエンティティ数: " & deleteCount & vbCrLf & _
"実行時間: " & Format(Timer - startTime, "0.00") & " 秒", vbInformation
CleanExit:
' オブジェクトの明示的解放:メモリリークを防ぐための儀式
Set swEnt = Nothing
Set swSketch = Nothing
Set swView = Nothing
Set swSheet = Nothing
Set swDraw = Nothing
Set swModel = Nothing
Set swApp = Nothing
Exit Sub
ErrorHandler:
swApp.SetUserPreferenceToggle swUserPreferenceToggle_e.swDisplayGraphicsUpdate, True
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
Resume CleanExit
End Sub
'==============================================================================
' Function: IsUnwantedEntity
' Description: エンティティが削除対象(不要な構築線等)か否かを判定する
'==============================================================================
Private Function IsUnwantedEntity(swEnt As SldWorks.SketchEntity) As Boolean
Dim swSeg As SldWorks.SketchSegment
Set swSeg = swEnt.GetSegment
If swSeg Is Nothing Then
' セグメントを持たないもの(点など)の判定
' 孤立した点などを検知する場合はここを拡張する
IsUnwantedEntity = True
Exit Function
End If
' 構築線(Construction Geometry)であるかを確認
If swSeg.ConstructionGeometry = True Then
IsUnwantedEntity = True
Else
IsUnwantedEntity = False
End If
End Function
3. 極限の知見:アーキテクトの視点
このコードを実戦投入するにあたり、以下の3点を肝に銘じておくことだ。
A. オブジェクトのライフサイクル管理
VBAはガベージコレクション(GC)に頼り切る設計は推奨されない。特にループ内で大量の `SketchEntity` オブジェクトを生成する場合、`Set swEnt = Nothing` による明示的な解放、あるいはスコープの限定を徹底しなければ、SolidWorksのプロセス(`SLDWORKS.exe`)のメモリ使用量が右肩上がりに増大し、最終的にクラッシュを引き起こす。
B. 逆方向走査(Reverse Iteration)の絶対原則
配列(`vEntities`)から要素を削除する場合、インデックスは動的に変化する。前から削除すると、削除した瞬間に次の要素のインデックスが繰り上がり、スキップが発生する。これはバグの温床だ。必ず `Step -1` を用いて、末尾から先頭へと走査せよ。これはエンジニアとしての「作法」である。
C. 描画更新の制御(Graphics Update Suppression)
`swApp.SetUserPreferenceToggle` による制御は、単なる高速化技術ではない。これは、API実行中の「状態の不安定さ」を回避するための防衛策でもある。描画更新がONのまま大量の削除を行うと、SolidWorksの内部計算エンジンとAPIの命令が競合し、不整合なデータが書き込まれるリスクがある。
結言
図面のクリーンアップは、単なる「掃除」ではない。それは、設計データの整合性を担保し、システムのライフサイクルを延命させるための「エンジニアリング」である。
提供したコードは、あくまで基盤に過ぎない。現場の要件に応じて、`IsUnwantedEntity` 内の判定ロジックを研ぎ澄ませ、独自の「不要定義」を組み込むこと。それが、真に堅牢な自動化システムを構築する唯一の道である。
