sammiequon | 16fd4e9 | 2016-12-07 07:02:41 | [diff] [blame^] | 1 | // Copyright (c) 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 <math.h> |
| 6 | |
| 7 | #include "ash/laser/laser_segment_utils.h" |
| 8 | #include "ash/test/ash_test_base.h" |
| 9 | #include "base/memory/ptr_util.h" |
| 10 | #include "ui/gfx/geometry/point.h" |
| 11 | #include "ui/gfx/geometry/point_f.h" |
| 12 | #include "ui/gfx/geometry/vector2d_f.h" |
| 13 | |
| 14 | namespace ash { |
| 15 | namespace { |
| 16 | // |kEpsilon| is used to check that AngleOfPointInNewCoordinates produces to |
| 17 | // expected angles. This function is only used after points are projected in |
| 18 | // opposite directions along the normal, so they should never be to close to |
| 19 | // each other, checking for equality up to 5 significant figures is more than |
| 20 | // enough. |
| 21 | const float kEpsilon = 0.0001f; |
| 22 | } |
| 23 | |
| 24 | // Helper function to check if a given |point| has the |expected_angle_degree| |
| 25 | // in the coordinate system formed by |origin| and |direction|. |
| 26 | void CheckAngleOfPointInNewCoordinates(const gfx::PointF& origin, |
| 27 | const gfx::Vector2dF& direction, |
| 28 | const gfx::PointF& point, |
| 29 | float expected_angle_degree) { |
| 30 | float result = AngleOfPointInNewCoordinates(origin, direction, point); |
| 31 | EXPECT_NEAR(expected_angle_degree * M_PI / 180.0f, result, kEpsilon); |
| 32 | } |
| 33 | |
| 34 | // Helper function to check if the computed variables match the expected ones. |
| 35 | void CheckNormalLineVariables(const gfx::PointF& start_point, |
| 36 | const gfx::PointF& end_point, |
| 37 | float expected_slope, |
| 38 | float expected_start_intercept, |
| 39 | float expected_end_intercept) { |
| 40 | float calculated_slope; |
| 41 | float calculated_start_y_intercept; |
| 42 | float calculated_end_y_intercept; |
| 43 | |
| 44 | ComputeNormalLineVariables(start_point, end_point, &calculated_slope, |
| 45 | &calculated_start_y_intercept, |
| 46 | &calculated_end_y_intercept); |
| 47 | EXPECT_NEAR(expected_slope, calculated_slope, kEpsilon); |
| 48 | EXPECT_NEAR(expected_start_intercept, calculated_start_y_intercept, kEpsilon); |
| 49 | EXPECT_NEAR(expected_end_intercept, calculated_end_y_intercept, kEpsilon); |
| 50 | } |
| 51 | |
| 52 | // Helper function to check if the a given line segment has an expected |
| 53 | // undefined normal line. |
| 54 | void CheckUndefinedNormalLine(const gfx::PointF& start_point, |
| 55 | const gfx::PointF& end_point) { |
| 56 | float calculated_slope; |
| 57 | float calculated_start_y_intercept; |
| 58 | float calculated_end_y_intercept; |
| 59 | |
| 60 | ComputeNormalLineVariables(start_point, end_point, &calculated_slope, |
| 61 | &calculated_start_y_intercept, |
| 62 | &calculated_end_y_intercept); |
| 63 | EXPECT_TRUE(isnan(calculated_slope)); |
| 64 | EXPECT_TRUE(isnan(calculated_start_y_intercept)); |
| 65 | EXPECT_TRUE(isnan(calculated_end_y_intercept)); |
| 66 | } |
| 67 | |
| 68 | // Helper function to check that the projections from the given line variables |
| 69 | // and |distance| match those expected in |expected_projections|. |
| 70 | void CheckProjectedPoints(const gfx::PointF& start_point, |
| 71 | float slope, |
| 72 | float y_intercept, |
| 73 | float distance, |
| 74 | std::vector<gfx::PointF>& expected_projections) { |
| 75 | // There can only be two projections. |
| 76 | EXPECT_EQ(2u, expected_projections.size()); |
| 77 | |
| 78 | gfx::PointF calculated_first_projection; |
| 79 | gfx::PointF calculated_second_projection; |
| 80 | |
| 81 | ComputeProjectedPoints(start_point, slope, y_intercept, distance, |
| 82 | &calculated_first_projection, |
| 83 | &calculated_second_projection); |
| 84 | |
| 85 | std::vector<gfx::PointF> calculated_projections = { |
| 86 | calculated_first_projection, calculated_second_projection}; |
| 87 | |
| 88 | // Sort the points, so that we do not have to enter the projections in the |
| 89 | // right order. |
| 90 | std::sort(calculated_projections.begin(), calculated_projections.end()); |
| 91 | std::sort(expected_projections.begin(), expected_projections.end()); |
| 92 | |
| 93 | EXPECT_EQ(expected_projections[0], calculated_projections[0]); |
| 94 | EXPECT_EQ(expected_projections[1], calculated_projections[1]); |
| 95 | } |
| 96 | |
| 97 | // Helper function that checks that an IsFirstPointSmallerAngle will return |
| 98 | // false if |larger_angle_point| is the first point argument and return true if |
| 99 | // |larger_angle_point| is the second point argument. |
| 100 | void CheckFirstPointSmaller(const gfx::PointF& start_point, |
| 101 | const gfx::PointF& end_point, |
| 102 | const gfx::PointF& larger_angle_point, |
| 103 | const gfx::PointF& smaller_angle_point) { |
| 104 | EXPECT_FALSE(IsFirstPointSmallerAngle( |
| 105 | start_point, end_point, larger_angle_point, smaller_angle_point)); |
| 106 | EXPECT_TRUE(IsFirstPointSmallerAngle( |
| 107 | start_point, end_point, smaller_angle_point, larger_angle_point)); |
| 108 | } |
| 109 | |
| 110 | using LaserSegmentUtilsTest = testing::Test; |
| 111 | |
| 112 | TEST_F(LaserSegmentUtilsTest, AngleOfPointInNewCoordinates) { |
| 113 | { |
| 114 | // Verify angles remain the same if the origin is at (0, 0) and the |
| 115 | // direction remains the same (1, 0). |
| 116 | const gfx::PointF origin(0.0f, 0.0f); |
| 117 | const gfx::Vector2dF direction(1.0f, 0.0f); |
| 118 | |
| 119 | // The functions range is (-180.0, 180.0). |
| 120 | for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| 121 | float rad = angle * M_PI / 180.0f; |
| 122 | gfx::PointF new_point(cos(rad), sin(rad)); |
| 123 | CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| 124 | } |
| 125 | } |
| 126 | { |
| 127 | // Verify angles are shifted by 45 degrees if the origin is at (0, 0) and |
| 128 | // the direction is (1, 1). |
| 129 | const gfx::PointF origin(0.0f, 0.0f); |
| 130 | const gfx::Vector2dF direction(1.0f, 1.0f); |
| 131 | |
| 132 | // The functions range is (-180.0, 180.0). |
| 133 | for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| 134 | float rad = (angle + 45.0f) * M_PI / 180.0f; |
| 135 | gfx::PointF new_point(cos(rad), sin(rad)); |
| 136 | CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| 137 | } |
| 138 | } |
| 139 | { |
| 140 | // Verify angles remain the same if the points are translated by (1, 1), |
| 141 | // if the origin is at (1, 1) and the direction remains the same (1, 0). |
| 142 | const gfx::PointF origin(1.0f, 1.0f); |
| 143 | const gfx::Vector2dF direction(1.0f, 0.0f); |
| 144 | |
| 145 | // The functions range is (-180.0f, 180.0f). |
| 146 | for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| 147 | float rad = angle * M_PI / 180.0f; |
| 148 | gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y()); |
| 149 | CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| 150 | } |
| 151 | } |
| 152 | { |
| 153 | // Verify angles are shifted by 45 degress if the points are translated by |
| 154 | // (1, 1), if the origin is at (1, 1) and the direction remains the same |
| 155 | // (1, 0). |
| 156 | const gfx::PointF origin(1.0f, 1.0f); |
| 157 | const gfx::Vector2dF direction(1.0f, 1.0f); |
| 158 | |
| 159 | // The functions range is (-180.0, 180.0). |
| 160 | for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) { |
| 161 | float rad = (angle + 45.0f) * M_PI / 180.0f; |
| 162 | gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y()); |
| 163 | CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle); |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | TEST_F(LaserSegmentUtilsTest, ComputeNormalLineVariables) { |
| 169 | { |
| 170 | // Verify a line y=x should have a normal line y=-x+b. At point (0,0), b |
| 171 | // should equal y+x = 0. At point (1,1), b shoudl equal y+x = 2. |
| 172 | const gfx::PointF start(0.0f, 0.0f); |
| 173 | const gfx::PointF end(1.0f, 1.0f); |
| 174 | float slope = -1.0f; |
| 175 | float start_intercept = 0.0f; |
| 176 | float end_intercept = 2.0f; |
| 177 | CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| 178 | } |
| 179 | { |
| 180 | // Verify a line y=-x should have a normal line y=x+b. At point 0.0f), b |
| 181 | // should equal y-x =.0f. At point (1,-1), b should equal y-x = -2. |
| 182 | const gfx::PointF start(0.0f, 0.0f); |
| 183 | const gfx::PointF end(1.0f, -1.0f); |
| 184 | float slope = 1.0f; |
| 185 | float start_intercept = 0.0f; |
| 186 | float end_intercept = -2.0f; |
| 187 | CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| 188 | } |
| 189 | { |
| 190 | // Verify a line x=5 should have a normal line y.0f with intercepts at the |
| 191 | // previous y point. |
| 192 | const gfx::PointF start(5.0f, 0.0f); |
| 193 | const gfx::PointF end(5.0f, 5.0f); |
| 194 | float slope = 0.0f; |
| 195 | float start_intercept = 0.0f; |
| 196 | float end_intercept = 5.0f; |
| 197 | CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept); |
| 198 | } |
| 199 | { |
| 200 | // Verify a line parallel to the x-axis should be undefined. The line values |
| 201 | // should not matter. |
| 202 | const gfx::PointF start(0.0f, 5.0f); |
| 203 | const gfx::PointF end(5.0f, 5.0f); |
| 204 | CheckUndefinedNormalLine(start, end); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | TEST_F(LaserSegmentUtilsTest, ComputeProjectedPoints) { |
| 209 | { |
| 210 | // Verify projecting along y=x from (0, 0) by distance sqrt(2) shoudl result |
| 211 | // in two projections: (1, 1) and (-1, -1). We start from (0, 0) and |
| 212 | // translate by (1, 1) and (-1, -1) (vectors with slope 1) to get to (1, 1) |
| 213 | // and (-1, -1). The length of the distance from (1, 1) is sqrt(1*1 + 1*1) = |
| 214 | // sqrt(2). |
| 215 | const gfx::PointF start(0.0f, 0.0f); |
| 216 | const float slope = 1.0f; |
| 217 | const float y_intercept = 0.0f; |
| 218 | const float distance = sqrt(2.0f); |
| 219 | std::vector<gfx::PointF> expected_projections = {gfx::PointF(1.0f, 1.0f), |
| 220 | gfx::PointF(-1.0f, -1.0f)}; |
| 221 | CheckProjectedPoints(start, slope, y_intercept, distance, |
| 222 | expected_projections); |
| 223 | } |
| 224 | { |
| 225 | // Verify projecting along y=-2x+2 from (2, -2) by distance 2*sqrt(5) should |
| 226 | // result in two projections: (0, 2) and (4, -6). We start from (2, -2) and |
| 227 | // translate by (-2, 4) and (2, -4) (vectors with slope -2) to get (0, 2) |
| 228 | // and (4, -6). The length of the distance from (2, -2) is sqrt(2*2 + 4*4) = |
| 229 | // sqrt(20) = 2*sqrt(5). |
| 230 | const gfx::PointF start(2.0f, -2.0f); |
| 231 | const float slope = -2.0f; |
| 232 | const float y_intercept = 2.0f; |
| 233 | const float distance = 2.0f * sqrt(5.0f); |
| 234 | std::vector<gfx::PointF> expected_projections = {gfx::PointF(0.0f, 2.0f), |
| 235 | gfx::PointF(4.0f, -6.0f)}; |
| 236 | CheckProjectedPoints(start, slope, y_intercept, distance, |
| 237 | expected_projections); |
| 238 | } |
| 239 | { |
| 240 | // Verify projecting along y=5 from (5,5) by distance 2 should |
| 241 | // result in two projections: (5,7) and (5,3). |
| 242 | const gfx::PointF start(5.0f, 5.0f); |
| 243 | const float slope = 0.0f; |
| 244 | const float y_intercept = 5.0f; |
| 245 | const float distance = 2.0f; |
| 246 | std::vector<gfx::PointF> expected_projections = {gfx::PointF(7.0f, 5.0f), |
| 247 | gfx::PointF(3.0f, 5.0f)}; |
| 248 | CheckProjectedPoints(start, slope, y_intercept, distance, |
| 249 | expected_projections); |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | TEST_F(LaserSegmentUtilsTest, IsFirstPointSmallerAngle) { |
| 254 | { |
| 255 | // Verify this function works in the case direction is unchanged. |
| 256 | const gfx::PointF start_point(0.0f, 0.0f); |
| 257 | const gfx::PointF end_point(1.0f, 0.0f); |
| 258 | const gfx::PointF positive_angle(1.0f, 1.0f); |
| 259 | const gfx::PointF negative_angle(-1.0f, -1.0f); |
| 260 | CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| 261 | negative_angle); |
| 262 | } |
| 263 | { |
| 264 | // Verify this function works in the case direction is 90 degrees. |
| 265 | const gfx::PointF start_point(0.0f, 0.0f); |
| 266 | const gfx::PointF end_point(0.0f, 1.0f); |
| 267 | const gfx::PointF positive_angle(-1.0f, 0.0f); |
| 268 | const gfx::PointF negative_angle(1.0f, 0.0f); |
| 269 | CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| 270 | negative_angle); |
| 271 | } |
| 272 | { |
| 273 | // Verify this function works in the case direction is 45 degrees. |
| 274 | const gfx::PointF start_point(0.0f, 0.0f); |
| 275 | const gfx::PointF end_point(1.0f, 1.0f); |
| 276 | const gfx::PointF positive_angle(0.0f, 1.0f); |
| 277 | const gfx::PointF negative_angle(1.0f, 0.0f); |
| 278 | CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| 279 | negative_angle); |
| 280 | } |
| 281 | { |
| 282 | // Verify this function works in the case direction is -135 degrees. |
| 283 | const gfx::PointF start_point(0.0f, 0.0f); |
| 284 | const gfx::PointF end_point(-1.0f, -1.0f); |
| 285 | const gfx::PointF positive_angle(0.0f, -1.0f); |
| 286 | const gfx::PointF negative_angle(-1.0f, 0.0f); |
| 287 | CheckFirstPointSmaller(start_point, end_point, positive_angle, |
| 288 | negative_angle); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | } // namespace ash |