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}");