C#ATIA

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

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

こちらの続きです。
同一形状のボディを判断する1 - C#ATIA
慣性モーメントを利用しての判断を追加することにします。


慣性モーメント自体については・・・え~正しい意味合いは、
検索してみて下さい。
自分の中ではこのようなイメージで捉えています。
f:id:kandennti:20210619145353p:plain
歪な形状では無くても良いのですが、ボディを包囲するような
最小の直方体を作る際のXYZのベクトルや中心が得られる
と思っています。

常に(恐らくですが) "最小" の直方体となるのであれば、同一の
ボディが移動されたり回転されたりしていても、
”同じ状態でボディを見る為の座標系を得られる”
と思ってます。 ・・・多分。

赤がボディの重心で、青が最小の直方体の重心です。
f:id:kandennti:20210619151350p:plain
これを利用すれば、お互いの重心のズレが発生するので
前回の円柱の位置の違いを判断することが出来るのでは
ないのかな? と感じています。
(球体と正多面体は重心の位置ずれ無し)

念の為、過去の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))

ついでに、面の数と頂点数の一致も一致判断の条件として追加しました。
見直すと色々と不満があるのですけどね。


では、この様なデータでテストします。
f:id:kandennti:20210619152752p:plain
ボディ1とボディ2は円柱の位置違いで、ボディ3はボディ2の円形状パターンで
作成しているので、一致していると判断されたいところです。

結果はこちら。
f:id:kandennti:20210619153108p:plain
”Area” ”Volume” に違いがありますが、非常に小さいため誤差の範囲。
”Misalignment of the center of gravity” が最初に説明した重心の
位置ずれ距離です。

無事、判断出来ています。でも、これで判断出来ないものがある事に、
途中で気が付いちゃいました。