はつねの日記

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

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