C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)※記載されている内容は個人の意見であり、所属する団体等を代表する意見では御座いません・・・・よ!

毛を生やす5

こちらの続きです。
毛を生やす4 - C#ATIA
え~今回は毛を生やしません。

前回のサーフェスを作る・作らないは、その面が該当するボディにとっての外側・内側を
判断し外側と判定した面の場合のみ作っていました。
(そもそも外側・内側の境界って何なのかな?)

これをボディ丸ごと処理するようにしたものがこちらです。

# Fusion360API Python script

# サンプルデータ
# https://grabcad.com/library/random-part-13

import traceback
import adsk
import adsk.core as core
import adsk.fusion as fusion

_app: core.Application = None
_ui: core.UserInterface = None
_handlers = []

_selFace: core.SelectionCommandInput = None

CMD_INFO = {
    'id': 'kantoku_test',
    'name': 'test',
    'tooltip': 'test'
}


class MyCommandCreatedHandler(core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandCreatedEventArgs):
        try:
            global _handlers
            cmd: core.Command = core.Command.cast(args.command)

            # cmd.isOKButtonVisible = False

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecutePreview = MyExecutePreviewHandler()
            cmd.executePreview.add(onExecutePreview)
            _handlers.append(onExecutePreview)

            inputs: core.CommandInputs = cmd.commandInputs

            global _selFace
            _selFace = inputs.addSelectionInput(
                "_selFaceId",
                "ボディ",
                "外側のみ抜き出すボディを選んで!",
            )
            _selFace.addSelectionFilter(core.SelectionCommandInput.Bodies)

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


class MyExecutePreviewHandler(core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandEventArgs):
        args.isValidResult = True
    
        body: fusion.BRepBody = _selFace.selection(0).entity
        outerBody: fusion.BRepBody = get_shell_faces(body)
        if not outerBody:
            return
        
        comp: fusion.Component = body.parentComponent
        des: fusion.Design = comp.parentDesign

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

        bodies: fusion.BRepBodies = comp.bRepBodies
        if baseFeat:
            baseFeat.startEdit()
            bodies.add(outerBody, baseFeat)
            baseFeat.finishEdit()
        else:
            bodies.add(outerBody, baseFeat)


def get_shell_faces(
        body: fusion.BRepBody,) -> fusion.BRepBody:

    comp: fusion.Component = body.parentComponent
    tmpFeat: fusion.TemporaryBRepManager = fusion.TemporaryBRepManager.get()
    
    # findBRepUsingRayの結果が自身のBodyとなる結果が、nodesの30%以上であれば内側面
    outerBody: fusion.BRepBody = None
    face: fusion.BRepFace = None
    for face in body.faces:
        meshCalculator: fusion.TriangleMeshCalculator = face.meshManager.createMeshCalculator()
        meshCalculator.maxAspectRatio = 1
        meshCalculator.maxSideLength = 0.1 if face.area < 1 else face.area * 0.1
        triMesh: fusion.TriangleMesh = meshCalculator.calculate()

        nodes = list(triMesh.nodeCoordinates)
        normals = list(triMesh.normalVectors)
        limit = int(len(nodes) * 0.3)

        innerPoints: int = 0

        # UsingRayで自身のボディと交差するかチェック
        for p, v in zip(nodes,normals):
            v2: core.Vector3D = v.copy()
            v2.scaleBy(0.01)
            p2: core.Point3D = p.copy()
            p2.translateBy(v2)
            hitBodies = comp.findBRepUsingRay(
                p2, 
                v, 
                fusion.BRepEntityTypes.BRepBodyEntityType,
                -1,
                False,
                None,
            )
            if hitBodies.count < 1:
                continue
            for hitBody in hitBodies:
                if hitBody.entityToken == body.entityToken:
                    innerPoints += 1
                    break
            
            if innerPoints > limit:
                break

        if not innerPoints > limit:
            # コピー
            if outerBody:
                tmpFeat.booleanOperation(
                    outerBody,
                    tmpFeat.copy(face),
                    fusion.BooleanTypes.UnionBooleanType,
                )
            else:
                outerBody = tmpFeat.copy(face)

    return outerBody


class MyCommandDestroyHandler(core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandEventArgs):
        adsk.terminate()


def run(context):
    try:
        global _app, _ui
        _app = core.Application.get()
        _ui = _app.userInterface

        cmdDef: core.CommandDefinition = _ui.commandDefinitions.itemById(
            CMD_INFO['id']
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                CMD_INFO['id'],
                CMD_INFO['name'],
                CMD_INFO['tooltip']
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

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

前回のデータで試すとこんな感じです。

久々にこうしておきます。

"これはいったい何に使うの?" と思われた方には役に立たないでしょうね。