1 // Copyright 2013 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "include/finger_merge_filter_interpreter.h"
6
7 #include <cmath>
8
9 #include "include/filter_interpreter.h"
10 #include "include/gestures.h"
11 #include "include/logging.h"
12 #include "include/prop_registry.h"
13 #include "include/tracer.h"
14 #include "include/util.h"
15
16 namespace gestures {
17
FingerMergeFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer)18 FingerMergeFilterInterpreter::FingerMergeFilterInterpreter(
19 PropRegistry* prop_reg, Interpreter* next, Tracer* tracer)
20 : FilterInterpreter(NULL, next, tracer, false),
21 finger_merge_filter_enable_(prop_reg,
22 "Finger Merge Filter Enabled", false),
23 merge_distance_threshold_(prop_reg,
24 "Finger Merge Distance Thresh", 140.0),
25 max_pressure_threshold_(prop_reg,
26 "Finger Merge Maximum Pressure", 83.0),
27 min_pressure_threshold_(prop_reg,
28 "Finger Merge Min Pressure", 51.0),
29 min_major_threshold_(prop_reg,
30 "Finger Merge Minimum Touch Major", 280.0),
31 merged_major_pressure_ratio_(prop_reg,
32 "Merged Finger Touch Major Pressure Ratio",
33 5.0),
34 merged_major_threshold_(prop_reg,
35 "Merged Finger Touch Major Thresh", 380.0),
36 x_jump_min_displacement_(prop_reg, "Merged Finger X Jump Min Disp", 6.0),
37 x_jump_max_displacement_(prop_reg, "Merged Finger X Jump Max Disp", 9.0),
38 suspicious_angle_min_displacement_(
39 prop_reg, "Merged Finger Suspicious Angle Min Displacement", 7.0),
40 max_x_move_(prop_reg, "Merged Finger Max X Move", 180.0),
41 max_y_move_(prop_reg, "Merged Finger Max Y Move", 60.0),
42 max_age_(prop_reg, "Merged Finger Max Age", 0.350) {
43 InitName();
44 }
45
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)46 void FingerMergeFilterInterpreter::SyncInterpretImpl(HardwareState* hwstate,
47 stime_t* timeout) {
48 if (finger_merge_filter_enable_.val_)
49 UpdateFingerMergeState(*hwstate);
50 next_->SyncInterpret(hwstate, timeout);
51 }
52
53 // Suspicious angle is between the 45 degree angle of going down and to the
54 // left and going straight to the left
IsSuspiciousAngle(const FingerState & fs) const55 bool FingerMergeFilterInterpreter::IsSuspiciousAngle(
56 const FingerState& fs) const {
57 // We consider initial contacts as suspicious:
58 if (!MapContainsKey(start_info_, fs.tracking_id))
59 return true;
60 const Start& start_info = start_info_.at(fs.tracking_id);
61 float dx = fs.position_x - start_info.position_x;
62 float dy = fs.position_y - start_info.position_y;
63 // All angles are suspicious at very low distance
64 if (dx * dx + dy * dy <
65 suspicious_angle_min_displacement_.val_ *
66 suspicious_angle_min_displacement_.val_)
67 return true;
68 if (dx > 0 || dy < 0)
69 return false;
70 return dy <= -1.0 * dx;
71 }
72
UpdateFingerMergeState(const HardwareState & hwstate)73 void FingerMergeFilterInterpreter::UpdateFingerMergeState(
74 const HardwareState& hwstate) {
75
76 RemoveMissingIdsFromMap(&start_info_, hwstate);
77 RemoveMissingIdsFromSet(&merge_tracking_ids_, hwstate);
78 RemoveMissingIdsFromSet(&never_merge_ids_, hwstate);
79 RemoveMissingIdsFromMap(&prev_x_displacement_, hwstate);
80 RemoveMissingIdsFromMap(&prev2_x_displacement_, hwstate);
81
82 // Append GESTURES_FINGER_MERGE flag for close fingers and
83 // fingers marked with the same flag previously
84 for (short i = 0; i < hwstate.finger_cnt; i++) {
85 FingerState *fs = hwstate.fingers;
86
87 // If it's a new contact, add the initial info
88 if (!MapContainsKey(start_info_, fs[i].tracking_id)) {
89 Start start_info = { fs[i].position_x,
90 fs[i].position_y,
91 hwstate.timestamp };
92 start_info_[fs[i].tracking_id] = start_info;
93 }
94
95 if (SetContainsValue(never_merge_ids_, fs[i].tracking_id))
96 continue;
97 if (SetContainsValue(merge_tracking_ids_, fs[i].tracking_id))
98 fs[i].flags |= GESTURES_FINGER_MERGE;
99 for (short j = i + 1; j < hwstate.finger_cnt; j++) {
100 float xfd = fabsf(fs[i].position_x - fs[j].position_x);
101 float yfd = fabsf(fs[i].position_y - fs[j].position_y);
102 if (xfd < merge_distance_threshold_.val_ &&
103 yfd < merge_distance_threshold_.val_) {
104 fs[i].flags |= GESTURES_FINGER_MERGE;
105 fs[j].flags |= GESTURES_FINGER_MERGE;
106 merge_tracking_ids_.insert(fs[i].tracking_id);
107 merge_tracking_ids_.insert(fs[j].tracking_id);
108 }
109 }
110 }
111
112 // Detect if there is a merged finger
113 for (short i = 0; i < hwstate.finger_cnt; i++) {
114 FingerState *fs = &hwstate.fingers[i];
115
116 if (SetContainsValue(never_merge_ids_, fs->tracking_id))
117 continue;
118
119 // If exceeded some thresholds, not a merged finger
120 const Start& start_info = start_info_[fs->tracking_id];
121 float dx = fs->position_x - start_info.position_x;
122 float dy = fs->position_y - start_info.position_y;
123 stime_t dt = hwstate.timestamp - start_info.start_time;
124 if (dx > max_x_move_.val_ || dy > max_y_move_.val_ || dt > max_age_.val_) {
125 merge_tracking_ids_.erase(fs->tracking_id);
126 never_merge_ids_.insert(fs->tracking_id);
127 fs->flags &= ~GESTURES_FINGER_MERGE;
128 }
129
130 if (MapContainsKey(prev2_x_displacement_, fs->tracking_id)) {
131 float displacement =
132 fabsf(fs->position_x - start_info_[fs->tracking_id].position_x);
133 float prev_disp = prev_x_displacement_[fs->tracking_id];
134 float prev2_disp = prev2_x_displacement_[fs->tracking_id];
135 if (prev2_disp <= x_jump_min_displacement_.val_ &&
136 prev_disp > x_jump_min_displacement_.val_ &&
137 displacement > prev_disp) {
138 if (displacement < x_jump_max_displacement_.val_) {
139 merge_tracking_ids_.erase(fs->tracking_id);
140 never_merge_ids_.insert(fs->tracking_id);
141 fs->flags &= ~GESTURES_FINGER_MERGE;
142 }
143 }
144 }
145
146 if (SetContainsValue(never_merge_ids_, fs->tracking_id))
147 continue;
148
149 // Basic criteria of a merged finger:
150 // - large touch major
151 // - small pressure value
152 // - good angle relative to start
153 if (fs->flags & GESTURES_FINGER_MERGE ||
154 fs->touch_major < min_major_threshold_.val_ ||
155 fs->pressure > max_pressure_threshold_.val_ ||
156 fs->pressure < min_pressure_threshold_.val_ ||
157 !IsSuspiciousAngle(*fs))
158 continue;
159
160
161 // Filter out most false positive cases
162 if (fs->touch_major > merged_major_pressure_ratio_.val_ * fs->pressure ||
163 fs->touch_major > merged_major_threshold_.val_) {
164 merge_tracking_ids_.insert(fs->tracking_id);
165 fs->flags |= GESTURES_FINGER_MERGE;
166 }
167 }
168
169 // Fill prev_x_displacement_
170 prev2_x_displacement_ = prev_x_displacement_;
171 for (size_t i = 0; i < hwstate.finger_cnt; i++) {
172 FingerState *fs = &hwstate.fingers[i];
173 prev_x_displacement_[fs->tracking_id] =
174 fabsf(start_info_[fs->tracking_id].position_x - fs->position_x);
175 }
176 }
177
178 }
179