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(nullptr, 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 const char name[] = "FingerMergeFilterInterpreter::SyncInterpretImpl";
49 LogHardwareStatePre(name, hwstate);
50
51 if (finger_merge_filter_enable_.val_)
52 UpdateFingerMergeState(hwstate);
53
54 LogHardwareStatePost(name, hwstate);
55 next_->SyncInterpret(hwstate, timeout);
56 }
57
58 // Suspicious angle is between the 45 degree angle of going down and to the
59 // left and going straight to the left
IsSuspiciousAngle(const FingerState & fs) const60 bool FingerMergeFilterInterpreter::IsSuspiciousAngle(
61 const FingerState& fs) const {
62 // We consider initial contacts as suspicious:
63 if (!MapContainsKey(start_info_, fs.tracking_id))
64 return true;
65 const Start& start_info = start_info_.at(fs.tracking_id);
66 float dx = fs.position_x - start_info.position_x;
67 float dy = fs.position_y - start_info.position_y;
68 // All angles are suspicious at very low distance
69 if (dx * dx + dy * dy <
70 suspicious_angle_min_displacement_.val_ *
71 suspicious_angle_min_displacement_.val_)
72 return true;
73 if (dx > 0 || dy < 0)
74 return false;
75 return dy <= -1.0 * dx;
76 }
77
UpdateFingerMergeState(const HardwareState & hwstate)78 void FingerMergeFilterInterpreter::UpdateFingerMergeState(
79 const HardwareState& hwstate) {
80
81 RemoveMissingIdsFromMap(&start_info_, hwstate);
82 RemoveMissingIdsFromSet(&merge_tracking_ids_, hwstate);
83 RemoveMissingIdsFromSet(&never_merge_ids_, hwstate);
84 RemoveMissingIdsFromMap(&prev_x_displacement_, hwstate);
85 RemoveMissingIdsFromMap(&prev2_x_displacement_, hwstate);
86
87 // Append GESTURES_FINGER_MERGE flag for close fingers and
88 // fingers marked with the same flag previously
89 for (short i = 0; i < hwstate.finger_cnt; i++) {
90 FingerState *fs = hwstate.fingers;
91
92 // If it's a new contact, add the initial info
93 if (!MapContainsKey(start_info_, fs[i].tracking_id)) {
94 Start start_info = { fs[i].position_x,
95 fs[i].position_y,
96 hwstate.timestamp };
97 start_info_[fs[i].tracking_id] = start_info;
98 }
99
100 if (SetContainsValue(never_merge_ids_, fs[i].tracking_id))
101 continue;
102 if (SetContainsValue(merge_tracking_ids_, fs[i].tracking_id))
103 fs[i].flags |= GESTURES_FINGER_MERGE;
104 for (short j = i + 1; j < hwstate.finger_cnt; j++) {
105 float xfd = fabsf(fs[i].position_x - fs[j].position_x);
106 float yfd = fabsf(fs[i].position_y - fs[j].position_y);
107 if (xfd < merge_distance_threshold_.val_ &&
108 yfd < merge_distance_threshold_.val_) {
109 fs[i].flags |= GESTURES_FINGER_MERGE;
110 fs[j].flags |= GESTURES_FINGER_MERGE;
111 merge_tracking_ids_.insert(fs[i].tracking_id);
112 merge_tracking_ids_.insert(fs[j].tracking_id);
113 }
114 }
115 }
116
117 // Detect if there is a merged finger
118 for (short i = 0; i < hwstate.finger_cnt; i++) {
119 FingerState *fs = &hwstate.fingers[i];
120
121 if (SetContainsValue(never_merge_ids_, fs->tracking_id))
122 continue;
123
124 // If exceeded some thresholds, not a merged finger
125 const Start& start_info = start_info_[fs->tracking_id];
126 float dx = fs->position_x - start_info.position_x;
127 float dy = fs->position_y - start_info.position_y;
128 stime_t dt = hwstate.timestamp - start_info.start_time;
129 if (dx > max_x_move_.val_ || dy > max_y_move_.val_ || dt > max_age_.val_) {
130 merge_tracking_ids_.erase(fs->tracking_id);
131 never_merge_ids_.insert(fs->tracking_id);
132 fs->flags &= ~GESTURES_FINGER_MERGE;
133 }
134
135 if (MapContainsKey(prev2_x_displacement_, fs->tracking_id)) {
136 float displacement =
137 fabsf(fs->position_x - start_info_[fs->tracking_id].position_x);
138 float prev_disp = prev_x_displacement_[fs->tracking_id];
139 float prev2_disp = prev2_x_displacement_[fs->tracking_id];
140 if (prev2_disp <= x_jump_min_displacement_.val_ &&
141 prev_disp > x_jump_min_displacement_.val_ &&
142 displacement > prev_disp) {
143 if (displacement < x_jump_max_displacement_.val_) {
144 merge_tracking_ids_.erase(fs->tracking_id);
145 never_merge_ids_.insert(fs->tracking_id);
146 fs->flags &= ~GESTURES_FINGER_MERGE;
147 }
148 }
149 }
150
151 if (SetContainsValue(never_merge_ids_, fs->tracking_id))
152 continue;
153
154 // Basic criteria of a merged finger:
155 // - large touch major
156 // - small pressure value
157 // - good angle relative to start
158 if (fs->flags & GESTURES_FINGER_MERGE ||
159 fs->touch_major < min_major_threshold_.val_ ||
160 fs->pressure > max_pressure_threshold_.val_ ||
161 fs->pressure < min_pressure_threshold_.val_ ||
162 !IsSuspiciousAngle(*fs))
163 continue;
164
165
166 // Filter out most false positive cases
167 if (fs->touch_major > merged_major_pressure_ratio_.val_ * fs->pressure ||
168 fs->touch_major > merged_major_threshold_.val_) {
169 merge_tracking_ids_.insert(fs->tracking_id);
170 fs->flags |= GESTURES_FINGER_MERGE;
171 }
172 }
173
174 // Fill prev_x_displacement_
175 prev2_x_displacement_ = prev_x_displacement_;
176 for (size_t i = 0; i < hwstate.finger_cnt; i++) {
177 FingerState *fs = &hwstate.fingers[i];
178 prev_x_displacement_[fs->tracking_id] =
179 fabsf(start_info_[fs->tracking_id].position_x - fs->position_x);
180 }
181 }
182
183 }
184