| // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/laser/laser_segment_utils.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ui/gfx/geometry/angle_conversions.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/point_f.h" |
| #include "ui/gfx/geometry/vector2d_f.h" |
| |
| namespace ash { |
| namespace { |
| // |kEpsilon| is used to check that AngleOfPointInNewCoordinates produces to |
| // expected angles. This function is only used after points are projected in |
| // opposite directions along the normal, so they should never be to close to |
| // each other, checking for equality up to 5 significant figures is more than |
| // enough. |
| const float kEpsilon = 0.0001f; |
| } |
| |
| // Helper function to check if a given |point| has the |expected_angle_degree| |
| // in the coordinate system formed by |origin| and |direction|. |
| void CheckAngleOfPointInNewCoordinates(const gfx::PointF& origin, |
| const gfx::Vector2dF& direction, |
| const gfx::PointF& point, |
| float expected_angle_degree) { |
| float result = AngleOfPointInNewCoordinates(origin, direction, point); |
| EXPECT_NEAR(gfx::DegToRad(expected_angle_degree), result, kEpsilon); |
| } |
| |
| // Helper function to check if the computed variables match the expected ones. |
| void CheckNormalLineVariables(const gfx::PointF& start_point, |
| const gfx::PointF& end_point, |
| float expected_slope, |
| float expected_start_intercept, |
| float expected_end_intercept) { |
| float calculated_slope; |
| float calculated_start_y_intercept; |
| float calculated_end_y_intercept; |
| |
| ComputeNormalLineVariables(start_point, end_point, &calculated_slope, |
| &calculated_start_y_intercept, |
| &calculated_end_y_intercept); |
| EXPECT_NEAR(expected_slope, calculated_slope, kEpsilon); |
| EXPECT_NEAR(expected_start_intercept, calculated_start_y_intercept, kEpsilon); |
| EXPECT_NEAR(expected_end_intercept, calculated_end_y_intercept, kEpsilon); |
| } |
| |
| // Helper function to check if the a given line segment has an expected |
| // undefined normal line. |
| void CheckUndefinedNormalLine(const gfx::PointF& start_point, |
| const gfx::PointF& end_point) { |
| float calculated_slope; |
| float calculated_start_y_intercept; |
| float calculated_end_y_intercept; |
| |
| ComputeNormalLineVariables(start_point, end_point, &calculated_slope, |
| &calculated_start_y_intercept, |
| &calculated_end_y_intercept); |
| EXPECT_TRUE(std::isnan(calculated_slope)); |
| EXPECT_TRUE(std::isnan(calculated_start_y_intercept)); |
| EXPECT_TRUE(std::isnan(calculated_end_y_intercept)); |
| } |
| |
| // Helper function to check that the projections from the given line variables |
| // and |distance| match those expected in |expected_projections|. |
| void CheckProjectedPoints(const gfx::PointF& start_point, |
| float slope, |
| float y_intercept, |
| float distance, |
| std::vector<gfx::PointF>& expected_projections) { |
| // There can only be two projections. |
| EXPECT_EQ(2u, expected_projections.size()); |
| |
| gfx::PointF calculated_first_projection; |
| gfx::PointF calculated_second_projection; |
| |
| ComputeProjectedPoints(start_point, slope, y_intercept, distance, |
| &calculated_first_projection, |
| &calculated_second_projection); |
| |
| std::vector<gfx::PointF> calculated_projections = { |
| calculated_first_projection, calculated_second_projection}; |
| |
| // Sort the points, so that we do not have to enter the projections in the |
| // right order. |
| std::sort(calculated_projections.begin(), calculated_projections.end()); |
| std::sort(expected_projections.begin(), expected_projections.end()); |
| |
| EXPECT_EQ(expected_projections[0], calculated_projections[0]); |
| EXPECT_EQ(expected_projections[1], calculated_projections[1]); |
| } |
| |
| // Helper function that checks that an IsFirstPointSmallerAngle will return |
| // false if |larger_angle_point| is the first point argument and return true if |
| // |larger_angle_point| is the second point argument. |
| void CheckFirstPointSmaller(const gfx::PointF& start_point, |
| const gfx::PointF& end_point, |
| const gfx::PointF& larger_angle_point, |
| const gfx::PointF& smaller_angle_point) { |
| EXPECT_FALSE(IsFirstPointSmallerAngle( |
| start_point, end_point, larger_angle_point, smaller_angle_point)); |
| EXPECT_TRUE(IsFirstPointSmallerAngle( |
| start_point, end_point, smaller_angle_point, larger_angle_point)); |
| } |
| |
| using LaserSegmentUtilsTest = testing::Test; |
| |
| TEST_F(LaserSegmentUtilsTest, AngleOfPointInNewCoordinates) { |
| { |
| // Verify angles remain the same if the origin is at (0, 0) and the |
| // direction remains the same (1, 0). |
| const gfx::PointF origin(0.0f, 0.0f); |
| const gfx::Vector2dF direction(1.0f, 0.0f); |
| |
| // The functions range is (-180.0, 180.0). |
| for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| float rad = gfx::DegToRad(angle); |
| gfx::PointF new_point(cos(rad), sin(rad)); |
| CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| } |
| } |
| { |
| // Verify angles are shifted by 45 degrees if the origin is at (0, 0) and |
| // the direction is (1, 1). |
| const gfx::PointF origin(0.0f, 0.0f); |
| const gfx::Vector2dF direction(1.0f, 1.0f); |
| |
| // The functions range is (-180.0, 180.0). |
| for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| float rad = gfx::DegToRad(angle + 45.0f); |
| gfx::PointF new_point(cos(rad), sin(rad)); |
| CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| } |
| } |
| { |
| // Verify angles remain the same if the points are translated by (1, 1), |
| // if the origin is at (1, 1) and the direction remains the same (1, 0). |
| const gfx::PointF origin(1.0f, 1.0f); |
| const gfx::Vector2dF direction(1.0f, 0.0f); |
| |
| // The functions range is (-180.0f, 180.0f). |
| for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| float rad = gfx::DegToRad(angle); |
| gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y()); |
| CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| } |
| } |
| { |
| // Verify angles are shifted by 45 degress if the points are translated by |
| // (1, 1), if the origin is at (1, 1) and the direction remains the same |
| // (1, 0). |
| const gfx::PointF origin(1.0f, 1.0f); |
| const gfx::Vector2dF direction(1.0f, 1.0f); |
| |
| // The functions range is (-180.0, 180.0). |
| for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| float rad = gfx::DegToRad(angle + 45.0f); |
| gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y()); |
| CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| } |
| } |
| } |
| |
| TEST_F(LaserSegmentUtilsTest, ComputeNormalLineVariables) { |
| { |
| // Verify a line y=x should have a normal line y=-x+b. At point (0,0), b |
| // should equal y+x = 0. At point (1,1), b should equal y+x = 2. |
| const gfx::PointF start(0.0f, 0.0f); |
| const gfx::PointF end(1.0f, 1.0f); |
| float slope = -1.0f; |
| float start_intercept = 0.0f; |
| float end_intercept = 2.0f; |
| CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| } |
| { |
| // Verify a line y=-x should have a normal line y=x+b. At point 0.0f), b |
| // should equal y-x =.0f. At point (1,-1), b should equal y-x = -2. |
| const gfx::PointF start(0.0f, 0.0f); |
| const gfx::PointF end(1.0f, -1.0f); |
| float slope = 1.0f; |
| float start_intercept = 0.0f; |
| float end_intercept = -2.0f; |
| CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| } |
| { |
| // Verify a line x=5 should have a normal line y.0f with intercepts at the |
| // previous y point. |
| const gfx::PointF start(5.0f, 0.0f); |
| const gfx::PointF end(5.0f, 5.0f); |
| float slope = 0.0f; |
| float start_intercept = 0.0f; |
| float end_intercept = 5.0f; |
| CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| } |
| { |
| // Verify a line parallel to the x-axis should be undefined. The line values |
| // should not matter. |
| const gfx::PointF start(0.0f, 5.0f); |
| const gfx::PointF end(5.0f, 5.0f); |
| CheckUndefinedNormalLine(start, end); |
| } |
| } |
| |
| TEST_F(LaserSegmentUtilsTest, ComputeProjectedPoints) { |
| { |
| // Verify projecting along y=x from (0, 0) by distance sqrt(2) should result |
| // in two projections: (1, 1) and (-1, -1). We start from (0, 0) and |
| // translate by (1, 1) and (-1, -1) (vectors with slope 1) to get to (1, 1) |
| // and (-1, -1). The length of the distance from (1, 1) is sqrt(1*1 + 1*1) = |
| // sqrt(2). |
| const gfx::PointF start(0.0f, 0.0f); |
| const float slope = 1.0f; |
| const float y_intercept = 0.0f; |
| const float distance = sqrt(2.0f); |
| std::vector<gfx::PointF> expected_projections = {gfx::PointF(1.0f, 1.0f), |
| gfx::PointF(-1.0f, -1.0f)}; |
| CheckProjectedPoints(start, slope, y_intercept, distance, |
| expected_projections); |
| } |
| { |
| // Verify projecting along y=-2x+2 from (2, -2) by distance 2*sqrt(5) should |
| // result in two projections: (0, 2) and (4, -6). We start from (2, -2) and |
| // translate by (-2, 4) and (2, -4) (vectors with slope -2) to get (0, 2) |
| // and (4, -6). The length of the distance from (2, -2) is sqrt(2*2 + 4*4) = |
| // sqrt(20) = 2*sqrt(5). |
| const gfx::PointF start(2.0f, -2.0f); |
| const float slope = -2.0f; |
| const float y_intercept = 2.0f; |
| const float distance = 2.0f * sqrt(5.0f); |
| std::vector<gfx::PointF> expected_projections = {gfx::PointF(0.0f, 2.0f), |
| gfx::PointF(4.0f, -6.0f)}; |
| CheckProjectedPoints(start, slope, y_intercept, distance, |
| expected_projections); |
| } |
| { |
| // Verify projecting along y=5 from (5,5) by distance 2 should |
| // result in two projections: (5,7) and (5,3). |
| const gfx::PointF start(5.0f, 5.0f); |
| const float slope = 0.0f; |
| const float y_intercept = 5.0f; |
| const float distance = 2.0f; |
| std::vector<gfx::PointF> expected_projections = {gfx::PointF(7.0f, 5.0f), |
| gfx::PointF(3.0f, 5.0f)}; |
| CheckProjectedPoints(start, slope, y_intercept, distance, |
| expected_projections); |
| } |
| } |
| |
| TEST_F(LaserSegmentUtilsTest, IsFirstPointSmallerAngle) { |
| { |
| // Verify this function works in the case direction is unchanged. |
| const gfx::PointF start_point(0.0f, 0.0f); |
| const gfx::PointF end_point(1.0f, 0.0f); |
| const gfx::PointF positive_angle(1.0f, 1.0f); |
| const gfx::PointF negative_angle(-1.0f, -1.0f); |
| CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| negative_angle); |
| } |
| { |
| // Verify this function works in the case direction is 90 degrees. |
| const gfx::PointF start_point(0.0f, 0.0f); |
| const gfx::PointF end_point(0.0f, 1.0f); |
| const gfx::PointF positive_angle(-1.0f, 0.0f); |
| const gfx::PointF negative_angle(1.0f, 0.0f); |
| CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| negative_angle); |
| } |
| { |
| // Verify this function works in the case direction is 45 degrees. |
| const gfx::PointF start_point(0.0f, 0.0f); |
| const gfx::PointF end_point(1.0f, 1.0f); |
| const gfx::PointF positive_angle(0.0f, 1.0f); |
| const gfx::PointF negative_angle(1.0f, 0.0f); |
| CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| negative_angle); |
| } |
| { |
| // Verify this function works in the case direction is -135 degrees. |
| const gfx::PointF start_point(0.0f, 0.0f); |
| const gfx::PointF end_point(-1.0f, -1.0f); |
| const gfx::PointF positive_angle(0.0f, -1.0f); |
| const gfx::PointF negative_angle(-1.0f, 0.0f); |
| CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| negative_angle); |
| } |
| } |
| |
| } // namespace ash |