Skip to content

Commit

Permalink
Add mixed type specializations on algebraic ops in TPoint (#21)
Browse files Browse the repository at this point in the history
Also adds missing RHS operator overloads for TSize.

For any algebraic ops involving TPoint and TSize:
1. `TPoint` takes precedent over `TSize`.
2. Floating point types take precedent over integer types.
3. If there's a tie (for example: `TPoint<int> + TPoint<long long>`),
   the LHS takes precedent.
  • Loading branch information
bdero authored and dnfield committed Apr 27, 2022
1 parent 542b8c4 commit 26090e4
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 8 deletions.
2 changes: 2 additions & 0 deletions impeller/geometry/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ impeller_component("geometry") {
"shear.h",
"size.cc",
"size.h",
"type_traits.cc",
"type_traits.h",
"vector.cc",
"vector.h",
]
Expand Down
157 changes: 157 additions & 0 deletions impeller/geometry/geometry_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) {
ASSERT_EQ(s2.height, 2u);
}

{
Size s1(1.0, 2.0);
Point p1 = static_cast<Point>(s1);
ASSERT_EQ(p1.x, 1u);
ASSERT_EQ(p1.y, 2u);
}

{
Rect r1(1.0, 2.0, 3.0, 4.0);
IRect r2 = static_cast<IRect>(r1);
Expand All @@ -235,6 +242,156 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) {
}
}

TEST(GeometryTest, CanPerformAlgebraicPointOps) {
{
IPoint p1(1, 2);
IPoint p2 = p1 + IPoint(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
IPoint p1(3, 6);
IPoint p2 = p1 - IPoint(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
IPoint p1(1, 2);
IPoint p2 = p1 * IPoint(2, 3);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 6u);
}

{
IPoint p1(2, 6);
IPoint p2 = p1 / IPoint(2, 3);
ASSERT_EQ(p2.x, 1u);
ASSERT_EQ(p2.y, 2u);
}
}

TEST(GeometryTest, PointIntegerCoercesToFloat) {
// Integer on LHS, float on RHS
{
IPoint p1(1, 2);
Point p2 = p1 + Point(1, 2);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 4u);
}

{
IPoint p1(3, 6);
Point p2 = p1 - Point(1, 2);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 4u);
}

{
IPoint p1(1, 2);
Point p2 = p1 * Point(2, 3);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 6u);
}

{
IPoint p1(2, 6);
Point p2 = p1 / Point(2, 3);
ASSERT_FLOAT_EQ(p2.x, 1u);
ASSERT_FLOAT_EQ(p2.y, 2u);
}

// Float on LHS, integer on RHS
{
Point p1(1, 2);
Point p2 = p1 + IPoint(1, 2);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 4u);
}

{
Point p1(3, 6);
Point p2 = p1 - IPoint(1, 2);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 4u);
}

{
Point p1(1, 2);
Point p2 = p1 * IPoint(2, 3);
ASSERT_FLOAT_EQ(p2.x, 2u);
ASSERT_FLOAT_EQ(p2.y, 6u);
}

{
Point p1(2, 6);
Point p2 = p1 / IPoint(2, 3);
ASSERT_FLOAT_EQ(p2.x, 1u);
ASSERT_FLOAT_EQ(p2.y, 2u);
}
}

