C#ATIA

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

スプリクトで2要素間の距離測定をしたい4

こちらの続きです。
スプリクトで2要素間の距離測定をしたい3 - C#ATIA


やっと極値コマンド代わりのスプリクトが出来ました。
予め書いておきますが、イマイチな部分が多々ありますので。

先にコードです。

#FusionAPI_python
#Author-kantoku
#Description-GetExtremumPoint ver0.02
#曲線の指定方向の最大・最小位置に点を作成します。

import adsk.core, adsk.fusion, traceback
ui = None
comp = None
scl = 100000 #遠くの平面用

def run(context):
    ui = None
    try:
        #準備
        app = adsk.core.Application.get()
        global ui
        ui = app.userInterface

        #向きと曲線選択
        crv = Sel(ui, '曲線選択/ESC-中止', 'Edges')
        if crv is None:
            ui.messageBox('キャンセル')
            return
        
        directionFilters = 'LinearEdges,SketchLines,PlanarFaces,ConstructionPlanes'
        line = Sel(ui, '向き選択/ESC-中止', directionFilters )
        if line is None:
            ui.messageBox('キャンセル')
            return

        #作業コンポーネント取得
        des = adsk.fusion.Design.cast(app.activeProduct)
        rootComp = des.rootComponent
        
        transform = adsk.core.Matrix3D.create()
        occ = rootComp.occurrences .addNewComponent(transform)
        global comp
        comp = occ.component
        
        #向きベクトル
        vec = GetVec(line)
        vecs = [adsk.core.Vector3D.create(vec.x*v, vec.y*v, vec.z*v) for v in (1,-1)]        
        
        #極値取得
        mins = []
        for direction in vecs:
            #評価用点郡取得
            pnts = GetComparisonPnts(direction, crv)
            
            #最短点取得
            mins.append(GetMinimumPointAtPoint(pnts, crv))
            
        #一時データ削除
        occ.deleteMe()
        
        #作業コンポーネント取得
        global comp
        crvCmp = crv.body.parentComponent
        
        #点の作成
        for minPnt in mins:
            if minPnt is None:
                continue
            
            if IsParametric():
                baseF = crvCmp.features.baseFeatures.add()
                baseF.startEdit()
                CreatePnt(crvCmp, minPnt)
                baseF.finishEdit()
            else:
                CreatePnt(crvCmp, minPnt)
        
        ui.messageBox('終了')
    except:
        if ui:
            ui.messageBox('エラー\n{}'.format(traceback.format_exc()))


#比較点郡取得
#param: vec-vector3d, crv-curve3d
#return: point3d()
def GetComparisonPnts(vec, crv):
    global comp
    
    #パラメトリックチェック
    para = IsParametric()
    if para:
        baseF = comp.features.baseFeatures.add()
        baseF.startEdit()    
    
    #平面
    pln = CreatePlane(vec)
    
    #投影
    skt = comp.sketches.add(pln)
    pros = skt.project(crv)
    global ui
    
    if isinstance(pros[0],adsk.fusion.SketchPoint):
        #直線
        pnts = [pros[0].worldGeometry]
    else:
        #曲線
        #Evaluator取得
        pro_eva = adsk.core.CurveEvaluator3D.cast(pros[0].worldGeometry.evaluator)
        
        #両端パラメータ取得
        (res,sprm,eprm) = pro_eva.getParameterExtents()
        
        #投影上に点郡作成
        (res,pnts) = pro_eva.getStrokes(sprm,eprm,0.001)
    
    #パラメトリックチェック
    if para:
        baseF.finishEdit()
        
    return pnts    
    
#ラインから単位ベクトル取得
#param: line-line3d,SketchLine,BRepFace,Plane
#return: vector3d
def GetVec(line):
    if isinstance(line,adsk.fusion.SketchLine):
        geo = line.worldGeometry
    else:
        geo = line.geometry
    eva = geo.evaluator
    
    if isinstance(eva,adsk.core.SurfaceEvaluator):
        res, vec = eva.getNormalAtParameter(adsk.core.Point2D.create(0,0))
    else:
        res, startPrm, endPrm = eva.getParameterExtents()
        res, vec = eva.getTangent(startPrm)
    vec.normalize()
    return vec
    
