はつねの日記

Kinect, Windows 10 UWP, Windows Azure, IoT, 電子工作

Leap SDKならWindowsストアアプリが作れます

Leap SDKは、.NET Framework 4.0用なので、Visual Studio 2012で.NET Framework 4.5のWPFアプリを作成することはできますが、Windows Runtimeにも対応しているためWindowsストアアプリも作成できます。には対応していないため、Windows 8 ストアアプリは作成することができませんでした。

Visual Studio 2013 PreviewでサポートされたWindows 8.1 Preview用のストアアプリでは.NET Framework用のDLLも使用できるため、Leap SDKを使ったWindows 8.1 ストアアプリを作成することができます。

http://code.msdn.microsoft.com/LeapSimpleSample-for-d253bc6e

 

なお、残念なことにIntel PerCだとWindows 8.1 + Visual Studio 2013 Previewをもってしても

のままでした。

この違いは一体なんだろう。

こっち(Intel PerC)でもストアアプリ作りたい。それこそ今月中に。

Leap SDKの日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

 

 

Understanding the C# Sample Applicationの日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Sample_CSharp_Tutorial.htmlの日本語訳となります。

Understanding the C# Sample Application

Topics:

Overview
Creating a Controller object
Subclassing the Listener class
Getting a Frame of data
Getting Gestures
Running the sample

Leap SDKフォルダ配下に、この資料で使う次のファイルがあります。

  • LeapSDK/samples/Sample.cs ? C# sample application
  • Windows:
    • LeapSDK/lib/LeapCSharp.NET3.5.dll ? Leap C# library for .NET 3.5
    • LeapSDK/lib/LeapCSharp.NET4.0.dll ? Leap C# library for .NET 4.0
    • LeapSDK/lib/x86/LeapCSharp.dll ? 32-bit Leap C# library for Windows
    • LeapSDK/lib/x64/LeapCSharp.dll ? 64-bit Leap C# library for Windows
    • LeapSDK/lib/x86/Leap.dll ? 32-bit Leap library for Windows
    • LeapSDK/lib/x64/Leap.dll ? 64-bit Leap library for Windows
  • Mac (Mono):
    • LeapSDK/lib/LeapCSharp.NET3.5.dll ? Leap C# library for .NET 3.5
    • LeapSDK/lib/LeapCSharp.NET4.0.dll ? Leap C# library for .NET 4.0
    • LeapSDK/lib/libLeapCSharp.dylib ? Leap C# library for Mac
    • LeapSDK/lib/libLeap.dylib ? Leap library for Mac

Leap APIリファレンスは、LeapSDK/docs/API_Reference/annotated.html にインストールされます。

Overview

一言で言えば、Leap Motionトラッキングデバイスが検出され、手とその検出範囲内にある指を追跡します。Leapは、1度に1フレームのデータをキャプチャします。アプリケーションからこのデータにアクセスするにはLeap APIを使います。

サンプル アプリケーションはLeap API を使用して、Leapからのフレームイベントを取得する方法と、各フレームの手や指のデータにアクセスする手段を提供します。Sample.csはコマンドラインアプリケーションとなっており、検出された手や指を標準出力に出力します。

サンプルアプリケーションでは、Leap APIの主要なクラスの大部分を使用しています。

  • Controller ? Leapとアプリケーション間のインターフェース
  • Listener ? Leapによって送出されたイベントを処理するために使用
  • Frame ? 手と指のトラッキングデータのセットが含まれています
  • Hand ? 検出された手のトラッキングデータが含まれています
  • Finger ? 検出された指のトラッキングデータが含まれています
  • Vector ? 3Dの位置または方向ベクトルを表します
  • Gesture ? 認識できたジェスチャーを表します

これらのクラスの詳細についてはLeap APIのリファレンスを参照してください。

Creating a Controller object

Controllerクラスは、Leapとアプリケーションの間の主要なインターフェイスを提供します。Controllerオブジェクトを作成すると実行中のLeapソフトウェアに接続して、Frameオブジェクトを通じて手のトラッキングデータが利用可能になります。Frameオブジェクトは、Controllerオブジェクトをインスタンス化して、Controller.Frame メソッドを呼び出すことによってアクセスできます。

アプリケーションからはループ処理によりController.Frameを呼び出して使います。ループ処理により定期的にポーリングする以外に、Controllerオブジェクトにリスナーを追加する方法もあります。Controllerオブジェクトはトラッキングデータの新たしいフレームが利用できるとき(もしくは他のLeapイベント発生時に)はListenerサブクラスで定義されているコールバックメソッドを呼び出します。

サンプルアプリケーションでは、MainメソッドでControllerオブジェクトを作成し、Controller.AddListenerメソッドでListenerのサブクラスインスタンスを追加します。

VB.NET
Class Sample
    Public Sub Main()
        ' Create a sample listener and controller
        Dim listener As new SampleListener
        Dim _controller As new Controller

        ' Have the sample listener receive events from the controller
        _controller.AddListener(listener);

        ' Keep this process running until Enter is pressed
        Console.WriteLine("Press Enter to quit...");
        Console.ReadLine();

        ' Remove the sample listener when done
        _controller.RemoveListener(listener)
        _controller.Dispose()
    End Sub
End Class
C#
class Sample
{
    public static void Main()
    {
        // Create a sample listener and controller
        SampleListener listener = new SampleListener();
        Controller controller = new Controller();

        // Have the sample listener receive events from the controller
        controller.AddListener(listener);

        // Keep this process running until Enter is pressed
        Console.WriteLine("Press Enter to quit...");
        Console.ReadLine();

        // Remove the sample listener when done
        controller.RemoveListener(listener);
        controller.Dispose();
    }
}

[ENTER]キーを押すまでサンプルアプリケーションは十恋し続けます。実行している間、Leapイベントが発生したときはControllerが適切なリスナーコールバックを呼び出します。上記のサンプルコードではSampleListenerクラスがリスナークラスの独自サブクラスになります。次はそのサブクラスの実装をみてみましょう。

Subclassing the Listener class

サンプル アプリケーションでは、リスナーのサブクラスであるSampleListenerでLeapからのイベントを処理するコールバックメソッドを実装しています。

