C#ATIA

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

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

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


あちらのスクリプトをコマンドダイアログ(?)仕様に修正しました。

#FusionAPI_python
#Author-kantoku
#Description-GetExtremumPoint ver0.04
#曲線の指定方向の最大・最小位置に点を作成します。
import adsk.core, adsk.fusion, traceback

_commandId = 'extremumPoint'
_commandName = 'ExtremumPoint'
_commandDescription = '曲線の指定方向の最大・最小位置に点を作成します。'

_handlers = []
_scl = 100000 #遠くの平面用

_app = adsk.core.Application.get()
if _app:
    _ui = _app.userInterface

class ExtremumPointCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            command = args.firingEvent.sender
            inputs = command.commandInputs

            crvIpt = inputs[0];
            crvSel = crvIpt.selection(0);

            dirIpt = inputs[1];
            dirSel = dirIpt.selection(0);

            extremumPoint = ExtremumPoint();
            extremumPoint.Execute(crvSel.entity, dirSel.entity);
        except:
            if _ui:
                _ui.messageBox('エラー:\n{}'.format(traceback.format_exc()))

class ExtremumPointCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            if _ui:
                _ui.messageBox('エラー:\n{}'.format(traceback.format_exc()))

class ExtremumPointValidateInputHandler(adsk.core.ValidateInputsEventHandler):
    def __init__(self):
        super().__init__()
       
    def notify(self, args):
        try:
            sels = _ui.activeSelections;
            if len(sels) == 2:
                args.areInputsValid = True
            else:
                args.areInputsValid = False
        except:
            if _ui:
                _ui.messageBox('エラー:\n{}'.format(traceback.format_exc()))

class ExtremumPointCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            cmd = args.command
            onExecute = ExtremumPointCommandExecuteHandler()
            cmd.execute.add(onExecute)
            onDestroy = ExtremumPointCommandDestroyHandler()
            cmd.destroy.add(onDestroy)

            onValidateInput = ExtremumPointValidateInputHandler()
            cmd.validateInputs.add(onValidateInput)
            
            _handlers.append(onExecute)
            _handlers.append(onDestroy)
            _handlers.append(onValidateInput)
            
            inputs = cmd.commandInputs
            i1 = inputs.addSelectionInput('crvIpt', '対象のエッジ', 'ボディ・パッチのエッジを選択')
            i1.addSelectionFilter(adsk.core.SelectionCommandInput.Edges);
            
            i2 = inputs.addSelectionInput('dirIpt', '向きとなる直線・平面', '向きとなる直線・平面を選択')
            i2.addSelectionFilter(adsk.core.SelectionCommandInput.PlanarFaces);
            i2.addSelectionFilter(adsk.core.SelectionCommandInput.LinearEdges);
            i2.addSelectionFilter(adsk.core.SelectionCommandInput.SketchLines);
            i2.addSelectionFilter(adsk.core.SelectionCommandInput.ConstructionLines);
            i2.addSelectionFilter(adsk.core.SelectionCommandInput.ConstructionPlanes);
        except:
            if _ui:
                _ui.messageBox('エラー:\n{}'.format(traceback.format_exc()))


class ExtremumPoint:
    def Execute(self, crvSel, dirSel):
        #ラインから単位ベクトル取得
        #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: 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    
             
        #比較点郡取得
        #param: vec-vector3d, crv-curve3d, scl-double
        #return: point3d()
        def GetComparisonPnts(vec, crv, scl):
            #変換行列設定
            zaxis = adsk.core.Vector3D.create(0,0,1)
            
            mat3d = adsk.core.Matrix3D.create()
            mat3d.setToRotateTo(vec,zaxis)
            
            remat3d = adsk.core.Matrix3D.create()
            remat3d = mat3d.copy()
            remat3d.invert() 
            
            mat3dpjt = adsk.core.Matrix3D.create()
            mat3dpjt.setWithArray((1,0,0,0,
                                   0,1,0,0,
                                   0,0,0,0,
                                   0,0,0,1))
            
            mat3dscl = adsk.core.Matrix3D.create()
            mat3dscl.setWithArray((scl,0,0,0,
                                   0,scl,0,0,
                                   0,0,scl,0,
                                   0,0,0,1))
                                   
            mat3d.transformBy(mat3dpjt)
            mat3d.transformBy(remat3d)
            mat3d.transformBy(mat3dscl)
            
            crveva = crv.geometry.evaluator
            (res,sprm,eprm) = crveva.getParameterExtents()
            (res,pnts) = crveva.getStrokes(sprm,eprm,0.001)   
            [pnt.transformBy(mat3d) for pnt in pnts]
            
            return pnts                
        
        def sss(path,txt):
            f = open(path, 'w')
            for t in txt:
                f.write('{}:{}\n'.format(t[0],t[1].asArray()))
            f.close()
             
        try:
            #向きベクトル
            global _scl
            vec = GetVec(dirSel)
            scls = [_scl*v for v in (1,-1)]        
            
            #極値取得
            mins = []
            for scl in scls:
                #評価用点郡取得
                pnts = GetComparisonPnts(vec, crvSel, scl)
                
                #最短点取得
                mins.append(GetMinimumPointAtPoint(pnts, crvSel))
            
            #作業コンポーネント取得
            crvCmp = crvSel.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()))   
                
def run(context):
    try:
        product = _app.activeProduct
        design = adsk.fusion.Design.cast(product)
        if not design:
            _ui.messageBox('モデルワークスペースで実行してください')
            return
        commandDefinitions = _ui.commandDefinitions
        
        cmdDef = commandDefinitions.itemById(_commandId)
        if not cmdDef:
            cmdDef = commandDefinitions.addButtonDefinition(_commandId,
                    _commandName,
                    _commandDescription)

        onCommandCreated = ExtremumPointCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        
        _handlers.append(onCommandCreated)
        inputs = adsk.core.NamedValues.create()
        cmdDef.execute(inputs)

        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('エラー:\n{}'.format(traceback.format_exc()))

何度も書きますが、苦手なんですイベント処理。
素直にサンプルのIntersectionsスプリクトをパクリました。

スクリプト実行後は、こんな感じのダイアログが出るので前回までのものより
わかりやすいかと思います。
f:id:kandennti:20161004161736p:plain

又、Undoで "ExtremumPoint" と表示されるようにしました。
スプリクトでは2個点を作成しますが、1回のUndoで2個とも削除されます。
f:id:kandennti:20161004162236p:plain


追記です。(Ver0.04)

一時的な要素の作成を止めたため、新規にコンポーネントを作成する際
デフォルトの名前が連番になるようにしました。