前回のマクロでは、2つを比較して色を反映させましたが、
同一の面を認識 = 異なる面を認識
なので、2つを比較して異なる面を抽出するマクロを作りました。
'vba sample_GetChangeArea_ver0.01 using-'KCL0.08' '2つのボディ/形状セットの異なる面を抽出 Option Explicit '*** 設定値 *** Const CogTolerance = 0.01 '同一判断重心距離 Const AreaTolerance = 0.01 '同一判断面積 '************** Const CogTolSqr = CogTolerance * CogTolerance Sub CATMain() 'ドキュメントのチェック If Not CanExecute(Array("PartDocument", "ProductDocument")) Then Exit Sub '対象ボディ選択 Dim Msg$: Msg = "チェックするボディ/形状セットを選択して下さい : ESCキー 終了" Dim TgtBody As AnyObject: Set TgtBody = KCL.SelectItem(Msg, "Body,HybridBody") If KCL.IsNothing(TgtBody) Then Exit Sub Dim TgtCount&: TgtCount = SearchTopoFaces(TgtBody) If TgtCount < 1 Then MsgBox TgtBody.Name + " に面が有りません!" Exit Sub End If '参照ボディ選択 Msg = "比較するボディ/形状セットを選択して下さい : ESCキー 終了" Dim RefBody As AnyObject: Set RefBody = KCL.SelectItem(Msg, "Body,HybridBody") If KCL.IsNothing(RefBody) Then Exit Sub Dim RefCount&: RefCount = SearchTopoFaces(RefBody) If RefCount < 1 Then MsgBox RefBody.Name + " に面が有りません!" Exit Sub End If '確認 Msg = TgtBody.Name + "(" + CStr(TgtCount + 1) + "枚)の変更箇所を" + vbNewLine + _ RefBody.Name + "(" + CStr(RefCount + 1) + "枚)を元に" + vbNewLine + _ "確認しますか?" If MsgBox(Msg, vbYesNo) = vbNo Then Exit Sub KCL.SW_Start '対象ボディ リファレンス取得 Dim TgtRefs As Variant: TgtRefs = GetTopoFacesRef(TgtBody) If IsEmpty(TgtRefs) Then Exit Sub '対象ボディ トポロジ情報取得 Dim TgtGeos As Variant: TgtGeos = GetGeoInfo(TgtBody, TgtRefs) '参照ボディ リファレンス取得 Dim RefRefs As Variant: RefRefs = GetTopoFacesRef(RefBody) If IsEmpty(RefRefs) Then Exit Sub '参照ボディ トポロジ情報取得 Dim RefGeos As Variant: RefGeos = GetGeoInfo(RefBody, RefRefs) '差分インデックス取得 Dim DifIdxs As Variant: DifIdxs = GetDifference(TgtGeos, RefGeos) If IsEmpty(DifIdxs) Then MsgBox "'" + TgtBody.Name + "' と '" + RefBody.Name + "' " + vbNewLine + _ "の違いは見つかりませんでした" Exit Sub End If '差分面作成 Call ExtractFace(TgtBody, TgtRefs, DifIdxs) '終了 Debug.Print KCL.SW_GetTime MsgBox CStr(UBound(DifIdxs) + 1) + "枚分の違いを作成しました" End Sub '差分面作成 Private Sub ExtractFace(ByVal ParentOj As AnyObject, ByRef Refs As Variant, ByVal Idx As Variant) Dim Doc As PartDocument: Set Doc = KCL.GetParent_Of_T(ParentOj, "PartDocument") Dim Pt As Part: Set Pt = Doc.Part CATIA.HSOSynchronized = False Dim Fact As HybridShapeFactory: Set Fact = Pt.HybridShapeFactory Dim Ref As Reference Set Ref = Pt.CreateReferenceFromBRepName(KCL.GetBrepName(Refs(Idx(0)).DisplayName), Refs(Idx(0)).Parent) Dim Exts As HybridShapeExtractMulti: Set Exts = Fact.AddNewExtractMulti(Ref) Dim i&, j& For i = 0 To UBound(Idx) Set Ref = Pt.CreateReferenceFromBRepName(KCL.GetBrepName(Refs(Idx(i)).DisplayName), Refs(Idx(i)).Parent) j = i + 1 With Exts .AddConstraintTolerant Ref, 3, False, False, 0.01, 0.5, 0.98, j .SetElement j, Ref .SetPropagationType i + 1, 3 .SetComplementaryExtractMulti j, False .SetIsFederated j, False .SetDistanceThresholdActivity j, False .SetAngularThresholdActivity j, False .SetCurvatureThresholdActivity j, False End With Next Pt.UpdateObject Exts Dim HB As HybridBody: Set HB = Doc.Part.HybridBodies.Add() HB.Name = "Change_Area" Set Ref = Pt.CreateReferenceFromObject(Exts) Dim Exp As HybridShapeSurfaceExplicit: Set Exp = Fact.AddNewSurfaceDatum(Ref) HB.AppendHybridShape Exp Call SetGraphicProperty(Doc.Selection, Exp) Pt.UpdateObject Exp Fact.DeleteObjectForDatum Ref CATIA.HSOSynchronized = True End Sub '色等設定 Private Sub SetGraphicProperty(ByRef Sel As Selection, ByVal Face As Variant) Dim VPS As VisPropertySet: Set VPS = Sel.VisProperties CATIA.HSOSynchronized = False Sel.Clear Sel.Add Face VPS.SetRealColor 255, 0, 0, 1 VPS.SetRealWidth 4, 1 Sel.Clear CATIA.HSOSynchronized = True End Sub '差分検索 return-異なるIdx Private Function GetDifference(ByRef TgtGeos As Variant, ByRef RefGeos As Variant) As Variant Dim Cnt&: Cnt = UBound(TgtGeos) Dim UnHit() As Variant: ReDim UnHit(Cnt) Dim UnHitCnt&: UnHitCnt = -1 Dim i&, j&, HitFg As Boolean For i = 0 To Cnt HitFg = False For j = 0 To UBound(RefGeos) If IsGeoEqual(TgtGeos(i), RefGeos(j)) Then HitFg = True Exit For End If Next If Not HitFg Then UnHitCnt = UnHitCnt + 1 UnHit(UnHitCnt) = i End If Next If UnHitCnt < 0 Then Exit Function ReDim Preserve UnHit(UnHitCnt) GetDifference = UnHit End Function 'Geo一致 Private Function IsGeoEqual(ByVal P1 As Variant, ByVal P2 As Variant) As Boolean IsGeoEqual = False If IsCogEqual(P1, P2) And IsAreaEqual(P1, P2) Then IsGeoEqual = True End Function 'COG一致 Private Function IsCogEqual(ByVal P1 As Variant, ByVal P2 As Variant) As Boolean IsCogEqual = False If Abs((P2(0) - P1(0)) * (P2(0) - P1(0)) + _ (P2(1) - P1(1)) * (P2(1) - P1(1)) + _ (P2(2) - P1(2)) * (P2(2) - P1(2))) < CogTolSqr Then IsCogEqual = True End If End Function 'Area一致 Private Function IsAreaEqual(ByVal P1 As Variant, ByVal P2 As Variant) As Boolean IsAreaEqual = False If Abs(P2(3) - P1(3)) < AreaTolerance Then IsAreaEqual = True End If End Function 'CogとAreaの取得 '0-CogX 1-CogY 2-CogZ 3-Area Private Function GetGeoInfo(ByVal ParentOj As AnyObject, ByRef Refs As Variant) As Variant Dim Doc As PartDocument: Set Doc = KCL.GetParent_Of_T(ParentOj, "PartDocument") Dim SPA As SPAWorkbench: Set SPA = Doc.GetWorkbench("SPAWorkbench") Dim Infos() As Variant: ReDim Infos(UBound(Refs)) Dim Cog(2) As Variant, i&, Mes As Variant 'Measurable For i = 0 To UBound(Infos) Set Mes = SPA.GetMeasurable(Refs(i)) Mes.GetCOG Cog Infos(i) = KCL.JoinAry(Cog, Array(Mes.Area)) Next GetGeoInfo = Infos End Function 'topologyのFaceのReference取得 Private Function GetTopoFacesRef(ByVal AnyOj As AnyObject) As Variant If SearchTopoFaces(AnyOj) < 1 Then Exit Function Dim Doc As PartDocument: Set Doc = KCL.GetParent_Of_T(AnyOj, "PartDocument") Dim Sel As Selection: Set Sel = Doc.Selection Dim Refs() As Reference: ReDim Refs(Sel.Count2 - 1) Dim i& For i = 0 To Sel.Count2 - 1 Set Refs(i) = Sel.Item(i + 1).Reference Next GetTopoFacesRef = Refs End Function 'topologyのFaceの検索 Private Function SearchTopoFaces(ByVal AnyOj As AnyObject) As Long Dim Sel As Selection: Set Sel = KCL.GetParent_Of_T(AnyOj, "PartDocument").Selection CATIA.HSOSynchronized = False With Sel .Clear .Add AnyOj .Search "Topology.CGMFace,sel" End With CATIA.HSOSynchronized = True SearchTopoFaces = Sel.Count2 End Function
前回同様、各面の重心と表面積で判断しています。
今回は、Referenceに手間取りました。
重心と表面積を取得する際と、抽出面を作成する際に必要だったのですが
同じReferenceじゃNGだったんです。
・重心と表面積 - Selection.Item(1).Reference のタイプを使用
・抽出面を作成 - Part.CreateReferenceFromBRepName のタイプを使用
過去にこれをやっていたのでわかりましたが、
SelectElementからReferenceの取得 - C#ATIA
知らなきゃ途中で諦めていたかも。
又、マクロ使用後はCATDUAを実行する事をお勧めします。
以前は気が付かなかったのですが、CreateReferenceFromBRepNameを利用して
Referenceを取得すると、どうしても1個だけゴミが出来上がるようで
防ぐ事ができませんでした。 行わなくても問題は無いとは思いますが。
実際に試した感じです。
こんな感じのデータで試します。 念の為ですが、GrabCADで拾ってきたデータです。
片方はFusion360で一部変更しました。 ダイレクトモードだと不要な面を選択し
Deleteキーでジャンジャン削除出来るんですよ。 (操作が直感的)
CATIAは厳密に面を選択しないとエラーになりますが、Fusion360の場合は、ある程度
判断して削除してくれるので、遥かに効率良いです。
まず変更後のボディ又は形状セットを選択し、続いて比較するボディ/形状セットを
選択します。処理前にこんな感じのダイアログが出現します。
大体15MBぐらいデータですが、面の枚数は瞬時に検索されてます。
HSOSynchronizedプロパティ使わなきゃ、かなり待たされるはず。
処理終了後は変更の有った面の枚数を表示します。
出来上がりは、最初に指定した方と同一のCATPart内に "Change_Area" と言う
名称の形状セットが出来上がり、異なる部分を抽出したサーフェスが出来上がります。
・・・本当は、"分解" - "ドメインのみ" を行いたかったのですが、大変そうだったので
諦めました。
オマケですが、イミディエイトウィンドウに処理時間が出ます。 今回の場合は100秒程でした。
我慢できるレベルです。
結構大きな面を差分抽出していますが、
青い面取り部分を削除しただけなんです。 あくまで "同じじゃなかった面"
を判断しているだけなのでご勘弁を。
実は、こんな感じの支給データを1度だけ受け取った事があるんです。
"きっと差分抽出できるモジュールがあるんだろうなぁ"
ぐらいに思っていましたが、多分こんな感じのマクロで行っていたんだろうと思います。
Part単体でも実行可能ですし、ボディ-形状セットでも利用可能です。
もっと早く作れば良かった・・・。