イベントは次のとおりです。

  • OnInit ? Listenerが登録されているControllerオブジェクトの初期化時のイベント。一度だけ発生します。
  • OnConnect ? Leapに接続し、モーショントラッキングデータのフレームの送信開始の準備ができたときのイベント
  • OnDisconnect ? Leapデバイスを外したりLeapソフトウェアを落としたときなど、ControllerオブジェクトがLeapから切り離されたときのイベント
  • OnExit ? ControllerオブジェクトからListenerが切り離された時のイベント
  • OnFrame ? モーショントラッ キングデータの新しいフレームが使用できるときのイベント

サンプルアプリケーションでは、 3つのライフサイクルイベントコールバック(OnInit OnDisconnect、OnExit)が発生すると標準出力にメッセージを出力します。OnConnect とOnFrameのイベント発生時にはメッセージ出力以外の処理もしています。 OnConnectコールバックで呼び出された関数の中では、認識したいジェスチャを使用可能にしています。OnFrameコールバックで呼び出された関数の中では、モーショントラッ キングデータの最新のフレームの内容を標準出力にメッセージ出力します。

Getting a Frame of data

Leapは、モーショントラッキングデータの新しいフレームを生成するときに、OnFrameコールバックメソッドを呼び出します。Controller.Frame()メソッドを呼び出すことによって、新たなデータにアクセスすることができます (Controllerオブジェクトへの参照がパラメータとしてコールバックに渡されます)。Frameオブジェクトには、ID、タイムスタンプおよび検出された手に対するHandオブジェクトを含むリストから構成されています。

サンプルアプリケーションのOnFrame実装の次の部分は、Controllerオブジェクトから最新のFrameオブジェクトを取得し、フレームID、タイムスタンプ、検出した手の数、検出した指の数、検出したツールの数を出力しています。

VB.NET
' Get the most recent frame and report some basic information
Dim _frame As Frame = _controller.Frame()
SafeWriteLine("Frame id: " + _frame.Id
            & ", timestamp: " & _frame.Timestamp
            & ", hands: " & _frame.Hands.Count
            & ", fingers: " & _frame.Fingers.Count
            & ", tools: " & _frame.Tools.Count)
C#
// Get the most recent frame and report some basic information
Frame frame = controller.Frame();
SafeWriteLine("Frame id: " + frame.Id
            + ", timestamp: " + frame.Timestamp
            + ", hands: " + frame.Hands.Count
            + ", fingers: " + frame.Fingers.Count
            + ", tools: " + frame.Tools.Count);

 

次のコードは、手が検出できたかをチェックし、検出できた場合はリストの最初の手を取得しています。

VB.NET
If (Not _frame.Hands.Empty) Then
    ' Get the first hand
    Dim _hand As Hand = _frame.Hands(0)