TEST(GeometryTest, SizeCoercesToPoint) {
// Point on LHS, Size on RHS
{
IPoint p1(1, 2);
IPoint p2 = p1 + ISize(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
IPoint p1(3, 6);
IPoint p2 = p1 - ISize(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
IPoint p1(1, 2);
IPoint p2 = p1 * ISize(2, 3);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 6u);
}

{
IPoint p1(2, 6);
IPoint p2 = p1 / ISize(2, 3);
ASSERT_EQ(p2.x, 1u);
ASSERT_EQ(p2.y, 2u);
}

// Size on LHS, Point on RHS
{
ISize p1(1, 2);
IPoint p2 = p1 + IPoint(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
ISize p1(3, 6);
IPoint p2 = p1 - IPoint(1, 2);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 4u);
}

{
ISize p1(1, 2);
IPoint p2 = p1 * IPoint(2, 3);
ASSERT_EQ(p2.x, 2u);
ASSERT_EQ(p2.y, 6u);
}

{
ISize p1(2, 6);
IPoint p2 = p1 / IPoint(2, 3);
ASSERT_EQ(p2.x, 1u);
ASSERT_EQ(p2.y, 2u);
}
}

TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
{
auto deg = Degrees{90.0};
Expand Down
90 changes: 82 additions & 8 deletions impeller/geometry/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "impeller/geometry/scalar.h"
#include "impeller/geometry/size.h"
#include "impeller/geometry/type_traits.h"

namespace impeller {

Expand All @@ -27,6 +28,11 @@ struct TPoint {
explicit constexpr TPoint(const TPoint<U>& other)
: TPoint(static_cast<Type>(other.x), static_cast<Type>(other.y)) {}

template <class U>
explicit constexpr TPoint(const TSize<U>& other)
: TPoint(static_cast<Type>(other.width),
static_cast<Type>(other.height)) {}

constexpr TPoint(Type x, Type y) : x(x), y(y) {}

static constexpr TPoint<Type> MakeXY(Type x, Type y) { return {x, y}; }
Expand All @@ -45,16 +51,18 @@ struct TPoint {
return {x + p.x, y + p.y};
}

constexpr TPoint operator+(const TSize<Type>& s) const {
return {x + s.width, y + s.height};
template <class U>
constexpr TPoint operator+(const TSize<U>& s) const {
return {x + static_cast<Type>(s.width), y + static_cast<Type>(s.height)};
}

constexpr TPoint operator-(const TPoint& p) const {
return {x - p.x, y - p.y};
}

constexpr TPoint operator-(const TSize<Type>& s) const {
return {x - s.width, y - s.height};
template <class U>
constexpr TPoint operator-(const TSize<U>& s) const {
return {x - static_cast<Type>(s.width), y - static_cast<Type>(s.height)};
}

constexpr TPoint operator*(Scalar scale) const {
Expand All @@ -65,8 +73,9 @@ struct TPoint {
return {x * p.x, y * p.y};
}

constexpr TPoint operator*(const TSize<Type>& s) const {
return {x * s.width, y * s.height};
template <class U>
constexpr TPoint operator*(const TSize<U>& s) const {
return {x * static_cast<Type>(s.width), y * static_cast<Type>(s.height)};
}

constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; }
Expand All @@ -75,8 +84,9 @@ struct TPoint {
return {x / p.x, y / p.y};
}

constexpr TPoint operator/(const TSize<Type>& s) const {
return {x / s.width, y / s.height};
template <class U>
constexpr TPoint operator/(const TSize<U>& s) const {
return {x / static_cast<Type>(s.width), y / static_cast<Type>(s.height)};
}

constexpr Type GetDistanceSquared(const TPoint& p) const {
Expand Down Expand Up @@ -112,6 +122,70 @@ struct TPoint {
constexpr bool IsZero() const { return x == 0 && y == 0; }
};

// Specializations for mixed (float & integer) algebraic operations.

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator+(const TPoint<F>& p1, const TPoint<I>& p2) {
return {p1.x + static_cast<F>(p2.x), p1.y + static_cast<F>(p2.y)};
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator+(const TPoint<I>& p1, const TPoint<F>& p2) {
return p2 + p1;
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator-(const TPoint<F>& p1, const TPoint<I>& p2) {
return {p1.x - static_cast<F>(p2.x), p1.y - static_cast<F>(p2.y)};
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator-(const TPoint<I>& p1, const TPoint<F>& p2) {
return {static_cast<F>(p1.x) - p2.x, static_cast<F>(p1.y) - p2.y};
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator*(const TPoint<F>& p1, const TPoint<I>& p2) {
return {p1.x * static_cast<F>(p2.x), p1.y * static_cast<F>(p2.y)};
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator*(const TPoint<I>& p1, const TPoint<F>& p2) {
return p2 * p1;
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator/(const TPoint<F>& p1, const TPoint<I>& p2) {
return {p1.x / static_cast<F>(p2.x), p1.y / static_cast<F>(p2.y)};
}

template <class F, class I, class = MixedOp<F, I>>
constexpr TPoint<F> operator/(const TPoint<I>& p1, const TPoint<F>& p2) {
return {static_cast<F>(p1.x) / p2.x, static_cast<F>(p1.y) / p2.y};
}

// RHS algebraic operations with TSize.

template <class T, class U>
constexpr TPoint<T> operator+(const TSize<U>& s, const TPoint<T>& p) {
return p + s;
}

template <class T, class U>
constexpr TPoint<T> operator-(const TSize<U>& s, const TPoint<T>& p) {
return {static_cast<T>(s.width) - p.x, static_cast<T>(s.height) - p.y};
}

template <class T, class U>
constexpr TPoint<T> operator*(const TSize<U>& s, const TPoint<T>& p) {
return p * s;
}

template <class T, class U>
constexpr TPoint<T> operator/(const TSize<U>& s, const TPoint<T>& p) {
return {static_cast<T>(s.width) / p.x, static_cast<T>(s.height) / p.y};
}

using Point = TPoint<Scalar>;
using IPoint = TPoint<int64_t>;

Expand Down
11 changes: 11 additions & 0 deletions impeller/geometry/type_traits.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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 "type_traits.h"

namespace impeller {

//

} // namespace impeller
20 changes: 20 additions & 0 deletions impeller/geometry/type_traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.

#pragma once

#include <type_traits>

namespace impeller {

template <class F,
class I,
class = std::enable_if_t<std::is_floating_point_v<F> &&
std::is_integral_v<I>>>
struct MixedOp_ : public std::true_type {};

template <class F, class I>
using MixedOp = typename MixedOp_<F, I>::type;

} // namespace impeller

0 comments on commit 26090e4

Please sign in to comment.