[LeapMotion]Xcode, Objective-Cで開発

2013/07/29

Leap Motion」を手に入れたのでXcodeで試してみた。 LeapMotion

海外がお得

国内で買うと倍くらいしてしまうので、海外で買った方がお得で、意外と早く届きます(3日で届いた)。

セットアップ

https://www.leapmotion.com/setupから「Leap Motion Installer」をダウンロードして、インストールします。 非常にわかりやすいセットアップナビゲーションです。

アプリ管理は「AirSpace」

アプリ管理は「AirSpace」で行います。 https://airspace.leapmotion.com/にアクセスすると、iTunesStore、GooglePlayのようにアプリが購入できます。 フリーのソフトもあるので、ざっと試す事ができます。

開発環境

Leap Motionは、色々な言語に対応しているので自分の得意分野で開発かと思います。 (自分は XCode、Objective-Cで開発)

SDKは、https://developer.leapmotion.com/downloads からダウンロードできます。

ダウンロードしたSDKは、自分の管理しやすいパスに配置します。 LeapMotion ※自分は「/Users/ユーザ/leap/LeapSDK/」にしました

XCode、Objective-Cで開発

Xcode、Objective-Cで開発するにはいくつか準備が必要です。

(1) libLeap.dylib (2) Leap、LeapMathヘッダファイル (3) LeapObjectiveC

プロジェクト作成

Cocoa Applicationでプロジェクト作成 LeapMotion

ARCで作成してみる LeapMotion

Build Settingsの設定

「LeapSDK」のライブラリを利用するには、パスを通してやる必要があります。

まず、配置したLeapSDKのパス定義 LeapMotion ※「LEAPSDK_PATH」としました

headerファイルのパス定義 LeapMotion $(LEAPSDK_PATH)/inclulde

libraryファイルのパス定義 LeapMotion $(LEAPSDK_PATH)/lib

includeファイルの追加

「Leap.h」「LeapMath.h」「LeapObjectiveC.h」「LeapObjectiveC.mm」をプロジェクトにドラッグ&ドロップ LeapMotion

LeapMotion

libLeap.dylib

「libLeap.dylib」をプロジェクトにドラッグ&ドロップ LeapMotion

Build Phasesで、ファイルが追加されるのを確認 LeapMotion

DEMO

メインプログラムは「LeapSDK/samples/Sample.cpp」をそのまま記述しました。

LeapTest.h

#import 
#import "LeapObjectiveC.h"

@interface LeapTest : NSObject

- (void)run;

@end

「LeapObjectiveC.h」を読み込んで、「LeapListener」で拡張

LeapTest.m

#import "LeapTest.h"

@implementation LeapTest
{
    LeapController *controller;
}

- (void)run
{
    controller = [[LeapController alloc] init];
    [controller addListener:self];
    NSLog(@"running");
}

- (void)onInit:(NSNotification *)notification
{
    NSLog(@"Initialized");
}

- (void)onConnect:(NSNotification *)notification
{
    NSLog(@"Connected");
    LeapController *aController = (LeapController *)[notification object];
    [aController enableGesture:LEAP_GESTURE_TYPE_CIRCLE enable:YES];
    [aController enableGesture:LEAP_GESTURE_TYPE_KEY_TAP enable:YES];
    [aController enableGesture:LEAP_GESTURE_TYPE_SCREEN_TAP enable:YES];
    [aController enableGesture:LEAP_GESTURE_TYPE_SWIPE enable:YES];
}

- (void)onDisconnect:(NSNotification *)notification
{
    //Note: not dispatched when running in a debugger.
    NSLog(@"Disconnected");
}

- (void)onExit:(NSNotification *)notification
{
    NSLog(@"Exited");
}