#平面作成
#param: vec-vector3d
#return: constructionPlane
def CreatePlane(vec):
    global scl
    ary = [x * y for (x, y) in zip(vec.asArray() , (scl,)*3)]
    global comp
    pln = comp.constructionPlanes.createInput()
    pln.setByPlane(adsk.core.Plane.create( \
            adsk.core.Point3D.create(ary[0], ary[1], ary[2]),vec))
    pln = comp.constructionPlanes.add(pln)
    return pln

#最短距離となる点の取得
#param: pnt-list(point3d), crv-curve3d
#return: Point3D
def GetMinimumPointAtPoint(pnts,crv):
    #評価点からの最短点郡
    minpnts =[GetMinimumPointAtCurve(p,crv.geometry) for p in pnts]
    
    #投影が点
    if len(minpnts) < 2:
        return None
        
    #各最短距離
    res =[(p1.distanceTo(p2),p2) for (p1,p2) in zip(pnts,minpnts)]
    
    #最短距離となる評価点の最短点
    try:
        #投影が曲線
        (res,minPnt) = min(res)
    except:
        #投影が直線
        minPnt = res[0][1]
    return minPnt
    
#点-線間の最短位置取得
#param: pnt-point3d, edg-curve3d
#return: point3d
def GetMinimumPointAtCurve(pnt, edg):
    try:
        eva = edg.evaluator

        res, prm = eva.getParameterAtPoint(pnt)
        if res is False:return None

        res, minPnt = eva.getPointAtParameter(prm)
        if res is False:return None

        return minPnt
    except:
        return None

#点作成
#param: comp-Component, pnt3d-point3d
#return - constructionPoint
def CreatePnt(comp, pnt3d):
    pnt = comp.constructionPoints.createInput()
    pnt.setByPoint(adsk.core.Point3D.create(pnt3d.x, pnt3d.y, pnt3d.z))
    return comp.constructionPoints.add(pnt)

#選択
#param: ui-userInterface, msg-string, selFilter-SelectionFilters
#return: entity
def Sel(ui, msg, selFilter):
    try:
        return ui.selectEntity(msg, selFilter).entity
    except:
        return None

#パラメトリックチェック
#return: boolen
def IsParametric():
    app = adsk.core.Application.get()
    isPara = False
    design = adsk.fusion.Design.cast(app.activeProduct)
    if design.designType == adsk.fusion.DesignTypes.ParametricDesignType:
        isPara = True
    return isPara

#range関数を実数に拡張したジェネレータ関数
#http://yu-write.blogspot.jp/2013/11/python-range.html
#param: begin-float, end-float, step-float
#return: generator(list(float))
def drange(begin, end, step):
    n = begin
    while n+step < end:
     yield n
     n += step

このスプリクトを利用するためには、方向を示すための直線が必要なのですが、
ボディやパッチのエッジでのみ指定可能です。
(この部分、不便で仕方ないので後日修正する予定です)

※修正しました。

使い方です。スプリクト実行後、まず極値を取得したい曲線を選択します。
マウスカーソルが無いですが、ツールチップ(マウスをしばらく止めておくと表示される
メッセージ)がこんな感じで表示されます。
f:id:kandennti:20161003164111p:plain

続いて、方向を示すための直線を選択します。
f:id:kandennti:20161003164118p:plain

指定した方向に対して、最大と最小の位置となる曲線上に点を
作成されます。(合計2点です)
f:id:kandennti:20161003164126p:plain


・利用の際は、自己責任でお願いします。
 (不具合っぽいお話は欲しいです)

極値を取得したい曲線を選択する際、直線を選ぶとエラーになります。
 (スイマセン)

・スプリクトでは作業スペースを行っていません。
 現状、モデル・パッチ・スカルプト・メッシュでは、動作確認しております。

・一時的に色々と作成し、最後に削除しているため
 新規でコンポーネントを作成する際、自動で割り当てられるネーミングの
 番号が進んでいます。


機能の無いものをスプリクトで補うのが、非常に苦しいのが本音です。
コンポーネントのネーミングの問題は、僕にもっと数学力があれば解決しそうな
雰囲気は感じているのですが、勉強が足りないなぁ。


追記です。
以下の部分を変更しました。
・最大最小を取得する曲線は、
"曲線" "直線" に変更しました。

・向きの選択を、
 "ボディ・パッチのエッジ" "ボディ・パッチの平面"
 "スケッチの直線" "平面" に変更しました。