C#ATIA

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

毛を生やす3

こちらの続きです。
毛を生やす2 - C#ATIA

んー今日も時間が無い。
ちょっと別の処理を行っていたのですが、思うような結果が得られず
突貫で確認するために直したコードの為、関数名がふさわしくないです・・・。
何をしているのかは後日。

# Author-
# Description-
# 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):
        core.Application.get().log(args.firingEvent.name)
        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.Faces)

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


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

    def notify(self, args: core.CommandEventArgs):
        core.Application.get().log(args.firingEvent.name)
    
        face: fusion.BRepFace = _selFace.selection(0).entity
        outerBody: fusion.BRepBody = get_shell_faces(face)
        if not outerBody:
            return
        
        comp: fusion.Component = face.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(
        face: fusion.BRepFace,) -> fusion.BRepBody:

    comp: fusion.Component = face.body.parentComponent
    tmpMgr: fusion.TemporaryBRepManager = fusion.TemporaryBRepManager.get()

    # findBRepUsingRayの結果が自身のBodyとなる結果が、nodesの30%以上であれば内側面
    outerBody: fusion.BRepBody = None
    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)

    skt: fusion.Sketch = comp.sketches.add(comp.xYConstructionPlane)
    skt.isComputeDeferred = True
    skt.arePointsShown = False
    for p1, v in zip(nodes, normals):
        p2: core.Point3D = p1.copy()
        p2.translateBy(v)

        skt.sketchCurves.sketchLines.addByTwoPoints(p1, p2)
    skt.isComputeDeferred = False

    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 == face.body.entityToken:
                innerPoints += 1
                break
        
        if innerPoints > limit:
            break

    _app.log(f"{limit}: {innerPoints} - {innerPoints > limit}-{face.area}")
    if not innerPoints > limit:
        # コピー
        if outerBody:
            tmpMgr.booleanOperation(
                outerBody,
                tmpMgr.copy(face),
                fusion.BooleanTypes.UnionBooleanType,
            )
        else:
            outerBody = tmpMgr.copy(face)

    return outerBody


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

    def notify(self, args: core.CommandEventArgs):
        core.Application.get().log(args.firingEvent.name)
        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()))