C#
if (!frame.Hands.Empty)
{
    // Get the first hand
    Hand hand = frame.Hands[0];

 

Handオブジェクトは、ID、手の物理的特性を表すプロパティおよびFingerオブジェクトリストが含まれています。各Fingerオブジェクトは、IDと指の特性を表すプロパティが含まれています。

次のコードはHandオブジェクトからFingerオブジェクトリストを取り出して、指の先端の平均的な位置を標準出力に出力します。

VB.NET
' Check if the hand has any fingers
Dim fingers As FingerList = _hand.Fingers
If (Not fingers.Empty) Then
    ' Calculate the hand's average finger tip position
    Dim avgPos As Vector = Vector.Zero
    For Each _finger In fingers
        avgPos += _finger.TipPosition
    Next
    avgPos /= fingers.Count
    SafeWriteLine("Hand has " & fingers.Count
                & " fingers, average finger tip position: " & avgPos)
End If
C#
// Check if the hand has any fingers
FingerList fingers = hand.Fingers;
if (!fingers.Empty)
{
    // Calculate the hand's average finger tip position
    Vector avgPos = Vector.Zero;
    foreach (Finger finger in fingers)
    {
        avgPos += finger.TipPosition;
    }
    avgPos /= fingers.Count;
    SafeWriteLine("Hand has " + fingers.Count
                + " fingers, average finger tip position: " + avgPos);
}

 

次に、手の平が形作っている球体の半径と手の平の位置を出力します。

VB.NET
' Get the hand's sphere radius and palm position
SafeWriteLine("Hand sphere radius: " & _hand.SphereRadius.ToString("n2") _
            & " mm, palm position: " & hand.PalmPosition)
C#
// Get the hand's sphere radius and palm position
SafeWriteLine("Hand sphere radius: " + hand.SphereRadius.ToString("n2")
            + " mm, palm position: " + hand.PalmPosition);

 

最後に、OnFrameメソッドは、手の法線ベクトルと方向ベクトルから手のピッチ、ロール、ヨー角を算出するベクトルを使用します。角度はラジアンから度に変換されます。

VB.NET
' Get the hand's normal vector and direction
Dim normal As Vector = _hand.PalmNormal
Dim direction As Vector = _hand.Direction

' Calculate the hand's pitch, roll, and yaw angles
SafeWriteLine("Hand pitch: " & direction.Pitch * 180.0F / CType(Math.PI, Single) & " degrees, " _
            & "roll: " & normal.Roll * 180.0F / CType(Math.PI, Single) & " degrees, " _
            & "yaw: " & direction.Yaw * 180.0F / CType(Math.PI, Single) & " degrees\n")
C#
// Get the hand's normal vector and direction
Vector normal = hand.PalmNormal;
Vector direction = hand.Direction;

// Calculate the hand's pitch, roll, and yaw angles
SafeWriteLine("Hand pitch: " + direction.Pitch * 180.0f / (float)Math.PI  + " degrees, "
            + "roll: " + normal.Roll * 180.0f / (float)Math.PI + " degrees, "
            + "yaw: " + direction.Yaw * 180.0f / (float)Math.PI + " degrees\n");

Getting Gestures

 

Leapからジェスチャーを受け取るには、最初に認識するジェスチャーを有効にする必要があります。Leapと接続したControllerオブジェクト(IsConnectedがTrue)は、いつでもジェスチャー認識を有効にできます。サンプルプログラムでは、OnConnect()コールバック関数の中でEnableGesture()Controllerクラスで定義されたメソッドを使用して認識したいすべてジェスチャを有効にしています。

VB.NET
Public Overrides Sub OnConnect(_controller As Controller)
    SafeWriteLine("Connected")
    _controller.EnableGesture(Gesture.GestureType.TYPECIRCLE)
    _controller.EnableGesture(Gesture.GestureType.TYPEKEYTAP)
    _controller.EnableGesture(Gesture.GestureType.TYPESCREENTAP)
    _controller.EnableGesture(Gesture.GestureType.TYPESWIPE)
End Sub
C#
public override void OnConnect (Controller controller)
{
        SafeWriteLine ("Connected");
        controller.EnableGesture (Gesture.GestureType.TYPECIRCLE);
        controller.EnableGesture (Gesture.GestureType.TYPEKEYTAP);
        controller.EnableGesture (Gesture.GestureType.TYPESCREENTAP);
        controller.EnableGesture (Gesture.GestureType.TYPESWIPE);
}

Leapは、Frameオブジェクトにおけるジェスチャーリストにジェスチャーの動きを定義したGestureオブジェクトを追加します。 サンプル・アプリケーションのOnFrame()イベントでは、検出されたジェスチャーに合わせた情報を標準出力に出力しています。

Gesture APIは、Gestureクラスをベースにして個々のジェスチャーを表すクラスに拡張されています。ジェスチャーリスト内のオブジェクトは、ジェスチャーインスタンスですので、あなたは正しいサブクラスのインスタンスにジェスチャーインスタンスを変換する必要があります。キャストはサポートされない代わりに各サブクラスは変換を実行するコンストラクターを提供します。例えば、例えば、円のジェスチャーは、以下のコードでCircleGestureインスタンスに変換できます。

VB.NET
CircleGesture circle = new CircleGesture (gesture)
C#
CircleGesture circle = new CircleGesture (gesture)

 

間違えているサブクラスにジェスチャインスタンスを変換する場合、コンストラクター関数は無効なジェスチャオブジェクトを返します。

ジェスチャーでは以前のフレームと現在のフレームでジェスチャーの特性を比較すると便利な場合があります。例えば、円のジェスチャーには円を何回描いたかをカウントするProgressプロパティがあります。今回のフレームの円のジェスチャーのProgressから前回のフレームの円のジェスチャーを引くことで1フレームの回数が取得できます。

VB.NET
' Calculate angle swept since last frame
Dim sweptAngle As Single = 0
If (circle.State <> Gesture.GestureState.STATESTART) Then
    Dim previousUpdate As New CircleGesture(_controller.Frame(1).Gesture(circle.Id))
    sweptAngle = (circle.Progress - previousUpdate.Progress) * 360
End If
C#
// Calculate angle swept since last frame
float sweptAngle = 0;
if (circle.State != Gesture.GestureState.STATESTART) {
    CircleGesture previousUpdate = new CircleGesture (controller.Frame (1).Gesture (circle.Id));
    sweptAngle = (circle.Progress - previousUpdate.Progress) * 360;
}

 

ジェスチャーループの完全なコードは次の通りです。

VB.NET
' Get gestures
Dim gestures As GestureList = _frame.Gestures()
For i As Integer = 0 To gestures.Count - 1
    Dim _gesture As Gesture = gestures(i)

    Select Case _gesture.Type
        Case Gesture.GestureType.TYPECIRCLE
            Dim circle As New CircleGesture(_gesture)

            ' Calculate clock direction using the angle between circle normal and pointable
            Dim clockwiseness As String
            If (circle.Pointable.Direction.AngleTo(circle.Normal) <= Math.PI / 4) Then
                'Clockwise if angle is less than 90 degrees
                clockwiseness = "clockwise"
            Else
                clockwiseness = "counterclockwise"
            End If

            Dim sweptAngle As Single = 0

            ' Calculate angle swept since last frame
            If (circle.State <> Gesture.GestureState.STATESTART) Then
                Dim previousUpdate As New CircleGesture(_controller.Frame(1).Gesture(circle.Id))
                sweptAngle = (circle.Progress - previousUpdate.Progress) * 360
            End If

            SafeWriteLine("Circle id: " & circle.Id _
                        & ", " & circle.State _
                        & ", progress: " & circle.Progress _
                        & ", radius: " & circle.Radius _
                        & ", angle: " & sweptAngle _
                        & ", " & clockwiseness)
        Case Gesture.GestureType.TYPESWIPE
            Dim swipe As New SwipeGesture(_gesture)
            SafeWriteLine("Swipe id: " & swipe.Id _
                        & ", " & swipe.State _
                        & ", position: " & swipe.Position.ToString _
                        & ", direction: " & swipe.Direction.ToString _
                        & ", speed: " & swipe.Speed)
        Case Gesture.GestureType.TYPEKEYTAP
            Dim keytap As New KeyTapGesture(_gesture)
            SafeWriteLine("Tap id: " & keytap.Id _
                        & ", " & keytap.State _
                        & ", position: " & keytap.Position.ToString _
                        & ", direction: " & keytap.Direction.ToString)
        Case Gesture.GestureType.TYPESCREENTAP
            Dim screentap As New ScreenTapGesture(_gesture)
            SafeWriteLine("Tap id: " & screentap.Id _
                        & ", " & screentap.State _
                        & ", position: " & screentap.Position.ToString _
                        & ", direction: " & screentap.Direction.ToString)
        Case Else
            SafeWriteLine("Unknown gesture type.")
    End Select
Next
C#
// Get gestures
GestureList gestures = frame.Gestures ();
for (int i = 0; i < gestures.Count; i++) {
        Gesture gesture = gestures [i];

        switch (gesture.Type) {
        case Gesture.GestureType.TYPECIRCLE:
                CircleGesture circle = new CircleGesture (gesture);

    // Calculate clock direction using the angle between circle normal and pointable
                String clockwiseness;
                if (circle.Pointable.Direction.AngleTo (circle.Normal) <= Math.PI / 4) {
                        //Clockwise if angle is less than 90 degrees
                        clockwiseness = "clockwise";
                } else {
                        clockwiseness = "counterclockwise";
                }

                float sweptAngle = 0;

    // Calculate angle swept since last frame
                if (circle.State != Gesture.GestureState.STATESTART) {
                        CircleGesture previousUpdate = new CircleGesture (controller.Frame (1).Gesture (circle.Id));
                        sweptAngle = (circle.Progress - previousUpdate.Progress) * 360;
                }

                SafeWriteLine ("Circle id: " + circle.Id
               + ", " + circle.State
               + ", progress: " + circle.Progress
               + ", radius: " + circle.Radius
               + ", angle: " + sweptAngle
               + ", " + clockwiseness);
                break;
        case Gesture.GestureType.TYPESWIPE:
                SwipeGesture swipe = new SwipeGesture (gesture);
                SafeWriteLine ("Swipe id: " + swipe.Id
               + ", " + swipe.State
               + ", position: " + swipe.Position
               + ", direction: " + swipe.Direction
               + ", speed: " + swipe.Speed);
                break;
        case Gesture.GestureType.TYPEKEYTAP:
                KeyTapGesture keytap = new KeyTapGesture (gesture);
                SafeWriteLine ("Tap id: " + keytap.Id
               + ", " + keytap.State
               + ", position: " + keytap.Position
               + ", direction: " + keytap.Direction);
                break;
        case Gesture.GestureType.TYPESCREENTAP:
                ScreenTapGesture screentap = new ScreenTapGesture (gesture);
                SafeWriteLine ("Tap id: " + screentap.Id
               + ", " + screentap.State
               + ", position: " + screentap.Position
               + ", direction: " + screentap.Direction);
                break;
        default:
                SafeWriteLine ("Unknown gesture type.");
                break;
        }
}

Running the sample

To run the sample application:

  1. サンプルのコンパイル
    • dows では、現在のディレクトリにある Sample.cs と LeapCSharp.NET3.5.dll または LeapCSharp.NET4.0.dll を確認します。(を使用している .NET framework の適切なライブラリ参照を使用して) コマンド ライン プロンプトで次のコマンドを実行します。

      csc /reference:LeapCSharp.NET4.0.dll /platform:x86 /target:exe Sample.cs

      注: .NET framework の適切なバージョンから「csc」コンパイラを使用します。

       

    • Mac では、c# プログラムをコンパイルする、Mono プロジェクトを使用してできます。現在のディレクトリにある Sample.cs と LeapCSharp.NET3.5.dll または LeapCSharp.NET4.0.dll を確認します。
      • .NET 3.5 framework

        gmcs /reference:LeapCSharp.NET3.5.dll /platform:x86 /target:exe Sample.cs

         

      • .NET 4.0 framework

        dmcs /reference:LeapCSharp.NET4.0.dll /platform:x86 /target:exe Sample.cs

  2. LeapデバイスをUSBに繋ぎ、自分の前に設置
  3. Leapソフトウェアをインストールしていなかったらインストール
  4. Leapソフトウェアをスタート。プロンプトが表示されたら登録したメールアドレスとパスワードを入力します。準備ができたらタスクバーの通知領域に表示されたアイコンが緑色になります。
  5. サンプルアプリの実行
    • Windowsでは、Sample.exe、LeapCSharp.dll、Leap.dll、LeapCSharp.NET3.5.dllまたはLeapCSharp.NET4.0.dllを同じディレクトリに配置します。32ビットのプロジェクトはlib\x86ディレクトリのライブラリを使用し、64ビットプロジェクトはlib\x64ディレクトリのライブラリを使用します。コマンドラインプロンプトで次のコマンドを実行します。

      Sample.exe

      Visual Studioでコンパイルする場合は、プロジェクトでlib\LeapCSharp.NET4.0.dllを参照設定し、ビルドターゲットをx86にした場合はlib\x86\LeapCSharp.dllとlib\x86\Leap.dllを取り込み、コンパイルアクション「なし」、コピーアクション「常にコピーする」と各ファイルのプロパティで設定しておくと便利です。なお、ターゲットフレームワークを4.5としても動作します。
      image

      なお、ビルドターゲットをx64にした場合はlib\x64\LeapCSharp.dllとlib\x64\Leap.dllを取り込みます。

       

    • MacではSample.exe、 libLeapCSharp.dylib、 libLeap.dylib、LeapCSharp.NET3.5.dllまたはLeapCSharp.NET4.0.dllを同じディレクトリに配置し、ターミナルウィンドウで次のコマンドを実行して確認してください。

      mono Sample.exe

サンプルアプリケーションを実行すると、初期化時とLeap接続時に「Initialized」と「Connected」が標準出力に出力されます。OnFrameのイベントが検出されると標準出力にフレーム情報が出力されます。Leapの上に手をかざすと指と手の平の位置情報が出力されます。

このサンプルでLeapからのモーショントラックングデータの取得方法が理解できたら、Leapを使ったC#アプリケーションの開発を始めることができます。

C#のサンプルを動かすには

Visual Studioでコンパイルする場合は、プロジェクトでlib\LeapCSharp.NET4.0.dllを参照設定し、ビルドターゲットをx86にした場合はlib\x86\LeapCSharp.dllとlib\x86\Leap.dllを取り込み、コンパイルアクション「なし」、コピーアクション「常にコピーする」と各ファイルのプロパティで設定しておくと便利です。なお、ターゲットフレームワークを4.5としても動作します。

image[3]

 

Leap Motionでの指の検出

image

Leap Motionの検出範囲はあまり広くありません。

14インチクラスのノートPCの画面のがLeapの上にあると思ってもらえるといいでしょう。

前後方向の検出範囲はあまり広くなさそうです。

 

指の検出も気持ち開き気味にしないとすぐに5本よりも少ない本数しか認識できなくなります。

腕の検出は比較的安定しているようなので確実な操作が必要な時は手の平の位置と方向を使うとよさそうです。

Leap Motion - Tracking Hands, Fingers, and Tools の日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Leap_Tracking.htmlの日本語訳となります。

Tracking Hands, Fingers, and Tools

手、指およびツールはLeap Motion Systemにおけるトラッキング基本エンティティです。本稿ではこれらのエンティティを表すオブジェクトの取得方法と使用方法の詳細を説明します。

Topics:
    • Overview
    • Hand, and Pointable Lists
    • Hands
      • Getting the Hand Characteristics
      • Getting the Fingers and Tools
      • Computing the Hand Orientation
      • Transforming Finger Coordinates into the Hand's Frame of Reference
    • Pointables
      • Converting a Pointable Object to a Finger or Tool
      • Calculating the Position of the Base of a Finger

Overview

Leap APIはトラッキング基本オブジェクトを表すクラスを定義しています。

Frameオブジェクトは、手、指およびツールのリストへのアクセスを提供します。例えばPointableオブジェクトである指とツールはPointableListとして一緒に扱うかFingerListとToolListクラスを使用して個別に扱うことができます。手のオブジェクトは指とツール(PointableListとして一緒に、または別個に)へのアクセスを提供します。

手、指およびツールの物理的特性はLeap座標系(mm単位で測定)の値として取得できます。Leap SDKは位置と方向を表すVectorクラスを提供します。Vectrorクラスを扱うための数学関数を提供します。

Hand, and Pointable Lists

Listクラスはすべて同じ構造を持っています。ベクトル形式の配列のように振る舞うし、イテレータ(反復子)をサポートしています。Leap APIから受信したメンバーオブジェクトを削除したり変更することはできませんが、同じオブジェクトタイプのリストを組み合わせる事ができます。

反復処理を行って例は次のようになります。

VB.NET
    For Each Hand hand in handList
        Console.WriteLine(hand.ToString())
    Next
C#
    foreach (Hand hand in handList) {
        Console.WriteLine(hand.ToString());
    }

 

手、Pointable、指およびツールリストは、Leap座標系の相対位置に基づいて、リストのメンバーを取得するためのleftmost()、rightmost()そしてfrontmostなどが定義されています。次のスニペットではこれらの関数を使用するいくつかの方法を示しています。

VB.NET
    Dim farLeft As Finger = _frame.Fingers.Leftmost
    Dim mostForwardOnHand As Finger = _frame.Hands(0).Fingers.Frontmost
    Dim rightTool As Tool = _frame.Tools.Rightmost
C#
    Finger farLeft = frame.Fingers.Leftmost;
    Finger mostForwardOnHand = frame.Hands[0].Fingers.Frontmost;
    Tool rightTool = frame.Tools.Rightmost;

 

より複雑な例では、検出されたすべてのPointableオブジェクトを含むバウンディングボックスの壁を計算してみます。APIには含まれていない機能なので、下記の例では、上、下を取得するための独自の関数及びリアPointablesを定義します。

VB.NET
    Private left As single = frame.Pointables.Leftmost.TipPosition.x
    Private right As single = frame.Pointables.Rightmost.TipPosition.x
    Private front As single = frame.Pointables.Frontmost.TipPosition.z

    Private back As single = backmost(frame.Pointables).TipPosition.z
    Private top As single = topmost(frame.Pointables).TipPosition.y
    Private bottom As single = bottommost(frame.Pointables).TipPosition.y

    Private Function backmost(pointables As PointableList) As Pointable
        If pointables.Count = 0 Then
            return Pointable.Invalid
        End If
        Dim backmost As Pointable = pointables(0)
        For p = 1 As Integer To p < pointables.Count - 1
            If pointables[p].TipPosition.z > backmost.TipPosition.z Then
                backmost = pointables(p)
            End If
        Next
        Return backmost
    End Function

    Private Function topmost(pointables As PointableList) As Pointable
        If pointables.Count = 0 Then
            return Pointable.Invalid
        End If
        Dim topmost As Pointable = pointables(0)
        For p = 1 As Integer To p < pointables.Count - 1
            If pointables[p].TipPosition.y > topmost.TipPosition.y Then
                topmost = pointables(p)
            End If
        Next
        Return topmost
    End Function

    Private Function bottommost(pointables As PointableList) As Pointable
        If pointables.Count = 0 Then
            return Pointable.Invalid
        End If
        Dim bottommost As Pointable = pointables(0)
        For p = 1 As Integer To p < pointables.Count - 1
            If pointables[p].TipPosition.y > bottommost.TipPosition.y Then
                bottommost = pointables(p)
            End If
        Next
        Return bottommost;
    End Function
C#
    float left = frame.Pointables.Leftmost.TipPosition.x;
    float right = frame.Pointables.Rightmost.TipPosition.x;
    float front = frame.Pointables.Frontmost.TipPosition.z;

    float back = backmost(frame.Pointables).TipPosition.z;
    float top = topmost(frame.Pointables).TipPosition.y;
    float bottom = bottommost(frame.Pointables).TipPosition.y;

    Pointable backmost(PointableList pointables)
    {
        if(pointables.Count == 0) return Pointable.Invalid;
        Pointable backmost = pointables[0];
        for( int p = 1; p < pointables.Count; p++ )
        {
            if( pointables[p].TipPosition.z > backmost.TipPosition.z)
                backmost = pointables[p];
        }
        return backmost;
    }

    Pointable topmost(PointableList pointables)
    {
        if(pointables.Count == 0) return Pointable.Invalid;
        Pointable topmost = pointables[0];
        for( int p = 1; p < pointables.Count; p++ )
        {
            if( pointables[p].TipPosition.y > topmost.TipPosition.y)
                topmost = pointables[p];
        }
        return topmost;
    }

    Pointable bottommost(PointableList pointables)
    {
        if(pointables.Count == 0) return Pointable.Invalid;
        Pointable bottommost = pointables[0];
        for( int p = 1; p < pointables.Count; p++ )
        {
            if( pointables[p].TipPosition.y < bottommost.TipPosition.y )
                bottommost = pointables[p];
        }
        return bottommost;
    }

Hands

HandクラスはLeapによって検出された物理的な手を表しています。HandオブジェクトはPointablesリストだけではなく、手の位置、向き、動きなどの属性へのアクセスを提供します。

次の例ではFrameからHandオブジェクトを取得してます。

VB.NET
    Dim _frame As Frame = controller.Frame ‘ controller is a Controller object
    Dim hands As HandList = _frame.Hands
    Dim firstHand AS Hand = hands(0)
C#
    Frame frame = controller.Frame(); // controller is a Controller object
    HandList hands = frame.Hands;
    Hand firstHand = hands[0];

 

Frameの中での相対的な手の位置を取得するには次のようなコードを記述します。

VB.NET
    Dim _frame As Frame = controller.Frame ‘ controller is a Controller object
    Dim hands As HandList = frame.Hands

    Dim leftmost As Hand = hands.Leftmost
    Dim rightmost As Hand = hands.Rightmost
    Dim frontmost As Hand = hands.Frontmost
C#
    Frame frame = controller.Frame(); // controller is a Controller object
    HandList hands = frame.Hands;

    Hand leftmost = hands.Leftmost;
    Hand rightmost = hands.Rightmost;
    Hand frontmost = hands.Frontmost;

注意:Leftmost()とRightmost()は、もっとも左または右に離れている手を識別することに注意してください。位置関係の判定であり左手や右手を認識しているわけではありません。

Getting the Hand Characteristics

手に対しては、位置、方向、動きを取得できます。

手の位置はLeap Motinからミリメートル単位で掌の中心点の3次元座標を含むベクターで表された掌位置属性で指定されます。手の方向は、掌の中心から指の方向と垂直方向の2つのベクトルによって与えられます。

手の動きはmm/secで手の瞬間的な動きがベクトルとして取得できます。また、2つの指定したフレーム間での手の平行移動、回転、拡大縮小値を変換するモーションファクターを取得できます。

次のコードスニペットでは、フレームからHandオブジェクトを取得し、その基本的な属性にアクセスする方法を示しています。

VB.NET
    Dim _hand As Hand = frame.Hands.Rightmost
    Dim position As Vector = hand.PalmPosition
    Dim velocity As Vector = hand.PalmVelocity
    Dim direction As Vector = hand.Direction
C#
    Hand hand = frame.Hands.Rightmost;
    Vector position = hand.PalmPosition;
    Vector velocity = hand.PalmVelocity;
    Vector direction = hand.Direction;

Getting the Fingers and Tools

IDを使ってリストや個別情報から手と一緒に指やツールも取得できます。

リストの例は次のようになります。

VB.NET
    ' hand is a Hand object
    DIm pointables As PointableList = hand.Pointables ' Both fingers and tools
    Dim fingers As FingerList = hand.Fingers
    Dim tools As ToolList = hand.Tools
C#
    // hand is a Hand object
    PointableList pointables = hand.Pointables; // Both fingers and tools
    FingerList fingers = hand.Fingers;
    ToolList tools = hand.Tools;

 

以前のフレームのIDを使った例は次のようになります。

VB.NET
    Dim knownPointable As Pointable = hand.Pointable(pointableID)
C#
    Pointable knownPointable = hand.Pointable(pointableID);

 

Leapの検出範囲内での指やツールの位置を取得するのに、listクラスのrightmost、leftmostおよびfrontmostが使えます。

VB.NET
    ' hand is a Hand object
    Dim leftPointable As Pointable = hand.Pointables.Leftmost
    Dim rightFinger As Finger = hand.Fingers.Rightmost
    Dim frontTool As Tool = hand.Tools.Frontmost
C#
    // hand is a Hand object
    Pointable leftPointable = hand.Pointables.Leftmost;
    Finger rightFinger = hand.Fingers.Rightmost;
    Tool frontTool = hand.Tools.Frontmost;

注意:手自体ではなくLeap Motionの中心点に対して相対的なことに注意してください。手を基準にして指を取得するにはLeap Matrixクラスを使用して変換します。

Transforming Finger Coordinates into the Hand's Frame of Reference

参照している手のフレームと手の指の座標が一緒に取得できると便利な場合が多いです。なぜなら指を並べたときに指の位置の分析を簡略化できるからです。指の位置と方向を変換するためLeap Matrixクラスを使用して変換行列を作成することができます。手の基準は、2つの間のクロス積で定義されている3番目の軸と手の方向と掌の法線ベクトルを使用して定義できます。

VB.NET
    Dim _frame As Frame = leap.Frame
    For h As Integer = 0 To h < frame.Hands.Count - 1
        Hand leapHand = frame.Hands(h)

        Vector handXBasis =  leapHand.PalmNormal.Cross(leapHand.Direction).Normalized
        Vector handYBasis = -leapHand.PalmNormal
        Vector handZBasis = -leapHand.Direction
        Vector handOrigin =  leapHand.PalmPosition
        handTransform As Matrix = new Matrix(handXBasis, handYBasis, handZBasis, handOrigin)
        handTransform = handTransform.RigidInverse()

        For f As Integer = 0 To f < leapHand.Fingers.Count - 1
            Dim leapFinger As Finger = leapHand.Fingers(f)
            Dim transformedPosition As Vector = handTransform.TransformPoint(leapFinger.TipPosition)
            Dim transformedDirection As Vector = handTransform.TransformDirection(leapFinger.Direction)
            ' Do something with the transformed fingers
        Next
    Next
C#
    Frame frame = leap.Frame();
    for( int h = 0; h < frame.Hands.Count; h++ )
    {
        Hand leapHand = frame.Hands[h];

        Vector handXBasis =  leapHand.PalmNormal.Cross(leapHand.Direction).Normalized;
        Vector handYBasis = -leapHand.PalmNormal;
        Vector handZBasis = -leapHand.Direction;
        Vector handOrigin =  leapHand.PalmPosition;
        Matrix handTransform = new Matrix(handXBasis, handYBasis, handZBasis, handOrigin);
        handTransform = handTransform.RigidInverse();

        for( int f = 0; f < leapHand.Fingers.Count; f++ )
        {
            Finger leapFinger = leapHand.Fingers[f];
            Vector transformedPosition = handTransform.TransformPoint(leapFinger.TipPosition);
            Vector transformedDirection = handTransform.TransformDirection(leapFinger.Direction);
            // Do something with the transformed fingers
        }
    }

Pointables

Pointableオブジェクトは指とツール、すなわち指し示すものを表します。指や手のオブジェクトから特定の手に関連付けられているツール(つまり手に持っている道具)を得ることができます。また、Frameオブジェクトから検出されたすべてのPointablesを得ることができます。Pointablesは必ずしもHandオブジェクトに関連付けられていません。例えば、物理的な手自体がLeap検出範囲の外に当ても良いし、別の手でブロックされている場合もあるでしょう)。したがってFrameからのPointablesリストには関連付けられていないLeapが検出できなかった指やツールを含めることができます。

