1. ベクターグラフィック処理における「一括フィレット」の本質
CADデータの取り込みや、サードパーティ製グラフィックツールから書き出された大量のSVG/EPSデータをCorelDRAWにインポートした際、我々が直面するのは「数百〜数千に及ぶ、エッジの立った鋭利なパスオブジェクトの山」である。これらに対して、手動で1つずつ「角の丸め(フィレット)」を適用していく作業は、生産性の破壊以外の何物でもない。
しかし、単にVBAからループを回して一括処理を実行すると、別の深刻な問題が浮上する。
- GUIのレンダリング再計算によるフリーズとパフォーマンス低下
- Undo(元に戻す)スタックの肥大化によるメモリ枯渇
- 矩形(Rectangle)と汎用ベクターパス(Curve)におけるAPIの挙動の相違による不具合
本稿では、CorelDRAW VBA内部の描画パイプラインとオブジェクトモデルを解剖し、数百~数千のベクターパスに対してミリメートル単位で数値制御されたフィレット効果を瞬時に適用する極限の自動化アーキテクチャを解説する。
—
2. パフォーマンスを限界突破させるアーキテクチャ設計
CorelDRAWのVBA環境において、オブジェクト処理を爆速化するためには、アプリケーションの内部挙動をコードから明示的に制御しなければならない。
① 描画パイプラインとイベントの完全停止
通常、シェイプのノードが更新されるたびに、CorelDRAWはドキュメントツリーを再構築し、画面描画(Redraw)とイベント通知(Events)を発生させる。これを全オブジェクトのループ内でそのまま実行させれば、処理時間は数分〜数時間へと跳ね上がる。
これを防ぐため、処理開始時に以下を確実に停止する。
Application.Optimization = True ‘ レンダリングの完全停止
Application.EventsEnabled = False ‘ ドキュメントイベントの抑制
② トランザクション処理(CommandGroup)によるメモリ管理
個々のノード変更をUndoバッファに逐一記録させると、CorelDRAWのメモリフットプリントは急激に増大し、最終的にアクセス違反(Access Violation)を起こす。これを単一のコマンドグループに束ねることで、Undo履歴を1ステップに圧縮し、メモリ消費を最低限に抑える。
ActiveDocument.BeginCommandGroup “Batch Fillet Optimization”
‘ … 処理本体 …
ActiveDocument.EndCommandGroup
③ 例外安全(Exception Safety)の担保
VBAにおいて最も惨烈なのは、処理途中のエラーで `Optimization = True` のまま制御が戻り、CorelDRAWのGUI操作が不能になることだ。`On Error GoTo` による構造化エラーハンドリングを構築し、どんな例外が発生しても必ず描画フラグを復元する設計が絶対要件となる。
—
3. 実装:一括フィレット適用マクロ(完全版)
以下のコードは、Win32 APIを用いたミリ秒精度の処理時間計測、エラーハンドリング、描画最適化、および `CreateFillet` / `ConvertToCurves` メソッドによる高精度な幾何学的角丸処理を統合した実用スクリプトである。
Option Explicit
‘ ==============================================================================
‘ Win32 API: 高精度パフォーマンスタイマー宣言
‘ ==============================================================================
If VBA7 Then
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
Else
Private Declare Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
End If
‘ ==============================================================================
‘ メイン処理: 選択オブジェクトに対する一括フィレット適用
‘ ==============================================================================
Public Sub BatchApplyFilletToSelection()
Dim startTime As Currency, endTime As Currency, freq As Currency
QueryPerformanceFrequency freq
QueryPerformanceCounter startTime
‘ ドキュメント状態の事前検証
If ActiveDocument Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbExclamation, “エラー”
Exit Sub
End If
Dim targetShapes As ShapeRange
Set targetShapes = ActiveSelectionRange
If targetShapes.Count = 0 Then
MsgBox “対象となるオブジェクトが選択されていません。”, vbExclamation, “警告”
Exit Sub
End If
‘ フィレット半径の設定(単位: mm を定義し、内部単位 inch へ自動変換)
Dim radiusInMM As Double
Dim radiusInInches As Double
radiusInMM = 3.0 ‘ 例: 角丸半径 3.0 mm
‘ CorelDRAW内部単位(Inch)への変換 (1 inch = 25.4 mm)
radiusInInches = radiusInMM / 25.4
‘ ————————————————————————–
‘ アプリケーション最適化状態の待避と移行
‘ ————————————————————————–
Dim origOptimization As Boolean
Dim origEvents As Boolean
origOptimization = Application.Optimization
origEvents = Application.EventsEnabled
On Error GoTo ErrorHandler
‘ パフォーマンスブースト制御の適用
Application.Optimization = True
Application.EventsEnabled = False
ActiveDocument.BeginCommandGroup “Batch Fillet Processing”
Dim sh As Shape
Dim processCount As Long
processCount = 0
‘ 選択オブジェクト群を再帰的・平坦化走査(グループ解体せずに内部走査)
Dim flatRange As ShapeRange
Set flatRange = targetShapes.UngroupAllEx ‘ ネスト構造を崩さず平坦参照を取得
For Each sh In flatRange
If ProcessShapeFillet(sh, radiusInInches) Then
processCount = processCount + 1
End If
Next sh
‘ トランザクションの正常終了
ActiveDocument.EndCommandGroup
‘ 描画・イベント制御の全復元
Application.Optimization = origOptimization
Application.EventsEnabled = origEvents
Application.Refresh
QueryPerformanceCounter endTime
Dim elapsedTimeSec As Double
elapsedTimeSec = (endTime – startTime) / freq
MsgBox “処理が正常に完了しました。” & vbCrLf & _
“適用オブジェクト数: ” & processCount & ” 件” & vbCrLf & _
“処理時間: ” & Format(elapsedTimeSec, “0.000”) & ” 秒”, _
vbInformation, “処理完了”
Exit Sub
ErrorHandler:
‘ 例外発生時の完全復旧ロジック(システムロックアップ防止)
On Error Resume Next
ActiveDocument.EndCommandGroup
Application.Optimization = origOptimization
Application.EventsEnabled = origEvents
Application.Refresh
MsgBox “致命的なエラーが発生しました。” & vbCrLf & _
“エラー詳細: ” & Err.Description & ” (Code: ” & Err.Number & “)”, _
vbCritical, “システムエラー”
End Sub
‘ ==============================================================================
‘ 個別シェイプへのフィレット適用関数
‘ ==============================================================================
Private Function ProcessShapeFillet(ByVal sh As Shape, ByVal radiusInches As Double) As Boolean
On Error GoTo ProcessError
ProcessShapeFillet = False
‘ ロックされているオブジェクト、または不可視レイヤーはスキップ
If sh.Locked Or Not sh.Layer.Visible Then Exit Function
‘ シェイプのタイプに応じた前処理
Select Case sh.Type
Case cdrRectangleShape
‘ 固有の矩形プロパティに対してフィレットを直接適用(幾何計算のオーバーヘッド回避)
sh.Rectangle.CornerType = cdrCornerFillet
sh.Rectangle.SetRadius radiusInches, radiusInches, radiusInches, radiusInches
ProcessShapeFillet = True
Case cdrCurveShape
‘ 既にベクター曲線の場合、フィレットメソッドを直接適用
sh.CreateFillet radiusInches
ProcessShapeFillet = True
Case cdrPolygonShape, cdrEllipseShape
‘ ポリゴンや楕円などは一度完全なパス(Curve)へ昇華させてから適用
sh.ConvertToCurves
sh.CreateFillet radiusInches
ProcessShapeFillet = True
Case Else
‘ テキスト、ビットマップ、コンテナ等はスキップ(非破壊の原則)
ProcessShapeFillet = False
End Select
Exit Function
ProcessError:
‘ 個別シェイプの幾何異常(半径がパス長を超過している等)は安全にスキップ
ProcessShapeFillet = False
Err.Clear
End Function
—
4. アーキテクトの視点:コードの深層解剖と保守設計
上記コードには、ミッションクリティカルな環境で長年培われた「システムを破壊させないための設計思想」が注ぎ込まれている。
[クライアント処理要求]
│
▼
[Win32 Performance Counter 開始]
│
▼
[ Optimization = True / Events = False ] ── (描画パイプライン遮断)
│
▼
[ BeginCommandGroup ] ───────────────────── (Undoバッファ単一化)
│
├─► [矩形 (cdrRectangleShape)] ────► 固有プロパティダイレクト書き換え
│
├─► [曲線 (cdrCurveShape)] ─────► CreateFillet(Radius) 幾何処理
│
└─► [その他 (Polygon等)] ────────► ConvertToCurves ➔ CreateFillet
│
[ EndCommandGroup ] ─────────────────────── (トランザクション確定)
│
▼
[ Optimization / Events 復元 & Refresh ] ─ (画面再描画)
1. 内部単位(Inch)とメートル法の厳密な整合性
CorelDRAWの内部ジオメトリ計算エンジンは、UI上の表示単位設定に関わらず一律で「インチ(Inch)」を基本単位として扱っている。コード内で `radiusInMM / 25.4` の明示的なスケーリングを行わずに `3.0` などの数値をダイレクトに与えると、76.2mm という巨大なフィレットが適用され、幾何データが破綻する。
2. `UngroupAllEx` によるグラフィックツリーの非破壊平坦化
グループ化されたオブジェクト(Group Shape)に対して直接操作を行うと、VBAは子要素の幾何データを直接参照できず、不調和を起こす。`UngroupAllEx` を用いることで、元のドキュメント構造上のグループ参照関係を保ちつつ、メモリ空間上に走査用のフラットな `ShapeRange` リストを展開し、安全かつ高速な走査を実現している。
3. `cdrRectangleShape` と `cdrCurveShape` の分岐処理
CorelDRAWにおける矩形(Rectangle)は、未変換の状態では「パラメトリックな特殊シェイプ」として存在する。これに対して `CreateFillet` を実行すると例外が発生するか、強制的にCurveへの変換が行われてしまう。
本コードでは、`sh.Type` で条件分岐を行い、矩形の場合は固有の `Rectangle.SetRadius` メソッドを呼び出すことで、オブジェクトの属性情報を極力保持したまま最小限のオーバーヘッドでフィレット化を行っている。
—
5. レガシー保守とシステム連携への展開
商業印刷や製造業におけるCAD/CAM連携の現場において、CorelDRAWは単体のデザインツールではなく、前後の工程(DB連携やプロッター制御)を繋ぐハブとして機能する。
高度な運用に向けた拡張のアドバイス
1. ノード制限の幾何判定:
パスのセグメント長(距離)に対して指定されたフィレット半径が大きすぎる場合、CorelDRAWの内部幾何計算エンジンがエラー(または不正なノード生成)を返す。堅牢性を高める場合は、`sh.Curve.Segments` の長さを事前にループでチェックし、`Radius 2` 未満のセグメントをスキップするガードロジックを組むと完璧である。
2. 外部環境(.NET/VB.NET)からのCOM呼び出し:
本ロジックを `.NET` 経由(C# や VB.NET)でスタンドアロンの自動化サービスとして運用する場合は、使用した `Shape` や `ShapeRange` オブジェクトの参照に対して、明示的に `System.Runtime.InteropServices.Marshal.ReleaseComObject(sh)` を呼び出し、RCW(Runtime Callable Wrapper)のメモリ解放を徹底しなければ、プロセスが常駐し続けてCorelDRAWが裏で増殖する原因となる。
—
6. 結論
大量のベクターデータに対する数値制御の一括フィレット適用は、単なる作業の自動化にとどまらない。ドキュメント全体のデザインのトーン&マナー(Tonality & Manner)を一瞬で統一し、製品や印刷データの幾何学的精度を保証する「品質管理の自動化」そのものである。
描画抑制(`Optimization`)、トランザクション制御(`CommandGroup`)、例外安全の構造化エラーハンドリング——これら3つの柱を正しく構築することで、CorelDRAW VBAはエンタープライズの現場にも耐えうる、強力で爆速な幾何処理エンジンへと変貌する。
