fusuma-plugin-touchscreen 0.0.1.alpha → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|