blob: 2de17a02e19ff8374bd26922b0a520e552dc07cf [file] [log] [blame]
// Copyright 2013 The Flutter 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 "flutter/impeller/geometry/point.h"
#include "flutter/impeller/geometry/geometry_asserts.h"
#include "gtest/gtest.h"
namespace impeller {
namespace testing {
TEST(PointTest, Length) {
for (int i = 0; i < 21; i++) {
EXPECT_EQ(Point(i, 0).GetLengthSquared(), i * i) << "i: " << i;
EXPECT_EQ(Point(0, i).GetLengthSquared(), i * i) << "i: " << i;
EXPECT_EQ(Point(-i, 0).GetLengthSquared(), i * i) << "i: " << i;
EXPECT_EQ(Point(0, -i).GetLengthSquared(), i * i) << "i: " << i;
EXPECT_EQ(Point(i, 0).GetLength(), i) << "i: " << i;
EXPECT_EQ(Point(0, i).GetLength(), i) << "i: " << i;
EXPECT_EQ(Point(-i, 0).GetLength(), i) << "i: " << i;
EXPECT_EQ(Point(0, -i).GetLength(), i) << "i: " << i;
EXPECT_EQ(Point(i, i).GetLengthSquared(), 2 * i * i) << "i: " << i;
EXPECT_EQ(Point(-i, i).GetLengthSquared(), 2 * i * i) << "i: " << i;
EXPECT_EQ(Point(i, -i).GetLengthSquared(), 2 * i * i) << "i: " << i;
EXPECT_EQ(Point(-i, -i).GetLengthSquared(), 2 * i * i) << "i: " << i;
EXPECT_FLOAT_EQ(Point(i, i).GetLength(), kSqrt2 * i) << "i: " << i;
EXPECT_FLOAT_EQ(Point(-i, i).GetLength(), kSqrt2 * i) << "i: " << i;
EXPECT_FLOAT_EQ(Point(i, -i).GetLength(), kSqrt2 * i) << "i: " << i;
EXPECT_FLOAT_EQ(Point(-i, -i).GetLength(), kSqrt2 * i) << "i: " << i;
}
}
TEST(PointTest, Distance) {
for (int j = 0; j < 21; j++) {
for (int i = 0; i < 21; i++) {
{
Scalar d = i - j;
EXPECT_EQ(Point(i, 0).GetDistanceSquared(Point(j, 0)), d * d)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(0, i).GetDistanceSquared(Point(0, j)), d * d)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(j, 0).GetDistanceSquared(Point(i, 0)), d * d)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(0, j).GetDistanceSquared(Point(0, i)), d * d)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(i, 0).GetDistance(Point(j, 0)), std::abs(d))
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(0, i).GetDistance(Point(0, j)), std::abs(d))
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(j, 0).GetDistance(Point(i, 0)), std::abs(d))
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(0, j).GetDistance(Point(0, i)), std::abs(d))
<< "i: " << i << ", j: " << j;
}
{
Scalar d_squared = i * i + j * j;
EXPECT_EQ(Point(i, 0).GetDistanceSquared(Point(0, j)), d_squared)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(-i, 0).GetDistanceSquared(Point(0, j)), d_squared)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(i, 0).GetDistanceSquared(Point(0, -j)), d_squared)
<< "i: " << i << ", j: " << j;
EXPECT_EQ(Point(-i, 0).GetDistanceSquared(Point(0, -j)), d_squared)
<< "i: " << i << ", j: " << j;
Scalar d = std::sqrt(d_squared);
EXPECT_FLOAT_EQ(Point(i, 0).GetDistance(Point(0, j)), d)
<< "i: " << i << ", j: " << j;
EXPECT_FLOAT_EQ(Point(-i, 0).GetDistance(Point(0, j)), d)
<< "i: " << i << ", j: " << j;
EXPECT_FLOAT_EQ(Point(i, 0).GetDistance(Point(0, -j)), d)
<< "i: " << i << ", j: " << j;
EXPECT_FLOAT_EQ(Point(-i, 0).GetDistance(Point(0, -j)), d)
<< "i: " << i << ", j: " << j;
}
}
}
}
TEST(PointTest, PerpendicularLeft) {
EXPECT_EQ(Point(1, 0).PerpendicularLeft(), Point(0, -1));
EXPECT_EQ(Point(0, 1).PerpendicularLeft(), Point(1, 0));
EXPECT_EQ(Point(-1, 0).PerpendicularLeft(), Point(0, 1));
EXPECT_EQ(Point(0, -1).PerpendicularLeft(), Point(-1, 0));
EXPECT_EQ(Point(1, 1).PerpendicularLeft(), Point(1, -1));
EXPECT_EQ(Point(-1, 1).PerpendicularLeft(), Point(1, 1));
EXPECT_EQ(Point(-1, -1).PerpendicularLeft(), Point(-1, 1));
EXPECT_EQ(Point(1, -1).PerpendicularLeft(), Point(-1, -1));
}
TEST(PointTest, PerpendicularRight) {
EXPECT_EQ(Point(1, 0).PerpendicularRight(), Point(0, 1));
EXPECT_EQ(Point(0, 1).PerpendicularRight(), Point(-1, 0));
EXPECT_EQ(Point(-1, 0).PerpendicularRight(), Point(0, -1));
EXPECT_EQ(Point(0, -1).PerpendicularRight(), Point(1, 0));
EXPECT_EQ(Point(1, 1).PerpendicularRight(), Point(-1, 1));
EXPECT_EQ(Point(-1, 1).PerpendicularRight(), Point(-1, -1));
EXPECT_EQ(Point(-1, -1).PerpendicularRight(), Point(1, -1));
EXPECT_EQ(Point(1, -1).PerpendicularRight(), Point(1, 1));
}
namespace {
typedef std::pair<Scalar, Scalar> PtSegmentDistanceFunc(Point);
void TestPointToSegmentGroup(Point segment0,
Point segment1,
Point p0,
Point delta,
int count,
PtSegmentDistanceFunc calc_distance) {
for (int i = 0; i < count; i++) {
auto [distance, squared] = calc_distance(p0);
EXPECT_FLOAT_EQ(p0.GetDistanceToSegmentSquared(segment0, segment1), squared)
<< p0 << " => [" << segment0 << ", " << segment1 << "]";
EXPECT_FLOAT_EQ(p0.GetDistanceToSegmentSquared(segment1, segment0), squared)
<< p0 << " => [" << segment0 << ", " << segment1 << "]";
EXPECT_FLOAT_EQ(p0.GetDistanceToSegment(segment0, segment1), distance)
<< p0 << " => [" << segment0 << ", " << segment1 << "]";
EXPECT_FLOAT_EQ(p0.GetDistanceToSegment(segment1, segment0), distance)
<< p0 << " => [" << segment0 << ", " << segment1 << "]";
p0 += delta;
}
}
} // namespace
TEST(PointTest, PointToSegment) {
// Horizontal segment and points to the left of it on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({0,10} through {10,10})
{0, 10}, {1, 0}, 11,
// Distance computation
[](Point p) {
Scalar d = 10 - p.x;
return std::make_pair(d, d * d);
});
// Horizontal segment and points on the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({11,10} through {19, 10})
{11, 10}, {1, 0}, 9,
// Distance computation
[](Point p) { //
return std::make_pair(0.0f, 0.0f);
});
// Horizontal segment and points to the right of it on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({20,10} through {30,10})
{20, 10}, {1, 0}, 11,
// Distance computation
[](Point p) {
Scalar d = p.x - 20;
return std::make_pair(d, d * d);
});
// Vertical segment and points above the top of it on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({10,0} through {10,10})
{10, 0}, {0, 1}, 11,
// Distance computation
[](Point p) {
Scalar d = 10 - p.y;
return std::make_pair(d, d * d);
});
// Vertical segment and points on the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({10,11} through {10, 19})
{10, 11}, {0, 1}, 9,
// Distance computation
[](Point p) { //
return std::make_pair(0.0f, 0.0f);
});
// Vertical segment and points below the bottom of it on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({10,20} through {10,30})
{10, 20}, {0, 1}, 11,
// Distance computation
[](Point p) {
Scalar d = p.y - 20;
return std::make_pair(d, d * d);
});
// Horizontal segment and points 5 pixels above and to the left of it
// on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({0,5} through {10,5})
{0, 5}, {1, 0}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (10 - p.x) * (10 - p.x) + 25;
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Horizontal segment and points 5 pixels directly above the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({11,5} through {19, 5})
{11, 5}, {1, 0}, 9,
// Distance computation
[](Point p) { //
return std::make_pair(5.0f, 25.0f);
});
// Horizontal segment and points 5 pixels above and to the right of it
// on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 10},
// Starting point, delta, count ({20,5} through {30,5})
{20, 5}, {1, 0}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (p.x - 20) * (p.x - 20) + 25;
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Vertical segment and points 5 pixels to the left and above the segment
// on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({5,0} through {5,10})
{5, 0}, {0, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = 25 + (10 - p.y) * (10 - p.y);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Vertical segment and points 5 pixels directly to the left of the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({5,11} through {5,19,})
{5, 11}, {0, 1}, 9,
// Distance computation
[](Point p) { //
return std::make_pair(5.0f, 25.0f);
});
// Vertical segment and points 5 pixels to the left and below the segment
// on the same line.
TestPointToSegmentGroup(
// Segment
{10, 10}, {10, 20},
// Starting point, delta, count ({20,5} through {30,5})
{5, 20}, {0, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = 25 + (p.y - 20) * (p.y - 20);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points up and to the right of the top of the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({5,-5} through {15,5})
{5, -5}, {1, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (p.x - 10) * (p.x - 10) + (p.y - 10) * (p.y - 10);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points up and to the right of the segment itself.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({15,5} through {24,14})
{15, 5}, {1, 1}, 9,
// Distance computation
[](Point p) {
Scalar d_sq = 50.0f;
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points up and to the right of the bottom of the
// segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({25,15} through {35,25})
{25, 15}, {1, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (p.x - 20) * (p.x - 20) + (p.y - 20) * (p.y - 20);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points down and to the left of the top of the segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({-5,5} through {5,15})
{-5, 5}, {1, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (p.x - 10) * (p.x - 10) + (p.y - 10) * (p.y - 10);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points down and to the left of the segment itself.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({5,15} through {14,24})
{5, 15}, {1, 1}, 9,
// Distance computation
[](Point p) {
Scalar d_sq = 50.0f;
return std::make_pair(std::sqrt(d_sq), d_sq);
});
// Diagonal segment and points down and to the left of the bottom of the
// segment.
TestPointToSegmentGroup(
// Segment
{10, 10}, {20, 20},
// Starting point, delta, count ({15,25} through {25,35})
{15, 25}, {1, 1}, 11,
// Distance computation
[](Point p) {
Scalar d_sq = (p.x - 20) * (p.x - 20) + (p.y - 20) * (p.y - 20);
return std::make_pair(std::sqrt(d_sq), d_sq);
});
}
TEST(PointTest, CrossProductThreePoints) {
// Colinear
EXPECT_FLOAT_EQ(Point::Cross(Point(-1, 0), Point(0, 0), Point(1, 0)), 0);
EXPECT_FLOAT_EQ(Point::Cross(Point(1, 0), Point(0, 0), Point(-1, 0)), 0);
// Right turn
EXPECT_FLOAT_EQ(Point::Cross(Point(-1, 0), Point(0, 0), Point(0, 1)), 1);
EXPECT_FLOAT_EQ(Point::Cross(Point(-2, 0), Point(0, 0), Point(0, 2)), 4);
// Left turn
EXPECT_FLOAT_EQ(Point::Cross(Point(-1, 0), Point(0, 0), Point(0, -1)), -1);
EXPECT_FLOAT_EQ(Point::Cross(Point(-2, 0), Point(0, 0), Point(0, -2)), -4);
// Convenient values for a less obvious left turn.
// p1 - p0 == (0, 0) - (3, -4) == (-3, 4)
// p2 - p0 == (1, 2) - (3, -4) == (-2, 6)
// product of the magnitude of the 2 legs and the sin of their angle
// (||(-3, 4)||) * (||(-2, 6)||) * sin(angle)
// 5 * sqrt(40) * sin(angle)
// angle = arcsin(4 / 5) - arcsin(6 / sqrt(40)) ~= -18.4349
// sin(angle) ~= -0.316227766
// 5 * sqrt(40) * sin(angle) == -10
// The math is cleaner with the cross product:
// (-3 * 6) - (-2 * 4) == -18 - -8 == -10
EXPECT_FLOAT_EQ(Point::Cross(Point(3, -4), Point(0, 0), Point(1, 2)), -10);
}
} // namespace testing
} // namespace impeller