Pointableオブジェクトは指やツールの特性を記述した多くの属性があります。

  • TipPosition:Leap Motionからの先端までの距離をmm単位で表す
  • TipVelocity:先端の移動速度(mm/sec)
  • StabilizedTipPosition:手ぶれ防止フィルタをかけた位置と速度
  • Direction:先端が差す方向
  • Length:指またはツールの見かけ上の長さ
  • Width:平均幅
  • TouchDistance:仮想タッチ面からの正規化した距離
  • TouchZone:先端と仮想タッチ面の関係

次の例は、FrameからPointableオブジェクトを取得し、その基本的な特性にアクセスする方法を示しています。

VB.NET
    Dim _pointable As Pointable = _frame.Pointables.Frontmost
    Dim direction As Vector = _pointable.Direction
    Dim length As Single = _pointable.Length
    Dim width As Single = _pointable.Width
    Dim stabilizedPosition As Vector = _pointable.StabilizedTipPosition
    Dim position As Vector = _pointable.TipPosition
    Dim speed As Vector = _pointable.TipVelocity
    Dim touchDistance As Single = _pointable.TouchDistance
    Dim zone As Pointable.Zone = _pointable.TouchZone
C#
    Pointable pointable = frame.Pointables.Frontmost;
    Vector direction = pointable.Direction;
    float length = pointable.Length;
    float width = pointable.Width;
    Vector stabilizedPosition = pointable.StabilizedTipPosition;
    Vector position = pointable.TipPosition;
    Vector speed = pointable.TipVelocity;
    float touchDistance = pointable.TouchDistance;
    Pointable.Zone zone = pointable.TouchZone;

