leap-motion-ws 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -10
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/leap-motion-ws.gemspec +1 -1
- data/lib/leap/motion/utils/history.rb +25 -0
- data/lib/leap/motion/ws.rb +111 -13
- data/lib/leap/motion/ws/gesture.rb +46 -5
- data/lib/leap/motion/ws/hand.rb +4 -5
- data/lib/leap/motion/ws/pointable.rb +4 -5
- data/samples/motion_gesture_sample.rb +38 -0
- data/samples/{motion_ws_sample.rb → motion_sample.rb} +0 -0
- data/test/helper.rb +12 -0
- data/test/test_history.rb +28 -0
- metadata +12 -8
- data/lib/leap/motion/utils/ring.rb +0 -19
- data/samples/ring_sample.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b27639657797b35580926d29c4c602055b3a6fb5
|
4
|
+
data.tar.gz: 0b2e9b8fe95693bc7c16a14124d253fb51807d1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7d3271db65abf83b26933269c6bd7485bf75620b0a91689156a404656e84a16c6ca0ff0259e1105723dad5e7e1f9050524883b86942fdc11e7fb2a2463b792d
|
7
|
+
data.tar.gz: 89a2ab1d862e865d529e02065965fa79658ab086701bf8919ff26624451e349ae178fa7a12f1ef6ecbce186e0294a381c02983ce67260f6f4a9c2f1f7f14ac19
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
leap-motion-ws (0.0.
|
4
|
+
leap-motion-ws (0.0.2)
|
5
5
|
json
|
6
6
|
websocket-eventmachine-client
|
7
7
|
|
@@ -11,25 +11,19 @@ GEM
|
|
11
11
|
activesupport (3.2.13)
|
12
12
|
i18n (= 0.6.1)
|
13
13
|
multi_json (~> 1.0)
|
14
|
-
bourne (1.4.0)
|
15
|
-
mocha (~> 0.13.2)
|
16
14
|
eventmachine (1.0.3)
|
17
15
|
i18n (0.6.1)
|
18
16
|
json (1.7.7)
|
19
|
-
metaclass (0.0.1)
|
20
|
-
mocha (0.13.3)
|
21
|
-
metaclass (~> 0.0.1)
|
22
17
|
multi_json (1.7.3)
|
23
18
|
rake (10.0.4)
|
24
19
|
rdoc (4.0.1)
|
25
20
|
json (~> 1.4)
|
26
|
-
shoulda (3.
|
21
|
+
shoulda (3.5.0)
|
27
22
|
shoulda-context (~> 1.0, >= 1.0.1)
|
28
|
-
shoulda-matchers (
|
23
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
29
24
|
shoulda-context (1.1.1)
|
30
|
-
shoulda-matchers (1.
|
25
|
+
shoulda-matchers (2.1.0)
|
31
26
|
activesupport (>= 3.0.0)
|
32
|
-
bourne (~> 1.3)
|
33
27
|
websocket (1.0.7)
|
34
28
|
websocket-eventmachine-base (1.0.2)
|
35
29
|
eventmachine (~> 1.0)
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/leap-motion-ws.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
|
11
11
|
s.authors = ["Gregoire Lejeune"]
|
12
12
|
s.email = "gregoire.lejeune@free.fr"
|
13
|
-
s.homepage = "
|
13
|
+
s.homepage = "https://github.com/glejeune/ruby-leap-motion-ws"
|
14
14
|
s.license = "MIT"
|
15
15
|
|
16
16
|
s.files = `git ls-files`.split("\n")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module LEAP
|
2
|
+
class Motion
|
3
|
+
module Utils
|
4
|
+
class History < Array
|
5
|
+
attr_reader :max_size
|
6
|
+
|
7
|
+
def initialize(max_size, enum = nil)
|
8
|
+
@max_size = max_size
|
9
|
+
enum.each { |e| self << e } if enum
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(el)
|
13
|
+
if self.size < @max_size || @max_size.nil?
|
14
|
+
super
|
15
|
+
else
|
16
|
+
self.shift
|
17
|
+
self.push(el)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :push :<<
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/leap/motion/ws.rb
CHANGED
@@ -2,56 +2,154 @@ require 'rubygems'
|
|
2
2
|
require 'websocket-eventmachine-client'
|
3
3
|
require 'json'
|
4
4
|
require 'leap/motion/ws/frame'
|
5
|
+
require 'leap/motion/utils/history'
|
5
6
|
|
6
7
|
module LEAP
|
7
8
|
class Motion
|
9
|
+
# Public: Class to be subclassed to interface with the Leap Motion
|
10
|
+
#
|
11
|
+
# Example
|
12
|
+
#
|
13
|
+
# class MyLeap < LEAP::Motion::WS
|
14
|
+
# def initialize(...)
|
15
|
+
# #...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def on_frame(frame)
|
19
|
+
# # ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def on_connect
|
23
|
+
# # ...
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def on_disconnect
|
27
|
+
# # ...
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# def on_error(message)
|
31
|
+
# # ...
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# on-frame is the only mandatory function.
|
8
36
|
class WS
|
37
|
+
# Public: Initialize a LEAP::Motion::WS
|
38
|
+
#
|
39
|
+
# options - The Hash options used to initialize the Leap access (default: {:uri => "ws://127.0.0.1:6437", :enable_gesture => false, :history_size => 1000}):
|
40
|
+
# :uri - the String uri for the WebSocket::EventMachine::Client connection (optional)
|
41
|
+
# :enable_gesture - boolean to indicate if we want to enable gesture or not (optional)
|
42
|
+
# :history_size : the Integer size of the history ()
|
9
43
|
def initialize(options = {})
|
10
|
-
@options = {:uri => "ws://127.0.0.1:6437", :enable_gesture => false}.merge(options)
|
11
|
-
@
|
12
|
-
@ws = nil
|
44
|
+
@options = {:uri => "ws://127.0.0.1:6437", :enable_gesture => false, :history_size => 1000}.merge(options)
|
45
|
+
@history = LEAP::Motion::Utils::History.new(@options[:history_size])
|
13
46
|
end
|
14
47
|
|
48
|
+
# Public: Return true if gestures are enabled, false otherwise
|
15
49
|
def gestures?
|
16
|
-
|
50
|
+
options[:enable_gesture]
|
17
51
|
end
|
18
52
|
|
53
|
+
# Public: Return the Frame History
|
54
|
+
def history
|
55
|
+
@history ||= LEAP::Motion::Utils::History.new(options[:history_size])
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Enable gestures
|
59
|
+
#
|
60
|
+
# Return nothing.
|
19
61
|
def gesture!
|
20
62
|
unless @ws.nil?
|
21
63
|
data = JSON "enableGestures" => true
|
22
|
-
|
64
|
+
options[:enable_gesture] = ws.send data
|
23
65
|
end
|
24
66
|
end
|
25
67
|
|
68
|
+
# Public: Start the interface
|
69
|
+
#
|
70
|
+
# enable_gesture : boolean to indicate if we want to enable gesture or not (default: false)
|
71
|
+
#
|
72
|
+
# Examples
|
73
|
+
#
|
74
|
+
# class MyLeap < LEAP::Motion::WS
|
75
|
+
# ...
|
76
|
+
# end
|
77
|
+
# MyLeap.new.start
|
78
|
+
#
|
79
|
+
# Returns self
|
26
80
|
def start(enable_gesture = false)
|
27
81
|
EM.run do
|
28
|
-
|
29
|
-
|
30
|
-
@ws.onopen do
|
82
|
+
ws.onopen do
|
31
83
|
on_connect if respond_to? :on_connect
|
32
|
-
gesture! if enable_gesture or
|
84
|
+
gesture! if enable_gesture or options[:enable_gesture]
|
33
85
|
end
|
34
86
|
|
35
|
-
|
87
|
+
ws.onmessage do |msg, type|
|
36
88
|
message = JSON(msg)
|
37
89
|
if message.key?("id") and message.key?("timestamp")
|
38
|
-
|
90
|
+
frame = LEAP::Motion::WS::Frame.new(message)
|
91
|
+
history << frame
|
92
|
+
on_frame frame
|
39
93
|
end
|
40
94
|
end
|
41
95
|
|
42
|
-
|
96
|
+
ws.onerror do |err|
|
43
97
|
on_error(err) if respond_to? :on_error
|
44
98
|
end
|
45
99
|
|
46
|
-
|
100
|
+
ws.onclose do
|
47
101
|
on_disconnect if respond_to? :on_disconnect
|
48
102
|
end
|
49
103
|
end
|
104
|
+
|
105
|
+
return self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Public: Start the interface
|
109
|
+
#
|
110
|
+
# enable_gesture : boolean to indicate if we want to enable gesture or not (default: false)
|
111
|
+
#
|
112
|
+
# Examples
|
113
|
+
#
|
114
|
+
# class MyLeap < LEAP::Motion::WS
|
115
|
+
# ...
|
116
|
+
# end
|
117
|
+
# MyLeap.start
|
118
|
+
#
|
119
|
+
# Returns the LEAP::Motion::WS subclass object
|
120
|
+
def self.start(enable_gesture = false)
|
121
|
+
_leap = new
|
122
|
+
_leap.start(enable_gesture)
|
50
123
|
end
|
51
124
|
|
125
|
+
|
126
|
+
# Public: Stop the interface
|
127
|
+
#
|
128
|
+
# Examples
|
129
|
+
#
|
130
|
+
# class MyLeap < LEAP::Motion::WS
|
131
|
+
# ...
|
132
|
+
# end
|
133
|
+
# my_leap = MyLeap.start
|
134
|
+
# ...
|
135
|
+
# my_leap.stop
|
136
|
+
#
|
137
|
+
# Returns self
|
52
138
|
def stop
|
53
139
|
EM::stop_event_loop
|
54
140
|
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# private: Get the options hash
|
145
|
+
def options
|
146
|
+
@options ||= {:uri => "ws://127.0.0.1:6437", :enable_gesture => false, :history_size => 1000}
|
147
|
+
end
|
148
|
+
|
149
|
+
# Private: Get the WebSocket::EventMachine::Client connection
|
150
|
+
def ws
|
151
|
+
@ws ||= WebSocket::EventMachine::Client.connect(:uri => options[:uri])
|
152
|
+
end
|
55
153
|
end
|
56
154
|
end
|
57
155
|
end
|
@@ -1,24 +1,65 @@
|
|
1
1
|
module LEAP
|
2
2
|
class Motion
|
3
3
|
class WS
|
4
|
+
# Recognized movement by the user
|
5
|
+
#
|
6
|
+
# There is 4 Gesture subclasses :
|
7
|
+
#
|
8
|
+
# Circle - A circular movement by a finger.
|
9
|
+
# Swipe - A straight line movement by the hand with fingers extended.
|
10
|
+
# KeyTap - A downward tapping movement by a finger.
|
11
|
+
# ScreenTap - A forward tapping movement by a finger.
|
4
12
|
class Gesture
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
5
15
|
def self.list(data)
|
6
16
|
gestures = []
|
7
17
|
if data["gestures"]
|
8
18
|
data["gestures"].each do |gesture|
|
9
|
-
gestures <<
|
19
|
+
gestures << make_gesture(gesture)
|
10
20
|
end
|
11
21
|
end
|
12
22
|
return gestures
|
13
23
|
end
|
14
24
|
|
15
|
-
def
|
16
|
-
|
25
|
+
def self.make_gesture(data)
|
26
|
+
unless data.has_key? "type"
|
27
|
+
raise Error, "gesture type unknown"
|
28
|
+
end
|
29
|
+
name = data["type"][0].upcase << data["type"][1..-1]
|
30
|
+
unless class_exists?(name)
|
31
|
+
raise Error, "gesture class `#{self}::#{name}' invalid"
|
32
|
+
end
|
33
|
+
const_get(name).new(data)
|
17
34
|
end
|
18
35
|
|
19
|
-
|
20
|
-
|
36
|
+
private
|
37
|
+
|
38
|
+
def self.class_exists?(class_name)
|
39
|
+
klass = const_get(class_name)
|
40
|
+
return klass.is_a?(Class)
|
41
|
+
rescue NameError
|
42
|
+
return false
|
21
43
|
end
|
44
|
+
|
45
|
+
def self.define_gesture *names
|
46
|
+
names.each do |n|
|
47
|
+
name = n.to_s
|
48
|
+
unless class_exists?(name)
|
49
|
+
c = Class.new(self) do
|
50
|
+
def initialize(data)
|
51
|
+
data.each do |k, v|
|
52
|
+
instance_variable_set("@#{k}", v)
|
53
|
+
self.class.send(:define_method, k.to_sym, lambda { instance_variable_get("@#{k}") })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
const_set name, c
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
define_gesture :Circle, :KeyTap, :ScreenTap, :Swipe
|
22
63
|
end
|
23
64
|
end
|
24
65
|
end
|
data/lib/leap/motion/ws/hand.rb
CHANGED
@@ -13,11 +13,10 @@ module LEAP
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def initialize(data)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@data[mid.id2name]
|
16
|
+
data.each do |k, v|
|
17
|
+
instance_variable_set("@#{k}", v)
|
18
|
+
self.class.send(:define_method, k.to_sym, lambda { instance_variable_get("@#{k}") })
|
19
|
+
end
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -13,11 +13,10 @@ module LEAP
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def initialize(data)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@data[mid.id2name]
|
16
|
+
data.each do |k, v|
|
17
|
+
instance_variable_set("@#{k}", v)
|
18
|
+
self.class.send(:define_method, k.to_sym, lambda { instance_variable_get("@#{k}") })
|
19
|
+
end
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$:.unshift "../lib"
|
2
|
+
|
3
|
+
require 'leap-motion-ws'
|
4
|
+
|
5
|
+
class LeapTest < LEAP::Motion::WS
|
6
|
+
def on_connect
|
7
|
+
puts "Connect"
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_frame frame
|
11
|
+
puts "Frame ##{frame.id}, timestamp: #{frame.timestamp}, hands: #{frame.hands.size}, pointables: #{frame.pointables.size}" if frame.gestures.size > 0
|
12
|
+
frame.gestures.each do |gesture|
|
13
|
+
puts " -> ##{gesture.id} : #{gesture.type} / #{gesture.state}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_disconnect
|
18
|
+
puts "disconect"
|
19
|
+
stop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
leap = LeapTest.new(:enable_gesture => true)
|
24
|
+
|
25
|
+
Signal.trap("TERM") do
|
26
|
+
puts "Terminating..."
|
27
|
+
leap.stop
|
28
|
+
end
|
29
|
+
Signal.trap("KILL") do
|
30
|
+
puts "Terminating..."
|
31
|
+
leap.stop
|
32
|
+
end
|
33
|
+
Signal.trap("INT") do
|
34
|
+
puts "Terminating..."
|
35
|
+
leap.stop
|
36
|
+
end
|
37
|
+
|
38
|
+
leap.start
|
File without changes
|
data/test/helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
cur = Pathname.new(File.expand_path("..", __FILE__))
|
8
|
+
lib = cur.join('..', 'lib')
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(lib.to_s, cur.to_s)
|
11
|
+
require 'leap-motion-ws'
|
12
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHistory < Test::Unit::TestCase
|
4
|
+
context "a new history" do
|
5
|
+
setup do
|
6
|
+
@h = LEAP::Motion::Utils::History.new(3)
|
7
|
+
end
|
8
|
+
|
9
|
+
should "be empty with a max_size of 3" do
|
10
|
+
assert_equal 0, @h.size
|
11
|
+
assert_equal 3, @h.max_size
|
12
|
+
end
|
13
|
+
|
14
|
+
should "have 3 elements" do
|
15
|
+
@h << "one"
|
16
|
+
@h << "two"
|
17
|
+
@h << "three"
|
18
|
+
assert_equal 3, @h.size
|
19
|
+
assert_equal 3, @h.max_size
|
20
|
+
assert_equal true, @h.include?("one")
|
21
|
+
|
22
|
+
@h << "four"
|
23
|
+
assert_equal 3, @h.size
|
24
|
+
assert_equal 3, @h.max_size
|
25
|
+
assert_equal false, @h.include?("one")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: leap-motion-ws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregoire Lejeune
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-eventmachine-client
|
@@ -111,17 +111,19 @@ files:
|
|
111
111
|
- VERSION
|
112
112
|
- leap-motion-ws.gemspec
|
113
113
|
- lib/leap-motion-ws.rb
|
114
|
-
- lib/leap/motion/utils/
|
114
|
+
- lib/leap/motion/utils/history.rb
|
115
115
|
- lib/leap/motion/ws.rb
|
116
116
|
- lib/leap/motion/ws/frame.rb
|
117
117
|
- lib/leap/motion/ws/gesture.rb
|
118
118
|
- lib/leap/motion/ws/hand.rb
|
119
119
|
- lib/leap/motion/ws/pointable.rb
|
120
120
|
- samples/itunes_controller.rb
|
121
|
-
- samples/
|
122
|
-
- samples/
|
121
|
+
- samples/motion_gesture_sample.rb
|
122
|
+
- samples/motion_sample.rb
|
123
123
|
- samples/rock_paper_scissors.rb
|
124
|
-
|
124
|
+
- test/helper.rb
|
125
|
+
- test/test_history.rb
|
126
|
+
homepage: https://github.com/glejeune/ruby-leap-motion-ws
|
125
127
|
licenses:
|
126
128
|
- MIT
|
127
129
|
metadata: {}
|
@@ -141,8 +143,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
143
|
version: '0'
|
142
144
|
requirements: []
|
143
145
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.0.
|
146
|
+
rubygems_version: 2.0.0
|
145
147
|
signing_key:
|
146
148
|
specification_version: 4
|
147
149
|
summary: Ruby interface to the Leap Motion Controller (via WebSocket)
|
148
|
-
test_files:
|
150
|
+
test_files:
|
151
|
+
- test/helper.rb
|
152
|
+
- test/test_history.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class Ring < Array
|
2
|
-
attr_reader :max_size
|
3
|
-
|
4
|
-
def initialize(max_size, enum = nil)
|
5
|
-
@max_size = max_size
|
6
|
-
enum.each { |e| self << e } if enum
|
7
|
-
end
|
8
|
-
|
9
|
-
def <<(el)
|
10
|
-
if self.size < @max_size || @max_size.nil?
|
11
|
-
super
|
12
|
-
else
|
13
|
-
self.shift
|
14
|
-
self.push(el)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
alias :push :<<
|
19
|
-
end
|