こちらの続きです。
同一形状のボディを判断する1 - C#ATIA
慣性モーメントを利用しての判断を追加することにします。
慣性モーメント自体については・・・え~正しい意味合いは、
検索してみて下さい。
自分の中ではこのようなイメージで捉えています。
歪な形状では無くても良いのですが、ボディを包囲するような
最小の直方体を作る際のXYZのベクトルや中心が得られる
と思っています。
常に(恐らくですが) "最小" の直方体となるのであれば、同一の
ボディが移動されたり回転されたりしていても、
”同じ状態でボディを見る為の座標系を得られる”
と思ってます。 ・・・多分。
赤がボディの重心で、青が最小の直方体の重心です。
これを利用すれば、お互いの重心のズレが発生するので
前回の円柱の位置の違いを判断することが出来るのでは
ないのかな? と感じています。
(球体と正多面体は重心の位置ずれ無し)
念の為、過去のAPIフォーラム質疑や自信でテストした事を記載して
おきます。
ボディ(BRepBody)のドキュメントがこちらですが
Fusion 360 Help
慣性モーメントを得ることが出来る、physicalPropertiesプロパティが
ありますが、精度が悪くて使い物になりません。
getPhysicalPropertiesメソッドを利用する事にします。
又、boundingBoxプロパティはルートコンポーネントの原点の
向きでのboundingBox(ボディを包囲する直方体)しか得られない上、
非常に精度が悪い為、MeasureManagerオブジェクトのgetOrientedBoundingBox
メソッドを利用します。
Fusion 360 Help
こちらは向きも自由ですし、精度が比較にならない程良いです。
これらを考慮した上で、こちらを作成しました。
# Fusion360API Python script import adsk.core, adsk.fusion, traceback import dataclasses _tolerance = 0.0001 @dataclasses.dataclass class bRepContainer: body : adsk.fusion.BRepBody area : float volume : float cogMisalignment : float faceCount : int vartexCount : int def isMatch( self :dataclasses.dataclass, cont :dataclasses.dataclass) -> bool: if abs(self.area - cont.area) > _tolerance: return False if abs(self.volume - cont.volume) > _tolerance: return False if abs(self.cogMisalignment - cont.cogMisalignment) > _tolerance: return False if self.faceCount != cont.faceCount: return False if self.vartexCount != cont.vartexCount: return False return True def run(context): ui = adsk.core.UserInterface.cast(None) try: app :adsk.fusion.Application = adsk.core.Application.get() ui = app.userInterface des :adsk.fusion.Design = app.activeProduct bodies = getAllBRepBodies(des) dumpMsg(f'** All Body Count : {len(bodies)} **') groups = groupByPhysicaProp(bodies) for group in groups: dumpMsg(f'-- Area:{group[0].area} , Volume:{group[0].volume} --') dumpMsg(f'-- Misalignment of the center of gravity:{group[0].cogMisalignment} --') dumpMsg(f'-- FaceCount:{group[0].faceCount} , VartexCount:{group[0].vartexCount} --') for cont in group: dumpMsg(cont.body.name) dumpMsg('') a=1 except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) def groupByPhysicaProp( bodies :list): groups = [[bodies[0]]] for cont in bodies[1:]: findFG = False for group in groups: if cont.isMatch(group[0]): group.append(cont) findFG = True if findFG: continue groups.append([cont]) return groups def initContainer( self :adsk.fusion.BRepBody) -> dataclasses.dataclass: # prop VERYHI = adsk.fusion.CalculationAccuracy.VeryHighCalculationAccuracy prop :adsk.fusion.PhysicalProperties = self.getPhysicalProperties(VERYHI) # Misalignment of the center of gravity bodyCenter :adsk.core.Point3D = prop.centerOfMass measMgr :adsk.core.MeasureManager = adsk.core.Application.get().measureManager _, xAxis, yAxis, _ = prop.getPrincipalAxes() bound :adsk.core.OrientedBoundingBox3D = measMgr.getOrientedBoundingBox( self, xAxis, yAxis) boundCenter :adsk.core.Point3D = bound.centerPoint length :float = bodyCenter.distanceTo(boundCenter) return bRepContainer( self, prop.area, prop.volume, length, self.faces.count, self.vertices.count) def getAllBRepBodies( des :adsk.fusion.Design) -> list: adsk.fusion.BRepBody.initContainer = initContainer root :adsk.fusion.Component = des.rootComponent bodies = [body.initContainer() for body in root.bRepBodies] for occ in root.allOccurrences: for comp in occ.Component: bodies.extend([body.initContainer() for body in comp.bRepBodies]) return bodies def dumpMsg(msg :str): adsk.core.Application.get().userInterface.palettes.itemById('TextCommands').writeText(str(msg))
ついでに、面の数と頂点数の一致も一致判断の条件として追加しました。
見直すと色々と不満があるのですけどね。
では、この様なデータでテストします。
ボディ1とボディ2は円柱の位置違いで、ボディ3はボディ2の円形状パターンで
作成しているので、一致していると判断されたいところです。
結果はこちら。
”Area” ”Volume” に違いがありますが、非常に小さいため誤差の範囲。
”Misalignment of the center of gravity” が最初に説明した重心の
位置ずれ距離です。
無事、判断出来ています。でも、これで判断出来ないものがある事に、
途中で気が付いちゃいました。