Converting a Pointable Object to a Finger or Tool

Pointableオブジェクトを適切なFingerサブクラスやToolサブクラスに変換するには、適切なコンストラクター(Leapクラスのコンストラクタを使う必要があります)を使ってください。

VB.NET
    If (_pointable.IsTool) Then
        Dim _tool As new Tool(_pointable)
    else
        Dim _finger As new Finger(_pointable)
    End If
C#
    if (pointable.IsTool) {
        Tool tool = new Tool(pointable);
    } else {
        Finger finger = new Finger(pointable);
    }

Calculating the Position of the Base of a Finger

指の起点を位置を計算する場合は、次のように指の先端の位置と方向を使用します。

VB.NET
    Vector basePosition = -pointable.Direction * pointable.Length
    basePosition += pointable.TipPosition
C#
    Vector basePosition = -pointable.Direction * pointable.Length;
    basePosition += pointable.TipPosition;

Leap Motion–Touch Emulation の日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Leap_Touch_Emulation.htmlの日本語訳となります。

Touch Emulation

Leap Motion APIには、タッチエミュレーションを実装するために必要な情報を提供するPointableクラスがあります。

Topics:
  • Overview
  • Getting the Touch Zone
  • Getting the Touch Distance
  • Getting the Stabilized Position of a Pointable
  • Converting from Leap Motion Coordinates to Application Coordinates
  • TouchPoints Example

