1

私は、グラフの 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 プリンターに送信するために使用されます)。

Makerware のグラフ

(左下に見える余分な円柱は、テスト目的で現在残したものです。原点にある 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

まだいくつかのバグがあるようですが、これは少なくとも単純なテスト ケースでは機能するようです。

4

1 に答える 1

1

回転は連続して発生するため、角度は互いに影響します。オイラー モデルを使用して一度に回転させることはできません。これが、最初の静的な状況に基づいて回転を計算できない理由です。立方体を回転させて角が直立するところを想像してみてください。はい、最初の回転は 45 ですが、2 番目の回転は、立方体がその時点までに回転しているためです (シーケンスの各ステップを描画して、何が起こるかを確認してください)。スペースの回転は簡単ではありません。

したがって、1 つの角度を回転させてから、2 番目の角度を再計算する必要があります。これが、最初のローテーションが正しく機能する理由でもあります。シャフトの周りの回転が特定の方向を持っていることを確認することに興味がない限り、必要なのは2回転だけです。

これを行うには、代わりに軸角度または行列を使用することをお勧めします。主な理由は、軸角度ではこれは些細なことであり、角度はチューブに沿った開始ベクトルと終了ベクトルの間の点であり、軸はこれら 2 つの交差です。必要に応じて、これらをオイラー角度に変換できます。しかし、おそらくマトリックスを直接使用することができます。変換方法と回転を直接計算する方法に関するアイデアについては、Christoph Gohlke によるtransformations.pyを参照してください。付属の c ソースも参照してください。

この答えを少し拡張する必要があると思います

この質問には、あなたや他の多くの人の問題を回避する、本当に簡単な方法があります。答えは、オイラー角回転を使用しないことです。私は多くの頭脳を使って、オイラー回転を使わずに最終的により簡単に解決できる問題にオイラー回転を説明しようとしました。正当化するために、さらにいくつかの答えを考えたい場合は、この理由を1つだけ残します.

オイラー回転シーケンスを使用する主な理由は、おそらくオイラー角を理解していないからです。実際、それらが適切な状況はほんの一握りです。この問題を解決するためにオイラー回転を使用する自尊心のあるプログラマーはいません。あなたがすることは、代わりにベクトル演算を使用することです。

したがって、通常は計算されるソースからターゲットへの方向ベクトルがあります。

along = normalize(target-source)

これは単に行列の行の 1 つ (または列の表記はモデル メーカー次第) であり、円柱の元の方向 (行は xyzw のみ) に対応するものであり、これに垂直な別のベクトルが必要です。上向き (または、アロングが上向きの場合は左向き) のような任意のベクトルを選択します。このアップ ベクトルを 2 番目の行方向に沿って外積します。最後に、ソースを最後の行として最後の列に 1 を配置します。円柱プリションを記述する完全に形成されたアフィン行列を作成しました。ベクトルを描くことができるので、はるかに理解しやすくなります。

もっと短い方法もありますが、これは理解しやすいです。

于 2013-06-07T05:42:39.800 に答える