「がんばれない」けど「がんばりたい」

ITエンジニアの仕事のこと。AI、機械学習、ディープラーニング。地頭力。車のこと。

openFrameworks.cc TUTORIAL|GENERATING MESHESを読む#3

前回に引き続き読んで行きます。

■Orbit

起動を与えてみます。今回はz軸は考えない。もし中央からの距離がわかれば、位置と角度を使用して位置を決める事が出来ます。

=>polar coordinates.極座標。 この距離と角度を使用してx,yを決めて行きます=>Cartesian coordinates|

  • x = distance * cos(angle)
  • y = distance * sin(angle)

875_01

angle値を増加させれば位置を回す事が出来ますね。

まとめると… 1. 円の中心を決める 2. 極座標系を初期化する 3. じょじょに各頂点のangleを増加させる

1)の円の中心を求めるには、ofMeshのメソッドmesh.getCentroid()を使用する。 mesh.getCentroid(); と、ここで初めて使うメソッドが出てきたので一応検証。 結果 centroid値が想定通りの値(100,100,0)になったので取りあえずOK。

    //vertics array
    ofVec3f p1(100.0, 50.0, 0.0);
    ofVec3f p2(100.0, 150.0, 0.0);

    //vertex info.
    mesh.addVertex(p1);
    mesh.addVertex(p2);

    ofVec3f centroid = mesh.getCentroid();
    cout << "centroid = " << centroid << endl;

オッケー。これで極座標の中心になるポイントをgetCentroid()で得る事が出来たので、後は各頂点と中心点(centroidで求めたポイント)までの距離と、中心点と各頂点がなす角度が分かれば、各頂点の極座標系での位置が求まる。

距離については、前回やったようにofMesh.distanceメソッドを使用して取得完了。後は角度。角度は、アークタンジェントを使えば産出出来る。 875_02

ここまでの整理をすると。 目標|各頂点に軌道モーションを与える。 準備|目標の準備として各頂点を極座標系にする。その為に、まず頂点全体の中心座標を求め、それを極座標系の原点とする。そして各頂点の極座標系における位置を求める為にsetup(初期化)で、中心座標からの距離と角度を求めておく。

あとは毎フレーム毎に各頂点の座標をupdateしていく。上述してあるとおり、angleの値を少しづつ増加させる。ここで注意?することは、中心点がgetCentroid()で取得したポイントになっている事。前回までの記事では原点はスクリーン座標系の原点=左上だったのに対して、今回の極座標系の原点はgetCentroid()で取得したポイントになります。その為setup内の初期化でセットした各頂点座標を毎フレーム毎にgetCentroid()を起点とした極座標系の位置にセットし直さないと今回の目標を達成出来ません。

oキーを押す度に、updateでの処理をする・しないのフラグを切り替えしています。押す度にsetupで設定したmeshの状態に戻しています。ちなみにstartOrbitTime = ofGetElapsedTimef();としているのは、こうしておかないとstartOrbitTimeは常に0のままになってしまうのでupdateで行っているrotatedAngleの値(位置を決める角度ですね)が、いきなり大きくなってしまうのを避ける為です。特にそういう意図でも良いのであればコメントしてもかまわないですね。

※コードの抜粋

void testApp::setup()
{
    //極座標系の中心
    meshCentroid = mesh.getCentroid();

    // centroidが得られたので、これで各頂点の極座標系を次に得る必要があります(distance and angle)
    // 以前、頂点間の距離を求めたと思います。では角度は?どのようにして得る事が出来るでしょうか?
    // atan2を使います。 atan2(y, x)で(0,0)を起点とした角度を得る事が出来ます。
    // ではある2点間の角度を求めるには、どうしたらいいか? (x1, y1) (x2, y2)とすると、
    //  atan(y2-y1, x2-x1)
    for (int i=0; i < numVerts; i++)
    {
        ofVec3f vert = mesh.getVertex(i);
        float distance = vert.distance(meshCentroid);
        float angle = atan2(vert.y - meshCentroid.y, vert.x - meshCentroid.x);
        //中心からの距離と成す角度をpush
        distances.push_back(distance);
        angles.push_back(angle);
    }

    //true時に極座標系にするためのフラグ
    orbiting = false;

    //毎フレーム毎にangleを変化させるが、その量を決定するのに経過時間を用いる
    startOrbitTime = 0.0;

    //setupで決めたmesh情報をストアしておく。ちなみにdeepCopyらしい。
    meshCopy = mesh;
}

void  testApp::update()
{
    if (orbiting)
    {
        //to give some motion to vertex array
        //[common]
        int numVers = mesh.getNumVertices();
        
        for (int i = 0; i < numVers; i++)
        {
            //頂点を取得[common]
            ofVec3f vert = mesh.getVertex(i);
            
            //距離、初期angle値、経過時間
            float distance = distances[i];
            float angle = angles[i];
            float elapsedTime = ofGetElapsedTimef() - startOrbitTime;
            
            //距離が中心に近いほどスピードが速くなるようにする
            float speed = ofMap(distance, 0, 200, 1, 0.25, true);
            
            //任意時間での角回転を決める。
            //現在の時間と開始した時の角回転(angle)を使用する。
            //  角度(angle)に経過時間(elapsedTime) x スピード
            //  スピードの値が1から0.25の間になっていて、この値次第でangleの変化量が変わってくる
            float rotatedAngle = elapsedTime * speed + angle;
            
            //極座標系での位置として各頂点座標の位置を、centroidを起点とした位置にする必要がある
            vert.x = distance * cos(rotatedAngle) + meshCentroid.x;
            vert.y = distance * sin(rotatedAngle) + meshCentroid.y;
            mesh.setVertex(i, vert);
        }
    }
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
    
    if (key == 'o')
    {
        orbiting = !orbiting;
        startOrbitTime = ofGetElapsedTimef();
        mesh = meshCopy;
    }

}