• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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/palm_classifying_filter_interpreter.h"
6 
7 #include "include/gestures.h"
8 #include "include/interpreter.h"
9 #include "include/tracer.h"
10 #include "include/util.h"
11 
12 namespace gestures {
13 
PalmClassifyingFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer)14 PalmClassifyingFilterInterpreter::PalmClassifyingFilterInterpreter(
15     PropRegistry* prop_reg, Interpreter* next,
16     Tracer* tracer)
17     : FilterInterpreter(NULL, next, tracer, false),
18       palm_pressure_(prop_reg, "Palm Pressure", 200.0),
19       palm_width_(prop_reg, "Palm Width", 21.2),
20       multi_palm_width_(prop_reg, "Multiple Palm Width", 75.0),
21       fat_finger_pressure_ratio_(prop_reg, "Fat Finger Pressure Ratio", 1.4),
22       fat_finger_width_ratio_(prop_reg, "Fat Finger Width Ratio", 1.3),
23       fat_finger_min_dist_(prop_reg, "Fat Finger Min Move Distance", 15.0),
24       palm_edge_min_width_(prop_reg, "Tap Exclusion Border Width", 8.0),
25       palm_edge_width_(prop_reg, "Palm Edge Zone Width", 14.0),
26       palm_top_edge_min_width_(prop_reg, "Top Edge Tap Exclusion Border Width",
27                                3.0),
28       palm_edge_point_speed_(prop_reg, "Palm Edge Zone Min Point Speed", 100.0),
29       palm_eval_timeout_(prop_reg, "Palm Eval Timeout", 0.1),
30       palm_stationary_time_(prop_reg, "Palm Stationary Time", 2.0),
31       palm_stationary_distance_(prop_reg, "Palm Stationary Distance", 4.0),
32       palm_pointing_min_dist_(prop_reg,
33                               "Palm Pointing Min Move Distance",
34                               8.0),
35       palm_pointing_max_reverse_dist_(prop_reg,
36                                       "Palm Pointing Max Reverse Move Distance",
37                                       0.3),
38       palm_split_max_distance_(prop_reg, "Palm Split Maximum Distance", 4.0),
39       filter_top_edge_(prop_reg, "Palm Filter Top Edge Enable", false)
40 {
41   InitName();
42   requires_metrics_ = true;
43 }
44 
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)45 void PalmClassifyingFilterInterpreter::SyncInterpretImpl(
46     HardwareState* hwstate,
47     stime_t* timeout) {
48   FillOriginInfo(*hwstate);
49   FillMaxPressureWidthInfo(*hwstate);
50   UpdateDistanceInfo(*hwstate);
51   UpdatePalmState(*hwstate);
52   UpdatePalmFlags(hwstate);
53   FillPrevInfo(*hwstate);
54   if (next_.get())
55     next_->SyncInterpret(hwstate, timeout);
56 }
57 
FillOriginInfo(const HardwareState & hwstate)58 void PalmClassifyingFilterInterpreter::FillOriginInfo(
59     const HardwareState& hwstate) {
60   RemoveMissingIdsFromMap(&origin_timestamps_, hwstate);
61   RemoveMissingIdsFromMap(&origin_fingerstates_, hwstate);
62   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
63     const FingerState& fs = hwstate.fingers[i];
64     if (MapContainsKey(origin_timestamps_, fs.tracking_id))
65       continue;
66     origin_timestamps_[fs.tracking_id] = hwstate.timestamp;
67     origin_fingerstates_[fs.tracking_id] = fs;
68   }
69 }
70 
FillPrevInfo(const HardwareState & hwstate)71 void PalmClassifyingFilterInterpreter::FillPrevInfo(
72     const HardwareState& hwstate) {
73   RemoveMissingIdsFromMap(&prev_fingerstates_, hwstate);
74   prev_time_ = hwstate.timestamp;
75   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
76     const FingerState& fs = hwstate.fingers[i];
77     prev_fingerstates_[fs.tracking_id] = fs;
78   }
79 }
80 
FillMaxPressureWidthInfo(const HardwareState & hwstate)81 void PalmClassifyingFilterInterpreter::FillMaxPressureWidthInfo(
82     const HardwareState& hwstate) {
83   RemoveMissingIdsFromMap(&max_pressure_, hwstate);
84   RemoveMissingIdsFromMap(&max_width_, hwstate);
85   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
86     const FingerState& fs = hwstate.fingers[i];
87     int id = fs.tracking_id;
88     if (MapContainsKey(max_pressure_, id)) {
89       if (fs.pressure > max_pressure_[id])
90         max_pressure_[id] = fs.pressure;
91       if (fs.touch_major > max_width_[id])
92         max_width_[id] = fs.touch_major;
93     } else {
94       max_pressure_[id] = fs.pressure;
95       max_width_[id] = fs.touch_major;
96     }
97   }
98 }
99 
UpdateDistanceInfo(const HardwareState & hwstate)100 void PalmClassifyingFilterInterpreter::UpdateDistanceInfo(
101     const HardwareState& hwstate) {
102   RemoveMissingIdsFromMap(&distance_positive_[0], hwstate);
103   RemoveMissingIdsFromMap(&distance_positive_[1], hwstate);
104   RemoveMissingIdsFromMap(&distance_negative_[0], hwstate);
105   RemoveMissingIdsFromMap(&distance_negative_[1], hwstate);
106   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
107     const FingerState& fs = hwstate.fingers[i];
108     int id = fs.tracking_id;
109     if (MapContainsKey(prev_fingerstates_, id)) {
110       float delta[2];
111       delta[0] = fs.position_x - prev_fingerstates_[id].position_x;
112       delta[1] = fs.position_y - prev_fingerstates_[id].position_y;
113       for (int i = 0; i < 2; i++) {
114         if (delta[i] > 0)
115           distance_positive_[i][id] += delta[i];
116         else
117           distance_negative_[i][id] -= delta[i];
118       }
119     } else {
120       distance_positive_[0][id] = 0;
121       distance_positive_[1][id] = 0;
122       distance_negative_[0][id] = 0;
123       distance_negative_[1][id] = 0;
124     }
125   }
126 }
127 
FingerNearOtherFinger(const HardwareState & hwstate,size_t finger_idx)128 bool PalmClassifyingFilterInterpreter::FingerNearOtherFinger(
129     const HardwareState& hwstate,
130     size_t finger_idx) {
131   const FingerState& fs = hwstate.fingers[finger_idx];
132   for (int i = 0; i < hwstate.finger_cnt; ++i) {
133     const FingerState& other_fs = hwstate.fingers[i];
134     if (other_fs.tracking_id == fs.tracking_id)
135       continue;
136     bool close_enough_together =
137         metrics_->CloseEnoughToGesture(Vector2(fs), Vector2(other_fs)) &&
138         !SetContainsValue(palm_, other_fs.tracking_id);
139     bool too_close_together = DistSq(fs, other_fs) <
140         palm_split_max_distance_.val_ * palm_split_max_distance_.val_;
141     if (close_enough_together && !too_close_together) {
142       was_near_other_fingers_.insert(fs.tracking_id);
143       return true;
144     }
145   }
146   return false;
147 }
148 
FingerInPalmEnvelope(const FingerState & fs)149 bool PalmClassifyingFilterInterpreter::FingerInPalmEnvelope(
150     const FingerState& fs) {
151   float limit = palm_edge_min_width_.val_ +
152       (fs.pressure / palm_pressure_.val_) *
153       (palm_edge_width_.val_ - palm_edge_min_width_.val_);
154   return fs.position_x < limit ||
155       fs.position_x > (hwprops_->right - limit) ||
156       (filter_top_edge_.val_ && fs.position_y < palm_top_edge_min_width_.val_);
157 }
158 
FingerInBottomArea(const FingerState & fs)159 bool PalmClassifyingFilterInterpreter::FingerInBottomArea(
160     const FingerState& fs) {
161   return fs.position_y > (hwprops_->bottom - palm_edge_min_width_.val_);
162 }
163 
UpdatePalmState(const HardwareState & hwstate)164 void PalmClassifyingFilterInterpreter::UpdatePalmState(
165     const HardwareState& hwstate) {
166   RemoveMissingIdsFromSet(&palm_, hwstate);
167   RemoveMissingIdsFromSet(&large_palm_, hwstate);
168   RemoveMissingIdsFromMap(&pointing_, hwstate);
169   RemoveMissingIdsFromSet(&non_stationary_palm_, hwstate);
170   RemoveMissingIdsFromSet(&fingers_not_in_edge_, hwstate);
171   RemoveMissingIdsFromSet(&was_near_other_fingers_, hwstate);
172 
173   // Some finger(s) just leaves, skip this update for stability
174   if (prev_fingerstates_.size() > hwstate.finger_cnt)
175     return;
176 
177   for (short i = 0; i < hwstate.finger_cnt; i++) {
178     const FingerState& fs = hwstate.fingers[i];
179     if (!(FingerInPalmEnvelope(fs) || FingerInBottomArea(fs)))
180       fingers_not_in_edge_.insert(fs.tracking_id);
181     // Mark anything over the palm thresh as a palm
182     if (fs.pressure >= palm_pressure_.val_ ||
183         fs.touch_major >= multi_palm_width_.val_) {
184       large_palm_.insert(fs.tracking_id);
185       palm_.insert(fs.tracking_id);
186       pointing_.erase(fs.tracking_id);
187       continue;
188     }
189   }
190 
191   if (hwstate.finger_cnt == 1 &&
192       hwstate.fingers[0].touch_major >= palm_width_.val_) {
193     large_palm_.insert(hwstate.fingers[0].tracking_id);
194     palm_.insert(hwstate.fingers[0].tracking_id);
195     pointing_.erase(hwstate.fingers[0].tracking_id);
196   }
197 
198   const float kPalmStationaryDistSq =
199       palm_stationary_distance_.val_ * palm_stationary_distance_.val_;
200   const float kFatFingerMinDistSq =
201       fat_finger_min_dist_.val_ * fat_finger_min_dist_.val_;
202   const float kFatFingerMaxPressure =
203       palm_pressure_.val_ * fat_finger_pressure_ratio_.val_;
204   const float kFatFingerMaxWidth =
205       palm_width_.val_ * fat_finger_width_ratio_.val_;
206 
207   for (short i = 0; i < hwstate.finger_cnt; i++) {
208     const FingerState& fs = hwstate.fingers[i];
209     bool prev_palm = SetContainsValue(palm_, fs.tracking_id);
210     bool prev_pointing = MapContainsKey(pointing_, fs.tracking_id);
211 
212     if (prev_palm) {
213       // If the finger's pressure & width are more like a fat finger
214       // and it has moved a lot, it might be a fat finger and remove
215       // it from palm.
216       float dist_sq = DistSq(origin_fingerstates_[fs.tracking_id], fs);
217       if (max_pressure_[fs.tracking_id] <= kFatFingerMaxPressure &&
218           max_width_[fs.tracking_id] <= kFatFingerMaxWidth &&
219           dist_sq > kFatFingerMinDistSq) {
220         large_palm_.erase(fs.tracking_id);
221         palm_.erase(fs.tracking_id);
222       } else {
223         // Lock onto palm
224         continue;
225       }
226     }
227 
228     // If the finger is recently placed, remove it from pointing/fingers.
229     // If it's still looking like pointing, it'll get readded.
230     if (FingerAge(fs.tracking_id, hwstate.timestamp) <
231         palm_eval_timeout_.val_) {
232       pointing_.erase(fs.tracking_id);
233 
234       prev_pointing = false;
235     }
236     // If another finger is close by, let this be pointing
237     bool near_finger = FingerNearOtherFinger(hwstate, i);
238     bool on_edge = FingerInPalmEnvelope(fs) ||
239         FingerInBottomArea(fs);
240     if (!prev_pointing && (near_finger || !on_edge)) {
241       unsigned reason = (near_finger ? kPointCloseToFinger : 0) |
242           ((!on_edge) ? kPointNotInEdge : 0);
243       pointing_[fs.tracking_id] = reason;
244     }
245 
246     // Check if fingers that only move within palm envelope are pointing.
247     int id = fs.tracking_id;
248     float min_dist = palm_pointing_min_dist_.val_;
249     float max_reverse_dist = palm_pointing_max_reverse_dist_.val_;
250 
251     // Ideally, we want to say that a finger is pointing if it moves only in
252     // one direction significantly without zig-zag. But due to touch sensor's
253     // inaccuratcy, we make the rule to be that a finger has to move in one
254     // direction significantly with little move in the opposite direction.
255     for (size_t j = 0; j < arraysize(distance_positive_); j++)
256       if ((distance_positive_[j][id] >= min_dist &&
257            distance_negative_[j][id] <= max_reverse_dist) ||
258           (distance_positive_[j][id] <= max_reverse_dist &&
259            distance_negative_[j][id] >= min_dist)) {
260         pointing_[id] |= kPointMoving;
261       }
262 
263     // However, if the contact has been stationary for a while since it
264     // touched down, it is a palm. We track a potential palm closely for the
265     // first amount of time to see if it fits this pattern.
266     if (FingerAge(fs.tracking_id, prev_time_) >
267         palm_stationary_time_.val_ ||
268         SetContainsValue(non_stationary_palm_, fs.tracking_id)) {
269       // Finger is too old to reconsider or is moving a lot
270       continue;
271     }
272     if (DistSq(origin_fingerstates_[fs.tracking_id], fs) >
273         kPalmStationaryDistSq || !(FingerInPalmEnvelope(fs) ||
274                                    FingerInBottomArea(fs))) {
275       // Finger moving a lot or not in palm envelope; not a stationary palm.
276       non_stationary_palm_.insert(fs.tracking_id);
277       continue;
278     }
279     if (FingerAge(fs.tracking_id, prev_time_) <=
280         palm_stationary_time_.val_ &&
281         FingerAge(fs.tracking_id, hwstate.timestamp) >
282         palm_stationary_time_.val_ &&
283         !SetContainsValue(non_stationary_palm_, fs.tracking_id) &&
284         !FingerNearOtherFinger(hwstate, i)) {
285       // Enough time has passed. Make this stationary contact a palm.
286       palm_.insert(fs.tracking_id);
287       pointing_.erase(fs.tracking_id);
288     }
289   }
290 }
291 
UpdatePalmFlags(HardwareState * hwstate)292 void PalmClassifyingFilterInterpreter::UpdatePalmFlags(HardwareState* hwstate) {
293   for (short i = 0; i < hwstate->finger_cnt; i++) {
294     FingerState* fs = &hwstate->fingers[i];
295     if (SetContainsValue(large_palm_, fs->tracking_id)) {
296       fs->flags |= GESTURES_FINGER_LARGE_PALM;
297     }
298     if (SetContainsValue(palm_, fs->tracking_id)) {
299       fs->flags |= GESTURES_FINGER_PALM;
300     } else if (!MapContainsKey(pointing_, fs->tracking_id) &&
301                !SetContainsValue(was_near_other_fingers_, fs->tracking_id)) {
302       if (FingerInPalmEnvelope(*fs)) {
303         fs->flags |= GESTURES_FINGER_PALM;
304       } else if (FingerInBottomArea(*fs)) {
305         fs->flags |= (GESTURES_FINGER_WARP_X | GESTURES_FINGER_WARP_Y);
306       }
307     } else if (MapContainsKey(pointing_, fs->tracking_id) &&
308                FingerInPalmEnvelope(*fs)) {
309       fs->flags |= GESTURES_FINGER_POSSIBLE_PALM;
310       if (pointing_[fs->tracking_id] == kPointCloseToFinger &&
311           !FingerNearOtherFinger(*hwstate, i)) {
312         // Finger was near another finger, but it's not anymore, and it was
313         // only this other finger that caused it to point. Mark it w/ warp
314         // until it moves sufficiently to have another reason to be
315         // pointing.
316         fs->flags |= (GESTURES_FINGER_WARP_X | GESTURES_FINGER_WARP_Y);
317       }
318     }
319   }
320 }
321 
FingerAge(short finger_id,stime_t now) const322 stime_t PalmClassifyingFilterInterpreter::FingerAge(short finger_id,
323                                                     stime_t now) const {
324   if (!MapContainsKey(origin_timestamps_, finger_id)) {
325     Err("Don't have record of finger age for finger %d", finger_id);
326     return -1;
327   }
328   return now - origin_timestamps_.at(finger_id);
329 }
330 
331 }  // namespace gestures
332