C#ATIA

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

面の外周のオフセットラインを作成する

こちらで質問したものをスクリプトにしました。
解決済み: 曲面の境界のオフセットラインを取得 - Autodesk Community
結果的にパイプ+面分割の方法で行ってみました。

例外処理とかほぼしていませんので、あまり無茶は出来ませんので。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core


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

        msg: str = 'Select'
        selFilter: str = 'Faces'
        sel: adsk.core.Selection = selectEnt(msg, selFilter)
        if not sel:
            return

        valStr, res = ui.inputBox(
            'オフセット距離を入力',
            '外周のオフセット線',
            '1.0'
        )

        if res:
            return

        if not valStr.isdecimal():
            return

        initOffsetEdges(sel.entity, float(valStr))

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

def initOffsetEdges(
    face: adsk.fusion.BRepFace,
    offsetLength: float):

    if not offsetLength > 0:
        return

    app: adsk.core.Application = adsk.core.Application.get()
    des: adsk.fusion.Design = app.activeProduct
    root: adsk.fusion.Component = des.rootComponent
    comp: adsk.fusion.Component = root

    # 元面のクローン
    tmpMgr: adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
    clone: adsk.fusion.BRepBody = tmpMgr.copy(face)

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

    bodies: adsk.fusion.BRepBodies = comp.bRepBodies

    cloneFace: adsk.fusion.BRepFace = None
    if baseFeat:
        baseFeat.startEdit()
        try:
            cloneFace = bodies.add(clone, baseFeat).faces[0]
        except:
            pass
    else:
        try:
            cloneFace = bodies.add(clone)
        except:
            pass

    # 境界
    loops = [l for l in cloneFace.loops]

    # 元エッジのジオメトリ
    edges = [e for e in loops[0].edges]

    # ゴリゴリオフセットエッジの作成
    plane: adsk.fusion.ConstructionPlane = initPlane(loops[0])
    skt: adsk.fusion.Sketch = initSketch(plane, offsetLength)
    sweepBody: adsk.fusion.BRepBody = initSweep(loops[0], skt)
    initSplitBody(cloneFace, sweepBody)
    sweepBody.deleteMe()
    skt.deleteMe()
    plane.deleteMe()

    if baseFeat:
        baseFeat.finishEdit()

    # オフセットジオメトリの取得
    geos = getOffsetCurves(cloneFace, edges)

    if baseFeat:
        baseFeat.deleteMe()

    # オフセットエッジを描く!
    offsetSkt: adsk.fusion.Sketch = initSketch(root.xYConstructionPlane)
    splines: adsk.fusion.SketchFixedSplines = offsetSkt.sketchCurves.sketchFittedSplines
    offsetSkt.isComputeDeferred = True
    offsetSkt.arePointsShown = False
    for geo in geos:
        if hasattr(geo, 'asNurbsCurve'):
            geo = geo.asNurbsCurve
        spline: adsk.fusion.SketchFixedSpline = splines.addByNurbsCurve(geo)
        spline.isReference = False
    offsetSkt.isComputeDeferred = False


def getOffsetCurves(
    face: adsk.fusion.BRepFace,
    edges: list) -> list:

    geos = []
    for f in face.body.faces:
        for e in f.edges:
            if not isIncludedEdge(e, edges):
                geos.append(e.geometry)

    return geos


def isIncludedEdge(
    edge: adsk.fusion.BRepEdge,
    edges: list) -> bool:

    for e in edges:
        if isEqEdge(edge, e):
            return True

    return False


def isEqEdge(
    e1: adsk.fusion.BRepEdge,
    e2: adsk.fusion.BRepEdge) -> bool:

    if e1.geometry.objectType != e2.geometry.objectType:
        return False

    app: adsk.core.Application = adsk.core.Application.get()
    if e1.length - e2.length > app.pointTolerance:
        return False

    measMgr: adsk.core.MeasureManager = app.measureManager
    vecL: adsk.core.Vector3D = adsk.core.Vector3D.create(0,1,0)
    vecW: adsk.core.Vector3D = adsk.core.Vector3D.create(1,0,0)
    bBox1: adsk.core.OrientedBoundingBox3D = measMgr.getOrientedBoundingBox(
        e1,
        vecL,
        vecW
    )
    bBox2: adsk.core.OrientedBoundingBox3D = measMgr.getOrientedBoundingBox(
        e2,
        vecL,
        vecW
    )

    if not bBox1.centerPoint.isEqualTo(bBox2.centerPoint):
        return False

    if bBox1.length - bBox2.length > app.pointTolerance:
        return False

    if bBox1.width - bBox2.width > app.pointTolerance:
        return False

    if bBox1.height - bBox2.height > app.pointTolerance:
        return False

    return True


def initSplitBody(
    targetFace: adsk.fusion.BRepFace,
    toolBody: adsk.fusion.BRepBody) -> adsk.fusion.BRepBody:

    comp: adsk.fusion.Component = targetFace.body.parentComponent

    objs: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create()
    objs.add(targetFace)

    splitFaceFeats: adsk.fusion.SplitFaceFeatures = comp.features.splitFaceFeatures
    splitIpt: adsk.fusion.SplitFaceFeatureInput = splitFaceFeats.createInput(
        objs,
        toolBody,
        True
    )
    splitFaceFeat: adsk.fusion.SplitFaceFeature = splitFaceFeats.add(splitIpt)


def initSweep(
    loop: adsk.fusion.BRepLoop,
    skt: adsk.fusion.Sketch) -> adsk.fusion.BRepBody:

    comp: adsk.fusion.Component = skt.parentComponent

    objs: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create()
    [objs.add(e) for e in loop.edges]
    path: adsk.fusion.Path = comp.features.createPath(
        objs,
        True
    )

    sweepFeats: adsk.fusion.SweepFeatures = comp.features.sweepFeatures
    sweepIpt: adsk.fusion.SweepFeatureInput = sweepFeats.createInput(
        skt.profiles[0],
        path,
        adsk.fusion.FeatureOperations.NewBodyFeatureOperation
    )
    sweepFeat: adsk.fusion.SweepFeature = sweepFeats.add(sweepIpt)

    return sweepFeat.bodies[0]


def initSketch(
    plane: adsk.fusion.ConstructionPlane,
    radius: float = -1) -> adsk.fusion.Sketch:

    comp: adsk.fusion.Component = plane.component
    skt: adsk.fusion.Sketch = comp.sketches.add(plane)

    if radius > 0:
        skt.sketchCurves.sketchCircles.addByCenterRadius(
            skt.originPoint,
            radius
        )

    return skt


def initPlane(
    loop: adsk.fusion.BRepLoop) -> adsk.fusion.ConstructionPlane:

    # ここOCC関係あるかも
    comp: adsk.fusion.Component = loop.body.parentComponent
    planes: adsk.fusion.ConstructionPlanes = comp.constructionPlanes

    planeIpt: adsk.fusion.ConstructionPlaneInput = planes.createInput()
    planeIpt.setByDistanceOnPath(
        loop.edges[0],
        adsk.core.ValueInput.createByReal(0)
    )

    return planes.add(planeIpt)


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

クッソ長くなったのは、APIでパイプコマンドが無い為、スイープで
代替えしている為です。

出来が悪いのですが、ここがゴールでは無いのでご勘弁を。