2018年9月7日金曜日

Jw_cadの外部変形のデータをC#で処理したら楕円弧の角度がずれてしまい、三角関数での計算が必要とのことで数学的に頑張ったメソッド

Jw_cadに外部変形と言う機能があります。
本来の使い方とは少し違うかも知れませんが、書き出したデータを自作アプリケーション(CamOnLaser)で読み込むことにより連携する処理を作っています。
(Jw_cad --外部変形-> CamOnLaser --USBシリアル通信-> レーザー加工機)

アプリケーションの完成が見えてきて、テストを兼ねた実加工を順調に行っていましたが、一部の加工がずれた…

最外周とその内側が楕円弧です
内側の楕円弧が一見では不規則にカットされている


よくよく調べてみると、Jw_cadが書き出している、楕円弧の開始角度と終了角度が思っていたのと違う。
どうやら、数学的に意味があってこうなっているぽい、らしい。とのことで、三角関数で変換を行いました。

作図した楕円弧は緑の部分
対して、出力されるのは赤の円弧に対応する角度
黒の矢印の経路で必要とする角度を取得
(数式はいろいろ怪しいので省略)


ノートに数式を、しかも三角関数とか20年ぶり位じゃなかろうかと怪しげに書いては直して表計算スプシで確認しつつ1日、その後、実装してしたところカッチリ動いている。想定外だっ・・・
上手くいっています
穴の直径が2mm
6角形の頂点距離(?)2.5mm
肉抜きの結果、残った幅が0.5mm
ここまで拡大すると粗が目立ちますが・・・


/// 楕円に関する角度を取得(変換)する
/// (JWC_TEMP.TXTの角度は外接する正円の座標ベースの角度であるので楕円の場合は変換が必要)
/// ※※ 引数は傾ける前の値が必要 ※※
/// ※※ ごにょごにょ計算した結果、radius(半径)は打ち消されて消滅したが、アレなので引数に含めておく ※※
private float GetEllipseAngle(float radius, float aspectRatio, float angle)
{
    // 引数の確認
    if (radius < float.Epsilon) { return angle; }   // そのまま返す
    
    // 正円(弧)の確認
    if (aspectRatio == (float)1.0) { return angle; }
    
    // 90度単位の確認
    if (angle % (float)90.0 < float.Epsilon) { return angle; }
    
    // 角度の管理
    float temp_angle = angle % (float)360.0;
    
    // 象限の取得(0:第一象限~3:第四象限)
    int quadrant = (int)Math.Floor(temp_angle / (float)90.0);
    
    // 第二象限と第四象限の場合は、半径と扁平率をX方向とY方向で入れ替える
    float temp_radius = radius;
    float temp_aspectRatio = aspectRatio;
    if (quadrant == 1 || quadrant == 3)
    {
        temp_radius = radius * aspectRatio;     // 使用しない
        temp_aspectRatio = (float)1.0 / aspectRatio;
    }
    
    // 角度を第一象限に移動する
    temp_angle = temp_angle - ((float)quadrant * (float)90.0);
    
    // コサインの計算
    float cosA = (float)Math.Cos(temp_angle * Math.PI / (double)180.0);
    
    // 楕円上の座標のタンジェントを計算
    float temp_ret = temp_aspectRatio * (float)Math.Sqrt((float)1.0 - cosA * cosA) / cosA;
    
    // アークタンジェントを取る
    temp_ret = (float)Math.Atan(temp_ret);
    
    // ラジアンから戻す
    temp_ret = temp_ret * (float)180.0 / (float)Math.PI;
    
    // 象限を戻す
    float ret = temp_ret + ((float)quadrant * (float)90.0);
    
    // 返す
    return ret;
}


そういえば、「180」を「(double)180.0」とかしていますが、そんなことをしなくても内部的にdouble型になった後に計算されるはずです。が、覚えておくのが面倒なので明示的にキャストするようにしています。(思考停止)