Overview

Leapはタッチを認識するための仮想平面を定義し、アプリケーションの 2D 要素との相互作用を調整することができます。仮想平面はほぼ x-y 平面と平行になっていて、指や手の位置を検出します。指やツールを前方に移動するとPointableオブジェクトはこの仮想平面と近い距離にあるか触れたかをタッチ面までの距離と共にレポートします。

Leap_Touch_Plane

タッチゾーンは、Leap Motionソフトウェアがタッチ画面の前後などの近くに指などがホバリングしている状態を考慮するかを指定します。ゾーンは「ホバリング」「タッチ」「なし」から構成されています。ゾーンの間の遷移はタッチの距離よりも遅れる傾向があります。この遅れはチャタリングのような繰り返しを検出しないようにするために使用されます。アプリケーション内でタッチ操作を実装する場合は、タッチゾーンを頻繁にチェックする必要はありません。

タッチの距離は、指などがホバリングまたはタッチゾーン内にある場合にのみ有効です。検出される値は+1~-1まで。ホバリングゾーンに指などがはいってくると+1.0を返し、タッチ面に近づくと0を返します。指などがタッチ面を突き抜けていくと-1の値になります。

マウスクリックやタッチ操作を判定するのにゾーンの値を使用できます。距離を使用して、さらにタッチ面への近さに基づいてUI要素の見た目を変化させるとよいでしょう。例えば、指はホバリングゾーンに入った時にコントロールの強調表示、近づいてきたらカーソルの変更をフィードバックします。

