C#ATIA

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

板状に分割したい4

こちらの続きです。
板状に分割したい3 - C#ATIA

どうもイマイチの様で・・・、完璧は難しいのですが、もう少し
打率を上げたいです。

データがあれば試せるのですが、こちらでそれっぽいものを
作りました。
球(ソリッド) → メッシュ化 → BRep化(ソリッド)→ シェル実行
で、ローポリゴンな形状のこんな感じです。

この形状に対して、少し改良を加えたスクリプトを実行します。

# Fusion360API Python script
# ver0.0.3

import traceback
import adsk.fusion
import adsk.core

DEBUG = False


def run(context):
    ui = adsk.core.UserInterface.cast(None)
    try:
        app: adsk.core.Application = adsk.core.Application.get()
        ui = app.userInterface

        msg: str = 'ボディを選択してください'
        selFilter: str = 'Bodies'
        sel: adsk.core.Selection = selectEnt(msg, selFilter)
        if not sel:
            return

        targetBody: adsk.fusion.BRepBody = sel.entity

        execDivided(targetBody)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def execDivided(
    body: adsk.fusion.BRepBody) -> list:

    app: adsk.core.Application = adsk.core.Application.get()
    comp: adsk.fusion.Component = body.parentComponent
    ngFaces = []
    notFind = []

    # ******
    def execLoft(pairFaces: list) -> list:
        bodies = []
        for faces in pairFaces:
            loftFeatIpt: adsk.fusion.LoftFeatureInput = loftFeats.createInput(
                # adsk.fusion.FeatureOperations.NewBodyFeatureOperation
                adsk.fusion.FeatureOperations.NewComponentFeatureOperation
            )
            loftFeatIpt.isSolid = True
            loftFeatIpt.isClosed = False

            loftSections: adsk.fusion.LoftSections = loftFeatIpt.loftSections
            [loftSections.add(f) for f in faces]

            app.executeTextCommand(u'Transaction.Start loft')
            try:
                # エラーが出るので苦肉の策・・・
                loftFeat: adsk.fusion.LoftFeature = loftFeats.add(loftFeatIpt)

                bodies.append(loftFeat.bodies[0])
                app.executeTextCommand(u'Transaction.Commit')
            except:
                ngFaces.extend(faces)
                adsk.core.Application.get().log(
                    'Failed:\n{}'.format(traceback.format_exc())
                )
                app.executeTextCommand(u'Transaction.Abort')

        return bodies


    def getVector(
        geo: adsk.core.Surface,
        point: adsk.core.Point3D) -> adsk.core.Vector3D:

        if hasattr(geo, 'normal'):
            return geo.normal

        eva: adsk.core.SurfaceEvaluator = geo.evaluator
        _, vec = eva.getNormalAtPoint(point)

        return vec


    def getPairFaces(shell: adsk.fusion.BRepShell):
        registeredList = [] # 組み合わせ済み
        pairFaces = []
        hitPoints: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create()
        face: adsk.fusion.BRepFace
        for face in shell.faces:

            # 既に組み合わせ済みか?
            # if face.entityToken in registeredList:
            #     continue

            pnt: adsk.core.Point3D = face.pointOnFace
            geo: adsk.core.Plane = face.geometry
            vec: adsk.core.Vector3D = getVector(geo, pnt)
            revVec: adsk.core.Vector3D = vec.copy()
            revVec.scaleBy(-1)
            vecs = [
                revVec,
                vec,
            ]

            # 候補の面取得
            tempFaces = []
            for vec in vecs:
                hitPoints.clear()
                res = comp.findBRepUsingRay(
                    pnt,
                    vec,
                    adsk.fusion.BRepEntityTypes.BRepFaceEntityType,
                    -1.0,
                    True,
                    hitPoints
                )

                if res.count < 1:
                    continue

                # 面と距離 - 向きが平行出ないものは除外
                tempFaces.extend(
                    [(f, pnt.distanceTo(p)) for f, p in zip(res, hitPoints) 
                        if vec.isParallelTo(getVector(f.geometry, p))]
                )

            # 距離0以下は除外
            tempFaces = [(f, l) for f, l in tempFaces if l > 0.001]

            if len(tempFaces) < 1:
                notFind.append(face)
                continue

            # 近いものが組み合わせ相手 同じボディか?ってチェック入れるべき?
            nearFace: adsk.fusion.BRepFace = min(tempFaces, key=lambda x: x[1])[0]

            # if nearFace.entityToken in registeredList:
            #     continue

            pairFaces.append(
                (
                    face,
                    nearFace
                )
            )

            # 登録
            registeredList.extend(
                [
                    face.entityToken,
                    nearFace.entityToken
                ]
            )

        return pairFaces


    def isAllPlanarFace(body: adsk.fusion.BRepBody) -> bool:
        planeType = adsk.core.Plane.classType()
        if all([f.geometry.classType() == planeType for f in body.faces]):
            return True

        return False
    # *******
    # 全て平面かチェック
    if not isAllPlanarFace(body):
        return

    # 完全な中空形状チェック
    # if not body.shells.count == 2:
    #     return

    # 外側 これ要らないかも・・・
    outer: adsk.fusion.BRepShell = max(body.shells, key=lambda x: x.area)

    # 外内面の組み合わせ
    pairFaces = getPairFaces(outer)

    # LoftFeatures
    loftFeats: adsk.fusion.LoftFeatures = comp.features.loftFeatures

    # 外内面でロフト
    bodies = execLoft(pairFaces)

    # debug 失敗面
    dumpFaces(notFind, 'NotFind', True)
    dumpFaces(ngFaces, 'LoopErr')

    return bodies


