画面上の図形を5本の指で操作する基本的なLeap Motionプログラムを作る

2013年10月10日(木)
薬師寺 国安

画面のレイアウト(MainWindow.xaml)

デフォルトでGridコントロールが配置されているのをCanvasコントロールに変更します。その理由は、座標値が取得しやすいからです。しかし、今回は座標値を取得していないので、Gridでも問題はなのですが、この連載においては基本的にGridよりCanvasで統一することとします。

ツールボックスからEllipseコントロールを1個配置します。名前をEllipse1としておきます。この図ではわかりやすくするためにWidthとHeightに100を指定して円を表示していますが、実際にはWidthとHeightにはプロパティ名をバインドするため、円は点としてしか表示されません。

またLeap Motionのタッチ・ポイントを表示させるために、「paintCanvas」という名前のInkPresenterコントロールをCanvasコントロール上に配置します。このInkPresenterコントロールは、どのコントロールよりも一番手前に配置しておく必要があります。他のコントロールの後に配置すると、実行した場合に、タッチ・ポイントが表示されなくなりますので、注意してください。レイアウトは図7のようになります。

図7:各コントロールを配置した(クリックで拡大)

書き出されるXAMLをリスト1のように編集します。Ellipse1のHeightに[EllipseHeight]をバインドします。Widthに[EllipseWidth] をバインドします。これらの名称は、後で作成するクラス(UpDownModel.vb)内で定義したプロパティ名です。

ルートのCanvasコントロールの最後に要素が記載されていますが、これによりInkPresenterコントロールが前面に表示されるようになります。この領域にタッチ・ポイントが表示されます。

リスト1 (MainWindo.xaml)

<Window x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" 
  Title="MainWindow" Height="768" Width="1366">
  <Canvas>
    <Ellipse x:Name="Ellipse1" Fill="Crimson" HorizontalAlignment="Left" Height="{Binding EllipseHeight}"  Stroke="Blue"  StrokeThickness="5" VerticalAlignment="Top" Width="{Binding EllipseWidth}" Canvas.Left="357" Canvas.Top="200"/>
    <InkPresenter Name="paintCanvas"/>
  </Canvas>
</Window>

クラスの作成(SwipeDirection.vb)

では、次にクラスを作成します。ソリューション・エクスプローラー内のFingerRcognizeUpDownプロジェクトを選択して、マウスの右クリックで表示されるメニューから[追加]−[クラス]と選択します。表示される画面から[クラス]を選択し、「名前」に[SwipeDirection.vb] と指定して[追加]ボタンをタップします。

SwipeDirection.vbのプログラム・コード

Enum列挙体は、値を定義するための入れ物として使用する特殊なクラスです。列挙体が便利なのはプログラム中に入力候補が自動的に表示される点です。

SwipeDirection列挙体の中に、UpとDownという値を入れておきます(リスト2)。

リスト2(SwipeDirection.vb) 列挙体を記述したコード

Public Enum SwipeDirection
  Up
  Down
End Enum

クラスの作成(LeapListener.vb)

次に、SwipeDirection.vbを作成した方法で、LeapListener.vbクラスを作成します。

LeapListener.vbのプログラム・コード

Leap Motionを扱うための名前空間「Leap」をインポートします。

指の個数を格納するためのメンバー変数「Private fingersCount As Integer」を宣言します。

OnConnectコールバックメソッドの処理

コントローラーがLeap Motionセンサーと接続されたときに呼び出される、OnConnectコールバックメソッド内で、オブジェクトを動かす値を、10以上動かし、100mm/s以上の速度で動かします。デフォルトの値は150mm以上動かす、1000mm/s以上の速度で、というのが設定されています。
EnableGestureでGestureType.TYPESWIPEを有効にします。

TYPESWIPEは「指を伸ばした状態の手で直線を描く動作」を指します。ジェスチャーには4つのTypeがあります。詳細については「C#開発者から見たLeap Motion開発のファースト・インプレッション」を参照してください。今回は、この中からSwipeを使用します。

OnFrameコールバックメソッドの処理

次に、フレームのデータが更新されたときに呼び出されるOnFrameコールバックメソッドの処理です。

最新のフレームを取得し、1つ目の手を変数firstHandに格納します。

手に指があるかをチェックして、指がある場合は、全ジェスチャーを取得して、個別に処理を行います。

GestureTypeがTYPESWIPEであった場合は、スワイプの方向(Y座標)が0より大きかった場合は、指の個数(fingers)とスワイプの方向(SwipDirection.Up)を引数にしてSwipeActionメソッドを実行します。それ以外の場合は、指の個数(fingers)とスワイプの方向(SwipDirection.Down)を引数にして、SwipeActionメソッドを実行します。

次にSwipeEventデリゲート経由で、LeapSwipeメソッドを呼び出します。

SwipeActionメソッドでは、指が5つの場合は、SwipeDirectionのUpとDownで条件分岐を行い、RaiseEventでLeapSwipeイベントを発生させます。その際、引数にSwipeDirectionのUpとDownを指定します。

これらのコードはリスト3になります。

