C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

同一形状のボディを判断する3

こちらの続きです。

前回のスクリプトで異なる形状と判断出来ないものは
対象形状です。
f:id:kandennti:20210623213256p:plain

直方体の上に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です。
f:id:kandennti:20210623214925p:plain
結果はこんな感じです。

** 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

ベクトルの違いから "別の物" と判断してくれました。


安心してはいられないと思い、この様な状態にしました。
f:id:kandennti:20210623215332p:plain
ボディ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・・を割り当てられる
わけでは無いようです。(恐らく近い絶対座標の軸になるのかな?)


重心のズレをベクトルとするのは良いアイデアだと思ったのですが、
上手く行きません。他に何か良いアイデアが無いかな?
そもそも確立されたアルゴリズムのようなものがあるのかな?