• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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