xref: /aosp_15_r20/external/libchrome-gestures/src/finger_merge_filter_interpreter.cc (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
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