def selectEnt(
        msg: str,
        filterStr: str) -> adsk.core.Selection:

    try:
        app: adsk.core.Application = adsk.core.Application.get()
        ui: adsk.core.UserInterface = app.userInterface
        sel = ui.selectEntity(msg, filterStr)
        return sel
    except:
        return None

def dumpFaces(faces, name, pointFg = False):
    if not DEBUG:
        return

    if len(faces) < 1:
        return

    app: adsk.core.Application = adsk.core.Application.get()
    des: adsk.fusion.Design = app.activeProduct

    comp: adsk.fusion.Component = faces[0].body.parentComponent

    if pointFg:
        skt: adsk.fusion.Sketch = comp.sketches.add(
            comp.xYConstructionPlane
        )

        sktPnts: adsk.fusion.SketchPoints = skt.sketchPoints
        face: adsk.fusion.BRepFace = None
        for face in faces:
            sktPnts.add(face.pointOnFace)

    tmpMgr: adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
    clones = [tmpMgr.copy(f) for f in faces]


    baseFeat: adsk.fusion.BaseFeature = None
    if des.designType == adsk.fusion.DesignTypes.ParametricDesignType:
        baseFeat = comp.features.baseFeatures.add()
        baseFeat.name = name

    bodies: adsk.fusion.BRepBodies = comp.bRepBodies

    if baseFeat:
        baseFeat.startEdit()
        for body in clones:
            try:
                bodies.add(body, baseFeat)
            except:
                pass
        baseFeat.finishEdit()
    else:
        for body in clones:
            try:
                bodies.add(body)
            except:
                pass

実行してみると、多くの物は満足出来るレベルなのです。
正直、ん?と思う部分はあるのですが。

中にはダメなやつもあります。

わかりにくいのですが、三角形と四角形をロフトした形状です。

これは元の形状で見ると、シェルで処理する際
外側2枚を内側1枚にして処理されています。

これは・・・GUIでやっても無理じゃないかな?
と言う事で諦めます。


もう一つ、どうしても抜ける部分が出てしまいました。

色々と調べていたら、どうやら内側の面を見つけだす事が
出来ていないようです。

DEBUG = True

にして実行すると、上手く行かない部分の面等も出力します。

相手の面を探す際に使用している点を表示させると

想像以上に端っこでした。

てっきり常に重心付近に出来ると思っていたのですが、違いました。
原因これだな。