タッチエミュレーションAPIの一部として標準的な位置にあるPointableオブジェクトのゆっくりとした安定した位置を提供します。Leap Motionソフトウェアは、スムースフィルタを使って位置を安定化し、小さい領域(のようなボタンやリンク)にタッチしやすいような動きにします。このように動きを遅くしてタッチ距離が0の位置に触れやすくするために滑らかにするのは大きな意味を持ちます。

Getting the Touch Zone

タッチゾーンはPointableクラスの属性によって取得できます。ゾーンの列挙型は次の通りです。

  • NONE ? 指などがタッチ面から離れている
  • HOVERING ? 指などがホバリングゾーンに入り、まだタッチゾーンに入っていない
  • TOUCHING ? 指などが仮想面にタッチした

次のコードスニペットは、一番手前の指がタッチゾーンに入ったかを取得する方法を示しています。

VB.NET
        Dim _frame As Frame = leap.Frame()
        Dim _pointable As Pointable = _frame.Pointables.Frontmost
        Dim zone As Pointable.Zone = _pointable.TouchZone
C#
        Frame frame = leap.Frame();
        Pointable pointable = frame.Pointables.Frontmost;
        Pointable.Zone zone = pointable.TouchZone;

Getting the Touch Distance

タッチ距離はPointableクラスのTouchDistance属性として取得できます。仮想面からの距離を+1~-1で表します。この値は実際の距離と一致しませんがタッチ面のどれくらい近くにいるかの判定に使用できます。

次のコードスニペットは、一番手前の指のタッチまでの距離を取得する方法を示しています。

VB.NET
    Dim _frame As Frame = leap.Frame()
    Dim _pointable As Pointable = _frame.Pointables.Frontmost
    Dim distance As Single = _pointable.TouchDistance
C#
    Frame frame = leap.Frame();
    Pointable pointable = frame.Pointables.Frontmost;
    float distance = pointable.TouchDistance;

Getting the Stabilized Position of a Pointable

ブレ防止フィルタをかけたあとの位置は、PointableクラスのStabilizedTipPosition属性で取得できます。 この位置は標準的なLeap Motion座標系で取得できますがフィルタリングとブレ防止を行ったあとの値になります。

次のコードスニペットは、一番手前の指のブレ防止フィルタをかけたときの位置を取得する方法を示しています。

VB.NET
    Dim _frame As Frame = leap.Frame()
    Dim _pointable As Pointable = _frame.Pointables.Frontmost
    Dim stabilizedPosition As Vector = _pointable.StabilizedTipPosition
C#
    Frame frame = leap.Frame();
    Pointable pointable = frame.Pointables.Frontmost;
    Vector stabilizedPosition = pointable.StabilizedTipPosition;

Converting from Leap Motion Coordinates to Application Coordinates

タッチ エミュレーションを実装するときは、アプリケーションのスクリーン空間にLeap Motion座標空間を割り当てる必要があります。このマッピングが容易にできるようにLeap Motion APIは、InteractionBoxクラスを提供します。InteractionBoxは、LeapMotion検出範囲で直線的な領域を表します。クラスはこの領域内での位置を範囲[0..1]座標を正規化する機能を提供します。位置を正規化し、アプリケーション座標のポイントを取得すると座標をスケールすることができます。

たとえば、変数windowWidthとwindowHeightで表されたクライアント領域のウィンドウがある場合、次のコードを使用してこのウィンドウ内のタッチ ポイントの2D座標を得ることができます。

