時系列データを管理するために、ofxTimelineを試してみる(その3)
便利そうなのは分かったけど、じゃあ自分のプログラムからデータを追加したい時にはどうすればいいのさ、ということで、調べていきましょう。
Curves
先ず、たくさん用意されているTrack Typesの中から使いたい型を検討します。LeapMotionで得られる手や指の位置のデータなど、アナログ値を時系列で保存したい場合には、Curvesタイプが良さそうです。このソースコードを眺めていくと、CurvesTypeの基底クラスになっているofxTLKeyframesにデータ追加用のメンバー関数がありそうだ、ということが分かりました。
virtual void addKeyframe();
virtual void addKeyframe(float value);
virtual void addKeyframeAtMillis(unsigned long long millis);
virtual void addKeyframeAtMillis(float value, unsigned long long millis);
KeyFrameに対応させずに生データを保持する方法は無いのかなと思い調べてみたものの、上手いやり方は無さそうなので、LeapMotionのframerate分のデータ取得ごとにKeyFrameを作って(ものすごい数になりますが)いってしまうことにします。後で、フィルタリング処理するなどして、KeyFrameを減らす検討をしてもいいかもしれません。
void testApp::update(){
ofxTLCurves *ct = (ofxTLCurves*)timeline.getTrack("curves");
unsigned long long current_time = ct->currentTrackTime();
ct->addKeyframeAtMillis(value, current_time);}
ofxLeapMotionで取得したデータをリアルタイムで入力してみる
以前作ったofxLeapMotion v2のデータを、実行中に逐次timelineに登録してみます。
とっても遅い!そのままでは使い物になりません。addKeyframeAtMillis関数内で行われているデータのソートと更新に相当時間がかかっているようですね。
ソートと更新のどちらの処理が重いのかを調べてみると、
timeline->flagTrackModified(this);
が重くなる原因であることが分かりました。ソートの負荷は大したこと無さそうです。なので、とりあえず、登録とソートだけをできるようにして、更新通知をしないようaddKeyframeAtMillisを改造してみます。
ofxTLKeyframes.h
virtual void addKeyframeAtMillis(float value, unsigned long long millis, bool track_notified = true, bool recomp_preview = true);
ofxTLKeyframes.cpp
void ofxTLKeyframes::addKeyframeAtMillis(float value, unsigned long long millis, bool track_notified, bool recomp_preview){
ofxTLKeyframe* key = newKeyframe();
key->time = key->previousTime = millis;
key->value = ofMap(value, valueRange.min, valueRange.max, 0, 1.0, true);
keyframes.push_back(key);
//smart sort, only sort if not added to end
if(keyframes.size() > 2 && keyframes[keyframes.size()-2]->time > keyframes[keyframes.size()-1]->time){
updateKeyframeSort();
}
lastKeyframeIndex = 1;
if(track_notified) timeline->flagTrackModified(this);
shouldRecomputePreviews = recomp_preview; // true;
}
適当にこんな感じ。bool型の引数を2つ付け足して、それぞれ更新通知と描画通知をするかどうかの指定をできるようにします。
また、ちょっと蛇足かもしれませんが、データ記録完了後に通知を強制できるようにFlush関数を付け足します。
ofxTLKeyframes.h
virtual void addKeyframeAtMillisFlush(bool track_notified = true, bool recomp_preview = true);
ofxTLKeyframes.cpp
void ofxTLKeyframes::addKeyframeAtMillisFlush(bool track_notified, bool recomp_preview){
if(track_notified) timeline->flagTrackModified(this);
shouldRecomputePreviews = recomp_preview;
}
この関数を使って、逐次登録できるようにするとこんな感じで動きます。記録しているのは、ハンドの位置のx座標です。まずまずの速度で動いていますね。
参考程度に記録のためのソースコード。データ取得ループの中に書きます。
static bool played = false;
if(timeline.getIsPlaying()){
ct->addKeyframeAtMillis(hand[i].basis.get().getTranslation().x, current_time, false, true);
played = true;
}
else if(played){
ct->addKeyframeAtMillisFlush(true, true);
played = false;
}
データ一個なのでちゃんと動いていますが、ofVec3fやofQuaternionなどもっと多くのデータを便利に使えるようにする必要がありそうですね。ま、追々。
[1]ofxTimelineでタイムラインのイベントを取得する: http://netdiever.com/archives/12578