私は、グラフの STL モデルを生成するグラフ描画プログラム用の Python ベースのプラグインを作成する任務を負っています。頂点とエッジで構成されるオブジェクトであるグラフ。頂点は 3D ボール (モザイク化された正 20 面体) で表され、エッジは両端の 2 つのボールで接続された円柱で表されます。3D モデルの最終結果は、3D プリント用に STL ファイルにダンプされることです。ボールと円柱の 3D モデルは問題なく生成できますが、モデル全体の生成とボールと円柱の適切な接続に問題があります。
私の最初のアイデアは、原点でモザイク化された 20 面体を作成し、それらを頂点の位置に変換することでした。これはうまくいきます。次に、エッジごとに、原点に円柱を作成し、正しい方向を向くように正しい角度に回転させてから、2 つの頂点の間の中点に平行移動させて、円柱の端が埋め込まれます。二十面体で。これは、物事がうまくいかないところです。回転を正しくするのに苦労しています。回転を計算するには、次のことを行っています。
まず、次のように 2 点間の角度を見つけます (ソースとターゲットはどちらもグラフ内の頂点であり、現在処理しているエッジに属します)。
deltaX = source.x - target.x
deltaY = source.y - target.y
deltaZ = source.z - target.z
xyAngle = math.atan2(deltaX, deltaY)
xzAngle = math.atan2(deltaX, deltaZ)
yzAngle = math.atan2(deltaY, deltaZ)
計算された角度は妥当なようで、私が知る限り、実際には頂点間の角度を表しています。たとえば、(1, 1, 0) に頂点があり、(3, 3, 0) に別の頂点がある場合、それらを接続する角度エッジは、2 つの頂点間の 45 度の角度として表示されます。(つまり、どの頂点がソースでどの頂点がターゲットかによって、-135 度)。
角度を計算したら、円柱を作成し、作成した他のクラスを使用して、計算された角度で円柱を回転させます。
c.rotateX(-yzAngle)
c.rotateY(xzAngle)
c.rotateZ(-xyAngle)
c.translate(edgePosition.x, edgePosition.y, edgePosition.z)
(ここで、edgePosition はグラフの 2 つの頂点の間の中間点、edgeThickness は作成される円柱の半径、edgeLength は 2 つの頂点間の距離です)。
前述のように、シリンダーの回転が期待どおりに機能しません。x/y 平面上で正しい回転を行っているように見えますが、3 つのコンポーネント (x、y、および z) すべてで異なる頂点がエッジに含まれるとすぐに、回転は失敗します。以下は、x 成分と y 成分が異なり、z 成分が異なるグラフの例です。
Makerware に表示される結果の STL ファイルは次のとおりです (3D モデルを 3D プリンターに送信するために使用されます)。
(左下に見える余分な円柱は、テスト目的で現在残したものです。原点にある z 軸の方向を指す円柱です)。
同じグラフを使用して中央の頂点を z 軸の外に移動すると、すべてのエッジが 3 つの軸すべての角度を含むようになり、次のような結果が得られます。
アプリに表示されるとおり:
作成された STL ファイルは、Makerware に表示されます。
...そして、横から見た同じモデル:
ご覧のとおり、シリンダーは、私が思っていたようにボールと一致していません. 私の質問は次のとおりです。これを行うための私のアプローチに欠陥がありますか、それとも、ローテーションのどこかで犯している小さな、しかし重大な間違いですか? ローテーション関数自体に問題はないと確信しています。回転関数が期待どおりに動作することを独自に検証できたからです。また、ヨー、ピッチ、ロールを取り込んで 3 つすべてを一度に実行する回転関数を作成しようとしましたが、次のように同じ結果が生成されるように見えました。
c.rotateYawPitchRoll(xzAngle, -yzAngle, -xyAngle)
だから...誰かが私が間違っているかもしれないことについて何か考えがありますか?
更新: joojaa が指摘したように、それは正しい角度の計算とそれらが適用された順序の組み合わせでした。物事を機能させるために、まず次のように x 軸の回転を計算します。
zyAngle = math.atan2(deltaVector.z, deltaVector.y)
ここで、deltaVector はターゲット ベクトルとソース ベクトルの差です。ただし、このローテーションはまだ適用されていません。次のステップは、次のように y 軸の回転を計算することです。
angle = vector.angleBetweenVectors(vector(target.x - source.x, target.y - source.y, target.z - source.z), vector(target.x - source.x, target.y - source.y, 0.0))
両方の回転が計算されると、それらが適用されます...逆の順序で! 最初に x、次に y:
c.rotateY(angle)
c.rotateX(-zyAngle) #... where c is a cylinder object
まだいくつかのバグがあるようですが、これは少なくとも単純なテスト ケースでは機能するようです。