- (void)onFrame:(NSNotification *)notification
{
    LeapController *aController = (LeapController *)[notification object];
    LeapFrame *frame = [aController frame:0];
    
    NSLog(@"Frame id: %lld, timestamp: %lld, hands: %ld, fingers: %ld, tools: %ld, gestures: %ld",
          [frame id], [frame timestamp], [[frame hands] count],
          [[frame fingers] count], [[frame tools] count], [[frame gestures:nil] count]);
    
    if ([[frame hands] count] != 0) {
        LeapHand *hand = [[frame hands] objectAtIndex:0];
        
        NSArray *fingers = [hand fingers];
        if ([fingers count] != 0) {
            LeapVector *avgPos = [[LeapVector alloc] init];
            for (int i = 0; i < [fingers count]; i++) {
                LeapFinger *finger = [fingers objectAtIndex:i];
                avgPos = [avgPos plus:[finger tipPosition]];
            }
            avgPos = [avgPos divide:[fingers count]];
            NSLog(@"Hand has %ld fingers, average finger tip position %@",
                  [fingers count], avgPos);
        }
        
        NSLog(@"Hand sphere radius: %f mm, palm position: %@",
              [hand sphereRadius], [hand palmPosition]);
        
        const LeapVector *normal = [hand palmNormal];
        const LeapVector *direction = [hand direction];
        
        NSLog(@"Hand pitch: %f degrees, roll: %f degrees, yaw: %f degrees\n",
              [direction pitch] * LEAP_RAD_TO_DEG,
              [normal roll] * LEAP_RAD_TO_DEG,
              [direction yaw] * LEAP_RAD_TO_DEG);
    }
    
    NSArray *gestures = [frame gestures:nil];
    for (int g = 0; g < [gestures count]; g++) {
        LeapGesture *gesture = [gestures objectAtIndex:g];
        switch (gesture.type) {
            case LEAP_GESTURE_TYPE_CIRCLE: {
                LeapCircleGesture *circleGesture = (LeapCircleGesture *)gesture;
                
                NSString *clockwiseness;
                if ([[[circleGesture pointable] direction] angleTo:[circleGesture normal]] <= LEAP_PI/4) {
                    clockwiseness = @"clockwise";
                } else {
                    clockwiseness = @"counterclockwise";
                }
                
                float sweptAngle = 0;
                if(circleGesture.state != LEAP_GESTURE_STATE_START) {
                    LeapCircleGesture *previousUpdate = (LeapCircleGesture *)[[aController frame:1] gesture:gesture.id];
                    sweptAngle = (circleGesture.progress - previousUpdate.progress) * 2 * LEAP_PI;
                }
                
                NSLog(@"Circle id: %d, %@, progress: %f, radius %f, angle: %f degrees %@",
                      circleGesture.id, [LeapTest stringForState:gesture.state],
                      circleGesture.progress, circleGesture.radius,
                      sweptAngle * LEAP_RAD_TO_DEG, clockwiseness);
                break;
            }
            case LEAP_GESTURE_TYPE_SWIPE: {
                LeapSwipeGesture *swipeGesture = (LeapSwipeGesture *)gesture;
                NSLog(@"Swipe id: %d, %@, position: %@, direction: %@, speed: %f",
                      swipeGesture.id, [LeapTest stringForState:swipeGesture.state],
                      swipeGesture.position, swipeGesture.direction, swipeGesture.speed);
                break;
            }
            case LEAP_GESTURE_TYPE_KEY_TAP: {
                LeapKeyTapGesture *keyTapGesture = (LeapKeyTapGesture *)gesture;
                NSLog(@"Key Tap id: %d, %@, position: %@, direction: %@",
                      keyTapGesture.id, [LeapTest stringForState:keyTapGesture.state],
                      keyTapGesture.position, keyTapGesture.direction);
                break;
            }
            case LEAP_GESTURE_TYPE_SCREEN_TAP: {
                LeapScreenTapGesture *screenTapGesture = (LeapScreenTapGesture *)gesture;
                NSLog(@"Screen Tap id: %d, %@, position: %@, direction: %@",
                      screenTapGesture.id, [LeapTest stringForState:screenTapGesture.state],
                      screenTapGesture.position, screenTapGesture.direction);
                break;
            }
            default:
                NSLog(@"Unknown gesture type");
                break;
        }
    }
    
    if (([[frame hands] count] > 0) || [[frame gestures:nil] count] > 0) {
        NSLog(@" ");
    }
}

- (void)onFocusGained:(NSNotification *)notification
{
    NSLog(@"Focus Gained");
}

- (void)onFocusLost:(NSNotification *)notification
{
    NSLog(@"Focus Lost");
}

+ (NSString *)stringForState:(LeapGestureState)state
{
    switch (state) {
        case LEAP_GESTURE_STATE_INVALID:
            return @"STATE_INVALID";
        case LEAP_GESTURE_STATE_START:
            return @"STATE_START";
        case LEAP_GESTURE_STATE_UPDATE:
            return @"STATE_UPDATED";
        case LEAP_GESTURE_STATE_STOP:
            return @"STATE_STOP";
        default:
            return @"STATE_INVALID";
    }
}
@end

サイクルは以下の種類があり、NSNotificationで検知して処理するようです。

  • (void)onInit:(NSNotification *)notification
  • (void)onConnect:(NSNotification *)notification
  • (void)onDisconnect:(NSNotification *)notification
  • (void)onExit:(NSNotification *)notification
  • (void)onFrame:(NSNotification *)notification
  • (void)onFocusGained:(NSNotification *)notification

ジェスチャーはonFrame()で主に処理してやる必要がありますが、普通に書いてしまうと分岐の嵐になりそうですね。

最後に、AppDelegateでメインプログラムを処理します。 ※ARCで書いたので「LeapTest.h」は@classでなくimportにしています

AppDelegate.h

#import 
#import "LeapTest.h"

@interface AppDelegate : NSObject 

@property (assign) IBOutlet NSWindow *window;
@property (nonatomic)LeapTest *leapTest;

@end

AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    _leapTest = [[LeapTest alloc]init];
    [_leapTest run];
}
@end

ビルドして動かすと、ジェスチャーとともにログが変化するのが確認できます。 LeapMotion

と、とりあず動作確認程度ですが、ここからが大変そうですね。