こちらの続きです。
前回のスクリプトで異なる形状と判断出来ないものは
対象形状です。
直方体の上にL字のエンボスを付けた形状の対象形状は、
色々と回転させても一致する事はありませんね。
しかし、前回のアイデアの重心のズレた距離だけでは
判断出来ません。
そこで距離だけでは無く、重心のズレをベクトルとして
受け止め、ベクトル自体が一致するかどうかも条件に
付け加える事で、対象形状を判断することが出来ないかな?
と考えました。
但し、比較対象となるボディが様々な方向になっている
可能性も考慮したいため、慣性モーメントの軸
(PhysicalProperties.getPrincipalAxesメソッド)を利用し
マトリックス(4x4)を作り出し変換した後のベクトルで
比較する事にします。
不安要素は、慣性モーメントの各軸が同一のボディの場合
同じになるものか? (一番長手方向がXAxisになる等)が
良くわかってません。・・・試すしかない。
# Fusion360API Python script import adsk.core, adsk.fusion, traceback import dataclasses _tolerance = 0.0001 @dataclasses.dataclass class bRepContainer: body : adsk.fusion.BRepBody faceCount : int vartexCount : int area : float volume : float cogLength : float cogVecter : adsk.core.Vector3D def isMatch( self :dataclasses.dataclass, cont :dataclasses.dataclass) -> bool: if self.faceCount != cont.faceCount: return False if self.vartexCount != cont.vartexCount: return False if abs(self.area - cont.area) > _tolerance: return False if abs(self.volume - cont.volume) > _tolerance: return False if abs(self.cogLength - cont.cogLength) > _tolerance: return False if not self.cogLength: return True # if not self.cogVecter.isEqualTo(cont.cogVecter): # return False if not self.cogVecter.isParallelTo(cont.cogVecter): 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 adsk.core.Point3D.dump = dumpPoint3D 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].cogLength} --') dumpMsg(f'-- FaceCount:{group[0].faceCount} , VartexCount:{group[0].vartexCount} --') group[0].cogVecter.asPoint().dump('-- cogVecter:') for cont in group: dumpMsg(cont.body.name) pass 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, zAxis = prop.getPrincipalAxes() bound :adsk.core.OrientedBoundingBox3D = measMgr.getOrientedBoundingBox( self, xAxis, yAxis) boundCenter :adsk.core.Point3D = bound.centerPoint length :float = bodyCenter.distanceTo(boundCenter) vec :adsk.core.Vector3D = adsk.core.Vector3D.cast(None) if length > 0: mat :adsk.core.Matrix3D = adsk.core.Matrix3D.create() mat.setWithCoordinateSystem(boundCenter, xAxis, yAxis, zAxis) mat.invert() vec = boundCenter.vectorTo(bodyCenter) vec.transformBy(mat) return bRepContainer( self, self.faces.count, self.vertices.count, prop.area, prop.volume, length, vec) 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)) def dumpPoint3D(self :adsk.core.Point3D, haeder = ''): dumpMsg(f'{haeder} - x:{self.x} y:{self.y} z:{self.z}')
まぁ、脳みその中で思い付いたことを言葉で表現するのは難しいです。
ちょっとベクトルの判定を "一致" とすべきか "平行" と判定すべきか
迷ってます。
最初の画像のボディで試します。
右がボディ1で左がボディ2です。
結果はこんな感じです。
** All Body Count : 2 ** -- Area:108.0 , Volume:64.0 -- -- Misalignment of the center of gravity:0.18772889041565421 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:0.1862544021475668 y:0.022587374504154684 z:-0.006422187349668939 ボディ1 -- Area:108.0 , Volume:64.0 -- -- Misalignment of the center of gravity:0.18772889041565421 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:-0.1862544021475668 y:0.022587374504154684 z:-0.006422187349668939 ボディ2
ベクトルの違いから "別の物" と判断してくれました。
安心してはいられないと思い、この様な状態にしました。
ボディ2を円形状パターンで複製(ボディ3、4)しました。
(=ボディ2,3,4は同じものと判断されたい)
実行結果はこちら。
** All Body Count : 4 ** -- Area:108.0 , Volume:64.0 -- -- Misalignment of the center of gravity:0.18772889041565421 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:0.1862544021475668 y:0.022587374504154684 z:-0.006422187349668939 ボディ1 -- Area:108.0 , Volume:64.0 -- -- Misalignment of the center of gravity:0.18772889041565421 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:-0.1862544021475668 y:0.022587374504154684 z:-0.006422187349668939 ボディ2 -- Area:108.00000000000003 , Volume:64.00000000000004 -- -- Misalignment of the center of gravity:0.18772889133855403 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:0.022587374420952516 y:0.18625440295632148 z:-0.006422191164590873 ボディ3 -- Area:108.0 , Volume:64.00000000000003 -- -- Misalignment of the center of gravity:0.18772888981099467 -- -- FaceCount:10 , VartexCount:16 -- -- cogVecter: - x:0.1862544016243073 y:-0.022587374504283015 z:-0.006422184849648054 ボディ4
・・・全部異なるものと判断されました。
色々調べた結果、原因は最初に懸念していた通り、慣性モーメントで
得られる軸がボディに対して一定のルールでXAxis・・を割り当てられる
わけでは無いようです。(恐らく近い絶対座標の軸になるのかな?)
重心のズレをベクトルとするのは良いアイデアだと思ったのですが、
上手く行きません。他に何か良いアイデアが無いかな?
そもそも確立されたアルゴリズムのようなものがあるのかな?