blob: 189f51b36bfb0b0eb9c30c47bcfeae1051721a6f [file] [log] [blame]
sammiequon16fd4e92016-12-07 07:02:411// 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
15namespace ash {
16namespace {
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.
22float 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
29float 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
41void 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
62void 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
108bool 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