blob: 26ffb1001231b8682043bb9c5730c8e161517c43 [file] [log] [blame]
// 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