VB.NET
    Dim _frame As Frame = leap.Frame()
    Dim _pointable As Pointable = _frame.Pointables.Frontmost
    Dim stabilizedPosition As Vector = _pointable.StabilizedTipPosition

    Dim iBox As InteractionBox = leap.Frame().InteractionBox
    Dim normalizedPosition As Vector = iBox.NormalizePoint(stabilizedPosition)
    Dim x As single = normalizedPosition.x * windowWidth
    Dim y As float = windowHeight - normalizedPosition.y * windowHeight
C#
    Frame frame = leap.Frame();
    Finger finger = frame.Fingers.Frontmost;
    Vector stabilizedPosition = finger.StabilizedTipPosition;

    InteractionBox iBox = leap.Frame().InteractionBox;
    Vector normalizedPosition = iBox.NormalizePoint(stabilizedPosition);
    float x = normalizedPosition.x * windowWidth;
    float y = windowHeight - normalizedPosition.y * windowHeight;

TouchPoints Example

Leap_Touch_Point_Example
VB.NET
Imports System.Collections.Generic
Imports System.Windows
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Shapes
Imports System.Windows.Ink
 
Imports Leap
 
Public Class MainWindow
     Private leap As New Controller
     Private windowWidth As Single = 1400
     Private windowHeight As Single = 800
     Private touchIndicator As New DrawingAttributes
     Public Sub New()
         ' この呼び出しはデザイナーで必要です。
         InitializeComponent()
         ' InitializeComponent() 呼び出しの後で初期化を追加します。
         AddHandler CompositionTarget.Rendering, AddressOf Update
         touchIndicator.Width = 20
         touchIndicator.Height = 20
         touchIndicator.StylusTip = StylusTip.Ellipse
     End Sub
     Private Sub Update(sender As Object, e As EventArgs)
         paintCanvas.Strokes.Clear()
         windowWidth = CType(Me.Width, Single)
         windowHeight = CType(Me.Height, Single)
         Dim _frame As Leap.Frame = leap.Frame()
         Dim _interactionBox As InteractionBox = leap.Frame().InteractionBox
         For Each _pointable As Pointable In leap.Frame().Pointables
             Dim normalizedPosition As Leap.Vector = _interactionBox.NormalizePoint(_pointable.StabilizedTipPositioninte
             Dim tx As Single = normalizedPosition.x * windowWidth
             Dim ty As Single = windowHeight - normalizedPosition.y * windowHeight
             Dim alpha As Integer = 255
             If (_pointable.TouchDistance > 0 AndAlso _pointable.TouchZone <> Pointable.Zone.ZONENONE) Then                  alpha = 255 - CType(255 * _pointable.TouchDistance, Integer)
                 touchIndicator.Color = Color.FromArgb(CType(alpha, Byte), &H0, &HFF, &H0)
             ElseIf (_pointable.TouchDistance <= 0) Then
                 alpha = -CType(255 * _pointable.TouchDistance, Integer)
                 touchIndicator.Color = Color.FromArgb(CType(alpha, Byte), &HFF, &H0, &H0)
             Else
                 alpha = 50
                 touchIndicator.Color = Color.FromArgb(CType(alpha, Byte), &H0, &H0, &HFF)
             End If
             Dim touchPoint As New StylusPoint(tx, ty)
             Dim tips As New StylusPointCollection(New StylusPoint(touchPoint))
             Dim touchStroke As New Stroke(tips, touchIndicator)
             paintCanvas.Strokes.Add(touchStroke)
         Next
     End Sub
End Class

<window title="MainWindow" x:class="TouchPoints.MainWindow" width="525" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" height="350">
<grid>
<inkpresenter name="paintCanvas">
</inkpresenter>
</grid> 
</window>
C#
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Ink;

using Leap;

namespace TouchPoints
{
    public partial class MainWindow : Window
    {
        Controller leap = new Controller();
        float windowWidth = 1400;
        float windowHeight = 800;
        DrawingAttributes touchIndicator = new DrawingAttributes();
        public MainWindow()
        {
            InitializeComponent();
            CompositionTarget.Rendering += Update;
            touchIndicator.Width = 20;
            touchIndicator.Height = 20;
            touchIndicator.StylusTip = StylusTip.Ellipse;
        }

        protected void Update(object sender, EventArgs e)
        {
            paintCanvas.Strokes.Clear();
            windowWidth = (float)this.Width;
            windowHeight = (float)this.Height;

            Leap.Frame frame = leap.Frame();
            InteractionBox interactionBox = leap.Frame().InteractionBox;

            foreach(Pointable pointable in leap.Frame().Pointables)
            {
                Leap.Vector normalizedPosition = interactionBox.NormalizePoint(pointable.StabilizedTipPosition);
                float tx = normalizedPosition.x * windowWidth;
                float ty = windowHeight - normalizedPosition.y * windowHeight;

                int alpha = 255;
                if(pointable.TouchDistance > 0 && pointable.TouchZone != Pointable.Zone.ZONENONE)
                {
                    alpha = 255 - (int)(255 * pointable.TouchDistance);
                    touchIndicator.Color = Color.FromArgb((byte)alpha, 0x0, 0xff, 0x0);
                }
                else if(pointable.TouchDistance <= 0)
                {
                    alpha = -(int)(255 * pointable.TouchDistance);
                    touchIndicator.Color = Color.FromArgb((byte)alpha, 0xff, 0x0, 0x0);
                }
                else
                {
                    alpha = 50;
                    touchIndicator.Color = Color.FromArgb((byte)alpha, 0x0, 0x0, 0xff);
                }
                StylusPoint touchPoint = new StylusPoint(tx, ty);
                StylusPointCollection tips = new StylusPointCollection(new StylusPoint[] { touchPoint });
                Stroke touchStroke = new Stroke(tips, touchIndicator);
                paintCanvas.Strokes.Add(touchStroke);
            }
        }
    }
}

<window title="MainWindow" x:class="TouchPoints.MainWindow" width="525" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" height="350">
<grid>
<inkpresenter name="paintCanvas">
</inkpresenter>
</grid> 
</window>

この例はVB.NET/C#によるWPFアプリケーションにおけるLeap Motionサンプルとなっています。

----

Copyright c 2012-2013 Leap Motion, Inc. All rights reserved.

Leap Motion proprietary and confidential. Not for distribution. Use subject to the terms of the Leap Motion SDK Agreement available at https://developer.leapmotion.com/sdk_agreement, or another agreement between Leap