C#ATIA

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

閉じた折れ線を円にする1

こちらに挑戦中です。
Polygons to circles - Autodesk Community
同様の問い合わせがあるので、需要はあるはず。

作ってみたものの、小さなデータはまぁまぁOKです。
添付されたDXFでは、殺意を覚えるぐらい遅いです・・・。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core
import time


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

        # select sketch
        msg: str = 'Select Skectch'
        selFiltter: str = 'Sketches,Profiles,Texts,SketchCurves,SketchLines,SketchCircles,SketchPoints'
        sel: adsk.core.Selection = selectEnt(msg, selFiltter)
        if not sel:
            return

        # get sketch
        selEntity = sel.entity
        skt: adsk.fusion.Sketch = None
        if selEntity.objectType == 'adsk::fusion::Sketch':
            skt = sel.entity
        else:
            skt = sel.entity.parentSketch

        # query
        msg = f'Process {skt.sketchCurves.sketchLines.count} lines.'
        msg += '\n Are you sure?'
        button = adsk.core.MessageBoxButtonTypes.OKCancelButtonType
        icon = adsk.core.MessageBoxIconTypes.QuestionIconType
        query = ui.messageBox(msg, 'Polygons To Circles', button, icon)
        if query == adsk.core.DialogResults.DialogCancel:
            return

        # time
        t = time.time()

        # Convert Polygon to Circle
        converts = polygons2Circles(skt)

        # finish
        msg = f'{len(converts)} circles were created.'
        msg += '\n({:.3f} s)'.format(time.time() - t)

        ui.messageBox(msg)

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


def polygons2Circles(
        skt: adsk.fusion.Sketch) -> list:

    # -- Support Functions --
    def updeteProgress(
            prog: adsk.core.ProgressDialog,
            msg: str):

        prog.progressValue += 1
        prog.message = f'Processing {msg} ....'
        adsk.doEvents()
        return progress.wasCancelled

    def execConvertCircle(
            lines: adsk.core.ObjectCollection):

        skt: adsk.fusion.Sketch = lines[0].parentSketch
        circle: adsk.core.Circle3D = skt.sketchCurves.sketchCircles.addByThreePoints(
            lines[0].startSketchPoint.geometry,
            lines[1].startSketchPoint.geometry,
            lines[2].startSketchPoint.geometry
        )

        for line in lines:
            line.isConstruction = True

        return circle

    def setCheck(
            lst: list,
            value: bool = True):

        for c in lst:
            if c.objectType != 'adsk::fusion::SketchLine':
                continue

            c.isCheck = value

    def isClosedChainedLines(
            curves: adsk.core.ObjectCollection) -> bool:

        objs: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create()
        for curve in curves:
            if curve.objectType != 'adsk::fusion::SketchLine':
                continue

            if curve.isConstruction:
                continue

            if not curve.is2D:
                continue

            objs.add(curve)

        path: adsk.fusion.Path = adsk.fusion.Path.create(
            objs, adsk.fusion.ChainedCurveOptions.noChainedCurves)

        return path.isClosed

    def isConvertCircle(
            lines: adsk.core.ObjectCollection,
            tolerance: float = 0.001) -> bool:

        if lines.count < 3:
            return False

        startPoints = [l.startSketchPoint.geometry for l in lines]

        tempCircle: adsk.core.Circle3D = adsk.core.Circle3D.createByThreePoints(
            startPoints[0],
            startPoints[1],
            startPoints[2]
        )

        eva: adsk.core.CurveEvaluator3D = tempCircle.evaluator
        _, prms = eva.getParametersAtPoints(startPoints)
        _, returnPnts = eva.getPointsAtParameters(prms)

        for p1, p2 in zip(startPoints, returnPnts):
            if not p1.isEqualToByTolerance(p2, tolerance):
                return False

        return True

    #  -----------

    # extention property
    adsk.fusion.SketchLine.isCheck = False

    # ProgressDialog
    app: adsk.fusion.Application = adsk.core.Application.get()
    progress: adsk.core.ProgressDialog = app.userInterface.createProgressDialog()
    progress.isCancelButtonShown = True

    # start
    skt.isComputeDeferred = True

    converts = []
    linesCount = skt.sketchCurves.sketchLines.count
    line: adsk.fusion.SketchLine
    for idx, line in enumerate(skt.sketchCurves.sketchLines):

        if updeteProgress(progress, f'{idx}/{linesCount}'):
            break
        adsk.doEvents()

        if line.isCheck:
            continue

        if line.isConstruction:
            continue

        curves: adsk.core.ObjectCollection = skt.findConnectedCurves(line)
        if curves.count < 3:
            setCheck(curves)
            continue

        if not isClosedChainedLines(curves):
            setCheck(curves)
            continue

        if not isConvertCircle(curves):
            setCheck(curves)
            continue

        converts.append(execConvertCircle(curves))
        setCheck(curves)

    skt.isComputeDeferred = False

    return converts


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

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

途中で中止出来るように、プロセスダイアログを付けたのですが、
それすら意味を成さないぐらい。
改善の余地しかない。