#0. はじめに
Freeradicalの中の人、yamarahです。
InteractionEvents
などを使ってSketch要素を操作する場合は、対象のSketchEntity
が編集中の(Activeな)Sketchに含まれるのか、はたまた他のSketchの要素なのかを判断したくなるでしょう。
「SketchEntity.Parent == ActiveDocument.ActivatedObject
で良いんじゃない?」と思うかもしれませんが、そう簡単にはいかないのです。
#1. 考慮すべき条件分け
Sketch編集中と言っても、実は色々な状況があります。すぐに思いつくのは、アセンブリ環境でパーツをInPlace編集している場合です。
同じパーツのオカレンスが他にもある可能性があるので、アセンブリ環境ではProxyに対して比較/操作する必要があります。
次に、忘れがちなのが、Sketchブロックです。Sketchブロック編集には2種類あって、通常のSketch編集中にInPlaceでブロック編集する方法と、モデルツリー内のブロックフォルダーから編集する場合です。
ですので、
- 2パターン (アセンブリでのInPlace編集かどうか)
- 3パターン (通常のSketch編集 / 通常のSketch中のInPlaceブロック編集 / ブロックのみを編集)
の掛け合わせで、計6パターンの状況があります。
#2. ブロックについて
SketchBlock
は、Sketchにおけるブロックの実体です。
SketchBlockDefinition
は、SketchBlock
の雛形です。
Assembly/Partの関係に例えると、次のようになります。
AssemblyとPartの関係 | SketchとSketchBlockの関係 |
---|---|
AssemblyDocument | PlanarSketch (*1) |
ComponentOccurrence | SketchBlock |
PartDocument | SketchBlockDefinition |
(*1) SketchBlockを内包する親SketchBlockDefinitionの可能性もある |
なお、SketchBlockDefinition
はPlanarSketch
を継承していますので、状況によってはSketchBlockDefinition
をPlanarSketch
と扱って処理できます。
#3. ActivatedObjectに入っているもの
次に、ActiveEditDocument.ActivatedObject
に入っているものを確認しておきましょう。
状況 | ActivatedObject |
---|---|
通常のSketch編集 | PlanarSketch |
Sketch編集中にBlockをInPlace編集 | SketchBlock |
Blockのみを編集 | SketchBlockDefinition |
なんとなく、2番目の処理がやっかいそうなのが分かります。 |
#4. 実際のコード
まずは、ActivatedObjectを可能性のある各形式に変換します。
// PartDocument編集中を確認。
if (!(InventorApplication.ActiveEditDocument is PartDocument partDoc)) return;
// ActivatedObjectを、PlanarSketch, SketchBlockDefinition, SketchBlockに変換してみる。
var activeSketch = partDoc.ActivatedObject as PlanarSketch; // 普通のSketch編集中と、InPlaceではないBlock編集中時に非null
var activeBlockDef = partDoc.ActivatedObject as SketchBlockDefinition; // InPlaceではないBlock編集中時に非null
var activeBlock = partDoc.ActivatedObject as SketchBlock; // InPlaceでBlock編集中に非null
先に述べた通り、SketchBlockDefinition
はPlanarSketch
に変換可能なので注意してください。
次に、SketchBlock
が取得できた場合は、そこからactiveSketch
とactiveBlockDef
を取得します。
// InPlaceでBlock編集中時は、activeBlockからactiveSketchとblockDefを取得する。
if (activeBlock != null)
{
activeBlockDef = activeBlock.Definition;
activeSketch = activeBlockDef as PlanarSketch;
}
この時点ではactiveSketch == null
の可能性があるので、それをはじきます。
// activeSketchがnullの場合は中止。例えば、ActivatedObjectがSketch3Dだった場合とか。
if (activeSketch == null) return;
次に、アセンブリ環境中にパーツをInPlace編集している場合を考慮します。その場合は、各ObjectをActiveOccurrence経由のproxyに差し替えます。
// Assembly環境で、PartDocumentをInPlace編集している可能性を考慮する。
ComponentOccurrence? activeOccurrence = null;
if (partDoc != InventorApplication.ActiveDocument)
{
// InPlace編集中だった場合
var topAssyDoc = InventorApplication.ActiveDocument as AssemblyDocument;
if (topAssyDoc == null) return; // あり得ないはずだが、念のため
// activeSketchをSketchProxyに変換する。
object proxy;
activeOccurrence = topAssyDoc.ComponentDefinition.ActiveOccurrence;
activeOccurrence.CreateGeometryProxy(activeSketch, out proxy);
activeSketch = (PlanarSketch)proxy;
// blockDefが非nullの場合は、activeBlockDef == activeSketchなので、同じproxy値を設定する。
if (activeBlockDef != null)
{
activeBlockDef = (SketchBlockDefinition)proxy;
}
// activeBlockが設定されている場合は、同様にSketchBlockProxyに変換する。
if (activeBlock != null)
{
activeOccurrence.CreateGeometryProxy(activeBlock, out proxy);
activeBlock = (SketchBlock)proxy;
}
}
Proxyに差し替えたので、以後はAssembly中かどうか気にしなくて良いです。
残りの条件分け、Blockがらみの条件を抽出します。
// SketchBlockを編集中かどうか
bool inSketchBlockEdit = activeBlockDef != null;
// Sketch編集中に、SketchBlockをInPlace編集中かどうか
bool inInplaceSketchBlockEdit = activeBlock != null;
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(inSketchBlockEdit)} : {inSketchBlockEdit}");
System.Diagnostics.Debug.WriteLine($"{nameof(inInplaceSketchBlockEdit)} : {inInplaceSketchBlockEdit}");
ここまでが下準備です。毎回評価する必要はないので、OnExecute
なりOnNewEditObject
で更新すると良いでしょう。
では、実際にSketchEntity
がActivatedObject
に含まれるか検証しましょう。
実験用のSketchEntity
の供給元として、毎度おなじみのSelectSet
を使います。
// SelectSetから要素を1つ取る。
var selectSet = InventorApplication.ActiveDocument.SelectSet; // ActiveEditDocumentではないことに注意
if (selectSet.Count == 0) return;
if (!(selectSet[1] is SketchEntity entity)) return; // Indexは1から始まる
やっと実際の確認部分です。
// entityが、ActivatedObjectの直接の要素かどうか。
bool isDirectChildOfActivatedObject = false;
if (inInplaceSketchBlockEdit)
{
// Sketch環境でBlockをInPlace編集中は、entityの親が編集中のBlockかどうかで確認する。
if (activeBlock == entity.ContainingSketchBlock)
{
isDirectChildOfActivatedObject = true;
}
}
else
{
// それ以外(通常のSketch編集、Blockのみを編集)の場合は、entityの親がactiveSketchかどうかで判断する。
if (entity.Parent == activeSketch)
{
isDirectChildOfActivatedObject = true;
}
}
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(isDirectChildOfActivatedObject)} : {isDirectChildOfActivatedObject}");
ここで、isDirectChildOfActivatedObject
の意味ですが、名前の通り、ActivatedObjectに直接所有されている(ユーザーに操作可能に見える)かどうかです。
例えば、通常のSketch編集中に、そのSketchに含まれているBlock内の各要素は、Sketch(== ActivatedObject
)に内包されていると言えます。しかし、ここで判断したいのは、その要素がActivatedObjectの直接の子要素かどうかなので、Block経由の場合はfalseになって欲しいわけです。
最後に、おまけです。アセンブリ環境でパーツをInPlace編集中に、あるSketchEntity
がInPlace編集中のOccurrence
に含まれるかどうかを確認します。
このコードは動作確認していないのですが、問題ないと思います。
// ActiveOccurrenceに含まれるかどうか
bool inActiveOccurrence = false;
if (activeOccurrence == null)
{
// アセンブリ中の編集でなければ、常にtrue
inActiveOccurrence = true;
}
else
{
var containingSketchProxy = entity.Parent as PlanarSketchProxy;
if (containingSketchProxy?.ContainingOccurrence == activeOccurrence)
{
inActiveOccurrence = true;
}
}
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(inActiveOccurrence)} : {inActiveOccurrence}");
以上です。
#5. まとめ
繰り返しになりますが、上記コードを1つにまとめたものを貼っておきます。
// PartDocument編集中を確認。
if (!(InventorApplication.ActiveEditDocument is PartDocument partDoc)) return;
// ActivatedObjectを、PlanarSketch, SketchBlockDefinition, SketchBlockに変換してみる。
var activeSketch = partDoc.ActivatedObject as PlanarSketch; // 普通のSketch編集中と、InPlaceではないBlock編集中時に非null
var activeBlockDef = partDoc.ActivatedObject as SketchBlockDefinition; // InPlaceではないBlock編集中時に非null
var activeBlock = partDoc.ActivatedObject as SketchBlock; // InPlaceでBlock編集中に非null
// InPlaceでBlock編集中時は、activeBlockからactiveSketchとblockDefを取得する。
if (activeBlock != null)
{
activeBlockDef = activeBlock.Definition;
activeSketch = activeBlockDef as PlanarSketch;
}
// activeSketchがnullの場合は中止。例えば、ActivatedObjectがSketch3Dだった場合とか。
if (activeSketch == null) return;
// Assembly環境で、PartDocumentをInPlace編集している可能性を考慮する。
ComponentOccurrence? activeOccurrence = null;
if (partDoc != InventorApplication.ActiveDocument)
{
// InPlace編集中だった場合
var topAssyDoc = InventorApplication.ActiveDocument as AssemblyDocument;
if (topAssyDoc == null) return; // あり得ないはずだが、念のため
// activeSketchをSketchProxyに変換する。
object proxy;
activeOccurrence = topAssyDoc.ComponentDefinition.ActiveOccurrence;
activeOccurrence.CreateGeometryProxy(activeSketch, out proxy);
activeSketch = (PlanarSketch)proxy;
// blockDefが非nullの場合は、activeBlockDef == activeSketchなので、同じproxy値を設定する。
if (activeBlockDef != null)
{
activeBlockDef = (SketchBlockDefinition)proxy;
}
// activeBlockが設定されている場合は、同様にSketchBlockProxyに変換する。
if (activeBlock != null)
{
activeOccurrence.CreateGeometryProxy(activeBlock, out proxy);
activeBlock = (SketchBlock)proxy;
}
}
// SketchBlockを編集中かどうか
bool inSketchBlockEdit = activeBlockDef != null;
// Sketch編集中に、SketchBlockをInPlace編集中かどうか
bool inInplaceSketchBlockEdit = activeBlock != null;
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(inSketchBlockEdit)} : {inSketchBlockEdit}");
System.Diagnostics.Debug.WriteLine($"{nameof(inInplaceSketchBlockEdit)} : {inInplaceSketchBlockEdit}");
// SelectSetから要素を1つ取る。
var selectSet = InventorApplication.ActiveDocument.SelectSet; // ActiveEditDocumentではないことに注意
if (selectSet.Count == 0) return;
if (!(selectSet[1] is SketchEntity entity)) return; // Indexは1から始まる
// entityが、ActivatedObjectの直接の要素かどうか。
bool isDirectChildOfActivatedObject = false;
if (inInplaceSketchBlockEdit)
{
// Sketch環境でBlockをInPlace編集中は、entityの親が編集中のBlockかどうかで確認する。
if (activeBlock == entity.ContainingSketchBlock)
{
isDirectChildOfActivatedObject = true;
}
}
else
{
// それ以外(通常のSketch編集、Blockのみを編集)の場合は、entityの親がactiveSketchかどうかで判断する。
if (entity.Parent == activeSketch)
{
isDirectChildOfActivatedObject = true;
}
}
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(isDirectChildOfActivatedObject)} : {isDirectChildOfActivatedObject}");
// ActiveOccurrenceに含まれるかどうか
bool inActiveOccurrence = false;
if (activeOccurrence == null)
{
// アセンブリ中の編集でなければ、常にtrue
inActiveOccurrence = true;
}
else
{
var containingSketchProxy = entity.Parent as PlanarSketchProxy;
if (containingSketchProxy?.ContainingOccurrence == activeOccurrence)
{
inActiveOccurrence = true;
}
}
// Debug
System.Diagnostics.Debug.WriteLine($"{nameof(inActiveOccurrence)} : {inActiveOccurrence}");
#99. 親の記事に戻る
Autodesk Inventor API Hacking (概略)