Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: ui/events/gesture_detection/scale_gesture_detector.cc

Issue 1778063002: Eliminate pinch drift by removing touch radius from span calculation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Shrink minimum scale span. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/events/gesture_detection/scale_gesture_detector.h" 5 #include "ui/events/gesture_detection/scale_gesture_detector.h"
6 6
7 #include <limits.h> 7 #include <limits.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <cmath> 10 #include <cmath>
11 11
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "ui/events/gesture_detection/motion_event.h" 13 #include "ui/events/gesture_detection/motion_event.h"
14 #include "ui/events/gesture_detection/scale_gesture_listeners.h" 14 #include "ui/events/gesture_detection/scale_gesture_listeners.h"
15 15
16 using base::TimeDelta; 16 using base::TimeDelta;
17 using base::TimeTicks; 17 using base::TimeTicks;
18 18
19 namespace ui { 19 namespace ui {
20 namespace { 20 namespace {
21 21
22 // Using a small epsilon when comparing slop distances allows pixel perfect 22 // Using a small epsilon when comparing slop distances allows pixel perfect
23 // slop determination when using fractional DPI coordinates (assuming the slop 23 // slop determination when using fractional DPI coordinates (assuming the slop
24 // region and DPI scale are reasonably proportioned). 24 // region and DPI scale are reasonably proportioned).
25 const float kSlopEpsilon = .05f; 25 const float kSlopEpsilon = .05f;
26 26
27 const int kTouchStabilizeTimeMs = 128;
28
29 const float kScaleFactor = .5f; 27 const float kScaleFactor = .5f;
30 28
31 } // namespace 29 } // namespace
32 30
33 // Note: These constants were taken directly from the default (unscaled) 31 // Note: These constants were taken directly from the default (unscaled)
34 // versions found in Android's ViewConfiguration. Do not change these default 32 // versions found in Android's ViewConfiguration. Do not change these default
35 // values without explicitly consulting an OWNER. 33 // values without explicitly consulting an OWNER.
36 ScaleGestureDetector::Config::Config() 34 ScaleGestureDetector::Config::Config()
37 : span_slop(16), 35 : span_slop(16),
38 min_scaling_touch_major(48),
39 min_scaling_span(200), 36 min_scaling_span(200),
40 min_pinch_update_span_delta(0), 37 min_pinch_update_span_delta(0),
41 stylus_scale_enabled(false) {} 38 stylus_scale_enabled(false) {}
42 39
43 ScaleGestureDetector::Config::~Config() {} 40 ScaleGestureDetector::Config::~Config() {}
44 41
45 ScaleGestureDetector::ScaleGestureDetector(const Config& config, 42 ScaleGestureDetector::ScaleGestureDetector(const Config& config,
46 ScaleGestureListener* listener) 43 ScaleGestureListener* listener)
47 : listener_(listener), 44 : listener_(listener),
48 stylus_scale_enabled_(config.stylus_scale_enabled), 45 stylus_scale_enabled_(config.stylus_scale_enabled),
49 focus_x_(0), 46 focus_x_(0),
50 focus_y_(0), 47 focus_y_(0),
51 curr_span_(0), 48 curr_span_(0),
52 prev_span_(0), 49 prev_span_(0),
53 initial_span_(0), 50 initial_span_(0),
54 curr_span_x_(0), 51 curr_span_x_(0),
55 curr_span_y_(0), 52 curr_span_y_(0),
56 prev_span_x_(0), 53 prev_span_x_(0),
57 prev_span_y_(0), 54 prev_span_y_(0),
58 in_progress_(0), 55 in_progress_(0),
59 span_slop_(0), 56 span_slop_(0),
60 min_span_(0), 57 min_span_(0),
61 touch_upper_(0),
62 touch_lower_(0),
63 touch_history_last_accepted_(0),
64 touch_history_direction_(0),
65 touch_min_major_(0),
66 touch_max_major_(0),
67 anchored_scale_start_x_(0), 58 anchored_scale_start_x_(0),
68 anchored_scale_start_y_(0), 59 anchored_scale_start_y_(0),
69 anchored_scale_mode_(ANCHORED_SCALE_MODE_NONE), 60 anchored_scale_mode_(ANCHORED_SCALE_MODE_NONE),
70 event_before_or_above_starting_gesture_event_(false) { 61 event_before_or_above_starting_gesture_event_(false) {
71 DCHECK(listener_); 62 DCHECK(listener_);
72 span_slop_ = config.span_slop + kSlopEpsilon; 63 span_slop_ = config.span_slop + kSlopEpsilon;
73 touch_min_major_ = config.min_scaling_touch_major;
74 touch_max_major_ = std::min(config.min_scaling_span / std::sqrt(2.f),
75 2.f * touch_min_major_);
76 min_span_ = config.min_scaling_span + kSlopEpsilon; 64 min_span_ = config.min_scaling_span + kSlopEpsilon;
77 ResetTouchHistory();
78 } 65 }
79 66
80 ScaleGestureDetector::~ScaleGestureDetector() {} 67 ScaleGestureDetector::~ScaleGestureDetector() {}
81 68
82 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) { 69 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
83 curr_time_ = event.GetEventTime(); 70 curr_time_ = event.GetEventTime();
84 71
85 const int action = event.GetAction(); 72 const int action = event.GetAction();
86 73
87 const int count = static_cast<int>(event.GetPointerCount()); 74 const int count = static_cast<int>(event.GetPointerCount());
(...skipping 13 matching lines...) Expand all
101 // Reset any scale in progress with the listener. 88 // Reset any scale in progress with the listener.
102 // If it's an ACTION_DOWN we're beginning a new event stream. 89 // If it's an ACTION_DOWN we're beginning a new event stream.
103 // This means the app probably didn't give us all the events. Shame on it. 90 // This means the app probably didn't give us all the events. Shame on it.
104 if (in_progress_) { 91 if (in_progress_) {
105 listener_->OnScaleEnd(*this, event); 92 listener_->OnScaleEnd(*this, event);
106 ResetScaleWithSpan(0); 93 ResetScaleWithSpan(0);
107 } else if (InAnchoredScaleMode() && stream_complete) { 94 } else if (InAnchoredScaleMode() && stream_complete) {
108 ResetScaleWithSpan(0); 95 ResetScaleWithSpan(0);
109 } 96 }
110 97
111 if (stream_complete) { 98 if (stream_complete)
112 ResetTouchHistory();
113 return true; 99 return true;
114 }
115 } 100 }
116 101
117 if (!in_progress_ && stylus_scale_enabled_ && !InAnchoredScaleMode() && 102 if (!in_progress_ && stylus_scale_enabled_ && !InAnchoredScaleMode() &&
118 !stream_complete && is_stylus_button_down) { 103 !stream_complete && is_stylus_button_down) {
119 // Start of a stylus scale gesture. 104 // Start of a stylus scale gesture.
120 anchored_scale_start_x_ = event.GetX(); 105 anchored_scale_start_x_ = event.GetX();
121 anchored_scale_start_y_ = event.GetY(); 106 anchored_scale_start_y_ = event.GetY();
122 anchored_scale_mode_ = ANCHORED_SCALE_MODE_STYLUS; 107 anchored_scale_mode_ = ANCHORED_SCALE_MODE_STYLUS;
123 initial_span_ = 0; 108 initial_span_ = 0;
124 } 109 }
(...skipping 28 matching lines...) Expand all
153 if (skip_index == i) 138 if (skip_index == i)
154 continue; 139 continue;
155 sum_x += event.GetX(i); 140 sum_x += event.GetX(i);
156 sum_y += event.GetY(i); 141 sum_y += event.GetY(i);
157 } 142 }
158 143
159 focus_x = sum_x * inverse_unreleased_point_count; 144 focus_x = sum_x * inverse_unreleased_point_count;
160 focus_y = sum_y * inverse_unreleased_point_count; 145 focus_y = sum_y * inverse_unreleased_point_count;
161 } 146 }
162 147
163 AddTouchHistory(event);
164
165 // Determine average deviation from focal point. 148 // Determine average deviation from focal point.
166 float dev_sum_x = 0, dev_sum_y = 0; 149 float dev_sum_x = 0, dev_sum_y = 0;
167 for (int i = 0; i < count; i++) { 150 for (int i = 0; i < count; i++) {
168 if (skip_index == i) 151 if (skip_index == i)
169 continue; 152 continue;
170 153
171 dev_sum_x += std::abs(event.GetX(i) - focus_x); 154 dev_sum_x += std::abs(event.GetX(i) - focus_x);
172 dev_sum_y += std::abs(event.GetY(i) - focus_y); 155 dev_sum_y += std::abs(event.GetY(i) - focus_y);
173 } 156 }
174 // Convert the resulting diameter into a radius, to include touch
175 // radius in overall deviation.
176 const float touch_radius = touch_history_last_accepted_ / 2;
177 157
178 const float dev_x = dev_sum_x * inverse_unreleased_point_count + touch_radius; 158 const float dev_x = dev_sum_x * inverse_unreleased_point_count;
179 const float dev_y = dev_sum_y * inverse_unreleased_point_count + touch_radius; 159 const float dev_y = dev_sum_y * inverse_unreleased_point_count;
180 160
181 // Span is the average distance between touch points through the focal point; 161 // Span is the average distance between touch points through the focal point;
182 // i.e. the diameter of the circle with a radius of the average deviation from 162 // i.e. the diameter of the circle with a radius of the average deviation from
183 // the focal point. 163 // the focal point.
184 const float span_x = dev_x * 2; 164 const float span_x = dev_x * 2;
185 const float span_y = dev_y * 2; 165 const float span_y = dev_y * 2;
186 float span; 166 float span;
187 if (InAnchoredScaleMode()) { 167 if (InAnchoredScaleMode()) {
188 span = span_y; 168 span = span_y;
189 } else { 169 } else {
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 } 268 }
289 269
290 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) { 270 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) {
291 // Double tap: start watching for a swipe. 271 // Double tap: start watching for a swipe.
292 anchored_scale_start_x_ = ev.GetX(); 272 anchored_scale_start_x_ = ev.GetX();
293 anchored_scale_start_y_ = ev.GetY(); 273 anchored_scale_start_y_ = ev.GetY();
294 anchored_scale_mode_ = ANCHORED_SCALE_MODE_DOUBLE_TAP; 274 anchored_scale_mode_ = ANCHORED_SCALE_MODE_DOUBLE_TAP;
295 return true; 275 return true;
296 } 276 }
297 277
298 void ScaleGestureDetector::AddTouchHistory(const MotionEvent& ev) {
299 const base::TimeTicks current_time = ev.GetEventTime();
300 DCHECK(!current_time.is_null());
301 const int count = static_cast<int>(ev.GetPointerCount());
302 bool accept = touch_history_last_accepted_time_.is_null() ||
303 (current_time - touch_history_last_accepted_time_) >=
304 base::TimeDelta::FromMilliseconds(kTouchStabilizeTimeMs);
305 float total = 0;
306 int sample_count = 0;
307 for (int i = 0; i < count; i++) {
308 const bool has_last_accepted = !std::isnan(touch_history_last_accepted_);
309 const int history_size = static_cast<int>(ev.GetHistorySize());
310 const int pointersample_count = history_size + 1;
311 for (int h = 0; h < pointersample_count; h++) {
312 float major;
313 if (h < history_size) {
314 major = ev.GetHistoricalTouchMajor(i, h);
315 } else {
316 major = ev.GetTouchMajor(i);
317 }
318 if (major < touch_min_major_)
319 major = touch_min_major_;
320 if (major > touch_max_major_)
321 major = touch_max_major_;
322 total += major;
323
324 if (std::isnan(touch_upper_) || major > touch_upper_) {
325 touch_upper_ = major;
326 }
327 if (std::isnan(touch_lower_) || major < touch_lower_) {
328 touch_lower_ = major;
329 }
330
331 if (has_last_accepted) {
332 const float major_delta = major - touch_history_last_accepted_;
333 const int direction_sig =
334 major_delta > 0 ? 1 : (major_delta < 0 ? -1 : 0);
335 if (direction_sig != touch_history_direction_ ||
336 (direction_sig == 0 && touch_history_direction_ == 0)) {
337 touch_history_direction_ = direction_sig;
338 touch_history_last_accepted_time_ = h < history_size
339 ? ev.GetHistoricalEventTime(h)
340 : ev.GetEventTime();
341 accept = false;
342 }
343 }
344 }
345 sample_count += pointersample_count;
346 }
347
348 const float avg = total / sample_count;
349
350 if (accept) {
351 float new_accepted = (touch_upper_ + touch_lower_ + avg) / 3;
352 touch_upper_ = (touch_upper_ + new_accepted) / 2;
353 touch_lower_ = (touch_lower_ + new_accepted) / 2;
354 touch_history_last_accepted_ = new_accepted;
355 touch_history_direction_ = 0;
356 touch_history_last_accepted_time_ = ev.GetEventTime();
357 }
358 }
359
360 void ScaleGestureDetector::ResetTouchHistory() {
361 touch_upper_ = std::numeric_limits<float>::quiet_NaN();
362 touch_lower_ = std::numeric_limits<float>::quiet_NaN();
363 touch_history_last_accepted_ = std::numeric_limits<float>::quiet_NaN();
364 touch_history_direction_ = 0;
365 touch_history_last_accepted_time_ = base::TimeTicks();
366 }
367
368 void ScaleGestureDetector::ResetScaleWithSpan(float span) { 278 void ScaleGestureDetector::ResetScaleWithSpan(float span) {
369 in_progress_ = false; 279 in_progress_ = false;
370 initial_span_ = span; 280 initial_span_ = span;
371 anchored_scale_mode_ = ANCHORED_SCALE_MODE_NONE; 281 anchored_scale_mode_ = ANCHORED_SCALE_MODE_NONE;
372 } 282 }
373 283
374 } // namespace ui 284 } // namespace ui
OLDNEW
« no previous file with comments | « ui/events/gesture_detection/scale_gesture_detector.h ('k') | ui/gfx/android/view_configuration.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698