fusuma-plugin-touchscreen 0.0.1.alpha → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +0 -0
- data/README.md +71 -2
- data/fusuma-plugin-touchscreen.gemspec +3 -2
- data/lib/fusuma/plugin/buffers/touch_buffer.rb +17 -81
- data/lib/fusuma/plugin/detectors/touch_detector.rb +31 -14
- data/lib/fusuma/plugin/detectors/touch_detectors/base.rb +7 -0
- data/lib/fusuma/plugin/detectors/touch_detectors/edge_detector.rb +0 -0
- data/lib/fusuma/plugin/detectors/touch_detectors/hold_detector.rb +11 -3
- data/lib/fusuma/plugin/detectors/touch_detectors/pinch_detector.rb +59 -1
- data/lib/fusuma/plugin/detectors/touch_detectors/rotate_detector.rb +65 -1
- data/lib/fusuma/plugin/detectors/touch_detectors/swipe_detector.rb +11 -9
- data/lib/fusuma/plugin/detectors/touch_detectors/tap_detector.rb +2 -2
- data/lib/fusuma/plugin/detectors/touch_detectors/tap_hold_base.rb +21 -0
- data/lib/fusuma/plugin/devices/touchscreen_device.rb +0 -0
- data/lib/fusuma/plugin/events/records/touch_record.rb +14 -1
- data/lib/fusuma/plugin/events/records/touch_records/base.rb +2 -0
- data/lib/fusuma/plugin/events/records/touch_records/features/direction.rb +36 -0
- data/lib/fusuma/plugin/events/records/touch_records/hold_record.rb +0 -0
- data/lib/fusuma/plugin/events/records/touch_records/pinch_record.rb +23 -0
- data/lib/fusuma/plugin/events/records/touch_records/rotate_record.rb +23 -0
- data/lib/fusuma/plugin/events/records/touch_records/swipe_record.rb +2 -12
- data/lib/fusuma/plugin/events/records/touch_records/tap_record.rb +0 -0
- data/lib/fusuma/plugin/parsers/touch_parser.rb +2 -47
- data/lib/fusuma/plugin/touchscreen/math.rb +68 -0
- data/lib/fusuma/plugin/touchscreen/version.rb +1 -1
- data/lib/fusuma/plugin/touchscreen.rb +0 -0
- data/spec/fusuma/plugin/buffers/touch_buffer_spec.rb +216 -0
- data/spec/fusuma/plugin/detectors/hold_detector_spec.rb +71 -0
- data/spec/fusuma/plugin/detectors/pinch_detector_spec.rb +96 -0
- data/spec/fusuma/plugin/detectors/rotate_detector_spec.rb +123 -0
- data/spec/fusuma/plugin/detectors/swipe_detector_spec.rb +141 -0
- data/spec/fusuma/plugin/detectors/tap_detector_spec.rb +84 -0
- data/spec/fusuma/plugin/detectors/touch_detector_spec.rb +266 -0
- data/spec/fusuma/plugin/devices/touchscreen_device_spec.rb +6 -3
- data/spec/fusuma/plugin/parsers/touch_parser_spec.rb +30 -5
- data/spec/samples/1-finger-hold.txt +0 -0
- data/spec/samples/2-fingers-rotate-clockwise.txt +426 -0
- data/spec/samples/2-fingers-rotate-counterclockwise.txt +459 -0
- data/spec/samples/2-fingers-swipe-right.txt +0 -0
- data/spec/samples/3-fingers-pinch-in.txt +140 -0
- data/spec/samples/3-fingers-pinch-out.txt +171 -0
- data/spec/samples/3-fingers-tap.txt +0 -0
- data/spec/{fixtures/libinput-list-devices_ms-surface-3-pro.txt → samples/libinput-list-devices.txt} +0 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/helpers/events_and_records.rb +43 -0
- data/spec/support/shared_contexts/with_touch_buffer.rb +10 -0
- data/spec/support/shared_examples/real_sample.rb +29 -0
- metadata +40 -10
- data/lib/fusuma/utils/angle.rb +0 -12
- data/spec/samples/libinput-devices.txt +0 -182
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34475baf5bb86b2a999b1d4184e07207f6cd9b978c9c593650cfb3198b7431c6
|
4
|
+
data.tar.gz: f83b7d67b09d75403f1aa1232ea24980d34faf6a09f1dfd38b5ea3f441f8d101
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d62633fd2feeacd1bcb880919b4665381d804bf0b80526ecb0b985488ad8be71fbbc6a41bf43ebf2c2b2d668c92856d529e9a99ef6a8e95dc2a9c6a2ede24e0
|
7
|
+
data.tar.gz: 8a411319e9ad71cdcf7d97c79c80d72a668c1858fa2c67bd8b0ae081733926157451a25da9a4805ab5d68e3c0d33c309b94ad25539e47267abdae12715632a43
|
data/LICENSE
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -1,2 +1,71 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# Fusuma::Plugin::Touchscreen
|
2
|
+
|
3
|
+
The Fusuma plugin for touchscreens.
|
4
|
+
|
5
|
+
## Fusuma
|
6
|
+
|
7
|
+
Fusuma is a multitouch gesture recognizer for Linux desktops.
|
8
|
+
You can read more about it on [the Fusuma GitHub page](https://github.com/iberianpig/fusuma).
|
9
|
+
|
10
|
+
This plugin adds support for touchscreens.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
First you need to [install Fusuma](https://github.com/iberianpig/fusuma#installation).
|
15
|
+
|
16
|
+
Then the plugin can be installed as [any other Fusuma plugin](https://github.com/iberianpig/fusuma#fusuma-plugins):
|
17
|
+
|
18
|
+
```bash
|
19
|
+
sudo gem install fusuma-plugin-touchscreen
|
20
|
+
```
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
If your Fusuma is already configured, you don't need to do anything else.
|
25
|
+
The plugin uses the very same configuration file / entries.
|
26
|
+
|
27
|
+
(So yes, your touchpad and your touchscreen will share the same gestures.)
|
28
|
+
|
29
|
+
Read more [how to configure Fusuma](https://github.com/iberianpig/fusuma#customize-gesture-mapping).
|
30
|
+
|
31
|
+
## Supported features
|
32
|
+
|
33
|
+
Tap, Hold, Swipe, Pinch, Rotate.
|
34
|
+
|
35
|
+
One or more fingers, as many as your device supports (through libinput).
|
36
|
+
|
37
|
+
begin / update / end events (for all but Tap of course).
|
38
|
+
|
39
|
+
## Known issues
|
40
|
+
|
41
|
+
As Fusuma itself, this plugin depends on the output of the `libinput debug-events`
|
42
|
+
command which may be unstable.
|
43
|
+
|
44
|
+
It is a plan to write some code to interact with libinput directly,
|
45
|
+
but that's a task for the future.
|
46
|
+
|
47
|
+
Threshold options are hard-coded and not configurable yet. That would be the next step.
|
48
|
+
|
49
|
+
## Possible issues
|
50
|
+
|
51
|
+
This plugin is tested on Microsoft Surface Pro 3 only.
|
52
|
+
If you can test it on other devices, please share your experience.
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
Bug reports and pull requests are welcome
|
57
|
+
on GitHub at https://github.com/Phaengris/fusuma-plugin-touchscreen
|
58
|
+
|
59
|
+
This project is intended to be a safe, welcoming space for collaboration,
|
60
|
+
and contributors are expected to adhere to
|
61
|
+
the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
62
|
+
|
63
|
+
## License
|
64
|
+
|
65
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
66
|
+
|
67
|
+
## Code of Conduct
|
68
|
+
|
69
|
+
Everyone interacting in the Fusuma::Plugin::Touchscreen project’s codebases,
|
70
|
+
issue trackers, chat rooms and mailing lists is expected to follow
|
71
|
+
the [code of conduct](https://github.com/iberianpig/fusuma-plugin-tap/blob/master/CODE_OF_CONDUCT.md).
|
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.required_ruby_version =
|
26
|
-
|
25
|
+
spec.required_ruby_version = ">= 2.5.1" # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all§ion=main
|
26
|
+
# support bionic (18.04LTS) 2.5.1
|
27
|
+
|
27
28
|
spec.add_dependency 'fusuma', '~> 2.0'
|
28
29
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'fusuma/plugin/buffers/buffer'
|
4
|
+
require 'fusuma/plugin/touchscreen/math'
|
5
|
+
|
3
6
|
module Fusuma
|
4
7
|
module Plugin
|
5
8
|
module Buffers
|
@@ -34,24 +37,12 @@ module Fusuma
|
|
34
37
|
end
|
35
38
|
|
36
39
|
def events
|
37
|
-
raise "Not supported, use finger_events_map instead"
|
40
|
+
raise NoMethodError, "Not supported, use finger_events_map instead"
|
38
41
|
end
|
39
42
|
|
40
43
|
def clear_expired(current_time: Time.now)
|
41
44
|
@seconds_to_keep ||= (config_params(:seconds_to_keep) || DEFAULT_SECONDS_TO_KEEP)
|
42
45
|
|
43
|
-
# @finger_events_map.each do |finger, events|
|
44
|
-
# next if events.empty?
|
45
|
-
#
|
46
|
-
# if events.last.record.status == "end"
|
47
|
-
# @finger_events_map.delete finger
|
48
|
-
# else
|
49
|
-
# @finger_events_map[finger].select! do |e|
|
50
|
-
# current_time - e.time < @seconds_to_keep
|
51
|
-
# end
|
52
|
-
# @finger_events_map.delete finger if @finger_events_map[finger].empty?
|
53
|
-
# end
|
54
|
-
# end
|
55
46
|
clear if ended?
|
56
47
|
@finger_events_map.each do |finger, events|
|
57
48
|
next if events.empty?
|
@@ -89,11 +80,13 @@ module Fusuma
|
|
89
80
|
|
90
81
|
def moved?
|
91
82
|
# TODO: a quicker way to do this?
|
92
|
-
@mem[:moved] ||=
|
83
|
+
@mem[:moved] ||= @finger_events_map.any? && @finger_events_map.keys.all? { |finger| finger_movements.key?(finger) }
|
93
84
|
end
|
94
85
|
|
95
86
|
def duration
|
96
|
-
@mem[:duration] ||= if
|
87
|
+
@mem[:duration] ||= if @finger_events_map.empty?
|
88
|
+
0
|
89
|
+
elsif ended?
|
97
90
|
end_time - begin_time
|
98
91
|
else
|
99
92
|
Time.now - begin_time
|
@@ -113,78 +106,21 @@ module Fusuma
|
|
113
106
|
position_events = events.select { |e| e.record.position? }
|
114
107
|
next if position_events.size < 2 # we need at least first and last position
|
115
108
|
|
116
|
-
first_position = position_events.first
|
117
|
-
last_position = position_events.last
|
118
|
-
|
119
|
-
|
120
|
-
when (first_position.record.x_mm - last_position.record.x_mm).abs < axis_threshold
|
121
|
-
orientation = :vertical
|
122
|
-
direction = (last_position.record.y_mm - first_position.record.y_mm) <=> 0
|
123
|
-
x_axis = (first_position.record.x_mm + last_position.record.x_mm) / 2.0
|
124
|
-
when (first_position.record.y_mm - last_position.record.y_mm).abs < axis_threshold
|
125
|
-
orientation = :horizontal
|
126
|
-
direction = (last_position.record.x_mm - first_position.record.x_mm) <=> 0
|
127
|
-
y_axis = (first_position.record.y_mm + last_position.record.y_mm) / 2.0
|
128
|
-
else
|
129
|
-
orientation = :diagonal
|
130
|
-
k = (first_position.record.y_mm - last_position.record.y_mm) / (first_position.record.x_mm - last_position.record.x_mm)
|
131
|
-
b = first_position.record.y_mm - k * first_position.record.x_mm
|
132
|
-
direction_x = (last_position.record.x_mm - first_position.record.x_mm) <=> 0
|
133
|
-
direction_y = (last_position.record.y_mm - first_position.record.y_mm) <=> 0
|
134
|
-
end
|
109
|
+
first_position = { x: position_events.first.record.x_mm, y: position_events.first.record.y_mm }
|
110
|
+
last_position = { x: position_events.last.record.x_mm, y: position_events.last.record.y_mm }
|
111
|
+
distance = Touchscreen::Math.distance(first_position, last_position)
|
112
|
+
next if distance < movement_threshold
|
135
113
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
delta_x = position.record.x_mm - prev_position.record.x_mm
|
140
|
-
delta_y = position.record.y_mm - prev_position.record.y_mm
|
141
|
-
|
142
|
-
jitter_x = delta_x.abs < jitter_threshold
|
143
|
-
jitter_y = delta_y.abs < jitter_threshold
|
144
|
-
next if jitter_x && jitter_y
|
145
|
-
|
146
|
-
case orientation
|
147
|
-
when :vertical
|
148
|
-
throw(:interrupted_movement) unless jitter_y || (delta_y <=> 0) == direction
|
149
|
-
throw(:interrupted_movement) unless (position.record.x_mm - x_axis).abs < jitter_threshold
|
150
|
-
when :horizontal
|
151
|
-
throw(:interrupted_movement) unless jitter_x || (delta_x <=> 0) == direction
|
152
|
-
throw(:interrupted_movement) unless (position.record.y_mm - y_axis).abs < jitter_threshold
|
153
|
-
else
|
154
|
-
throw(:interrupted_movement) unless jitter_x || (delta_x <=> 0) == direction_x
|
155
|
-
throw(:interrupted_movement) unless jitter_y || (delta_y <=> 0) == direction_y
|
156
|
-
throw(:interrupted_movement) unless (position.record.y_mm - k * position.record.x_mm - b).abs < jitter_threshold
|
157
|
-
end
|
158
|
-
prev_position = position
|
159
|
-
end
|
160
|
-
|
161
|
-
case orientation
|
162
|
-
when :vertical
|
163
|
-
angle = direction == 1 ? 90 : 270
|
164
|
-
distance = (last_position.record.y_mm - first_position.record.y_mm).abs
|
165
|
-
when :horizontal
|
166
|
-
angle = direction == 1 ? 0 : 180
|
167
|
-
distance = (last_position.record.x_mm - first_position.record.x_mm).abs
|
168
|
-
else
|
169
|
-
angle = (Math.atan(k) * 180 / Math::PI).round
|
170
|
-
angle += 360 if angle < 0
|
171
|
-
distance = Math.sqrt((last_position.record.x_mm - first_position.record.x_mm)**2 + (last_position.record.y_mm - first_position.record.y_mm)**2)
|
172
|
-
end
|
173
|
-
next if distance < jitter_threshold
|
174
|
-
|
175
|
-
[finger, { angle: angle, distance: distance }]
|
176
|
-
end
|
114
|
+
# TODO: check if there were trajectory changes in between?
|
115
|
+
|
116
|
+
[finger, { first_position: first_position, last_position: last_position }]
|
177
117
|
end.compact.to_h
|
178
118
|
end
|
179
119
|
|
180
120
|
private
|
181
121
|
|
182
|
-
def
|
183
|
-
|
184
|
-
end
|
185
|
-
|
186
|
-
def jitter_threshold
|
187
|
-
5.0 # TODO: configurable
|
122
|
+
def movement_threshold
|
123
|
+
0.5 # TODO: configurable
|
188
124
|
end
|
189
125
|
|
190
126
|
def reset_memoized
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'fusuma/plugin/detectors/detector'
|
4
|
+
|
3
5
|
require_relative './touch_detectors/tap_detector'
|
4
6
|
require_relative './touch_detectors/hold_detector'
|
5
7
|
require_relative './touch_detectors/swipe_detector'
|
@@ -16,13 +18,13 @@ module Fusuma
|
|
16
18
|
def initialize(*)
|
17
19
|
super
|
18
20
|
@detectors = [
|
19
|
-
Fusuma::Plugin::Detectors::TouchDetectors::TapDetector,
|
20
|
-
Fusuma::Plugin::Detectors::TouchDetectors::
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
Fusuma::Plugin::Detectors::TouchDetectors::TapDetector.new,
|
22
|
+
(@hold_detector = Fusuma::Plugin::Detectors::TouchDetectors::HoldDetector.new),
|
23
|
+
Fusuma::Plugin::Detectors::TouchDetectors::SwipeDetector.new,
|
24
|
+
Fusuma::Plugin::Detectors::TouchDetectors::PinchDetector.new,
|
25
|
+
# Fusuma::Plugin::Detectors::TouchDetectors::RotateDetector.new,
|
26
|
+
# Fusuma::Plugin::Detectors::TouchDetectors::EdgeDetector.new
|
27
|
+
]
|
26
28
|
@last_known_gesture = nil
|
27
29
|
end
|
28
30
|
|
@@ -51,10 +53,11 @@ module Fusuma
|
|
51
53
|
return events if touch_buffer.nil? || touch_buffer.empty?
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
# current gesture ended or even new gesture began
|
57
|
+
# if (@touch_buffer.ended? || @touch_buffer.began?) && @last_known_gesture
|
58
|
+
# events << create_event(record: @last_known_gesture.record.create_index_record(status: 'end', trigger: :repeat)) if @last_known_gesture.record.repeatable?
|
59
|
+
# @last_known_gesture = nil
|
60
|
+
# end
|
58
61
|
|
59
62
|
gesture_record = nil
|
60
63
|
if touch_buffer
|
@@ -68,16 +71,30 @@ module Fusuma
|
|
68
71
|
end
|
69
72
|
|
70
73
|
if gesture_record
|
71
|
-
|
74
|
+
# repeat previous gesture
|
72
75
|
if gesture_record.repeatable? && @last_known_gesture&.record == gesture_record
|
73
76
|
@last_known_gesture = create_event(record: gesture_record)
|
74
|
-
|
77
|
+
if touch_buffer.ended?
|
78
|
+
events << create_event(record: gesture_record.create_index_record(status: 'end', trigger: :repeat))
|
79
|
+
else
|
80
|
+
events << create_event(record: gesture_record.create_index_record(status: 'update', trigger: :repeat))
|
81
|
+
end
|
82
|
+
# new gesture (repeatable or not)
|
75
83
|
else
|
76
84
|
events << create_event(record: @last_known_gesture.record.create_index_record(status: 'end', trigger: :repeat)) if @last_known_gesture&.record&.repeatable?
|
77
85
|
@last_known_gesture = create_event(record: gesture_record)
|
78
86
|
events << create_event(record: gesture_record.create_index_record)
|
79
|
-
|
87
|
+
if gesture_record.repeatable?
|
88
|
+
events << create_event(record: gesture_record.create_index_record(status: 'begin', trigger: :repeat))
|
89
|
+
# already ended? (in this very same tick)
|
90
|
+
events << create_event(record: gesture_record.create_index_record(status: 'end', trigger: :repeat)) if touch_buffer.ended?
|
91
|
+
end
|
80
92
|
end
|
93
|
+
@touch_buffer.clear
|
94
|
+
# no new gesture, but may be the previous one ended?
|
95
|
+
elsif @touch_buffer&.ended? && @last_known_gesture
|
96
|
+
events << create_event(record: @last_known_gesture.record.create_index_record(status: 'end', trigger: :repeat)) if @last_known_gesture.record.repeatable?
|
97
|
+
@last_known_gesture = nil
|
81
98
|
end
|
82
99
|
|
83
100
|
events
|
@@ -3,6 +3,9 @@ module Fusuma
|
|
3
3
|
module Detectors
|
4
4
|
module TouchDetectors
|
5
5
|
class Base
|
6
|
+
def detect
|
7
|
+
raise NotImplementedError, "override #{self.class.name}##{__method__}"
|
8
|
+
end
|
6
9
|
|
7
10
|
protected
|
8
11
|
|
@@ -10,6 +13,10 @@ module Fusuma
|
|
10
13
|
0.5 # TODO: configurable
|
11
14
|
end
|
12
15
|
|
16
|
+
def jitter_threshold
|
17
|
+
5.0 # TODO: configurable
|
18
|
+
end
|
19
|
+
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
File without changes
|
@@ -1,16 +1,24 @@
|
|
1
|
-
require_relative './
|
1
|
+
require_relative './tap_hold_base'
|
2
2
|
|
3
3
|
module Fusuma
|
4
4
|
module Plugin
|
5
5
|
module Detectors
|
6
6
|
module TouchDetectors
|
7
|
-
class HoldDetector <
|
7
|
+
class HoldDetector < TapHoldBase
|
8
8
|
|
9
9
|
def detect(touch_buffer)
|
10
10
|
MultiLogger.debug('> hold detector')
|
11
11
|
|
12
12
|
MultiLogger.debug(' no movement?')
|
13
|
-
|
13
|
+
if touch_buffer.moved?
|
14
|
+
touch_buffer.finger_movements.each do |finger, movement|
|
15
|
+
distance = Touchscreen::Math.distance(movement[:first_position], movement[:last_position])
|
16
|
+
if distance > jitter_threshold
|
17
|
+
MultiLogger.debug(" finger #{finger} moved too much: #{distance} > #{jitter_threshold}")
|
18
|
+
return
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
14
22
|
|
15
23
|
MultiLogger.debug(" tap / hold threshold? (duration #{touch_buffer.duration})")
|
16
24
|
return unless touch_buffer.duration > tap_hold_threshold
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative './base'
|
2
|
+
require 'fusuma/plugin/touchscreen/math'
|
2
3
|
|
3
4
|
module Fusuma
|
4
5
|
module Plugin
|
@@ -6,7 +7,64 @@ module Fusuma
|
|
6
7
|
module TouchDetectors
|
7
8
|
class PinchDetector < Base
|
8
9
|
|
9
|
-
|
10
|
+
def detect(touch_buffer)
|
11
|
+
MultiLogger.debug('> pinch detector')
|
12
|
+
|
13
|
+
MultiLogger.debug(' movement?')
|
14
|
+
return unless touch_buffer.moved?
|
15
|
+
|
16
|
+
MultiLogger.debug(' at least 2 fingers?')
|
17
|
+
return unless touch_buffer.finger_movements.size >= 2
|
18
|
+
|
19
|
+
MultiLogger.debug(' distance change between first 2 fingers?')
|
20
|
+
first_finger, second_finger = touch_buffer.finger_movements.values[0..1]
|
21
|
+
begin_distance = Touchscreen::Math.distance(first_finger[:first_position], second_finger[:first_position])
|
22
|
+
end_distance = Touchscreen::Math.distance(first_finger[:last_position], second_finger[:last_position])
|
23
|
+
distance = end_distance - begin_distance
|
24
|
+
return unless distance.abs > jitter_threshold
|
25
|
+
|
26
|
+
direction = distance <=> 0
|
27
|
+
MultiLogger.debug(" assuming direction is #{direction}, testing all pairs of fingers")
|
28
|
+
is_pinch = catch(:not_a_pinch) do
|
29
|
+
pairs = touch_buffer.finger_movements.keys.combination(2).to_a
|
30
|
+
pairs.each do |finger1, finger2|
|
31
|
+
begin_distance = Touchscreen::Math.distance(
|
32
|
+
touch_buffer.finger_movements[finger1][:first_position],
|
33
|
+
touch_buffer.finger_movements[finger2][:first_position]
|
34
|
+
)
|
35
|
+
end_distance = Touchscreen::Math.distance(
|
36
|
+
touch_buffer.finger_movements[finger1][:last_position],
|
37
|
+
touch_buffer.finger_movements[finger2][:last_position]
|
38
|
+
)
|
39
|
+
MultiLogger.debug(" fingers #{finger1} and #{finger2} moved from #{begin_distance} to #{end_distance}")
|
40
|
+
this_distance = end_distance - begin_distance
|
41
|
+
if this_distance.abs < jitter_threshold
|
42
|
+
MultiLogger.debug(" !distance between fingers #{finger1} and #{finger2} is not changed enough (#{this_distance.abs}), not a pinch")
|
43
|
+
throw(:not_a_pinch)
|
44
|
+
end
|
45
|
+
this_direction = this_distance <=> 0
|
46
|
+
if this_direction != direction
|
47
|
+
MultiLogger.debug(" !fingers #{finger1} and #{finger2} moved in direction #{this_direction}, not a pinch")
|
48
|
+
throw(:not_a_pinch)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
true
|
52
|
+
end
|
53
|
+
return unless is_pinch
|
54
|
+
|
55
|
+
direction = direction == -1 ? :in : :out
|
56
|
+
MultiLogger.debug(" pinch #{direction} detected!")
|
57
|
+
|
58
|
+
Plugin::Events::Records::TouchRecords::PinchRecord.new(finger: touch_buffer.finger, direction: direction)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def jitter_threshold
|
64
|
+
5.0 # TODO: configurable
|
65
|
+
end
|
66
|
+
|
67
|
+
end # class PinchDetector
|
10
68
|
end
|
11
69
|
end
|
12
70
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative './base'
|
2
|
+
require 'fusuma/plugin/touchscreen/math'
|
2
3
|
|
3
4
|
module Fusuma
|
4
5
|
module Plugin
|
@@ -6,7 +7,70 @@ module Fusuma
|
|
6
7
|
module TouchDetectors
|
7
8
|
class RotateDetector < Base
|
8
9
|
|
9
|
-
|
10
|
+
def detect(touch_buffer)
|
11
|
+
MultiLogger.debug('> rotate detector')
|
12
|
+
|
13
|
+
MultiLogger.debug(' movement?')
|
14
|
+
return unless touch_buffer.moved?
|
15
|
+
|
16
|
+
MultiLogger.debug(' at least 2 fingers?')
|
17
|
+
return unless touch_buffer.finger_movements.size >= 2
|
18
|
+
|
19
|
+
# MultiLogger.debug(' center is static?')
|
20
|
+
# begin_center = Touchscreen::Math.center(touch_buffer.finger_movements.map { |_, v| v[:first_position] })
|
21
|
+
# end_center = Touchscreen::Math.center(touch_buffer.finger_movements.map { |_, v| v[:last_position] })
|
22
|
+
# return unless Touchscreen::Math.distance(begin_center, end_center) <= center_jitter_threshold
|
23
|
+
|
24
|
+
MultiLogger.debug(' angle change for the first finger?')
|
25
|
+
center = Touchscreen::Math.center(touch_buffer.finger_movements.map { |_, v| v[:first_position] })
|
26
|
+
first_finger = touch_buffer.finger_movements.values.first
|
27
|
+
begin_angle = Touchscreen::Math.angle_between(center, first_finger[:first_position])
|
28
|
+
end_angle = Touchscreen::Math.angle_between(center, first_finger[:last_position])
|
29
|
+
angle_change = Touchscreen::Math.angles_difference(end_angle, begin_angle)
|
30
|
+
MultiLogger.debug(" (#{angle_change})")
|
31
|
+
return unless angle_change.abs > angle_threshold
|
32
|
+
|
33
|
+
direction = angle_change <=> 0
|
34
|
+
MultiLogger.debug(" assuming direction is #{direction} (from #{begin_angle} to #{end_angle}), testing all fingers")
|
35
|
+
|
36
|
+
is_rotate = catch(:not_a_rotate) do
|
37
|
+
touch_buffer.finger_movements.except(0).each do |finger, movement|
|
38
|
+
begin_angle = Touchscreen::Math.angle_between(center, movement[:first_position])
|
39
|
+
end_angle = Touchscreen::Math.angle_between(center, movement[:last_position])
|
40
|
+
this_angle_change = Touchscreen::Math.angles_difference(end_angle, begin_angle)
|
41
|
+
if this_angle_change.abs < angle_threshold
|
42
|
+
MultiLogger.debug(" !angle for finger #{finger} is not changed enough (#{this_angle_change.abs}), not a rotate")
|
43
|
+
throw(:not_a_rotate)
|
44
|
+
end
|
45
|
+
this_direction = this_angle_change <=> 0
|
46
|
+
if this_direction != direction
|
47
|
+
MultiLogger.debug(" !finger #{finger} moved in direction #{this_direction} (from #{begin_angle} to #{end_angle}, not a rotate")
|
48
|
+
throw(:not_a_rotate)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
true
|
52
|
+
end
|
53
|
+
return unless is_rotate
|
54
|
+
|
55
|
+
direction = direction == -1 ? :clockwise : :counterclockwise
|
56
|
+
MultiLogger.debug(" rotate #{direction} detected!")
|
57
|
+
|
58
|
+
Plugin::Events::Records::TouchRecords::RotateRecord.new(finger: touch_buffer.finger, direction: direction)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def angle_threshold
|
64
|
+
# such low value is because of how a human usually rotates fingers
|
65
|
+
# it is barely possible to draw a geometrically perfect circle
|
66
|
+
# in reality one finger can move very little, and another can move a lot
|
67
|
+
# so for the "base" finger the angle change will be very small, and for the "moving" finger it will be big
|
68
|
+
# if we increase the threshold, we will get a lot of false negatives
|
69
|
+
# which will make continuous rotate gestures very bulky
|
70
|
+
0.1 # TODO: make configurable
|
71
|
+
end
|
72
|
+
|
73
|
+
end # class RotateDetector
|
10
74
|
end
|
11
75
|
end
|
12
76
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require_relative './base'
|
2
|
-
|
2
|
+
require 'fusuma/plugin/touchscreen/math'
|
3
3
|
|
4
4
|
module Fusuma
|
5
5
|
module Plugin
|
6
6
|
module Detectors
|
7
7
|
module TouchDetectors
|
8
|
-
class SwipeDetector
|
8
|
+
class SwipeDetector < Base
|
9
9
|
|
10
10
|
def detect(touch_buffer)
|
11
11
|
MultiLogger.debug('> swipe detector')
|
@@ -14,24 +14,26 @@ module Fusuma
|
|
14
14
|
return unless touch_buffer.moved?
|
15
15
|
|
16
16
|
MultiLogger.debug(' angles?')
|
17
|
-
angles = touch_buffer.finger_movements.map
|
17
|
+
angles = touch_buffer.finger_movements.map do |_, movement|
|
18
|
+
Touchscreen::Math.angle_between(movement[:first_position], movement[:last_position])
|
19
|
+
end
|
18
20
|
angles.combination(2).each do |angle1, angle2|
|
19
|
-
if
|
21
|
+
if Touchscreen::Math.angles_difference(angle1, angle2).abs > movement_angle_threshold
|
20
22
|
MultiLogger.debug(" !too much difference between #{angle1} and #{angle2}, not a swipe")
|
21
23
|
return
|
22
24
|
end
|
23
25
|
end
|
24
|
-
angle =
|
26
|
+
angle = Touchscreen::Math.angles_average(angles)
|
25
27
|
|
26
28
|
MultiLogger.debug(' direction?')
|
27
29
|
case angle
|
28
|
-
when 0
|
30
|
+
when 0..(direction_angle_width / 2), (360 - (direction_angle_width / 2))..360
|
29
31
|
direction = :right
|
30
|
-
when (90 - (direction_angle_width / 2))
|
32
|
+
when (90 - (direction_angle_width / 2))..(90 + (direction_angle_width / 2))
|
31
33
|
direction = :down
|
32
|
-
when (180 - (direction_angle_width / 2))
|
34
|
+
when (180 - (direction_angle_width / 2))..(180 + (direction_angle_width / 2))
|
33
35
|
direction = :left
|
34
|
-
when (270 - (direction_angle_width / 2))
|
36
|
+
when (270 - (direction_angle_width / 2))..(270 + (direction_angle_width / 2))
|
35
37
|
direction = :up
|
36
38
|
else
|
37
39
|
MultiLogger.debug(" !gesture angle of #{angle} does not fall into any direction")
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative './
|
1
|
+
require_relative './tap_hold_base'
|
2
2
|
|
3
3
|
module Fusuma
|
4
4
|
module Plugin
|
5
5
|
module Detectors
|
6
6
|
module TouchDetectors
|
7
|
-
class TapDetector <
|
7
|
+
class TapDetector < TapHoldBase
|
8
8
|
|
9
9
|
def detect(touch_buffer)
|
10
10
|
MultiLogger.debug('> tap detector')
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fusuma
|
2
|
+
module Plugin
|
3
|
+
module Detectors
|
4
|
+
module TouchDetectors
|
5
|
+
class TapHoldBase
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def tap_hold_threshold
|
10
|
+
0.5 # TODO: configurable
|
11
|
+
end
|
12
|
+
|
13
|
+
def jitter_threshold
|
14
|
+
5.0 # TODO: configurable
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class TapHoldBase
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'fusuma/plugin/events/records/record'
|
2
2
|
|
3
3
|
require_relative './touch_records/tap_record'
|
4
4
|
require_relative './touch_records/hold_record'
|
@@ -31,6 +31,19 @@ module Fusuma
|
|
31
31
|
def position?
|
32
32
|
@x_px || @y_px
|
33
33
|
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
s = "#{self.class} status: #{status} finger: #{finger} time offset: #{time_offset}"
|
37
|
+
s << " x_px: #{x_px}" if x_px
|
38
|
+
s << " y_px: #{y_px}" if y_px
|
39
|
+
s << " x_mm: #{x_mm}" if x_mm
|
40
|
+
s << " y_mm: #{y_mm}" if y_mm
|
41
|
+
s
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
inspect
|
46
|
+
end
|
34
47
|
end
|
35
48
|
end
|
36
49
|
end
|