2020年2月27日木曜日

MELで、ある直線に対称の座標を計算する

「yz平面に反転する」みたいな、ワールド軸に沿ったミラーリングの場合は、単純にxのプラスマイナスを反対にする。みたいな感じなので簡単です。
しかし、任意の直線に対して反転させたい場合、それはなかなか厄介なのです。


この赤いロケータを、緑の線を基準に、青いロケータと対称な位置へ移動させたい。
みたいなシチュエーションです。


まずは、こんな感じで、直線と垂直に交わる座標が求まれば、あとは、その交点と青いロケータの距離と同じ距離だけ延長すれば良い感じなのかなと想像できます。


ある直線に対称な座標」を google検索すると結構ヒットするのですが、ほとんどが連立方程式の解き方なんですよねぇ・・・。
あまり役に立たなかったので、キーワードのアプローチを変えてみます。
この交点を計算するために「正射影ベクトル」で検索。

こんな感じの図形が見つかります。

cos θ を使わないように変形していくと


こんな感じになります。

まぁ、同じことなんですけど、コードにし易いよう、もう少し図を検討しやすいものにします。

で、これを上の式に当てはめると


となります。
俄然コードにしやすくなった気がします。

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/// @file  tryLineSymmetry.mel
/// @brief 線対称の座標を返す。
/// @param $B [in] 対称にしたい座標     @n [0]:横軸値 @n [1]:縦軸値 @n
/// @param $O [in] 線対称の線の開始座標 @n [0]:横軸値 @n [1]:縦軸値 @n
/// @param $A [in] 線対称の線の終了座標 @n [0]:横軸値 @n [1]:縦軸値 @n
///
/// @retval [0] 横軸座標
/// @retval [1] 縦軸座標
///
/// @author Teruyuki Ishiko
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/



global proc float[] tryLineSymmetry(
    float $B[],
    float $O[],
    float $A[] )
/**************************************************************************//**
    @brief main procedure.
 ******************************************************************************/
{
    // ベクトル型へ変更
    vector $sp=<<$B[0], $B[1], 0.0>>;
    vector $ax=<<$O[0], $O[1], 0.0>>;
    
    // O(基準点)を基にしたベクトル作成
    vector $a=<<$A[0]-$O[0], $A[1]-$O[1], 0.0>>;
    vector $b=<<$B[0]-$O[0], $B[1]-$O[1], 0.0>>;
    
    
    //------------------------------------------------------------------------------
    // 交点を計算
    float $dot=dotProduct( {($a.x),($a.y),($a.z)}, {($b.x),($b.y),($b.z)},0 );
    float $pow=($a * $a);
    
    vector $c=($dot / $pow) * $a;
    
    // 原点基準に戻す
    vector $ep=($c + $ax);
    
    
    //------------------------------------------------------------------------------
    // 外分点の計算
    vector $P=((-1.0) * $sp) + (2.0 * $ep);
    
    
    return {($P.x), ($P.y)};
    
}



こんな感じになるでしょうか。
内部ではベクトル型で計算しているのは、コマンドが充実しているから・・・。
とりあえず、平面のみです。
立体にすると対象先をどれにするか選択するような機能で複雑になりそうだったので、まずは単純なものを。
あとは、呼ぶ側で、どの平面にするかを指定します。

ロケータ2つと EditPointが2つのリニアカーブオブジェクトを作成し、下のコードをスクリプトエディタで実行します。

{
    source "tryLineSymmetry.mel";
    string $lctA="lctA";
    string $lctB="lctB";
    // 対称の基準のカーブオブジェクト
    string $crv ="curve1";
    // 座標取得
    vector $B=`xform -q -ws -t $lctA`;
    vector $O=`xform -q -ws -t ($crv+".ep[0]")`;
    vector $A=`xform -q -ws -t ($crv+".ep[1]")`;
    // 線対称の座標を計算
    float $P[]=tryLineSymmetry({($B.x),($B.z)}, {($O.x),($O.z)}, {($A.x),($A.z)});
    // 移動
    move -ws $P[0] ($B.y) $P[1] $lctB;
    
}

上のコードでは、xz面を基準にして対称移動しています。


うまくいった感じ。

0 件のコメント:

コメントを投稿