sammiequon | 16fd4e9 | 2016-12-07 07:02:41 | [diff] [blame^] | 1 | // Copyright 2016 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 "ash/laser/laser_segment_utils.h" |
| 6 | |
| 7 | #include <limits> |
| 8 | |
| 9 | #include "base/logging.h" |
| 10 | #include "ui/gfx/geometry/point3_f.h" |
| 11 | #include "ui/gfx/geometry/point_f.h" |
| 12 | #include "ui/gfx/geometry/vector2d_f.h" |
| 13 | #include "ui/gfx/transform.h" |
| 14 | |
| 15 | namespace ash { |
| 16 | namespace { |
| 17 | |
| 18 | // Solves the equation x = (-b (+|-) sqrt(b^2 - 4ac)) / 2a. |use_plus| |
| 19 | // determines whether + or - is used in the equation; if |use_plus| is true, + |
| 20 | // is used. |a| cannot be 0 (linear equation). Note: This does not handle the |
| 21 | // case where the roots are complex. |
| 22 | float QuadraticEquation(bool use_plus, float a, float b, float c) { |
| 23 | DCHECK_NE(0.0f, a); |
| 24 | return (-1.0f * b + sqrt(b * b - 4.0f * a * c) * (use_plus ? 1.0f : -1.0f)) / |
| 25 | (2.0f * a); |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | float AngleOfPointInNewCoordinates(const gfx::PointF& origin, |
| 30 | const gfx::Vector2dF& direction, |
| 31 | const gfx::PointF& point) { |
| 32 | double angle_degrees = atan2(direction.y(), direction.x()) * 180.0f / M_PI; |
| 33 | gfx::Transform transform; |
| 34 | transform.Rotate(-angle_degrees); |
| 35 | transform.Translate(-origin.x(), -origin.y()); |
| 36 | gfx::Point3F point_to_transform(point.x(), point.y(), 0.0f); |
| 37 | transform.TransformPoint(&point_to_transform); |
| 38 | return atan2(point_to_transform.y(), point_to_transform.x()); |
| 39 | } |
| 40 | |
| 41 | void ComputeNormalLineVariables(const gfx::PointF& start_point, |
| 42 | const gfx::PointF& end_point, |
| 43 | float* normal_slope, |
| 44 | float* start_y_intercept, |
| 45 | float* end_y_intercept) { |
| 46 | float rise = end_point.y() - start_point.y(); |
| 47 | float run = end_point.x() - start_point.x(); |
| 48 | // If the rise of line between the two points is close to zero, the normal of |
| 49 | // the line is undefined. |
| 50 | if (fabs(rise) < 0.0001f) { |
| 51 | *normal_slope = std::numeric_limits<float>::quiet_NaN(); |
| 52 | *start_y_intercept = std::numeric_limits<float>::quiet_NaN(); |
| 53 | *end_y_intercept = std::numeric_limits<float>::quiet_NaN(); |
| 54 | return; |
| 55 | } |
| 56 | |
| 57 | *normal_slope = -1.0f * (run / rise); |
| 58 | *start_y_intercept = start_point.y() - *normal_slope * start_point.x(); |
| 59 | *end_y_intercept = end_point.y() - *normal_slope * end_point.x(); |
| 60 | } |
| 61 | |
| 62 | void ComputeProjectedPoints(const gfx::PointF& point, |
| 63 | float line_slope, |
| 64 | float line_y_intercept, |
| 65 | float projection_distance, |
| 66 | gfx::PointF* first_projection, |
| 67 | gfx::PointF* second_projection) { |
| 68 | // If the slope is NaN, the y-intercept should be NaN too. The line is thus |
| 69 | // vertical and projections will be projected straight up/down from |point|. |
| 70 | if (isnan(line_slope)) { |
| 71 | DCHECK(isnan(line_y_intercept)); |
| 72 | |
| 73 | *first_projection = |
| 74 | gfx::PointF(point.x(), point.y() + round(projection_distance)); |
| 75 | *second_projection = |
| 76 | gfx::PointF(point.x(), point.y() - round(projection_distance)); |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | // |point| must be on the line defined by |line_slope| and |line_y_intercept|. |
| 81 | DCHECK_LE(fabs(point.y() - (line_slope * point.x() + line_y_intercept)), 2.f); |
| 82 | |
| 83 | // We want the two points along the line given by |slope|(m) and |
| 84 | // |y_intercept|(b). If |original_point| is defined as (x,y) and |
| 85 | // |distance_from_old_point| is d, we want the two (dx,dy) which satisfys the |
| 86 | // two equations (1)dx^2+dy^2=d^2 and (2)y+dy=m(x+dx)+b. Since y,x,b and m are |
| 87 | // constants we form a new equation (3)dy=mdx + K, where K=mx+b-y. Plugging |
| 88 | // (3) into (1) we get dx^2+(mdx)^2+2Kmdx+K^2=d^2 -> |
| 89 | // (m^2+1)dx^2+(2Km)dx+(K^2-d^2)=0. We can then solve for dx using the |
| 90 | // quadratic equation with variables a=m^2+1, b=2Km, c=K^2-d^2. We plug |
| 91 | // dx into (3) to find dy. The new points will then be (x+dx,y+dy). |
| 92 | float constant = line_y_intercept + line_slope * point.x() - point.y(); |
| 93 | float a = 1.0f + line_slope * line_slope; |
| 94 | float b = 2.0f * line_slope * constant; |
| 95 | float c = constant * constant - projection_distance * projection_distance; |
| 96 | float p1_delta_x = QuadraticEquation(true, a, b, c); |
| 97 | float p1_delta_y = |
| 98 | line_slope * (point.x() + p1_delta_x) + line_y_intercept - point.y(); |
| 99 | float p2_delta_x = QuadraticEquation(false, a, b, c); |
| 100 | float p2_delta_y = |
| 101 | line_slope * (point.x() + p2_delta_x) + line_y_intercept - point.y(); |
| 102 | *first_projection = |
| 103 | gfx::PointF(point.x() + round(p1_delta_x), point.y() + round(p1_delta_y)); |
| 104 | *second_projection = |
| 105 | gfx::PointF(point.x() + round(p2_delta_x), point.y() + round(p2_delta_y)); |
| 106 | } |
| 107 | |
| 108 | bool IsFirstPointSmallerAngle(const gfx::PointF& start_point, |
| 109 | const gfx::PointF& end_point, |
| 110 | const gfx::PointF& first_point, |
| 111 | const gfx::PointF& second_point) { |
| 112 | gfx::PointF new_origin( |
| 113 | start_point.x() + (end_point.x() - start_point.x()) / 2.0f, |
| 114 | start_point.y() + (end_point.y() - start_point.y()) / 2.0f); |
| 115 | gfx::Vector2dF direction = end_point - start_point; |
| 116 | |
| 117 | // Compute the angles of the projections relative to the the new origin and |
| 118 | // direction. |
| 119 | float end_first_projection_angle = |
| 120 | AngleOfPointInNewCoordinates(new_origin, direction, first_point); |
| 121 | float end_second_projection_angle = |
| 122 | AngleOfPointInNewCoordinates(new_origin, direction, second_point); |
| 123 | |
| 124 | // We want to always have the smaller angle come first. |
| 125 | return end_first_projection_angle < end_second_projection_angle; |
| 126 | } |
| 127 | |
| 128 | } // namespace ash |