Home | History | Annotate | Download | only in gestures
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      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 "ui/events/gestures/gesture_point.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/basictypes.h"
     10 #include "ui/events/event.h"
     11 #include "ui/events/event_constants.h"
     12 #include "ui/events/gestures/gesture_configuration.h"
     13 #include "ui/events/gestures/gesture_types.h"
     14 
     15 namespace ui {
     16 
     17 GesturePoint::GesturePoint()
     18     : first_touch_time_(0.0),
     19       second_last_touch_time_(0.0),
     20       last_touch_time_(0.0),
     21       second_last_tap_time_(0.0),
     22       last_tap_time_(0.0),
     23       velocity_calculator_(
     24           GestureConfiguration::points_buffered_for_velocity()),
     25       point_id_(-1),
     26       touch_id_(-1),
     27       source_device_id_(-1) {
     28   max_touch_move_in_pixels_for_click_squared_ =
     29       GestureConfiguration::max_touch_move_in_pixels_for_click() *
     30       GestureConfiguration::max_touch_move_in_pixels_for_click();
     31   max_distance_between_taps_for_double_tap_squared_ =
     32       GestureConfiguration::max_distance_between_taps_for_double_tap() *
     33       GestureConfiguration::max_distance_between_taps_for_double_tap();
     34 }
     35 
     36 GesturePoint::~GesturePoint() {}
     37 
     38 void GesturePoint::Reset() {
     39   first_touch_time_ = second_last_touch_time_ = last_touch_time_ = 0.0;
     40   ResetVelocity();
     41   point_id_ = -1;
     42   clear_enclosing_rectangle();
     43   source_device_id_ = -1;
     44 }
     45 
     46 void GesturePoint::ResetVelocity() {
     47   velocity_calculator_.ClearHistory();
     48 }
     49 
     50 gfx::Vector2dF GesturePoint::ScrollDelta() const {
     51   return last_touch_position_ - second_last_touch_position_;
     52 }
     53 
     54 void GesturePoint::UpdateValues(const TouchEvent& event) {
     55   const int64 event_timestamp_microseconds =
     56       event.time_stamp().InMicroseconds();
     57   if (event.type() == ui::ET_TOUCH_MOVED) {
     58     velocity_calculator_.PointSeen(event.location().x(),
     59                                    event.location().y(),
     60                                    event_timestamp_microseconds);
     61     gfx::Vector2d sd(ScrollVelocityDirection(velocity_calculator_.XVelocity()),
     62                      ScrollVelocityDirection(velocity_calculator_.YVelocity()));
     63   }
     64 
     65   last_touch_time_ = event.time_stamp().InSecondsF();
     66   last_touch_position_ = event.location_f();
     67 
     68   if (event.type() == ui::ET_TOUCH_PRESSED) {
     69     ResetVelocity();
     70     clear_enclosing_rectangle();
     71     first_touch_time_ = last_touch_time_;
     72     first_touch_position_ = event.location();
     73     second_last_touch_position_ = last_touch_position_;
     74     second_last_touch_time_ = last_touch_time_;
     75     velocity_calculator_.PointSeen(event.location().x(),
     76                                    event.location().y(),
     77                                    event_timestamp_microseconds);
     78   }
     79 
     80   UpdateEnclosingRectangle(event);
     81 }
     82 
     83 void GesturePoint::UpdateForTap() {
     84   // Update the tap-position and time, and reset every other state.
     85   second_last_tap_position_ = last_tap_position_;
     86   second_last_tap_time_ = last_tap_time_;
     87   last_tap_time_ = last_touch_time_;
     88   last_tap_position_ = last_touch_position_;
     89 }
     90 
     91 void GesturePoint::UpdateForScroll() {
     92   second_last_touch_position_ = last_touch_position_;
     93   second_last_touch_time_ = last_touch_time_;
     94 }
     95 
     96 bool GesturePoint::IsInClickWindow(const TouchEvent& event) const {
     97   return IsInClickTimeWindow() && IsInsideTouchSlopRegion(event);
     98 }
     99 
    100 bool GesturePoint::IsInDoubleClickWindow(const TouchEvent& event) const {
    101   return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) &&
    102          IsPointInsideDoubleTapTouchSlopRegion(
    103              event.location(), last_tap_position_);
    104 }
    105 
    106 bool GesturePoint::IsInTripleClickWindow(const TouchEvent& event) const {
    107   return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) &&
    108          IsInClickAggregateTimeWindow(second_last_tap_time_, last_tap_time_) &&
    109          IsPointInsideDoubleTapTouchSlopRegion(
    110              event.location(), last_tap_position_) &&
    111          IsPointInsideDoubleTapTouchSlopRegion(last_tap_position_,
    112                                       second_last_tap_position_);
    113 }
    114 
    115 bool GesturePoint::IsInScrollWindow(const TouchEvent& event) const {
    116   return event.type() == ui::ET_TOUCH_MOVED &&
    117          !IsInsideTouchSlopRegion(event);
    118 }
    119 
    120 bool GesturePoint::IsInFlickWindow(const TouchEvent& event) {
    121   return IsOverMinFlickSpeed() &&
    122          event.type() != ui::ET_TOUCH_CANCELLED;
    123 }
    124 
    125 int GesturePoint::ScrollVelocityDirection(float v) {
    126   if (v < -GestureConfiguration::min_scroll_velocity())
    127     return -1;
    128   else if (v > GestureConfiguration::min_scroll_velocity())
    129     return 1;
    130   else
    131     return 0;
    132 }
    133 
    134 bool GesturePoint::DidScroll(const TouchEvent& event, int dist) const {
    135   gfx::Vector2dF d = last_touch_position_ - second_last_touch_position_;
    136   return fabs(d.x()) > dist || fabs(d.y()) > dist;
    137 }
    138 
    139 bool GesturePoint::IsInHorizontalRailWindow() const {
    140   gfx::Vector2dF d = last_touch_position_ - second_last_touch_position_;
    141   return std::abs(d.x()) >
    142       GestureConfiguration::rail_start_proportion() * std::abs(d.y());
    143 }
    144 
    145 bool GesturePoint::IsInVerticalRailWindow() const {
    146   gfx::Vector2dF d = last_touch_position_ - second_last_touch_position_;
    147   return std::abs(d.y()) >
    148       GestureConfiguration::rail_start_proportion() * std::abs(d.x());
    149 }
    150 
    151 bool GesturePoint::BreaksHorizontalRail() {
    152   float vx = XVelocity();
    153   float vy = YVelocity();
    154   return fabs(vy) > GestureConfiguration::rail_break_proportion() * fabs(vx) +
    155       GestureConfiguration::min_rail_break_velocity();
    156 }
    157 
    158 bool GesturePoint::BreaksVerticalRail() {
    159   float vx = XVelocity();
    160   float vy = YVelocity();
    161   return fabs(vx) > GestureConfiguration::rail_break_proportion() * fabs(vy) +
    162       GestureConfiguration::min_rail_break_velocity();
    163 }
    164 
    165 bool GesturePoint::IsInClickTimeWindow() const {
    166   double duration = last_touch_time_ - first_touch_time_;
    167   return duration >=
    168       GestureConfiguration::min_touch_down_duration_in_seconds_for_click() &&
    169       duration <
    170       GestureConfiguration::max_touch_down_duration_in_seconds_for_click();
    171 }
    172 
    173 bool GesturePoint::IsInClickAggregateTimeWindow(double before,
    174                                                 double after) const {
    175   double duration =  after - before;
    176   return duration < GestureConfiguration::max_seconds_between_double_click();
    177 }
    178 
    179 bool GesturePoint::IsInsideTouchSlopRegion(const TouchEvent& event) const {
    180   const gfx::PointF& p1 = event.location();
    181   const gfx::PointF& p2 = first_touch_position_;
    182   float dx = p1.x() - p2.x();
    183   float dy = p1.y() - p2.y();
    184   float distance = dx * dx + dy * dy;
    185   return distance < max_touch_move_in_pixels_for_click_squared_;
    186 }
    187 
    188 bool GesturePoint::IsPointInsideDoubleTapTouchSlopRegion(gfx::PointF p1,
    189                                                          gfx::PointF p2) const {
    190   float dx = p1.x() - p2.x();
    191   float dy = p1.y() - p2.y();
    192   float distance = dx * dx + dy * dy;
    193   return distance < max_distance_between_taps_for_double_tap_squared_;
    194 }
    195 
    196 bool GesturePoint::IsOverMinFlickSpeed() {
    197   return velocity_calculator_.VelocitySquared() >
    198       GestureConfiguration::min_flick_speed_squared();
    199 }
    200 
    201 void GesturePoint::UpdateEnclosingRectangle(const TouchEvent& event) {
    202   int radius;
    203 
    204   // Ignore this TouchEvent if it has a radius larger than the maximum
    205   // allowed radius size.
    206   if (event.radius_x() > GestureConfiguration::max_radius() ||
    207       event.radius_y() > GestureConfiguration::max_radius())
    208     return;
    209 
    210   // If the device provides at least one of the radius values, take the larger
    211   // of the two and use this as both the x radius and the y radius of the
    212   // touch region. Otherwise use the default radius value.
    213   // TODO(tdanderson): Implement a more specific check for the exact
    214   // information provided by the device (0-2 radii values, force, angle) and
    215   // use this to compute a more representative rectangular touch region.
    216   if (event.radius_x() || event.radius_y())
    217     radius = std::max(event.radius_x(), event.radius_y());
    218   else
    219     radius = GestureConfiguration::default_radius();
    220 
    221   gfx::RectF rect(event.location_f().x() - radius,
    222                   event.location_f().y() - radius,
    223                   radius * 2,
    224                   radius * 2);
    225   if (IsInClickWindow(event))
    226     enclosing_rect_.Union(rect);
    227   else
    228     enclosing_rect_ = rect;
    229 }
    230 
    231 }  // namespace ui
    232