Log in
  • Blogs
  • 山下 雅也
  • Leap Motion でアプリ開発 - Leap Motion で認識できるエリア

Leap Motion でアプリ開発 - Leap Motion で認識できるエリア

  • Public
By 山下 雅也 1555 days ago

目次へ戻る

今回は WPF の 3D 機能を使って、Leap Motion のサンプルアプリケーションをつくります。

WPF の 3D 機能を使うにあたっては、3DTools というモジュールが Microsoft より公開されていますので、以下のサイトからダウンロードしておきましょう。
ちなみに、3DTools はソースコードも公開されていますので、プロジェクトという形で追加することもでき、好きなようにカスタマイズして利用することもできます。

http://3dtools.codeplex.com/releases/view/2058

ダウンロードしたら 3DTools に含まれる 3DTools.dll も参照の追加を行っておきます。

今回は 3DTools に含まれる TrackballDecorator クラスと ScreenSpaceLines3D クラスを使います。
TrackballDecorator クラスは Viewport3D クラスの外側に置くだけで、マウス操作でカメラ位置を操作することができるようになります。
また、WPF の 3D には単に線を引くという機能がありませんが、ScreenSpaceLines3D クラスでそれが可能になります。


Leap Motion の座標系は右手系と呼ばれる座標系になっており、右がX軸、上がY軸、手前がZ軸の正方向です。WPF の 3D 空間も同じく右手系となっていますので、座標系について特別な変換は必要ありません。

Leap_Axes



Leap Motion で認識できるエリアは、Leap Motion デバイスの上に、以下のような形で広がっています。逆ピラミッド型で、Z軸方向が少し狭くなっています。

Leap_InteractionBox



上図で、内側にある直方体は InteractionBox と呼びます。
一般的なアプリケーションでは逆ピラミッド型での座標より、直方体での座標のほうが扱いやすいことから InteractionBox という概念が用意されており、相互に変換を行うことができるようになっています。


今回はとりあえず、座標軸と、Leap Motion が認識する逆ピラミッド型のエリアを作ってみます。
MainWindows.xaml.cs を以下のように書き換えます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace LeapSample
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private Leap.Controller controller = new Leap.Controller();
        private ModelVisual3D trackingAreaModel = null;
 
        public MainWindow()
        {
            InitializeComponent();
 
            var root = this.Content as Grid;
            root.Children.Add(new TextBlock());
 
            Init();
 
            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }
 
        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            var root = this.Content as Grid;
            var textBlock = root.Children[0] as TextBlock;
 
            Leap.Frame frame = controller.Frame();
            string msg = string.Format("IsValid={0}\n", frame.IsValid);
            msg += string.Format("Id={0}\n", frame.Id);
            msg += string.Format("Timestamp={0}\n", frame.Timestamp);
            msg += string.Format("InteractionBox={0}\n", frame.InteractionBox);
            msg += string.Format("CurrentFramesPerSecond={0}\n", frame.CurrentFramesPerSecond);
            msg += string.Format("Hands.Count={0}\n", frame.Hands.Count);
            msg += string.Format("Pointables.Count={0}\n", frame.Pointables.Count);
            msg += string.Format("Fingers.Count={0}\n", frame.Fingers.Count);
            msg += string.Format("Tools.Count={0}\n", frame.Tools.Count);
            textBlock.Text = msg;
 
            var td = root.Children[1] as _3DTools.TrackballDecorator;
            var viewport = td.Content as Viewport3D;
 
            if (frame.IsValid)
            {
                if (trackingAreaModel == null)
                {
                    trackingAreaModel = CreateTrackingAreaModel(frame.InteractionBox);
                    viewport.Children.Add(trackingAreaModel);
                }
            }
            else
            {
                viewport.Children.Remove(trackingAreaModel);
                trackingAreaModel = null;
            }
        }
 
        private void Init()
        {
            var root = this.Content as Grid;
 
            // trackball
            var td = new _3DTools.TrackballDecorator();
            root.Children.Add(td);
 
            // viewport
            var viewport = new Viewport3D();
            td.Content = viewport;
 
            // camera
            var camera = new PerspectiveCamera();
            camera.Position = new Point3D(0, 1000, 1200);
            camera.UpDirection = new Vector3D(0, 1, 0);
            camera.LookDirection = (new Point3D(0, 100, 0) - camera.Position);
            viewport.Camera = camera;
 
            // light
            var model = new ModelVisual3D();
            var light = new DirectionalLight { Color = Colors.White, Direction = new Vector3D(-1, -1, -1) };
            model.Content = light;
            viewport.Children.Add(model);
 
            // axis
            viewport.Children.Add(CreateAxisModel());
        }
 
        private ModelVisual3D CreateAxisModel()
        {
            var xAxis = new _3DTools.ScreenSpaceLines3D();
            xAxis.Points.Add(new Point3D(-2000, 0, 0));
            xAxis.Points.Add(new Point3D(2000, 0, 0));
            xAxis.Color = Colors.Red;
            xAxis.Thickness = 1;
            var yAxis = new _3DTools.ScreenSpaceLines3D();
            yAxis.Points.Add(new Point3D(0, -2000, 0));
            yAxis.Points.Add(new Point3D(0, 2000, 0));
            yAxis.Color = Colors.Green;
            yAxis.Thickness = 1;
            var zAxis = new _3DTools.ScreenSpaceLines3D();
            zAxis.Points.Add(new Point3D(0, 0, -2000));
            zAxis.Points.Add(new Point3D(0, 0, 2000));
            zAxis.Color = Colors.Blue;
            zAxis.Thickness = 1;
            var axis = new ModelVisual3D();
            axis.Children.Add(xAxis);
            axis.Children.Add(yAxis);
            axis.Children.Add(zAxis);
            return axis;
        }
 
        private ModelVisual3D CreateTrackingAreaModel(Leap.InteractionBox ib)
        {
            double k = (ib.Center.y + ib.Height / 2) / (ib.Center.y - ib.Height / 2);
            var lb = new Point3D((ib.Center.x - ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z - ib.Depth / 2) * k);    // left back
            var rb = new Point3D((ib.Center.x + ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z - ib.Depth / 2) * k);    // right back
            var rf = new Point3D((ib.Center.x + ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z + ib.Depth / 2) * k);    // right front
            var lf = new Point3D((ib.Center.x - ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z + ib.Depth / 2) * k);    // left front
            var origin = new Point3D(0, 0, 0);
 
            var m = new _3DTools.ScreenSpaceLines3D();
            m.Points.Add(lb);
            m.Points.Add(rb);
            m.Points.Add(rb);
            m.Points.Add(rf);
            m.Points.Add(rf);
            m.Points.Add(lf);
            m.Points.Add(lf);
            m.Points.Add(lb);
            m.Points.Add(origin);
            m.Points.Add(lb);
            m.Points.Add(origin);
            m.Points.Add(rb);
            m.Points.Add(origin);
            m.Points.Add(rf);
            m.Points.Add(origin);
            m.Points.Add(lf);
            m.Color = Colors.SlateGray;
            return m;
        }
    }
}



実行結果は以下となります。

leapsample-2


マウスを左クリックしながら動かすとカメラ位置を移動できます。右クリックしながら動かすと拡大縮小となります。

leapmotion-3


今回はここまでです。

目次へ戻る