こちらの続きです。
スプリクトで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
このスプリクトを利用するためには、方向を示すための直線が必要なのですが、
ボディやパッチのエッジでのみ指定可能です。
(この部分、不便で仕方ないので後日修正する予定です)
※修正しました。
使い方です。スプリクト実行後、まず極値を取得したい曲線を選択します。
マウスカーソルが無いですが、ツールチップ(マウスをしばらく止めておくと表示される
メッセージ)がこんな感じで表示されます。
続いて、方向を示すための直線を選択します。
指定した方向に対して、最大と最小の位置となる曲線上に点を
作成されます。(合計2点です)
・利用の際は、自己責任でお願いします。
(不具合っぽいお話は欲しいです)
・極値を取得したい曲線を選択する際、直線を選ぶとエラーになります。
(スイマセン)
・スプリクトでは作業スペースを行っていません。
現状、モデル・パッチ・スカルプト・メッシュでは、動作確認しております。
・一時的に色々と作成し、最後に削除しているため
新規でコンポーネントを作成する際、自動で割り当てられるネーミングの
番号が進んでいます。
機能の無いものをスプリクトで補うのが、非常に苦しいのが本音です。
コンポーネントのネーミングの問題は、僕にもっと数学力があれば解決しそうな
雰囲気は感じているのですが、勉強が足りないなぁ。
追記です。
以下の部分を変更しました。
・最大最小を取得する曲線は、
"曲線" "直線" に変更しました。
・向きの選択を、
"ボディ・パッチのエッジ" "ボディ・パッチの平面"
"スケッチの直線" "平面" に変更しました。