C#ATIA

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

3D曲線のパイプの中心線を求める4

こちらの続きです。
3D曲線のパイプの中心線を求める3 - C#ATIA


マニアの為の解説です。

        # surface evaluator取得
        surfEva: adsk.core.SurfaceEvaluator = face.evaluator

"Surface Evaluatorって何" と聞かれても答えにくいのですが・・・。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-GUID-31dad9b2-ee1d-4216-8100-09ea44f9967a
サーフェスの詳細な情報を取得する際に利用します。

今回は曲率や法線等が必要なのですが、実際に選択した面のBRepFace
オブジェクトでは取得出来ない為、こちらを利用します。

        # 面上の点からパラメータ取得
        refPrm: adsk.core.Point2D = None
        _, refPrm = surfEva.getParameterAtPoint(
            face.pointOnFace
        )

"face.pointOnFace" は、何処かは分かりませんがサーフェス上に
ある事が保証された点です。
大体重心付近になりますが、何処になるかは分かりません。

"getParameterAtPoint" メソッドは3Dの点がサーフェス上のどの位置
に存在するかをパラメータとして取得します。
Fusion 360 Help
パラメータ・・・便利過ぎる言葉で悩んでしまいますよね。

僕たちはGPS等を利用する事で、今どの位置に居るかを確認する事が出来ます。
緯度・経度による位置ですよね。
一方、僕たちは間違いなく三次元の世界を生きています。(もっと多くの次元の
世界かも知れないと言う説もありますが・・・)
つまり、三次元の位置情報(XYZ)を、高さを無視し地球の表面上の二次元の
位置情報(緯度・経度)として変換している事になります。

この部分のパラメータと言うのはサーフェス上の緯度・経度(正しくはUとV)
を表しており、Fusion360APIではPoint2DオブジェクトのXとYを利用して
表現しています。
パラメータは "どうしても必要" と言うわけでは無いのですが、後に利用する
メソッドで利用する機会が有る為、取得しています。

        # アイソメトリック曲線のV方向取得
        isoCrvLst = surfEva.getIsoCurve(refPrm.y, False)
        isoCrv: adsk.core.NurbsCurve3D = isoCrvLst[0]

"getIsoCurve" メソッドでアイソメトリック曲線を取得します。
Fusion 360 Help
アイソメトリック曲線は先程の緯度・経度の線のようなものです。
"アイソカーブ解析" コマンドで表示させると見ることが出来ます。

赤矢印がUで、青矢印がVの方向です。
"Fusion360の場合、必ず径方向がUなのか?" と言う疑問は残るのですが、
幾つか試した所そのようになっていました。
今回はアイソメトリックV曲線を取得しています。

        # アイソメトリックV曲線 evaluator取得
        crvEva: adsk.core.CurveEvaluator3D = isoCrv.evaluator

続いて曲線のEvaluatorを取得します。
Fusion 360 Help

        # 曲線のパラメータ範囲取得
        _, sPrm, ePrm = crvEva.getParameterExtents()

曲線の始点と終点のパラメータを取得します。
Fusion 360 Help
曲線のパラメータはfloatです。

        # トレランス以内で曲線上の点群を取得
        _, points = crvEva.getStrokes(sPrm, ePrm, 0.01)

"getStrokes" メソッドで、曲線上にトレランス以内で大量の点を作成します。
Fusion 360 Help

この部分での処理はこの様なイメージを想定しています。

赤色曲線に対し、始点終点を結ぶ黄色直線を作成します。
この時の最大距離がトレランス以上の距離だった場合はNGです。

その為、中間地点に点を作成し、始点-中間、中間-終点
の2本の直線を作成し、同様に最大距離がトレランス以内に
収まっているかどうかを評価し点を作って行きます。

これらで得られた点(始点・終点を含む)を取得しています。
最終的なゴールでは曲線を作るのですが、こちらのトレランスは
折れ線化した際のトレランスとなる為、意図的に甘いトレランスに
しています。
(トレランスを厳しくすると点の数が多くなる = 処理遅くなる)

        # 点群の面上のパラメータ群を取得
        _, prms = surfEva.getParametersAtPoints(points)

先程取得した曲線上の点群から、サーフェス上のパラメータ群を
取得します。
Fusion 360 Help
リストでまとめて処理出来るので便利です。
引数と戻り値のお互いのインデックスは一致しています。

        # パラメータ群の曲率を取得
        _, _, crvTures, _ = surfEva.getCurvatures(prms)

        # パラメータ群の法線ベクトルを取得 これは単位ベクトル
        _, normals = surfEva.getNormalsAtParameters(prms)

        # 曲率からRサイズを取得 Rサイズは曲率の逆数
        radius = [1/c for c in crvTures]

        # 法線ベクトルをRサイズ分考慮する 中心方向・サイズのベクトル
        [n.scaleBy(r) for n, r in zip(normals, radius)]

"getCurvatures" メソッドはパラメータから曲率を取得しています。
Fusion 360 Help
2方向の曲率が得られるのですが、今回は最大側(U方向)です。

Rサイズ(曲率半径)は曲率の逆数です。
曲率と曲率半径 [物理のかぎしっぽ]
数式は吹っ飛ばして構わないです。まぁそういうものだと思っておけば・・・。

"getNormalsAtParameters" メソッドでは法線ベクトルを取得しています。
Fusion 360 Help
こちらは単位ベクトルとなっていました。
サーフェスの表裏の関係もありますが、Fusion360の場合は中心方向でした。

最後は、これらを組み合わせて中心に向かってのベクトルを取得します。
断面で描くとこんな感じです。

ベクトルのサイズをRサイズにしてあげれば、中心点が求まります。

        # 面上の点群を中心方向に移動 この時点で点群は中心を示す
        [p.translateBy(v) for p, v in zip(points, normals)]

曲線上の点群を、先程求めたベクトルを利用して移動させます。
Fusion 360 Help
これで中心点の点群が求まりました。

        # Point3Dの座標値をリスト化
        p: adsk.core.Point3D = None
        aryList = [p.asArray() for p in points]

        # 点群を通過するNurbs曲線を取得
        crv: 'curve' = fitting.interpolate_curve(aryList, 3)

この部分は、外部モジュールの "NURBS-Python(geomdl)" を利用しています。
Curve and Surface Fitting — NURBS-Python 5.3.1 documentation

"interpolate_curve" メソッドが通過点でBSplineを作成してくれました。
Fusion360のPoint3DではNGで(X,Y,Z)のリスト等のリストを取得しています。

        # NURBS-PythonのコントロールポイントをPoint3D化
        pnt3D: adsk.core.Point3D = adsk.core.Point3D
        controlPoints = [pnt3D.create(c[0], c[1], c[2]) for c in crv.ctrlpts]

        # 他の要素はそのまま流用可
        degree = crv.degree
        knots = crv.knotvector
        isPeriodic = crv.rational

"NURBS-Python" で得たBSplineから必要な情報を取得しています。
先程と逆に、コントロールポイントはリストからPoint3Dに変換しています。

        # Fusion360用のNurbs曲線作成
        nurbs: adsk.core.NurbsCurve3D = adsk.core.NurbsCurve3D.createNonRational(
            controlPoints,
            degree,
            knots, 
            isPeriodic
        )

ここで目的の中心線となるスプラインを作り出し、無事終了です。
Fusion 360 Help


非常に手間なのは確かなのですが、Fusion360APIは結構低レベルな
処理を提供してくれている為、何でも自動的に行ってしまうソフトより
込み入った処理が可能なんですよ。