リスト3(LeapListener.vb) TYPESWIPEの処理を行うクラス

Imports Leap

Public Class LeapListener
  Inherits Listener
  Private fingersCount As Integer

  '   コントローラーがLeap Motionセンサーと接続されたときに呼び出される、
  '  OnConnectコールバックメソッド
  ' スワイプの動作を有効にする
  Public Overrides Sub OnConnect(ctlr As Controller)
     ’ オブジェクトの移動距離(mm)と速度(mm/s)を指定
      ctlr.Config.SetFloat("Gesture.Swipe.MinLength", 10)
      ctlr.Config.SetFloat("Gesture.Swipe.MinVelocity", 100)
      ctlr.Config.Save()
  
  '   EnableGestureでGestureType.TYPESWIPEを有効にする
  ctlr.EnableGesture(Gesture.GestureType.TYPESWIPE)
  End Sub

' フレームのデータが更新されたときに呼び出される
'  OnFrameコールバックメソッドの処理
    Public Overrides Sub OnFrame(ctlr As Controller)
      Dim currentFrame As Frame = ctlr.Frame
      If (Not currentFrame.Hands.Empty) Then
   
      ’ 最新のフレームを取得し、1つ目の手を変数firstHandに格納する。
      Dim firstHand As Hand = currentFrame.Hands(0)
 
      ' 手に指があるかをチェックして、指がある場合は、
      '  全ジェスチャーを取得して、個別に処理を行う。
      Dim fingers As FingerList = firstHand.Fingers
      If (Not fingers.Empty) Then
        Dim gestures As GestureList = currentFrame.Gestures
        For Each gst As Gesture In gestures
 
      '  GestureTypeがTYPESWIPEであった場合は、スワイプの方向で条件分岐を
      ’ 行う。
      ’ SwipeActionメソッドを実行する。
            If gst.Type = Gesture.GestureType.TYPESWIPE Then
              Dim swipe As New SwipeGesture(gst)
            If (swipe.Direction.y > 0) Then 
              SwipeAction(fingers, SwipeDirection.Up)
            Else 
              SwipeAction(fingers, SwipeDirection.Down)
            End If
            Exit For
          End If
        Next
      End If
    End If
  End Sub
 ’ SwipeEventデリゲート経由で、LeapSwipeメソッドを呼び出す。
  Public Delegate Sub SwipeEvent(ByVal sd As SwipeDirection)
  Public Event LeapSwipe As SwipeEvent
'  GestureTypeがTYPESWIPEであった場合は、スワイプの方向で条件分岐を
  '  行うSwipeActionメソッドの処理。
  Private Sub SwipeAction(ByVal fingers As FingerList, ByVal sd As SwipeDirection)
  fingersCount = fingers.Count
  If (fingersCount = 5) Then
  Select Case sd
  Case SwipeDirection.Up
           ’ RaiseEventでLeapSwipeイベントを発生させる。
  RaiseEvent LeapSwipe(SwipeDirection.Up)
  Exit Select
  Case SwipeDirection.Down
  '  RaiseEventでLeapSwipeイベントを発生させる。
  RaiseEvent LeapSwipe(SwipeDirection.Down)
  Exit Select
  End Select
  End If
  End Sub
  End Class

クラスの作成(UpDownModel.vb)

次に、SwipeDirection.vbを作成した方法で、UpDownModel.vbクラスを作成します。

UpDownModel.vbのプログラム・コード

プロパティの値が変更されたことをクライアントに通知する、INotifyPropertyChangedインターフェースをインプリメントするために、System.ComponentModel名前空間をインポートしておきます。

Leap Motionを扱うためにLeap名前空間をインポートしておきます。

Controllerクラス型のメンバー変数ctlrを宣言します。

先ほど作成したLeapListenerクラス型のメンバー変数listenerを宣言します。

次に名前がNewであるメソッドを定義します。自動的にこれがコンストラクタになります。

Newメソッド内では以下の処理が行われます。

Controllerの新しいインスタンスctlrオブジェクトを作成します。新しいLeapListenerのインスタンスlistenerオブジェクトを作成します。AddListenerメソッドでctlrオブジェクトにlistenerオブジェクトを追加します。

AddHandlerステートメントで、listenerオブジェクトのLeapSwipeイベントに、SwipeActionイベントハンドラ—を追加します。

次に各プロパティを定義します。EllipseHeightとEllipseWidthプロパティを定義し、INotifyPropertyChangedインターフェースに各プロパティの名前を指定しておきます。

次はSwipeActionメソッドの処理です。
 
Enum列挙体で値を定義していた、SwipeDirectionの値で条件分岐を行います。Upの場合はEllipseHeightとEllipseWidthの値を10ずつ増加させ、Downの場合は、EllipseHeightとEllipseWidthの値を10ずつ減少させます。

プロパティの値が変更された時に、変更されたことをクライアントに通知するPropertyChangedイベントを定義します。

OnPropertyChangedメソッドで、プロパティ名で初期化されたPropertyChangedイベントを発生させます。

  • 5本の指でオブジェクトを拡大・縮小させるプログラム

薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています