highlighter_gesture_util.cc 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2017 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. #include "ash/highlighter/highlighter_gesture_util.h"
  5. #include <cmath>
  6. #include "ash/fast_ink/fast_ink_points.h"
  7. #include "base/numerics/math_constants.h"
  8. namespace ash {
  9. namespace {
  10. constexpr float kHorizontalStrokeLengthThreshold = 20;
  11. constexpr float kHorizontalStrokeThicknessThreshold = 2;
  12. constexpr float kHorizontalStrokeFlatnessThreshold = 0.1;
  13. constexpr double kClosedShapeSweepThreshold = base::kPiDouble * 2 * 0.8;
  14. constexpr double kClosedShapeJiggleThreshold = 0.1;
  15. bool DetectHorizontalStroke(const gfx::RectF& box,
  16. const gfx::SizeF& pen_tip_size) {
  17. return box.width() > kHorizontalStrokeLengthThreshold &&
  18. box.height() <
  19. pen_tip_size.height() * kHorizontalStrokeThicknessThreshold &&
  20. box.height() < box.width() * kHorizontalStrokeFlatnessThreshold;
  21. }
  22. bool DetectClosedShape(const gfx::RectF& box,
  23. const fast_ink::FastInkPoints& points) {
  24. if (points.GetNumberOfPoints() < 3)
  25. return false;
  26. const gfx::PointF center = box.CenterPoint();
  27. // Analyze vectors pointing from the center to each point.
  28. // Compute the cumulative swept angle and count positive
  29. // and negative angles separately.
  30. double swept_angle = 0.0;
  31. int positive = 0;
  32. int negative = 0;
  33. double prev_angle = 0.0;
  34. bool has_prev_angle = false;
  35. for (const auto& point : points.points()) {
  36. const double angle =
  37. atan2(point.location.y() - center.y(), point.location.x() - center.x());
  38. if (has_prev_angle) {
  39. double diff_angle = angle - prev_angle;
  40. if (diff_angle > base::kPiDouble) {
  41. diff_angle -= base::kPiDouble * 2;
  42. } else if (diff_angle < -base::kPiDouble) {
  43. diff_angle += base::kPiDouble * 2;
  44. }
  45. swept_angle += diff_angle;
  46. if (diff_angle > 0)
  47. positive++;
  48. if (diff_angle < 0)
  49. negative++;
  50. } else {
  51. has_prev_angle = true;
  52. }
  53. prev_angle = angle;
  54. }
  55. if (std::abs(swept_angle) < kClosedShapeSweepThreshold) {
  56. // Has not swept enough of the full circle.
  57. return false;
  58. }
  59. if (swept_angle > 0 && (static_cast<double>(negative) / positive) >
  60. kClosedShapeJiggleThreshold) {
  61. // Main direction is positive, but went too often in the negative direction.
  62. return false;
  63. }
  64. if (swept_angle < 0 && (static_cast<double>(positive) / negative) >
  65. kClosedShapeJiggleThreshold) {
  66. // Main direction is negative, but went too often in the positive direction.
  67. return false;
  68. }
  69. return true;
  70. }
  71. } // namespace
  72. HighlighterGestureType DetectHighlighterGesture(
  73. const gfx::RectF& box,
  74. const gfx::SizeF& pen_tip_size,
  75. const fast_ink::FastInkPoints& points) {
  76. if (DetectHorizontalStroke(box, pen_tip_size))
  77. return HighlighterGestureType::kHorizontalStroke;
  78. if (DetectClosedShape(box, points))
  79. return HighlighterGestureType::kClosedShape;
  80. return HighlighterGestureType::kNotRecognized;
  81. }
  82. } // namespace ash