1. slowsingle

    No comment

    slowsingle
Changes in body
Source | HTML | Preview
@@ -1,120 +1,120 @@
格安でLeapMotionが手元に届いたので、何か作ってみようと思い、仮想マウスを作りました。
LeapMotionの上で指を動かすとポインタが動き、クリックもできるようにしました。また、Macにおける画面遷移もできるようにしました。画面遷移ができるとカッコイイよね!
# ソースコードの在り処
コードはgithubにあります。コード量は少なめです。 </br>
https://github.com/slowsingle/leapmotion_controller/tree/master
ReadMeにインストール方法が記載されていますので、お使いになる場合は是非ごらんください。
# プログラム
リープモーションから取得した人差し指の座標(X, Y, Z)から、PCのディスプレイ上の座標系に変換して、その座標にポインタを移動させます。
![01.gif](https://qiita-image-store.s3.amazonaws.com/0/72509/639b4fde-4264-5734-8b89-6692b17e3a12.gif)
```cpp:座標系の変換
new_x = (x + 120.0) / 240.0 * 1440.0;
if (new_x < 0.0) {
new_x = 0.0;
} else if (new_x > 1440.0 - 1.0) {
new_x = 1440.0 - 1.0;
}
new_y = (250.0 - y) / 150.0 * 900.0;
if (new_y < 0.0) {
new_y = 0.0;
} else if (new_y > 900.0 - 1.0) {
new_y = 900.0 - 1.0;
}
```
touchDistance()を用いることでタッチの奥行きを測ることができます。
![03.gif](https://qiita-image-store.s3.amazonaws.com/0/72509/1b9a56df-eb7f-5dca-a9ff-224d8361f31d.gif)
-一定のしきい値(以下のコードの場合は-0.1)を下回ると、押されたと判定し、そこから先ほどとは異なる一定のしきい値(以下のコードの場合は0.0)を上回ると離されたと判定し、タッチされたと判定する
+一定のしきい値(以下のコードの場合は-0.1)を下回ると、押されたと判定し、そこから先ほどとは異なる一定のしきい値(以下のコードの場合は0.0)を上回ると離されたと判定し、タッチされたと判定します
```cpp:タッチ認識
// タッチの認識
if (isPressed) {
if (pointable.touchDistance() > 0.0) {
isPressed = false;
PyObject_CallMethod(pModule, "touch", "(dd)", touch_x, touch_y);
std::cout << "===== PRESS_OK =====" << std::endl;
}
} else {
if (pointable.touchDistance() < -0.1) {
isPressed = true;
touch_x = new_x;
touch_y = new_y;
std::cout << "***** PRESS_Prepare *****" << std::endl;
}
}
```
スワイプのジェスチャー認識は以下の通りです。
```cpp:スワイプのジェスチャー認識
Leap::GestureList gestures = frame.gestures();
// スワイプのジェスチャーを認識する
if (swipe_counter == 0) {
for (int g = 0; g < gestures.count(); g++) {
gesture = gestures[g];
if (gesture.type() == Leap::Gesture::TYPE_SWIPE) {
Leap::SwipeGesture swipe = gesture;
if (swipe.direction().x < -0.7) {
std::cout << swipe.direction().x << std::endl;
PyObject_CallMethod(pModule, "swipe_screen", "(i)", 0);
// スワイプを認識したら関数を抜ける
isPressed = false;
swipe_counter = 100;
return;
} else if (swipe.direction().x > 0.7) {
std::cout << swipe.direction().x << std::endl;
PyObject_CallMethod(pModule, "swipe_screen", "(i)", 1);
// スワイプを認識したら関数を抜ける
isPressed = false;
swipe_counter = 100;
return;
}
}
}
}
```
リープモーションによる認識はC++で実装し、PCに対してポインタを移動させたりスワイプさせたりするのはPythonで行わせています。未確認ですが、これでOSに非依存な仮想マウスができたかと思います。
C++からPythonを呼び出すのには`PyObject_CallMethod()`を使用しています。</br>
この関数を呼ぶ前に初期化が必要になります。おまじない的な`Py_Initialize()`を呼び出し、その後(必要があれば)パスを通し、モジュールのインポートを行います。
```cpp:C++からPythonを呼び出すための初期化処理
bool SampleListener::initializeForPython() {
Py_Initialize();
// カレントディレクトリのパスを通す
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyString_FromString("."));
// カレントディレクトリにあるpythonモジュールをインポートする
pModule = PyImport_ImportModule("script");
if (pModule != NULL) {
return true;
} else {
return false;
}
}
```
なお、`PyObject_CallMethod()`の引数は、1つ目がインポートしたモジュールのポインタ、2つ目が呼び出す関数名、3つ目がこれから呼び出すpython内の関数における引数の数と型((i)ならint型の引数が1つで、(dd)ならdouble型の引数が2つ)を表し、4つ目がその引数の具体的な値となります。
`PyObject_CallMethod(pModule, "swipe_screen", "(i)", 1)`ならば、pModule(インポートしたモジュール)の`swipe_screen`関数に、引数としてint型の引数1を渡して、関数を実行することになります。
# 終わりに
やる前から何となく覚悟してたけどタッチしづらいです。  </br>
ただ、画面遷移が手の動きでできるとカッコイイのは確かです。未来感を感じます。