【実務・中級編】【中級者向け】大判図面(A0/A1)を複数枚のA3図面へ「ゾーン分割」して自動レイアウトするスプリッターVBA – SolidWorks VBA解析バイブル

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

【極限の設計論】巨大図面の「ゾーン分割」自動化: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つの幾何学的整合性を、プログラムによって再定義する高度な設計プロセスである。

この視点を持たない者は、単なる「マクロ作成者」に留まる。この視点を持つ者こそが、設計プロセス全体を支配する「アーキテクト」となるのだ。

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