こちらの続きです。
前回のスクリプトで異なる形状と判断出来ないものは
対象形状です。
直方体の上にL字のエンボスを付けた形状の対象形状は、
色々と回転させても一致する事はありませんね。
しかし、前回のアイデアの重心のズレた距離だけでは
判断出来ません。
そこで距離だけでは無く、重心のズレをベクトルとして
受け止め、ベクトル自体が一致するかどうかも条件に
付け加える事で、対象形状を判断することが出来ないかな?
と考えました。
但し、比較対象となるボディが様々な方向になっている
可能性も考慮したいため、慣性モーメントの軸
(PhysicalProperties.getPrincipalAxesメソッド)を利用し
マトリックス(4x4)を作り出し変換した後のベクトルで
比較する事にします。
不安要素は、慣性モーメントの各軸が同一のボディの場合
同じになるものか? (一番長手方向がXAxisになる等)が
良くわかってません。・・・試すしかない。
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.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:
VERYHI = adsk.fusion.CalculationAccuracy.VeryHighCalculationAccuracy
prop :adsk.fusion.PhysicalProperties = self.getPhysicalProperties(VERYHI)
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・・を割り当てられる
わけでは無いようです。(恐らく近い絶対座標の軸になるのかな?)
重心のズレをベクトルとするのは良いアイデアだと思ったのですが、
上手く行きません。他に何か良いアイデアが無いかな?
そもそも確立されたアルゴリズムのようなものがあるのかな?