【極限の設計論】巨大図面の「ゾーン分割」自動化:A0/A1からA3への再構成をマスターする
プラント設計や大型構造物の設計現場において、避けて通れない「地獄」がある。それは、A0やA1といった巨大な図面を、現場への配布や承認のためにA3サイズへと分割して出力しなければならない作業だ。
多くの開発者は、この課題に対して「画面のキャプチャを撮る」ような場当たり的なアプローチ、あるいは「図面ビューを力技でコピー&ペーストする」ような、極めて脆弱なコードを書いてしまう。しかし、それでは図面の精度、スケール、そして何より「保守性」が保てない。
本記事では、単なる「分割ツール」の作り方を教えるのではない。「座標系を数学的に制御し、モデルの参照性を維持したまま、新しい図面コンテキストへ再構成する」という、プロフェッショナルな設計思想を伝授する。
1. なぜ、あなたの「図面分割VBA」は失敗するのか
実装に入る前に、凡庸なエンジニアが陥る3つの致命的なミスを指摘しておく。これらを回避できない限り、業務で使えるツールを作ることは不可能だ。
① 座標系の誤解(ピクセル vs メートル)
SolidWorks APIの内部単位は、基本的にメートル (m) である。画面上のピクセル座標や、WindowsのAPIで使う座標系をそのまま図面操作に持ち込むと、スケールが狂い、図面はあらぬ方向へ飛んでいく。すべての計算は、図面のシート原点からの絶対座標(メートル)で行わなければならない。
② オブジェクトの「浮き」と参照切れ
図面ビューを `Copy` / `Paste` で移動させようとすると、ビューに紐付いたモデルの参照や、注釈(Annotation)の座標が壊れることが多々ある。プロの設計者は、既存のビューを「動かす」のではなく、「新しいシートに、同じモデル・同じスケール・計算された位置で、ビューを再生成する」というアプローチを取る。
③ リビルドの無視
大量のビューを生成・配置する際、逐一リビルドを走らせるコードは、処理速度を著しく低下させる。また、リビルドを全く行わないコードは、計算結果が反映されない不完全な図面を生む。`swApp.BlockRebuild` と `swApp.SkipUpdate` を戦略的に使い分ける制御が必要だ。
2. アーキテクチャ設計:グリッド・マッピング・エンジン
堅牢なスプリッターを構築するための設計指針は、「ソース図面をグリッド(格子)として定義し、各グリッドの境界内に存在するビューを抽出・再配置する」というロジックに集約される。
設計フロー
1. ソース解析: A0図面の全ビューを走査し、各ビューの `GetDisplayArea`(表示領域)を取得する。
2. 分割領域の定義: A0を $N \times M$ のグリッドに分割し、各グリッド(A3相当)の境界座標(左下・右上)を算出する。
3. 衝突判定(Collision Detection): 各ビューの表示領域と、各グリッドの境界が重なっているかを数学的に判定する。
4. 再構成(Reconstruction):
- 新しい図面(A3テンプレート)を作成。
- 対象のビューを、新しいシートの適切な位置・スケールで `InsertModelView` する。
3. 実装例:プロダクション・クオリティのコード
以下に、実務に耐えうる構造を持ったVBAコードの骨子を示す。これは単なるサンプルではない。エラーハンドリングと、オブジェクトのライフサイクルを意識した設計パターンだ。
'**
' Module: DrawingSplitterEngine
' Description: A0/A1図面をA3の複数シートに分割・再構成するエンジン
'**
Option Explicit
' 座標計算用の構造体
Private Type Rect
XMin As Double
YMin As Double
XMax As Double
YMax As Double
End Type
Sub Main()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc
' 1. ガード節: 図面ファイルかチェック
If swModel Is Nothing Then Exit Sub
If swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いた状態で実行してください。"
Exit Sub
End If
' 2. 設定値(本来は外部DBやExcelから取得すべき)
Dim targetSheetSize As Double: targetSheetSize = 0.42 ' A3の長辺(m)
Dim splitRows As Integer: splitRows = 2
Dim splitCols As Integer: splitCols = 2
' 3. 処理開始
On Error GoTo ErrorHandler
ExecuteSplit swApp, swModel, splitRows, splitCols, targetSheetSize
MsgBox "分割処理が完了しました。"
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
End Sub
''' <summary>
''' 分割ロジックの本体
''' </summary>
Private Sub ExecuteSplit(swApp As SldWorks.SldWorks, swSrcDoc As SldWorks.ModelDoc2, _
rows As Integer, cols As Integer, sheetSize As Double)
Dim swDraw As SldWorks.DrawingDoc
Set swDraw = swSrcDoc
Dim swSheet As SldWorks.Sheet
Set swSheet = swDraw.GetCurrentSheet
' ソースシートのサイズ取得
Dim vSheetProps As Variant
vSheetProps = swSheet.GetProperties
Dim srcWidth As Double: srcWidth = vSheetProps(1)
Dim srcHeight As Double: srcHeight = vSheetProps(2)
' 新規図面の作成(A3テンプレートを指定)
' ※テンプレートパスは環境に合わせて動的に取得することを推奨
Dim swNewDoc As SldWorks.ModelDoc2
Set swNewDoc = swApp.NewDocument("C:\Templates\A3_Standard.drwdot", 0, 0, 0)
If swNewDoc Is Nothing Then Err.Raise 999, , "テンプレートが見つかりません。"
Dim swNewDraw As SldWorks.DrawingDoc
Set swNewDraw = swNewDoc
' 高速化のための制御
swApp.SkipUpdate = True
Dim r As Integer, c As Integer
Dim gridXMin As Double, gridYMin As Double, gridXMax As Double, gridYMax As Double
' グリッドループ
For r = 0 To rows - 1
For c = 0 To cols - 1
' 1. 現在の分割領域(グリッド)の座標を計算
' 原点は左下を想定
gridXMin = (c * srcWidth) / cols
gridXMax = ((c + 1) * srcWidth) / cols
gridYMin = (r * srcHeight) / rows
gridYMax = ((r + 1) * srcHeight) / rows
' 2. 新しいシートを追加
Dim sheetName As String
sheetName = "Zone_" & r & "_" & c
swNewDraw.NewSheet sheetName, swSheet.GetTemplateName, 0, 0, 0, 0, 0
' 3. ソース図面のビューを走査し、領域内のビューをコピー
Dim swView As SldWorks.View
Set swView = swDraw.GetFirstView ' Sheet自体が最初のViewとして取得される
Do While Not swView Is Nothing
' Sheet自身はスキップ
If swView.Name <> swSheet.GetName Then
' ビューの表示領域を取得
Dim vDispArea As Variant
vDispArea = swView.GetDisplayArea
' vDispArea = [x_min, y_min, x_max, y_max]
' 判定: ビューの領域とグリッドの領域が重なっているか
If IsOverlapping(vDispArea, gridXMin, gridYMin, gridXMax, gridYMax) Then
' ★重要: ビューの再生成
' 既存のビューをコピーするのではなく、モデルを参照して新規挿入する
Dim swModelView As SldWorks.ModelDoc2
Set swModelView = swView.ReferencedDocument
Dim viewType As Long: viewType = swView.Type
Dim viewScale As Double: viewScale = swView.ScaleFactor
' 新しい図面にビューを挿入
Dim swNewView As SldWorks.View
Set swNewView = swNewDraw.CreateDrawViewFromModelView3(swModelView.GetPathName, _
swView.ViewOrientation, 0, 0, 0)
If Not swNewView Is Nothing Then
' スケールの同期
swNewView.ScaleFactor = viewScale
' 位置の再計算 (グリッド内での相対位置)
' ここでは簡易的にグリッド中心に配置しているが、
' 本来はビューの重心を計算して配置すべき
swNewView.Position = Array((gridXMin + gridXMax) / 2, (gridYMin + gridYMax) / 2)
End If
End If
End If
Set swView = swView.GetNextView
Loop
Next c
Next r
' 後処理
swApp.SkipUpdate = False
swNewDoc.ForceRebuild3 True
End Sub
''' <summary>
''' 二つの矩形の重なりを判定する数学的関数
''' </summary>
Private Function IsOverlapping(vArea As Variant, gXMin As Double, gYMin As Double, gXMax As Double, gYMax As Double) As Boolean
' vArea: [0]=x_min, [1]=y_min, [2]=x_max, [3]=y_max
If vArea(2) < gXMin Or vArea(0) > gXMax Or vArea(3) < gYMin Or vArea(1) > gYMax Then
IsOverlapping = False
Else
IsOverlapping = True
End If
End Function
4. 現場で「勝てる」エンジニアのためのアドバイス
このコードをベースに、実務レベルのツールへと昇華させるための追加要件を伝えておく。
データベース(Excel/SQL)との連携
分割ルールをコードにハードコードするのは素人のすることだ。「どのゾーンをどのシート名にするか」「どのビューを優先的に配置するか」というメタデータは、外部のExcelファイルやデータベースで管理せよ。これにより、プログラムを書き換えずに、現場の運用変更に対応できる。
エラーログの永続化
大量の図面をバッチ処理する場合、途中で一つエラーが出ただけで全てが止まるような設計は許されない。`Try-Catch` 構造を使い、エラーが発生した図面名、ビュー名、エラーコードをテキストファイルやログDBに書き出す仕組みを必ず組み込め。
パフォーマンスの極限追求
数千枚の図面を処理する場合、`CreateDrawViewFromModelView3` の呼び出しはコストが高い。
- モデルの読み込み: `swApp.ActivateDoc3` を使い、メモリ上に展開されているドキュメントを最大限活用すること。
- 並列処理: VBA単体では限界があるが、VBScriptやC#を用いて、複数のSolidWorksインスタンスを立ち上げて並列処理させることで、処理時間は劇的に短縮できる。
結論
図面の自動分割は、単なる「配置作業」ではない。それは、「座標系、スケール、参照関係」という3つの幾何学的整合性を、プログラムによって再定義する高度な設計プロセスである。
この視点を持たない者は、単なる「マクロ作成者」に留まる。この視点を持つ者こそが、設計プロセス全体を支配する「アーキテクト」となるのだ。
