📏

モデルをXYZ平面上に回転させる(C#)

2024/06/21に公開

モデルをXYZ平面にあわせたいのはどんな時?

  • 製品のCADデータは、当たり前ですが、取付位置に合わせて設計されます。
    取り付けられている状態の解析では、このままでも問題ありません。
    しかし、製品単体の強度解析などで加重方向を製品のある面に対して
    垂直に与えたいと思っても、XYZ方向にしか方向を指定することができません
    (ローカル座標を作ればできますが、面倒ですし設定ミスの元です)
  • そのため加重方向に対してモデルが垂直になるよう回転させる必要があります。

今回やること

  • 下に示す①から②がX方向に、①から③がY方向になるように回転させます

    *回転後

モデルを回転させるには?

  • 3次元モデルを回転させるには、四元数(クォータニオン)、オイラー角、回転行列、DCM
    っていうものがあるそうです。(以下に詳しく書かれています)
    https://qiita.com/drken/items/0639cf34cce14e8d58a5
  • この中で書きやすさを考慮して四元数で書いてみました。
  • 今回は、①から②の単位ベクトルが、回転後に(1,0,0)になるような四元数を求めて、
    Y方向、Z方向も同様に求めて回転させれば実現できます。
  • 四元数Q(x,y,z,w)=(λxSinθ/2, λySinθ/2, λz*Sinθ/2, Cosθ/2,)となっていて
    λは、回転軸の単位ベクトルです。
  • 回転前のベクトル(以下V1)と回転後のベクトル(以下V2)から四元数を求めるには、
  • 1:V1とV2の外積から回転軸の単位ベクトル (V1×V2)/|V1×V2|=λ を求める
  • 2:|V1×V2|/|V1||V2|=sinθからsinθを求める。
  • 3:半角の公式からsinθ/2とcosθ/2を求めれば、四元数を求めることができます。

プログラムの説明

*四元数を求めて回転させる

    class Move
    {
        public CAE_DATA Rot_to_TargetAxis(CAE_DATA DATA,List<int> BaseNodes,Vector3 Base_Axis)
        {
            //X軸に平行になるように回転させる
            var BaseVec_X =Vector3.Normalize(Vector3.Subtract(DATA.Node[BaseNodes[1]].Crd, DATA.Node[BaseNodes[0]].Crd));
            var Q_X = TargetAxis_Quaternion_Make(BaseVec_X, new Vector3(Base_Axis.X,0,0));
            DATA.Node.Values.ToList().ForEach(i => i.Crd = Vector3.Transform(i.Crd, Quaternion.Inverse(Q_X)));

            //Y軸に平行になるように回転させる
            var BaseVec_Y = Vector3.Normalize(Vector3.Subtract(DATA.Node[BaseNodes[2]].Crd, DATA.Node[BaseNodes[0]].Crd));
            var Q_Y = TargetAxis_Quaternion_Make(BaseVec_Y, new Vector3(0,Base_Axis.Y,0));
            DATA.Node.Values.ToList().ForEach(i => i.Crd = Vector3.Transform(i.Crd, Quaternion.Inverse(Q_Y)));

            //Z軸に平行になるように回転させる
            var P1 = Vector3.Normalize(Vector3.Subtract(DATA.Node[BaseNodes[1]].Crd, DATA.Node[BaseNodes[0]].Crd));
            var P2= Vector3.Normalize(Vector3.Subtract(DATA.Node[BaseNodes[2]].Crd, DATA.Node[BaseNodes[0]].Crd));
            var BaseVec_Z = Vector3.Cross(P1, P2);
            var Q_Z = TargetAxis_Quaternion_Make(BaseVec_Z, new Vector3(0, 0, Base_Axis.Z));
            DATA.Node.Values.ToList().ForEach(i => i.Crd = Vector3.Transform(i.Crd, Quaternion.Inverse(Q_Z)));
            
            return DATA;
        }
        
        private Quaternion TargetAxis_Quaternion_Make(Vector3 BeforVec, Vector3 AfterVec)
        {
            //BeforVecをAfterVecにするために必要な四元数を作成する
            var Rot_Axis_vec = Vector3.Cross(AfterVec, BeforVec);//回転軸λ(x,y,z)を作成
            var Cos_theta = Math.Sqrt(1 - Math.Pow((double)Rot_Axis_vec.Length(), 2));
            var Sin_theta_helf = Math.Sqrt((1 - Cos_theta) / 2);
            var Cos_theta_helf = Math.Sqrt((1 + Cos_theta) / 2);
            var N_Vec =Rot_Axis_vec.Length() != 0 ? Vector3.Normalize(Rot_Axis_vec):Rot_Axis_vec;
            //四元数Q(x,y,z,w)=(λx*Sinθ/2,λy*Sinθ/2,λz*Sinθ/2,Cosθ/2,)
            return new Quaternion((float)(N_Vec.X * Sin_theta_helf),
                                                (float)(N_Vec.Y * Sin_theta_helf),
                                                (float)(N_Vec.Z * Sin_theta_helf),
                                                (float)(Cos_theta_helf));
        }
    }
  • 回転させたモデルをkeyword形式で書きだす(前回記事で書いたFILE_CAEクラスに追加してます)
public void to_Key_File(string FileName,CAE_DATA DATA)
{
    var S = "*KEYWORD" + Environment.NewLine +
              Write_String_Node_Make(DATA) +
              Write_String_Shell_Make(DATA) +
              Write_String_Solid_Make(DATA) +
              "END";
              File.WriteAllText(FileName, S);
}
private string Write_String_Node_Make(CAE_DATA DATA)
{
    var temp = new List<string> { "*NODE" + Environment.NewLine };
    foreach (var i in DATA.Node)
    {
        temp.Add( $"{i.Key,8}" +
                  $"{Math.Round(i.Value.Crd.X, 3),16}" +
                  $"{Math.Round(i.Value.Crd.Y, 3),16}" +
                  $"{Math.Round(i.Value.Crd.Z, 3),16}" +
                  $"{Environment.NewLine}");
    }
    return string.Join("", temp);
}
private string Write_String_Shell_Make(CAE_DATA DATA)
{
    var temp = new List<string> { "*ELEMENT_SHELL" + Environment.NewLine };
    foreach(var i in DATA.Shell)
    {
        var Nid = string.Join("", i.Value.Nid.Select(j => $"{j,8}"));
        temp.Add($"{i.Key,8}{i.Value.Pid,8}{Nid}{Environment.NewLine}");
    }
    return string.Join("", temp);
}
private string Write_String_Solid_Make(CAE_DATA DATA)
{
        var temp = new List<string> { "*ELEMENT_SOLID" + Environment.NewLine };
        foreach (var i in DATA.Solid)
        {
        var Nid = string.Join("", i.Value.Nid.Select(j => $"{j,8}"));
        temp.Add($"{i.Key,8}{i.Value.Pid,8}{Nid}{Environment.NewLine}");
    }
    return string.Join("", temp);
}

使い方

static void Main(string[] args)
        {
            
            var Infile = @"C:\Users/test.key";
            var DATA=new FILE_CAE().from_Key_File(Infile);
            var DATA2 = new Move().Rot_to_TargetAxis(DATA, new List<int> {1の節点ID,2の節点ID,3の節点ID },new Vector3(1,1,1));
//3つ目の引数の数は、1か-1のみ対応してます。          
            new FILE_CAE().to_Key_File(Infile.Replace(".key", "output_.key"), DATA2);
           
        }

Discussion