C#ATIA

↑タイトル詐欺 主にCATIA V5 の VBA(最近はPMillマクロとFusion360APIが多い)

スケッチ曲線を円弧近似化する

以前こちらで作成したCATIAのマクロをFusion360用にしてみました。
曲線と戦ってみる9 - C#ATIA

#FusionAPI_python
#Author-kantoku
#Description-ArcApproximation
#スケッチ曲線の円弧近似化

import adsk.core, adsk.fusion, adsk.cam, traceback

def run(context):
    ui = None
    
    #円弧近似化トレランス 単位はCm!!
    tolerance = 0.001
    
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        #面選択
        selFilters = 'SketchCurves'
        sel = Sel('カーブを選択/ESC-中止', selFilters )
        if sel is None: return
        crv = sel.entity        
        
        #点群
        points = InitPointsOnCurve(crv.geometry, tolerance)
        
        #円弧近似
        threeArcs = []
        threeArcs = InitThreePointArc(0, len(points)-1, tolerance, points, threeArcs)
        
        if len(threeArcs) < 1:
            ui.messageBox('円弧近似化できませんでした')
            return
        
        #スケッチ作成
        des = adsk.fusion.Design.cast(app.activeProduct)
        comp = des.rootComponent
        skt = comp.sketches.add(comp.xYConstructionPlane)
        skt.name = "ArcApproximation"
        
        #円弧作成
        skt_arcs = skt.sketchCurves.sketchArcs
        [skt_arcs.addByThreePoints(p1,p2,p3) for (p1,p2,p3) in threeArcs]
        
        ui.messageBox('{}個の円弧を作成しました'.format(len(threeArcs)))
        
    except:
        if ui:
           ui.messageBox('エラー\n{}'.format(traceback.format_exc()))

#外心円
def GetCircumCircleOFTraiangle(p1,p2,p3):
    #3点ベクトル取得
    vec1_2 = adsk.core.Vector3D.cast(p2.asVector())
    vec1_2.subtract(p1.asVector())
    vec1_2.normalize()
    
    vec2_3 = adsk.core.Vector3D.cast(p3.asVector())
    vec2_3.subtract(p2.asVector())
    vec2_3.normalize()
    
    #平行チェック
    if vec1_2.isParallelTo(vec2_3):
        return None
    
    #P2_P3間 オイラー線
    eulerLine = vec2_3.crossProduct(vec1_2.crossProduct(vec2_3))
    eulerLine.normalize()
    
    #中間点
    p1_2 = GetMidPoint3D(p1,p2)
    p2_3 = GetMidPoint3D(p2,p3)

    #不明
    nv = vec1_2.dotProduct(eulerLine)
    t = (vec1_2.dotProduct(p1_2.asVector()) - vec1_2.dotProduct(p2_3.asVector())) / nv
    
    #外心
    center = GetCircumCenter(p2_3,eulerLine,t)
    
    #半径
    radius = center.distanceTo(p1)
    
    return (center, radius)
    
#外心
def GetCircumCenter(p,v,l):
    pos = (p.x + v.x * l,
           p.y + v.y * l,
           p.z + v.z * l)    
    return adsk.core.Point3D.create(pos[0],pos[1],pos[2])

#中間点
def GetMidPoint3D(p1,p2):
    pos = ((p1.x + p2.x) * 0.5,
           (p1.y + p2.y) * 0.5,
           (p1.z + p2.z) * 0.5)
    return adsk.core.Point3D.create(pos[0],pos[1],pos[2])

def IsInTolerance(center, radius, tol, points):
    dist_lst = [radius - center.distanceTo(p) for p in points]
    res = [d for d in dist_lst if d > tol]
    
    return True if len(res) < 1 else False

#3点円弧 再帰
def InitThreePointArc(startIdx, endIdx, tol, points, threeArcs):
    
    if 2 > (endIdx - startIdx):
        return threeArcs
    
    #中間idx
    midIdx = int((startIdx + endIdx) * 0.5)
    arc = GetCircumCircleOFTraiangle(points[startIdx],
                                     points[midIdx], 
                                     points[endIdx])
    if arc is None:
        return threeArcs
    
    #円弧評価
    if IsInTolerance(arc[0], arc[1], 0.001, points[startIdx+1:endIdx-1]):
        threeArcs.append([points[startIdx],points[midIdx],points[endIdx]])
    else:
        threeArcs = InitThreePointArc(startIdx, midIdx, tol, points, threeArcs)
        threeArcs = InitThreePointArc(midIdx, endIdx, tol, points, threeArcs)
        
    return threeArcs
    
#トレランス以内の曲線上の点群
def InitPointsOnCurve(geo, tol):
    #evaluator
    eva = geo.evaluator
    
    #始点終点
    (returnValue, startPoint, endPoint) = eva.getEndPoints()
    (returnValue, startPram) = eva.getParameterAtPoint(startPoint)
    (returnValue, endPram) = eva.getParameterAtPoint(endPoint)
    
    #トレランス以内の点群
    (returnValue, pnts) = eva.getStrokes(startPram, endPram, tol)
    return pnts
    
#選択
def Sel(msg, selFilter):
    app = adsk.core.Application.get()
    ui  = app.userInterface
    try:
        return ui.selectEntity(msg, selFilter)
    except:
        return None

役に立つものかどうかは、かなり謎です。
CATIAに比べ、Fusion360APIはベクトルのクラスが既存であり
ベクトル演算が簡単に行えるのはかなり楽です。(破壊的・非破壊的メソッドの違いには迷う)
又、折れ線近似化する事が可能なトレランス以内で点群を取得できるメソッド(getStrokes)が
備わっているのもかなり楽です。

処理自体もCATIAより軽い気もしますね。